###########################################
#
#  Cave Explorer
#
#  Discover the secrets of a mysterious
#  tunnel system. A fairly elaborate
#  Chip8 adventure game. Best played at
#  about 20 cycles/frame.
#
#  On the overworld, press ASWD to move.
#  In platformer levels, A/D move,
#  E picks up/sets down blocks,
#  Q resets the level.
#
###########################################

: font
0x20 0x20 0x20 0x00 0x20 0xF0 0x10 0x70 0x00 0x40 0xE0 0x90 0xE0 0x90 0x90 0x90 
0xE0 0x90 0xE0 0x90 0xE0 0x80 0x80 0xD0 0xB0 0xB0 0x90 0x90 0x90 0x90 0x60 0x60 
0x90 0x90 0x90 0x60 0x90 0xD0 0xB0 0x90 0x90 0x90 0x50 0x20 0x40 0x00 0x00 0x00 
0x00 0x00 0x60 0x90 0xF0 0x90 0x90 0xF0 0x90 0x90 0xF0 0x80 0xE0 0x80 0x80 0x80 
0x80 0xF0 0x80 0xE0 0x80 0xF0 0x40 0x40 0x40 0xF0 0x20 0x40 0x80 0xF0 0x20 0x20 
0x20 0xC0 0x60 0x90 0x80 0x90 0x60 0x90 0x90 0xB0 0x70 0x80 0xB0 0x90 0x70 0x80 
0x60 0x10 0xE0 0x90 0xA0 0xC0 0xA0 0x90 0x90 0xB0 0xB0 0xD0 0xF0 0x40 0x40 0x40 
0x40 0x90 0x90 0x70 0x10 0xE0

: special-complete
: title1
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0xE0 0x00 0x00 0x00 0x00 0x00 0x00 
0x08 0x18 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x04 0x00 0x00 0x00 0x00 0x00 0x00 
0x20 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x02 0x00 0x00 0x00 0x00 0x00 0x00 
0x28 0x21 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
0x40 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x01 0x00 0x00 0x00 0x00 0x0F 0xC0 
0x4F 0x81 0x00 0x00 0x00 0x00 0x10 0x30 0x46 0x01 0x00 0x00 0x00 0x00 0x20 0x08 
0x20 0x02 0x00 0x00 0x00 0x00 0x30 0xE8 0x20 0x02 0x00 0x00 0x00 0x00 0x4F 0x04 
0x10 0x0C 0x00 0x00 0x00 0x00 0x50 0x84 0x1F 0xF6 0x00 0x00 0x00 0x00 0x40 0x04 
0x77 0x09 0x00 0x00 0x00 0x00 0x4F 0x84 0x97 0x90 0xC0 0x00 0x00 0x00 0x50 0x4C 
0x0E 0xE0 0x20 0x00 0x00 0x00 0x20 0x1E 0x0C 0x00 0x10 0x00 0x00 0x00 0x7F 0xFF

: block-reg-buffer
: phrase1
0x0A 0x41 0x32 0x0C 0x71 0x2D 0x3A 0x1F 0x0A 0x2D 0x2D 0x2D 0x5E 0x52 0x35 0x1F 
0x1F 0x3D 0x05

: phrase2
0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x24 0x1F 0x00

: phrase3
0x35 0x1F 0x67 0x2D 0x32 0x10 0x1F 0x1A 0x6C 0x2D 0x2D 0x2D 0x2D

: level-buffer
: title2
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x70 0x00 0x00 
0x00 0x3E 0x00 0x70 0x3C 0x8C 0x7F 0x00 0x01 0xC1 0x81 0x88 0x42 0x84 0x80 0xE0 
0x02 0x00 0x42 0x04 0x42 0x84 0x80 0x10 0x04 0x00 0x44 0x02 0x21 0x04 0x80 0x10 
0x08 0x00 0x44 0x01 0x21 0x05 0x00 0x10 0x10 0x03 0x88 0x01 0x10 0x05 0x07 0x10 
0x14 0x07 0x08 0x00 0x90 0x45 0x0F 0xD0 0x10 0x0C 0x08 0x60 0x90 0x09 0x06 0x70 
0x20 0x18 0x08 0x70 0x90 0x09 0x01 0xA0 0x20 0x18 0x10 0xB0 0x54 0x19 0x00 0x80 
0x28 0x10 0x10 0x90 0x4C 0x1A 0x07 0x00 0x20 0x10 0xD0 0xF0 0xA8 0x32 0x46 0x60 
0x20 0x0F 0x58 0x00 0x28 0xB2 0x03 0x90 0x29 0x20 0xD1 0x00 0x24 0x33 0x00 0x10 
0x30 0x09 0xD4 0x72 0x17 0x73 0x40 0x10 0x14 0x43 0x98 0xF8 0xB6 0x63 0x02 0x10 
0x1A 0xA7 0x1B 0x9A 0x33 0x61 0xD0 0x30 0x0D 0x1E 0x1F 0x8D 0x63 0xC1 0xFA 0xD0 
0x0F 0xFC 0x0D 0x07 0xE1 0x81 0xFF 0x70 0x04 0x60 0x00 0x02 0xC0 0x00 0x8C 0x20 
0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
0x00 0xFB 0x37 0x98 0x73 0xCF 0xBC 0x00 0x00 0xFB 0xF7 0xD8 0xFB 0xEF 0xBE 0x00 
0x00 0xC1 0xE6 0xD8 0xDB 0x6C 0x36 0x00 0x00 0xF0 0xC7 0xD8 0xDB 0xEF 0x3E 0x00 
0x00 0xC1 0xE7 0x98 0xDB 0xCC 0x3C 0x00 0x00 0xFB 0xF6 0x1E 0xFB 0x6F 0xB6 0x00 
0x00 0xFB 0x36 0x1E 0x73 0x6F 0xB6 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

: bus
0x00 0x04 0x00 0x3D 0x97 0xFF 0xFF 0xFF 0xFF 0xFD 0xFF 0x9D 0x4B 0xFF 0x8B 0x47 
0x20 0x04 0x00 0x5D 0xCB 0xFF 0xD8 0x4F 0x26 0x04 0x00 0x1D 0x05 0xFF 0xDB 0x5F 
0x21 0x04 0x00 0x1D 0x85 0xFF 0xDB 0x47 0x20 0x84 0x7C 0x1C 0x05 0xFF 0xFF 0xFF 
0x20 0x84 0xDA 0x5D 0xC5 0xFF 0x8B 0x47 0x20 0x94 0xBC 0xDD 0xE5 0xFF 0x99 0x5B 
0x20 0xB4 0xE6 0x1D 0xE6 0x1F 0xBA 0x5B 0x20 0x94 0xC2 0xDD 0xCF 0xE7 0x8B 0x47 
0x20 0x85 0x7D 0xDC 0x3F 0xF9 0xFF 0xFF 0x2F 0xF5 0x83 0x9F 0xFF 0xFE 0xFF 0xFF 
0x20 0x05 0xFF 0x1F 0xFF 0xFF 0x7F 0xFF 0xFF 0xFC 0xFE 0x1F 0xFF 0xF3 0xBF 0x07 
0xFF 0xFC 0xFE 0x1F 0xFF 0xED 0xDE 0xFB 0xA2 0x2D 0xF8 0x0C 0x07 0xDD 0xED 0xFD 
0x2A 0xA8 0x07 0xF7 0xF9 0xD9 0xE3 0x7E 0xAA 0xAB 0xFF 0xEC 0x1E 0xD6 0xF3 0xFE 
0xA2 0x25 0xF8 0x00 0x06 0xE7 0x1B 0xFE 0xFF 0xFA 0x07 0xF8 0x03 0x77 0xC0 0x7E 
0x00 0x0B 0xBF 0xFE 0x03 0x7B 0xF8 0xFC 0xFF 0xFB 0x9F 0xFF 0x81 0x7C 0xED 0xF8 
0x00 0x0B 0xBF 0xFF 0x81 0x7F 0x0E 0x00 0xFF 0xFB 0xAF 0xF0 0x61 0x7F 0xEE 0x60 
0xFF 0xFB 0xBA 0x3F 0xF9 0x7F 0xDC 0x98 0xFF 0xFD 0xBD 0xFF 0xFE 0x3F 0xDC 0xF8 
0xFF 0xFD 0xBC 0xFF 0xFF 0xCF 0xDC 0xFC 0x00 0x01 0xBD 0x7F 0xFF 0xF1 0xBC 0xFC 
0xFF 0xFD 0xBD 0xBF 0x00 0x0E 0x3C 0xDC 0x00 0x01 0x1D 0xC8 0xFF 0xFF 0x8F 0xDE 
0x00 0x00 0x1D 0xF3 0xFF 0xFF 0xF3 0xBE 0x00 0x00 0x04 0xFB 0xFF 0xFF 0xFC 0x7C

: mouth 0xF8

: rumbletext # "YOU HEAR    A RUMBLE IN THE DISTANCE"
0x71 0x1F 0x1A 0x2D 0x35 0x41 0x32 0x0A 0x2D 0x2D 0x2D 0x2D 0x32 0x2D 0x0A 0x1A 
0x17 0x10 0x3D 0x41 0x2D 0x45 0x24 0x2D 0x6C 0x35 0x41 0x2D 0x0C 0x45 0x5E 0x6C 
0x32 0x24 0x52 0x41

: uselesstext # "JUST A BUNCHOF GOLD? HOWUSELESS!"
0x4D 0x1A 0x5E 0x6C 0x2D 0x32 0x2D 0x10 0x1A 0x24 0x52 0x35 0x1F 0x3A 0x2D 0x5A 
0x1F 0x3D 0x0C 0x05 0x2D 0x35 0x1F 0x67 0x1A 0x5E 0x41 0x3D 0x41 0x5E 0x5E 0x00 

: exittext # "CLIMBING OUTYOU EMERGE  TO DAZZLING SUNLIGHT"
0x52 0x3D 0x45 0x17 0x10 0x45 0x24 0x5A 0x2D 0x1F 0x1A 0x6C 0x71 0x1F 0x1A 0x2D 
0x41 0x17 0x41 0x0A 0x5A 0x41 0x2D 0x2D 0x6C 0x1F 0x2D 0x0C 0x32 0x49 0x49 0x3D 
0x45 0x24 0x5A 0x2D 0x5E 0x1A 0x24 0x3D 0x45 0x5A 0x35 0x6C

: pyramidtext # "A VISION OF A PYRAMID? ASECRET WALL?"
0x32 0x2D 0x27 0x45 0x5E 0x45 0x1F 0x24 0x2D 0x1F 0x3A 0x2D 0x32 0x2D 0x12 0x71 
0x0A 0x32 0x17 0x45 0x0C 0x05 0x2D 0x32 0x5E 0x41 0x52 0x0A 0x41 0x6C 0x2D 0x67 
0x32 0x3D 0x3D 0x05

: skulltext # "SOMEONE LEFTTHEIR SKULL HERE? GROSS!"
0x5E 0x1F 0x17 0x41 0x1F 0x24 0x41 0x2D 0x3D 0x41 0x3A 0x6C 0x6C 0x35 0x41 0x45 
0x0A 0x2D 0x5E 0x63 0x1A 0x3D 0x3D 0x2D 0x35 0x41 0x0A 0x41 0x05 0x2D 0x5A 0x0A 
0x1F 0x5E 0x5E 0x00 

: puzzlestext # "YOU MUST    RETURN TO   WHERE THIS  ALL BEGAN"
0x71 0x1F 0x1A 0x2D 0x17 0x1A 0x5E 0x6C 0x2D 0x2D 0x2D 0x2D 0x0A 0x41 0x6C 0x1A 
0x0A 0x24 0x2D 0x6C 0x1F 0x2D 0x2D 0x2D 0x67 0x35 0x41 0x0A 0x41 0x2D 0x6C 0x35 
0x45 0x5E 0x2D 0x2D 0x32 0x3D 0x3D 0x2D 0x10 0x41 0x5A 0x32 0x24

: deadendtext # "A DEAD END? NUTS!"
0x32 0x2D 0x0C 0x41 0x32 0x0C 0x2D 0x41 0x24 0x0C 0x05 0x2D 0x24 0x1A 0x6C 0x5E 
0x00

: mazetext # "SNWEW"
0x5E 0x24 0x67 0x41 0x67

: draw-bitmap
	clear
	v0 := 0 # x
	v1 := 0 # y
	v2 := 0 # byte
	v3 := 1 # constant
	loop
		sprite v0 v1 1
		i  += v3
		v2 += 1
		v0 += 8
		if v0 == 64 then v1 += 1
		if v0 == 64 then v0 := 0
		if v1 != 32 then
	again
;

: draw-text
	v1 := 2 # x
	v2 := 1 # y
	v3 := 0 # byte
	# v4 contains length
	loop
		: text-addr i := 0 # self-modify to alter
		i += v3
		load v0
		i := font
		i += v0
		sprite v1 v2 5
		v1 += 5
		if v1 == 62 then v2 += 6
		if v1 == 62 then v1 := 2
		v3 += 1
		if v3 != v4 then
	again
;

: wait
	v0 := 128
	delay := v0
	loop
		v0 := delay
		if v0 != 0 then
	again
;

: swapmouth
	i  := mouth
	v0 := 4
	v1 := 22
	sprite v0 v1 1
	v0 := 52
	v1 := 29
	sprite v0 v1 1
;

: draw-phrase-1
	:unpack 0xA phrase1
	i  := text-addr
	save v1
	v4 := 19
	draw-text
;

: draw-phrase-2
	:unpack 0xA phrase2
	i  := text-addr
	save v1
	v4 := 12
	draw-text
;

: draw-phrase-3
	:unpack 0xA phrase3
	i  := text-addr
	save v1
	v4 := 13
	draw-text
;
	
###########################################
#
#  Block pushing puzzle
#
###########################################

: solid   0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: empty   0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
: floor   0x00 0xFE 0x55 0x22 0x88 0x00 0x00 0x00
: roof    0x00 0x00 0x00 0x11 0x91 0x8B 0x8F 0xDF
: exit    0xC3 0xBD 0xBD 0xBD 0xB9 0xB9 0xBD 0xBD
: block   0x81 0x7E 0x5A 0x7E 0x7E 0x5A 0x7E 0x81

: left    0xE7 0xE7 0xC3 0x81 0x82 0x42 0xDB 0x93
: right   0xE7 0xE7 0xC3 0x81 0x41 0x42 0xDB 0xC9

:const  SOLID    0
:const  EMPTY    1
:const  FLOOR    2
:const  ROOF     3
:const  EXIT     4
:const  BLOCK    5
:const  PLAYER_L 6
:const  PLAYER_R 7

:alias  levelno      ve
:alias  playerx      vd
:alias  playery      vc
:alias  input        vb
:alias  direction    va
:alias  win          v9

# v0-v8 reserved for temporary use.
# v0-v7 are used together for fast buffer copies.

# player facing
:const FACING_LEFT  0
:const FACING_RIGHT 1

# input keys:
:const MOVE_LT 7
:const MOVE_RT 9
:const RESET   4
:const ACTION  6

###########################################
#
#  Level data
#
###########################################

: level-0
	4 1 1 3 1 1 3 0
	2 1 1 1 1 1 1 3
	0 2 1 1 1 1 1 1
	0 0 2 2 2 2 1 6
: level-1
	4 1 3 1 3 1 1 1
	2 1 1 1 1 1 1 5
	0 1 1 6 1 1 2 2
	0 1 2 2 1 5 0 0
: level-2
	3 1 1 1 1 1 1 4
	1 1 1 2 2 2 2 2
	5 1 1 1 1 1 1 1
	2 5 1 1 6 1 1 5
: level-3
	1 1 1 1 3 1 1 3
	4 1 6 1 1 1 1 5
	2 1 2 1 1 1 2 2
	0 1 0 5 1 1 0 0
: level-4
	5 1 3 0 3 1 1 4
	1 1 1 3 1 1 1 2
	1 1 1 1 1 1 1 0
	2 2 1 6 1 5 2 0
: level-5
	4 1 1 1 1 1 1 1
	2 1 1 6 1 5 1 5
	0 1 1 2 1 1 1 1
	0 1 1 0 1 5 1 5
: level-6
	0 3 1 5 1 1 1 4
	3 1 1 1 1 1 1 2
	1 1 1 5 2 1 1 0
	5 5 1 6 0 1 1 0
: level-7
	3 3 1 4 1 5 3 3
	5 5 1 1 1 1 5 5
	1 1 1 1 1 1 1 1
	2 1 1 6 1 1 1 5

###########################################
#
#  Level utilities
#
###########################################

: level-table
	i := level-0 return
	i := level-1 return
	i := level-2 return
	i := level-3 return
	i := level-4 return
	i := level-5 return
	i := level-6 return
	i := level-7 return

: get-level
	v0 <<= levelno
	v0 <<= v0
	jump0 level-table

: copy-level
	v8 := 0
	loop
		get-level
		i += v8
		load v7

		i := level-buffer
		i += v8
		save v7

		v8 += 8
		if v8 != 32 then
	again
;

: get-tile
	# takes tile index in v0
	v0 <<= v0
	v0 <<= v0
	v0 <<= v0
	i := solid
	i += v0
;

: spawn-player
	direction := FACING_LEFT
	win       := 0
	playerx := v1
	playerx >>= playerx
	playerx >>= playerx
	playerx >>= playerx
	playery := v2
	playery >>= playery
	playery >>= playery
	playery >>= playery
;

: init-level
	copy-level
	clear
	v1 := 0 # sprite x
	v2 := 0 # sprite y
	v3 := 0 # tile index
	loop
		i := level-buffer
		i += v3
		load v0
		if v0 == PLAYER_L then spawn-player
		get-tile
		sprite v1 v2 8
		v3 += 1
		v1 += 8
		if v1 == 64 then v2 += 8
		if v1 == 64 then v1 := 0
		if v2 != 32 then
	again
;

: index-at
	# takes x/y in v1/v2
	# returns type in v0
	v0 <<= v2
	v0 <<= v0
	v0 <<= v0
	v0 +=  v1
	i := level-buffer
	i += v0
;

: draw-at
	# takes x/y in v1/v2
	# trashes v0-v2
	index-at
	load v0
	get-tile
	v1 <<= v1
	v1 <<= v1
	v1 <<= v1
	v2 <<= v2
	v2 <<= v2
	v2 <<= v2
	sprite v1 v2 8
;

: copy-loc
	v1 := v3
	v2 := v4
;

: set-at
	# takes x/y in v3/v4, value in v5
	copy-loc
	draw-at
	copy-loc
	index-at
	v0 := v5
	save v0
	copy-loc
	draw-at
;

###########################################
#
#  Block pusher player helpers
#
###########################################

:alias canpass  v7
:alias deltadir v6

: player-loc
	v1 := playerx
	v2 := playery
;

: player-copy
	v3 := playerx
	v4 := playery
;

: player-ahead
	player-loc
	v1 += deltadir
;

: player-above
	player-loc
	v2 += -1
;

: player-below
	player-loc
	v2 += 1
;

: draw-player
	player-loc
	draw-at
;

: player-above-type
	player-above
	index-at
	load v0
;

: carrying?
	# returns result in v0
	v0 := 0
	if playery == 0 then return
	player-above-type
	if v0 != BLOCK then v0 := 0
	if v0 == BLOCK then v0 := 1
;

###########################################
#
#  Block pusher movement
#
###########################################

: passable?
	# takes x/y in v1/v2
	# returns flag in canpass
	canpass := 0
	if v1 > 7 then return
	if v2 > 3 then return
	index-at
	load v0
	if v0 == EMPTY then canpass := 1
	if v0 == EXIT  then canpass := 1
;

: fall-scan
	player-copy
	v3 += deltadir
	loop
		copy-loc
		v2 += 1
		passable?
		while canpass == 1
		v4 += 1
	again
;

: move-player
	# if we're stepping onto
	# an exit, we win.
	copy-loc
	index-at
	load v0
	if v0 == EXIT then win := 1
	
	# takes x/y in v3/v4
	draw-player
	player-loc
	index-at
	v0 := EMPTY
	save v0
	draw-player
	
	playerx := v3
	playery := v4
	
	v5 := PLAYER_L
	v5 += direction
	set-at
;

: hide-block
	v5 := EMPTY

: set-block
	player-copy
	v4 += -1
	set-at
;

: show-block
	v5 := BLOCK
	jump set-block

: climb-move
	player-copy
	v3 += deltadir
	v4 += -1
	move-player	
;

: climb-carry
	# can't climb if blocked above-side
	player-above
	v1 += deltadir
	passable?
	if canpass != 1 then return

	# can't climb if blocked above-above-side
	player-above
	v1 += deltadir
	v2 += -1
	passable?
	if canpass != 1 then return

	hide-block
	climb-move
	show-block
;

: climb
	# can't climb if blocked above:
	player-above
	passable?
	if canpass != 1 then return
	
	# can't climb if blocked above-side
	player-above
	v1 += deltadir
	passable?
	if canpass != 1 then return
	
	climb-move
;

: carry
	# make sure we don't need to climb:
	player-loc
	v1 += deltadir
	passable?
	if canpass != 1 then jump climb-carry

	# make sure there's horizontal space for the block
	player-loc
	v1 += deltadir
	v2 += -1
	passable?
	if canpass != 1 then return
	
	hide-block

	# scan down and 'fall':
	fall-scan
	move-player

	show-block
;

: walk
	# make sure we aren't carrying a block:
	carrying?
	if v0 != 0 then jump carry
	
	# make sure we don't need to climb:
	player-loc
	v1 += deltadir
	passable?
	if canpass != 1 then jump climb
	
	# scan down and 'fall':
	fall-scan
	move-player
;

: walk-left
	deltadir := -1
	if direction == FACING_LEFT then jump walk

	# face left
	player-copy
	v5 := PLAYER_L
	set-at
	direction := FACING_LEFT
;

: walk-right
	deltadir := 1
	if direction == FACING_RIGHT then jump walk
	
	# face right
	player-copy
	v5 := PLAYER_R
	set-at
	direction := FACING_RIGHT
;

: drop
	# there must be at least one empty space
	player-ahead
	passable?
	if canpass != 1 then return

	# erase block
	player-copy
	v4 += -1
	v5 := EMPTY
	set-at

	# scan for drop position
	fall-scan
	
	# draw repositioned block
	v5 := BLOCK
	set-at
;

: action
	deltadir := 1
	if direction == FACING_LEFT then deltadir := -1
	carrying?
	if v0 != 0 then jump drop
	
	# we can only pick up block tiles
	player-ahead
	index-at
	load v0
	if v0 != BLOCK then return

	# the space above us must be empty
	player-above-type
	if v0 != EMPTY then return

	# erase block
	player-copy
	v3 += deltadir
	v5 := EMPTY
	set-at

	# draw repositioned block
	player-copy
	v4 += -1
	v5 := BLOCK
	set-at
;

###########################################
#
#  Block pusher main routine
#
###########################################

: puzzle-finished-count 0

: block-puzzle
	i := block-reg-buffer
	save vd

	init-level
	loop
		input := key
		if input == RESET   then init-level
		if input == MOVE_LT then walk-left
		if input == MOVE_RT then walk-right
		if input == ACTION  then action
		if win == 0 then
	again

	v0 := 32
	buzzer := v0
	clear

	i := puzzle-finished-count
	load v0
	v0 += 1
	i := puzzle-finished-count
	save v0

	i := block-reg-buffer
	load vd
;

###########################################
#
#  Overworld
#
###########################################

: rocks1 0x00 0x40 0x00 0x00
: rocks2 0x00 0x20 0x40 0x00
: rocks3 0x00 0x40 0x20 0x00
: rocks4 0x00 0x00 0x00 0x00

: path   0x60 0xF0 0xF0 0x60
: man    0x00 0x60 0x60 0x00
: target 0x70 0x40 0xF0 0x50

: bits   1 2 4 8 16 32 64 128

:alias boardno     vd
:alias manx        vc
:alias many        vb
:alias specialx    va
:alias specialy    v9
:alias oldx        v8
:alias oldy        v7
:alias specialflag v6

###########################################
#
#  Board data
#
###########################################

: special-pos
	 3 6 # board 0
	 7 4 # board 1
	14 6 # board 2
	 7 4 # board 3
	 3 3 # board 4
	12 4 # board 5
	 7 5 # board 6
	 7 4 # board 7
	 7 6 # board 8
	 9 4 # board 9
	10 4 # board A
	11 3 # board B
	 5 3 # board C
	 5 3 # board D
	 5 3 # board E
	 5 3 # board F

# board in each direction
#     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
: board-n
	0x0 0x2 0x1 0x5 0xA 0x6 0x7 0x7 0x8 0x9 0x3 0xC 0xC 0xC 0xC 0xE 
: board-e
	0x3 0x4 0x2 0x1 0x9 0x8 0x6 0x7 0xA 0x9 0xA 0xB 0xD 0xE 0xF 0xD 
: board-s
	0x0 0x2 0x1 0xA 0xD 0x3 0x5 0x6 0x8 0x9 0x4 0xB 0xB 0xB 0xB 0xB 
: board-w
	0x0 0x3 0x2 0x0 0x1 0x5 0x6 0x7 0x5 0x4 0x8 0xB 0xD 0xD 0x3 0xF 

# pixel data
: board0
	0x28 0xEB 0x0A 0x7A 0x02 0xEF 0x28 0x2A 
	0x6A 0x4A 0x5A 0x42 0x7A 0x0A 0x6E 0x28 
: board1
	0x08 0x08 0x4A 0x08 0xFF 0x40 0x5E 0x52 
	0x42 0x7E 0x40 0xCF 0x08 0x4A 0x08 0x08
: board2
	0x00 0x70 0x50 0x50 0xD7 0x54 0x54 0x54 
	0x54 0x54 0x54 0xD5 0x14 0x14 0x7C 0x00
: board3
	0x28 0x28 0x28 0xEB 0x0A 0x6A 0x0A 0x3A 
	0x22 0x3A 0x2A 0x2E 0x20 0x38 0x08 0x28 
: board4
	0x08 0x08 0x1C 0x7F 0x5C 0xC9 0x08 0x1C 
	: gate1 0x60 0x1C 0x08 0x08 0x1C : gate2 0x60 0x1C 0x08 
: board5
	0x10 0x00 0x03 0xF3 0x10 0x10 0x10 0xDF 
	0xD0 0x16 0x16 0x10 0x10 0x10 0x10 0x10 
: board6
	0x00 0x02 0x4A 0x66 0x70 0x78 0x7C 0xFF 
	0x7C 0x78 0x70 0x66 0x4A 0x02 0x00 0x00
: board7
	0x00 0x76 0x52 0x5A 0x04 0x00 0x39 0xFB 
	0x39 0x00 0x04 0x5A 0x52 0x76 0x00 0x00 
: board8
	0x10 0x7E 0x08 0x7E 0x10 0x7E 0x08 0x7E 
	0x10 0x7E 0x08 0x7E 0x10 0x7E 0x10 0x10
: board9
	0x08 0x08 0xD8 0x52 0xD0 0x10 0x38 0x2B 
	0x3A 0x12 0x02 0xC2 0x4E 0x78 0x10 0x18
: boardA
	0x10 0x54 0x10 0xFF 0x04 0x7C 0x40 0x7C 
	0x04 0x1C 0x10 0x00 0x28 0x6C 0x74 0x38 
: boardB
	0x00 0xF2 0xEA 0x02 0x02 0xC3 0x0A 0xFA 
	0x02 0xFA 0xA2 0x8A 0x22 0xF2 0x9A 0x38 
: boardC
	0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E 
	0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 
: boardD
	0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E 
	0x08 0x08 0x08 0x08 0x08 0x08 0x0A 0x08
: boardE
	0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E 
	0x08 0x08 0x08 0x08 0x0A 0x08 0x0A 0x08
: boardF
	0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E 
	0x08 0x08 0x0A 0x08 0x0A 0x08 0x0A 0x08

###########################################
#
#  Overworld game logic
#
###########################################

: secret-wall
	if boardno != 6 then return
	if v4      != 7 then return
	v5 := 0b11111100
;

: draw-board
	clear
	v1 := 0        # x tile
	v4 := 0        # data offset
	v3 <<= boardno # data base
	v3 <<= v3
	v3 <<= v3
	v3 <<= v3
	loop
		i := board0
		i += v3
		i += v4
		load v0
		v5 := v0 # column
		v2 := 0  # y tile
		secret-wall

		loop
			i := rocks1
			v0 := random 0b1100
			i += v0
			v5 >>= v5
			if vf != 0 then i := path
			sprite v1 v2 4
			v2 += 4
			if v2 != 32 then
		again

		v1 += 4
		v4 += 1
		if v4 != 16 then
	again

	specialx := -1
	specialy := -1
	i := special-complete
	i += boardno
	load v0
	if v0 != 0 then return

	v0 <<= boardno
	i := special-pos
	i += v0
	load v1
	specialx := v0
	specialy := v1
	
	v0 <<= v0
	v0 <<= v0
	v1 <<= v1
	v1 <<= v1
	i := path
	sprite v0 v1 4
	i := target
	sprite v0 v1 4
;

: draw-man
	v0 <<= manx
	v0 <<= v0
	v1 <<= many
	v1 <<= v1
	i := man
	sprite v0 v1 4
;

: copy-pos
	oldx := manx
	oldy := many
;

: try-special
	if manx != specialx then return
	if many != specialy then return
	specialflag := 1
;

: try-move
	# fetch column data
	i := board0
	v0 <<= boardno
	v0 <<= v0
	v0 <<= v0
	v0 <<= v0
	i += v0

	v3 := 15
	v2 := manx
	v2 &= v3
	i += v2
	load v0
	v1 := v0
	
	# fetch row mask
	i := bits
	v3 := 7
	v2 := many
	v2 &= v3
	i += v2
	load v0

	v0 &= v1
	vf := 1
	if v0 != 0 then try-special
	if v0 != 0 then return

	# roll back
	manx := oldx
	many := oldy
	vf   := 0
;

: change-board
	i += boardno
	load v0
	if v0 == boardno then return
	boardno := v0
	draw-board
;

: overworld-walk-w
	manx += -1
	try-move
	if vf == 0 then return
	if manx != -1 then return
	i := board-w
	manx := 15
	jump change-board

: overworld-walk-e
	manx += 1
	try-move
	if vf == 0 then return
	if manx != 16 then return
	manx := 0
	i := board-e
	jump change-board

: overworld-walk-n
	many += -1
	try-move
	if vf == 0 then return
	if many != -1 then return
	many := 7
	i := board-n
	jump change-board

: overworld-walk-s
	many += 1
	try-move
	if vf == 0 then return
	if many != 8 then return
	many := 0
	i := board-s
	jump change-board

: overworld-walk
	if v2 == 7 then jump overworld-walk-w
	if v2 == 9 then jump overworld-walk-e
	if v2 == 5 then jump overworld-walk-n
	if v2 == 8 then jump overworld-walk-s
;

###########################################
#
#  Special scripted events
#
###########################################

: rumble
	v0 := 0b00001100
	save v0

	clear
	:unpack 0xA rumbletext
	i  := text-addr
	save v1
	v4 := 36
	draw-text
	v0 := key
;

: open-gate-1
	i := gate1
	rumble
;

: open-gate-2
	i := gate2
	rumble
;

: useless-gold
	clear
	:unpack 0xA uselesstext
	i  := text-addr
	save v1
	v4 := 32
	draw-text
	v0 := key
;

: pyramid-hint
	clear
	:unpack 0xA pyramidtext
	i  := text-addr
	save v1
	v4 := 36
	draw-text
	v0 := key
;

: gross-skull
	clear
	:unpack 0xA skulltext
	i  := text-addr
	save v1
	v4 := 36
	draw-text
	v0 := key
;

: dead-end
	clear
	:unpack 0xA deadendtext
	i  := text-addr
	save v1
	v4 := 17
	draw-text
	v0 := key
;

: maze-hint
	clear
	:unpack 0xA mazetext
	i  := text-addr
	save v1
	v4 := 5
	draw-text
	v0 := key
;

: nop ;

: puzzle-0  ve := 0  block-puzzle ;
: puzzle-1  ve := 1  block-puzzle ;
: puzzle-2  ve := 2  block-puzzle ;
: puzzle-3  ve := 3  block-puzzle ;
: puzzle-4  ve := 4  block-puzzle ;
: puzzle-5  ve := 5  block-puzzle ;
: puzzle-6  ve := 6  block-puzzle ;

: puzzle-7
	ve := 7
	block-puzzle

	clear
	:unpack 0xA exittext
	i  := text-addr
	save v1
	v4 := 44
	draw-text
	v0 := key

	i := bus
	draw-bitmap
	loop again

: special-things
	jump puzzle-7     # 0
	jump useless-gold # 1
	jump puzzle-1     # 2
	jump puzzle-0     # 3
	jump open-gate-2  # 4
	jump gross-skull  # 5
	jump puzzle-2     # 6
	jump open-gate-1  # 7
	jump puzzle-3     # 8
	jump puzzle-6     # 9
	jump dead-end     # A
	return            # B
	jump pyramid-hint # C
	jump puzzle-4     # D
	jump maze-hint    # E
	jump puzzle-5     # F

: check-puzzles
	i := puzzle-finished-count
	load v0
	if v0 != 7 then return
	v0 += 1
	i := puzzle-finished-count
	save v0
	
	clear
	:unpack 0xA puzzlestext
	i  := text-addr
	save v1
	v4 := 45
	draw-text
	v0 := key

	i := board-e
	v0 := 0
	save v0
;

###########################################
#
#  Overworld main routine
#
###########################################

: do-special
	i := special-complete
	i += boardno
	v0 := 1
	save v0

	v0 <<= boardno
	jump0 special-things

: main
	# intro sequence
	i := title1
	draw-bitmap
	draw-phrase-1
	wait
	draw-phrase-1
	swapmouth
	draw-phrase-2
	wait
	draw-phrase-2
	swapmouth
	draw-phrase-3
	wait
	draw-phrase-3
	i := title2
	draw-bitmap
	v0 := key

	# main routine
	manx := 10
	many := 4
	draw-board
	loop
		draw-man
		v2 := key
		draw-man
		copy-pos
		specialflag := 0
		overworld-walk
		if specialflag != 0 then do-special
		check-puzzles
		if specialflag != 0 then draw-board
	again
