You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

15278 lines
267 KiB
HTML

<!DOCTYPE HTML>
<html>
<!-- HEADER -->
<head>
<meta charset="UTF-8">
<title>Welcome to this Imaginary Library of Ghost Files!{pg}A ghost file is a file that cannot be deleted by normal means.{pg}Like a thought that sticks around ...{pg}... A remnant of the past ...{pg}... A surviving trace ...{pg}... A souvenir of a process ...{pg}... An echo of your mind.</title>
<script type="text/bitsyGameData" id="exportedGameData">
Welcome to this Imaginary Library of Ghost Files!{pg}A ghost file is a file that cannot be deleted by normal means.{pg}Like a thought that sticks around ...{pg}... A remnant of the past ...{pg}... A surviving trace ...{pg}... A souvenir of a process ...{pg}... An echo of your mind.
# BITSY VERSION 7.12
! ROOM_FORMAT 1
PAL 0
NAME black
0,0,0
57,255,20
57,255,20
ROOM 5
e,e,e,e,e,e,e,e,e,e,e,0,0,0,0,0
e,e,e,e,e,e,e,e,e,e,e,0,0,0,0,0
e,e,e,e,e,e,e,e,e,e,a,0,0,0,0,0
e,e,e,e,e,e,e,e,e,a,a,a,0,0,0,0
e,e,e,e,e,e,e,e,a,a,a,a,a,a,0,0
e,e,e,e,b,b,b,b,a,a,a,a,a,a,0,0
e,e,e,b,b,b,b,b,a,a,a,a,a,a,0,0
e,e,b,b,b,b,b,0,a,a,a,a,a,0,0,d
e,b,b,b,b,b,0,0,0,0,0,a,a,a,0,f
b,b,0,0,0,b,0,0,0,0,0,0,a,0,0,0
b,b,0,0,b,0,0,0,0,0,0,0,0,0,0,0
b,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,b,b,0,0,a,a,0,0,0,0,0,0,0,0
b,b,b,0,0,0,a,a,a,a,0,0,0,b,b,b
b,b,0,0,0,0,a,a,a,0,0,0,0,0,b,b
b,b,0,0,0,0,0,a,a,0,i,0,0,0,b,0
NAME test
ITM 0 4,9
ITM 0 7,7
ITM 1 8,12
ITM 1 6,15
PAL 0
ROOM 10
81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,0,80
80,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,0,0,0,80
80,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,80
80,0,0,0,0,86,87,8a,0,8e,8f,8i,8l,0,0,80
80,0,y,10,0,83,84,88,0,8c,8g,8j,8m,0,0,80
80,0,0,0,0,85,0,89,8b,8d,8h,8k,0,0,0,80
80,0,0,0,0,46,0,0,0,0,0,0,0,46,0,80
81,81,81,81,81,a2,81,81,81,81,81,81,81,a2,81,80
80,80,80,80,80,a1,80,80,80,80,80,80,80,a1,80,80
80,0,0,0,0,0,0,0,0,0,0,80,80,a1,80,80
80,0,x,y,z,0,18,10,t,0,0,80,80,a1,80,80
80,0,0,0,0,0,0,0,0,0,0,80,80,0,0,0
80,0,0,0,u,y,1b,t,10,0,0,80,80,81,81,81
80,0,0,0,0,0,0,0,0,0,0,80,80,80,80,80
ITM 5 13,6
PAL 0
ROOM 11
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,86,87,8a,0,8e,8f,8i,8l,0,0,0,0,0
0,0,0,83,84,88,0,8c,8g,8j,8m,0,0,0,0,0
0,0,0,85,0,89,8b,8d,8h,8k,0,0,y,10,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,8p,8q,8e,8f,8t,8w,8z,94,0,0,0,0,0
0,0,0,8n,8s,8c,8g,8u,8x,90,93,0,0,0,0,0
0,0,0,8o,8r,8d,8h,8v,8y,91,92,0,0,0,0,0
0,0,0,0,0,0,y,10,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,8a,0,8z,94,96,98,9b,9e,9j,7c,7e,8z,94,0,0
0,88,0,90,93,95,99,9c,9f,9i,7a,7d,90,93,0,0
0,89,8b,91,92,97,9a,9d,9g,9h,7b,7f,91,92,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
PAL 0
ROOM 12
80,80,0,1b,1k,t,1a,0,x,y,z,0,1m,y,0,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,0,s,y,1l,t,w,1k,1d,1a,19,0,1c,10,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,0,y,1m,z,v,w,1d,30,t,0,1d,1a,0,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,0,x,y,z,10,0,11,10,t,t,0,w,1d,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,0,1l,t,0,1d,s,0,1d,w,0,s,w,1d,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,0,u,u,0,u,t,1d,s,z,10,t,1n,0,0
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81
80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80
80,0,0,0,0,80,80,0,0,0,0,80,80,0,0,80
80,0,0,0,0,80,80,0,0,0,0,80,80,81,81,80
PAL 0
ROOM 13
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,8a,0,8e,8f,9l,9n,74,79,9e,9j,7c,7e,0,0,0
0,88,0,8c,8g,9k,9o,75,78,9f,9i,7a,7d,0,0,0
0,89,8b,8d,8h,9m,9p,76,77,9g,9h,7b,7f,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,98,9b,8z,94,8a,0,9q,9t,0,0,0,0,0,0,0
0,99,9c,90,93,88,0,9r,9u,a3,0,0,0,0,0,0
0,9a,9d,91,92,89,8b,9s,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,9v,9w,8e,8f,7c,7e,8z,94,0,0,0,0
0,0,0,0,9x,9z,8c,8g,7a,7d,90,93,0,0,0,0
0,0,0,0,9y,a0,8d,8h,7b,7f,91,92,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
PAL 0
ROOM 14
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,98,9b,8z,94,8a,0,9q,9t,0,0,0,0,0
0,0,0,99,9c,90,93,88,0,9r,9u,a3,0,0,0,0
0,0,0,9a,9d,91,92,89,8b,9s,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,0,0
0,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,0,0,0,0
0,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
PAL 0
ROOM 15
81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81
80,0,0,0,0,0,0,0,0,0,0,0,0,0,80,80
80,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,80,80
80,7r,7q,7n,75,78,7a,7d,7g,7l,0,y,10,0,0,80
80,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,80
80,0,0,0,0,86,87,8a,0,8e,8f,8i,8l,0,0,80
80,81,0,0,0,83,84,88,0,8c,8g,8j,8m,0,0,80
80,80,0,0,0,85,0,89,8b,8d,8h,8k,0,1n,0,80
80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,80
80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80
80,80,80,80,80,80,80,80,80,80,80,80,0,0,0,80
80,0,0,0,0,0,0,0,0,0,80,80,0,0,0,80
80,0,18,0,19,z,1d,1m,t,0,80,80,0,46,0,80
80,0,0,0,0,0,0,0,0,0,80,80,81,a2,81,80
80,81,81,81,81,81,81,81,81,81,80,80,80,a1,80,80
NAME main work/play
EXT 13,15 16 13,0
PAL 0
ROOM 16
81,81,81,81,81,81,81,81,81,81,81,81,81,0,81,81
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,8a,0,8e,8f,9l,9n,74,79,9e,9j,7c,7e,0,80
80,0,88,0,8c,8g,9k,9o,75,78,9f,9i,7a,7d,0,80
80,0,89,8b,8d,8h,9m,9p,76,77,9g,9h,7b,7f,0,80
80,0,0,0,7s,0,7m,74,79,7c,7e,7i,7j,0,0,80
80,0,y,10,7r,7q,7n,75,78,7a,7d,7g,7l,0,81,80
80,0,0,0,0,7p,7o,76,77,7b,7f,7h,7k,0,80,80
80,0,0,46,0,0,0,0,0,0,0,0,0,0,80,80
80,81,81,a2,81,81,81,81,81,81,81,81,81,81,80,80
80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80,80
80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80,80
80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,18,31,y,z,10,3t,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 1 labour
EXT 14,15 17 14,0
PAL 0
ROOM 17
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,18,31,y,z,10,0,v,10,t,18,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,s,0,10,t,1c,u,18,v,t,18,31,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,t,0,v,y,1a,s,z,1l,18,31,u,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,s,0,1d,1a,0,18,1a,0,t,1a,1m,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,t,s,s,0,v,x,v,u,t,0,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,11,0,1a,t,v,t,s,s,1d,w,x,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,18,w,0,31,t,1a,t,11,1d,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 2 labour
EXT 14,15 18 14,0
PAL 0
ROOM 18
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,0,w,1k,t,0,1c,10,y,11,1d,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1l,18,10,19,1d,1a,s,0,y,11,0,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,v,y,y,1c,t,10,18,w,1d,y,1a,6i,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,1a,0,w,1k,t,0,1b,y,10,u,1m,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,11,0,u,18,31,y,z,10,0,w,1k,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,0,t,1l,1c,u,y,x,t,t,0,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,0,18,0,s,1l,18,u,u,0,18,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 3 labour
EXT 14,15 19 14,0
PAL 0
ROOM 19
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1m,0,10,t,1c,u,18,v,t,18,31,u,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,0,1c,18,10,w,0,1d,1a,0,18,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,31,1d,19,0,18,1a,y,1a,x,1l,y,z,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,0,s,x,s,w,t,1l,0,w,1k,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,0,w,18,35,t,s,0,18,1b,18,x,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,t,1d,10,0,1d,1a,1m,1d,30,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1m,z,18,u,1d,w,x,0,18,1a,1m,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 4 labour
EXT 14,15 1a 14,0
PAL 0
ROOM a
b,g,c,a,a,a,b,e,a,c,a,c,a,b,c,a
c,0,0,0,0,0,c,0,a,0,a,c,0,a,0,f
e,b,a,a,c,0,0,0,a,0,a,0,0,0,0,b
c,c,c,c,b,c,a,0,0,0,0,0,a,0,e,e
a,0,c,0,e,e,c,c,a,0,c,0,0,0,0,a
c,0,0,0,c,a,c,d,0,0,b,0,a,b,0,a
a,0,a,0,0,0,0,d,a,0,a,0,a,c,0,c
c,0,a,b,0,a,a,d,a,0,a,0,0,c,0,e
e,e,b,a,a,c,c,d,c,0,a,b,0,a,0,e
e,c,0,0,0,0,0,d,b,0,e,b,c,a,0,b
c,0,c,a,a,0,a,d,e,0,0,0,0,a,0,a
a,0,b,e,0,0,c,d,c,0,a,0,a,c,0,c
a,0,0,0,0,a,a,d,a,0,a,0,b,b,0,c
b,c,a,c,0,0,c,d,a,0,a,c,c,a,0,a
h,0,0,0,0,c,b,d,c,0,0,0,d,c,0,a
e,c,c,a,a,a,e,e,c,c,c,a,e,b,i,c
NAME Now
ITM 7 11,10
EXT 1,0 b 2,11 FX wave DLG n
EXT 14,15 b 2,11 FX wave DLG n
EXT 0,14 b 2,11 FX wave DLG n
PAL 0
ROOM b
ag,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,aj
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ae,0,s,t,u,t,v,w,0,x,y,z,10,0,0,af
ae,0,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,0,0,af
ae,0,t,17,1d,w,0,0,0,0,0,0,0,0,0,af
ae,0,1y,1y,1y,1y,0,0,0,0,0,0,0,0,0,af
ae,0,s,w,10,18,w,t,19,x,3t,0,0,0,0,af
ae,0,1y,1y,1y,1y,1y,1y,1y,1y,ah,0,0,0,0,af
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ae,0,r,1c,18,s,w,0,0,0,0,0,0,0,0,af
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ae,0,r,1c,10,t,s,t,1a,w,0,0,0,0,0,af
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ae,0,r,11,z,w,z,10,t,0,0,0,0,0,0,af
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad
NAME select time
ITM 6 15,0
EXT 2,9 e 5,10 FX wave DLG h
EXT 2,13 f 0,0 FX wave DLG j
EXT 2,11 a 12,14 FX wave DLG m
END 19 0,0
PAL 0
ROOM c
ai,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,aj
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,af
ae,0,0,18,10,t,0,x,y,z,0,1d,1a,0,0,af
ae,0,0,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,0,0,af
ae,0,s,t,18,10,v,1k,0,y,11,0,0,0,0,af
ae,0,1y,1y,1y,1y,1y,1y,1y,1y,1y,0,0,0,0,af
ae,0,0,0,18,0,0,0,0,0,0,0,0,0,0,af
ae,0,0,0,1y,0,0,0,0,0,0,0,0,0,0,af
ae,0,0,0,0,0,10,18,1a,1m,y,1l,0,0,0,af
ae,0,0,0,0,0,1y,1y,1y,1y,1y,1y,0,0,0,af
ae,0,0,t,17,0,0,0,0,0,0,0,0,0,0,af
ae,0,0,1y,1y,1d,0,0,0,0,0,0,0,0,0,af
ae,0,0,0,0,1y,0,w,0,1n,0,0,0,0,0,af
ae,0,0,0,0,0,0,1y,0,0,0,0,0,0,0,af
ae,0,0,0,0,0,0,0,0,0,0,0,0,0,r,af
ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad,ad
NAME main time
ITM 5 9,12
EXT 14,14 b 13,6 FX wave
PAL 0
ROOM e
c,42,4g,1r,27,3s,26,41,42,26,3u,5t,44,1w,26,24
43,4k,4m,5r,41,2b,26,2s,3w,3v,3w,43,32,26,24,29
47,27,8l,41,27,2b,26,r,f,26,2m,2k,26,1h,29,29
65,2s,27,46,27,2b,26,26,26,1r,5g,2w,24,29,29,42
27,2m,27,46,1r,2k,26,26,26,26,26,2m,4a,29,1u,29
25,27,41,4x,81,2m,3v,26,2k,26,24,2t,42,4i,29,29
29,6z,1r,44,44,2b,26,26,2x,24,29,5t,5p,47,2k,29
29,29,25,46,2s,2b,2b,26,24,2w,43,2q,46,4g,3v,6v
32,29,3j,25,27,1v,26,24,29,29,29,57,28,46,44,41
2t,2w,3e,29,25,2b,24,44,5i,29,29,74,29,46,3w,48
1r,6v,6u,2k,2a,2c,ab,b,42,46,43,42,2a,2a,2t,2a
72,44,72,2d,22,aa,23,17,41,41,5a,28,28,28,28,28
5k,28,72,22,27,aa,26,43,1r,28,28,28,28,28,28,41
2s,28,22,2b,43,aa,26,26,23,28,28,3w,3f,2e,28,28
28,1f,27,27,27,aa,2k,26,32,23,28,32,4k,29,2i,28
22,27,72,6w,27,74,6n,42,32,26,23,28,36,2k,1r,28
NAME Past
ITM 8 9,5
ITM 8 3,9
ITM 8 1,13
ITM 8 7,6
ITM 8 12,12
ITM 8 6,1
ITM 8 6,8
ITM 8 4,14
ITM 8 1,7
ITM 8 10,9
ITM 8 14,2
EXT 5,10 b 2,9 DLG o
PAL 0
ROOM f
1q,1x,0,21,18,1a,z,18,10,x,0,k,l,k,20,0
1y,1z,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y
1l,1x,0,0,0,m,0,o,k,0,o,12,0,k,20,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
w,1x,0,0,0,20,0,o,q,0,k,l,0,k,n,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1b,1x,0,0,0,n,0,o,13,0,k,o,0,k,p,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
w,1x,o,0,0,p,0,o,m,0,k,k,0,k,12,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
11,1x,k,0,0,12,0,o,20,0,k,q,0,q,l,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
s,1x,q,0,o,l,0,o,n,0,k,13,0,q,o,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
s,1x,13,0,o,o,0,o,p,0,k,m,0,0,0,0
0,1x,0,0,0,0,0,0,0,0,0,0,0,0,0,0
NAME future
ITM 4 3,2
ITM 4 4,2
ITM 4 5,2
ITM 4 6,2
ITM 4 7,2
ITM 4 7,4
ITM 4 5,4
ITM 4 6,3
ITM 4 7,3
ITM 4 8,3
ITM 4 8,2
ITM 4 8,4
ITM 4 9,4
ITM 4 11,4
ITM 4 10,4
ITM 4 10,2
ITM 4 11,2
ITM 4 10,3
ITM 4 11,3
ITM 4 12,4
ITM 4 13,4
ITM 4 14,4
ITM 4 14,2
ITM 4 13,2
ITM 4 13,3
ITM 4 14,3
ITM 4 12,3
ITM 4 13,5
ITM 4 10,6
ITM 4 11,6
ITM 4 8,8
ITM 4 5,8
ITM 4 6,8
ITM 4 7,8
ITM 4 7,7
ITM 4 6,7
ITM 4 6,6
ITM 4 7,6
ITM 4 8,6
ITM 4 8,7
ITM 4 2,8
ITM 4 2,10
ITM 4 2,9
ITM 4 4,9
ITM 4 4,8
ITM 4 5,10
ITM 4 4,11
ITM 4 4,12
ITM 4 2,12
ITM 4 2,14
ITM 4 2,13
ITM 4 3,13
ITM 4 4,14
ITM 4 5,14
ITM 4 5,12
ITM 4 5,11
ITM 4 6,12
ITM 4 8,12
ITM 4 7,12
ITM 4 7,14
ITM 4 7,13
ITM 4 10,14
ITM 4 9,13
ITM 4 8,14
ITM 4 10,12
ITM 4 10,11
ITM 4 10,10
ITM 4 8,10
ITM 4 7,10
ITM 4 9,10
ITM 4 8,11
ITM 4 9,11
ITM 4 11,12
ITM 4 11,14
ITM 4 10,8
ITM 4 11,8
ITM 4 11,7
ITM 4 12,7
ITM 4 12,6
ITM 4 13,6
ITM 4 14,6
ITM 4 14,7
ITM 4 13,7
ITM 4 13,8
ITM 4 14,8
ITM 4 12,8
ITM 4 11,9
ITM 4 11,10
ITM 4 12,10
ITM 4 12,9
ITM 4 14,9
ITM 4 13,10
ITM 4 13,9
ITM 4 14,10
ITM 4 13,11
ITM 4 14,11
ITM 4 14,12
ITM 4 13,12
ITM 4 5,6
ITM 4 5,7
EXT 0,0 b 2,13 DLG k
PAL 0
ROOM g
1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o
1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y
1q,19,1k,y,s,w,0,11,1d,u,t,s,0,1v,0,0
1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y,1y
1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o
1o,2l,2l,2m,2l,2l,2m,2l,2l,2m,2l,2l,2m,2l,2l,2m
1o,2f,2e,2j,2f,2e,2j,2f,2e,2j,2f,2e,2j,2f,2e,2j
1o,2g,2h,2j,2g,2h,2j,2g,2h,2j,2g,2h,2j,2g,2h,2j
1o,2i,2i,2k,2i,2i,2k,2i,2i,2k,2i,2i,2k,2i,2i,2k
1o,2l,2l,2m,2l,2l,2m,2l,2l,2m,2l,2l,2m,1o,1o,1o
1o,2f,2e,2j,2f,2e,2j,2f,2e,2j,2f,2e,2j,1o,1o,1o
1o,2g,2h,2j,2g,2h,2j,2g,2h,2j,2g,2h,2j,1o,1o,1o
1o,2i,2i,2k,2i,2i,2k,2i,2i,2k,2i,2i,2k,1o,1o,1o
1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o
1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o
1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o,1o
NAME main ghost files
EXT 1,6 h 0,0
EXT 4,6 i 0,0
EXT 7,6 j 0,0
EXT 10,6 k 0,0
EXT 13,6 l 0,0
EXT 1,10 m 0,0
EXT 4,10 n 0,0
EXT 7,10 o 0,0
EXT 10,10 p 0,0
END 1b 0,2
PAL 0
ROOM h
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3k,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,1m,1d,s,10,z,1c,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,w,1d,30,t,0,1c,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,u,18,x,2z,v,18,1a,0,19,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,18,1l,1d,1a,19,0,31,t,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,18,0,s,z,31,30,t,10,s,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1d,30,t,0,18,v,w,1n,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 1
EXT 0,0 g 1,6
EXT 15,15 i 0,15 FX slide_r
EXT 0,15 p 15,15 FX slide_l
PAL 0
ROOM i
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3l,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,v,18,u,u,0,w,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,y,0,18,v,w,1d,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,y,1a,33,0,1l,y,1m,0,19,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,18,1l,t,s,33,0,1l,18,35,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,t,0,x,y,z,10,0,y,1b,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1a,0,10,z,u,t,s,33,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 2
EXT 0,0 g 4,6
EXT 0,15 h 15,15 FX slide_l
EXT 15,15 j 0,15 FX slide_r
PAL 0
ROOM j
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3m,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,y,s,1l,y,s,1d,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,s,0,y,11,0,0,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,w,1k,y,z,19,1k,w,s,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,r,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,3e,3e,3e,3e,36,3g,1r,3g,3h,2r,0,0,0
0,0,2n,3f,3e,3e,3f,36,3h,3h,3h,3h,2r,0,0,0
0,0,2n,3e,3e,3f,3e,36,1r,1r,3h,3h,2r,0,0,0
0,0,2n,3f,3e,3f,3e,36,3g,1r,3g,1r,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 3
EXT 0,0 g 7,6
EXT 0,15 i 15,15 FX slide_l
EXT 15,15 k 0,15 FX slide_r
PAL 0
ROOM k
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3n,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,z,1a,11,1d,1a,1d,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,s,1k,t,1m,0,w,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1k,y,z,19,1k,w,s,0,31,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,u,t,t,1m,1d,1a,19,0,1d,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1a,w,y,0,t,18,v,1k,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,y,w,1k,t,10,1j,3y,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 4
EXT 0,0 g 10,6
EXT 0,15 j 15,15 FX slide_l
EXT 15,15 l 0,15 FX slide_r
PAL 0
ROOM l
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3o,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,1c,1d,1a,19,2z,1c,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,y,1a,19,0,y,11,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1d,1m,t,18,s,3t,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,3x,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,3x,0,3u,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,3x,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,3x,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 5
EXT 0,0 g 13,6
EXT 0,15 k 15,15 FX slide_l
EXT 15,15 m 0,15 FX slide_r
PAL 0
ROOM m
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3p,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,1k,y,1b,0,w,y,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,1m,1d,s,w,1d,1a,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,19,z,1d,s,1k,0,x,y,z,2r,0,0,0
0,0,2n,0,0,0,0,0,0,1y,1y,1y,2r,0,0,0
0,0,2n,10,0,w,1k,y,z,19,1k,w,2r,0,0,0
0,0,2n,1y,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,s,0,11,10,y,1l,0,1l,x,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,1y,1y,2r,0,0,0
0,0,2n,w,1k,y,z,19,1k,w,s,1n,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 6
EXT 0,0 g 1,10
EXT 0,15 l 15,15 FX slide_l
EXT 15,15 n 0,15 FX slide_r
PAL 0
ROOM n
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3q,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,w,y,0,1c,u,18,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,x,0,18,0,19,18,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1l,t,0,1c,u,18,x,t,10,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,s,0,1a,t,t,1m,0,w,y,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,18,19,10,t,t,0,y,1a,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,w,1k,t,0,10,z,u,t,s,2r,0,0,0
0,0,2n,0,0,0,0,1y,1y,1y,1y,1y,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 7
EXT 0,0 g 4,10
EXT 0,15 m 15,15 FX slide_l
EXT 15,15 o 0,15 FX slide_r
PAL 0
ROOM o
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3r,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,1d,s,0,10,t,18,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,u,0,u,1d,11,t,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,11,10,18,19,1d,u,t,1n,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1b,1k,y,0,s,t,w,s,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,w,1k,t,0,10,z,u,t,s,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,1n,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,40,3z,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,3v,3w,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 8
EXT 0,0 g 7,10
EXT 0,15 n 15,15 FX slide_l
EXT 15,15 p 0,15 FX slide_r
PAL 0
ROOM p
1q,0,0,0,0,0,0,0,0,0,0,0,0,0,3s,3j
0,0,a6,a4,a4,a4,a4,a4,a4,a5,2w,2y,0,0,0,0
0,0,2n,v,18,1a,0,1b,t,2r,2x,2w,2y,0,0,0
0,0,2n,0,0,0,0,0,0,2r,0,2x,2w,0,0,0
0,0,2n,18,19,10,t,t,0,2v,a4,a4,a5,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,y,1a,0,18,1a,0,18,u,w,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,t,10,1a,18,w,t,0,10,t,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,18,u,1d,w,x,1n,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2n,0,0,0,0,0,0,0,0,0,2r,0,0,0
0,0,2o,2p,2p,2p,2p,2p,2p,2p,2p,2p,2q,0,0,0
32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,r
NAME ghost file 9
EXT 0,0 g 10,10
EXT 0,15 o 15,15 FX slide_l
EXT 15,15 h 0,15 FX slide_r
PAL 0
ROOM s
47,1t,1t,1t,1t,1t,1t,1t,1t,1t,1t,48,1r,1r,1r,1r
1r,38,38,4a,49,55,1r,1r,1r,1r,1r,4b,5v,5y,0,0
1r,d,0,1r,55,1r,1r,59,54,1r,4d,4e,5u,5x,0,0
59,4h,4h,1r,4s,4t,4m,4z,4x,50,4f,4g,4c,5w,0,0
65,5n,5l,5m,4r,4u,4n,4y,53,4v,57,58,60,2p,2p,2p
64,1r,5k,5j,4q,4p,4o,52,4w,51,4d,4i,55,14,61,62
64,6v,6u,1r,1r,1r,1r,1r,1r,56,4j,4k,55,14,61,62
66,4d,6r,6t,1r,1r,1r,1r,65,1r,1r,1r,71,5z,2p,2p
6w,4f,6s,1r,d,1r,1r,1r,64,71,70,6y,70,14,0,62
6m,6x,6o,1r,6o,2s,6o,2s,64,2u,2t,6n,6z,14,63,62
47,6k,6l,1r,6p,6q,6p,6q,66,2n,0,48,1r,5z,2p,2p
68,67,67,69,67,6a,67,6b,0,6c,6d,6e,72,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
h,0,0,0,0,0,0,0,0,0,0,0,0,0,0,f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,5h,5g
10,18,s,1c,31,t,10,10,x,0,1c,1d,0,0,5f,5i
NAME raspberry pi 2D
END 14 12,11
END 15 0,13
END 16 15,13
PAL 0
ROOM w
47,1t,1t,1t,1t,1t,1t,1t,1t,1t,1t,48,1r,1r,1r,1r
1r,38,38,4a,49,55,1r,1r,1r,1r,1r,4b,5v,5y,0,0
1r,d,0,1r,55,1r,1r,59,54,1r,4d,4e,5u,5x,0,0
59,4h,4h,1r,4s,4t,4m,4z,4x,50,4f,4g,4c,5w,0,0
65,5n,5l,5m,4r,4u,4n,4y,53,4v,57,58,60,2p,2p,2p
64,1r,5k,5j,4q,4p,4o,52,4w,51,4d,4i,55,14,61,62
64,6v,6u,1r,1r,1r,1r,1r,1r,56,4j,4k,55,14,61,62
66,4d,6r,6t,1r,1r,1r,1r,65,1r,1r,1r,71,5z,2p,2p
6w,4f,6s,1r,d,1r,1r,1r,64,71,70,6y,70,14,0,62
6m,6x,6o,1r,6o,2s,6o,2s,64,2u,2t,6n,6z,14,63,62
47,6k,6l,1r,6p,6q,6p,6q,66,2n,0,48,1r,5z,2p,2p
68,67,67,69,67,6a,67,6b,0,6c,6d,6e,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
h,0,0,0,0,0,0,0,0,0,0,0,0,0,0,f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,5h,5g
10,18,s,1c,31,t,10,10,x,0,1c,1d,0,0,5f,5i
NAME raspberry pi 3D
ITM c 6,0
ITM 9 5,4
ITM a 8,4
ITM b 1,2
ITM d 14,2
ITM e 14,9
ITM f 10,10
ITM g 8,9
ITM h 4,10
ITM h 6,10
ITM i 1,10
ITM j 0,3
ITM k 14,6
ITM l 11,1
ITM m 0,6
END 13 15,13
END 13 0,13
PAL 0
ROOM x
1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g
2f,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7t
7v,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,0,7u
7v,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,0,0,0,7u
7v,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,7u
7v,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
7v,0,0,y,10,0,u,18,31,y,z,10,1n,0,0,7u
7v,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
2g,39,39,39,39,39,39,39,39,39,39,39,39,39,39,7x
1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u
1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g,1u,1g
2f,38,38,38,38,38,38,38,38,38,38,38,38,38,38,7t
3a,31,0,31,0,31,31,31,31,0,31,31,31,31,31,7u
3a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
3a,31,31,31,31,31,31,31,0,31,31,31,31,31,0,7u
3a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
PAL 0
ROOM y
2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l
2f,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7w,7t
7v,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,0,7u
7v,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,0,0,0,7u
7v,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,7u
7v,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
7v,0,0,0,y,10,0,u,18,31,y,z,10,1n,0,7u
7v,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
2g,39,39,39,39,39,39,39,39,39,39,39,39,39,39,7x
2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i,2i
2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l,2l
2f,38,38,38,38,38,38,38,38,38,38,38,38,38,38,7t
3a,31,0,31,0,31,31,31,31,0,31,31,31,31,31,7u
3a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
3a,31,31,31,31,31,31,31,0,31,31,31,31,31,0,7u
3a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7u
PAL 0
ROOM z
81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,7s,0,7m,74,79,7c,7e,7i,7j,0,0,0,0,0,80
80,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,0,0,0,80
80,0,7p,7o,76,77,7b,7f,7h,7k,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,y,10,0,u,18,31,y,z,10,1n,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,46,0,0,46,0,0,0,0,0,80
81,81,81,81,81,81,a2,81,81,a2,81,81,81,81,81,80
80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80
80,0,0,0,0,0,0,80,80,0,0,0,0,0,0,80
80,0,0,0,0,0,0,80,80,0,0,0,0,0,0,80
80,0,0,0,0,0,0,80,80,0,0,0,0,0,0,80
80,0,0,0,0,0,46,80,80,46,0,0,0,0,0,80
80,81,81,81,81,81,a2,80,80,a2,81,81,81,81,81,80
ITM 5 13,6
PAL 0
ROOM 1a
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1c,z,10,1c,y,s,t,6i,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,46,0,0,80
81,81,81,81,81,81,81,81,81,81,81,81,a2,81,81,80
80,80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80
80,0,0,0,0,0,0,0,80,80,80,80,a1,80,80,80
80,0,1b,y,10,35,3t,0,80,80,80,80,a1,80,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1b,y,10,35,0,1d,s,0,18,0,1c,10,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,v,t,s,s,0,1b,1k,t,10,t,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,t,0,1d,1a,1m,1d,30,1d,1m,z,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,u,0,1l,1d,1a,1m,0,18,1a,1m,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 5 labour
EXT 14,15 1b 14,0
PAL 0
ROOM 1b
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,31,y,1m,x,0,y,11,0,w,1k,t,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1b,y,10,35,t,10,s,0,18,10,t,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1a,y,w,0,18,u,1d,t,1a,18,w,t,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1m,0,y,10,0,1m,1d,30,1d,1m,t,1m,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,11,10,y,1l,0,w,1k,t,0,1c,10,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1m,z,v,w,6i,0,1b,y,10,35,t,10,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,0,18,10,t,0,1a,y,w,0,1c,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 6 labour
EXT 14,15 1c 14,0
PAL 0
ROOM 1c
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,1m,0,1m,1d,10,t,v,w,u,x,6i,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,1a,s,w,t,18,1m,0,w,1k,t,x,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,t,u,u,0,w,1k,t,0,1c,10,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1m,z,v,w,0,y,11,0,w,1k,t,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,0,1b,y,10,35,6i,0,0,46,0,46,0,80
80,0,0,0,0,0,0,0,0,0,81,a2,81,a2,81,80
80,0,0,0,0,0,0,0,0,0,80,a1,80,a1,80,80
81,81,81,81,81,81,81,81,81,81,80,a1,80,a1,80,80
80,80,80,80,80,80,80,80,80,80,80,a1,80,0,0,0
80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80,80
80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80,80
NAME 7 labour
EXT 11,15 1d 0,6
EXT 15,13 1d 1,0
PAL 0
ROOM 1d
80,a1,80,80,80,80,80,80,80,80,80,80,80,80,80,80
80,a1,80,0,0,0,0,0,0,0,0,0,0,0,0,80
80,a1,80,0,7s,0,7m,74,79,7c,7e,7i,7j,0,0,80
80,0,0,0,7r,7q,7n,75,78,7a,7d,7g,7l,0,0,80
80,80,80,0,0,7p,7o,76,77,7b,7f,7h,7k,0,0,80
80,80,80,0,y,10,0,8p,8q,8e,8f,8t,8w,8z,94,80
0,0,0,0,0,0,0,8n,8s,8c,8g,8u,8x,90,93,80
80,80,80,0,0,0,0,8o,8r,8d,8h,8v,8y,91,92,80
80,80,80,0,0,46,0,0,0,0,0,0,0,0,0,80
80,80,80,81,81,a2,81,81,81,81,81,81,81,81,81,80
80,80,80,80,80,a1,80,80,80,80,80,80,80,80,80,80
80,80,80,80,80,a1,80,80,80,80,80,80,80,80,80,80
80,80,80,80,80,a1,80,80,80,80,80,80,80,80,80,80
80,80,80,80,80,0,0,0,0,0,0,0,a1,80,80,80
80,80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80
80,80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80
NAME 1 work
EXT 12,15 1e 12,0
PAL 0
ROOM 1e
80,80,80,80,80,80,80,80,80,80,80,80,a1,80,80,80
80,80,0,0,0,0,0,0,0,0,0,0,a1,80,80,80
80,80,0,80,80,80,80,80,80,80,80,80,80,80,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1c,u,18,x,0,1d,s,0,1a,y,0,u,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,1a,19,t,10,0,18,0,v,y,z,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,t,10,0,w,y,0,1b,y,10,35,6i,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1c,u,18,x,0,31,t,v,y,1l,t,s,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1b,y,10,35,6i,0,1b,y,10,35,0,31,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,v,y,1l,t,s,0,1c,u,18,x,6i,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 2 work
EXT 14,15 1f 14,0
PAL 0
ROOM 1f
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,19,18,1l,1d,11,1d,v,18,w,1d,y,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1l,18,s,35,s,0,u,18,31,y,z,10,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,s,0,s,y,1l,t,w,1k,1d,1a,19,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,1a,21,y,x,18,31,u,t,6i,0,31,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,z,w,0,1d,w,0,1d,s,0,11,y,10,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,t,0,31,t,1a,t,11,1d,w,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,11,0,w,1k,t,0,31,z,s,1d,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 3 work
EXT 14,15 1g 14,0
PAL 0
ROOM 1g
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,s,s,0,18,1a,1m,0,1a,y,w,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,11,y,10,0,w,1k,t,0,11,z,u,11,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,u,u,1l,t,1a,w,0,y,11,0,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1k,t,0,1b,y,10,35,t,10,s,6i,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1b,y,10,35,0,w,1k,18,w,0,1d,s,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,18,10,1a,t,1m,0,18,s,0,18,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,19,18,1l,t,0,1d,s,0,s,w,1d,u,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 4 work
EXT 14,15 1h 14,0
PAL 0
ROOM 1h
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,0,18,0,1m,z,w,x,0,18,1a,1m,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,31,x,0,w,1k,18,w,0,18,19,18,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1a,s,w,0,w,1k,t,0,1a,18,w,z,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,t,0,y,11,0,1c,u,18,x,6i,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,46,0,80
80,81,81,81,81,81,81,81,81,81,81,81,81,a2,81,80
80,80,80,80,80,80,80,80,80,80,80,80,80,a1,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,19,18,1l,t,s,0,y,11,w,t,1a,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,z,s,t,0,18,0,s,w,10,z,v,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 5 work
EXT 14,15 1i 14,0
PAL 0
ROOM 1i
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,z,10,t,0,y,11,0,1m,y,1l,1d,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,w,1d,y,1a,0,s,1d,1l,1d,u,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,0,w,y,0,1b,1k,18,w,0,1b,t,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,11,1d,1a,1m,0,1d,1a,0,w,1k,t,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,t,18,u,0,1b,y,10,u,1m,6i,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,31,z,w,0,w,1k,t,x,0,v,10,t,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,w,t,0,18,0,s,18,11,t,0,s,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 6 work
EXT 14,15 1j 14,0
PAL 0
ROOM 1j
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1c,18,v,t,0,1b,1k,t,10,t,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,t,0,1c,u,18,x,t,10,0,v,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,1a,0,18,v,w,z,18,u,u,x,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,z,v,v,t,t,1m,6i,0,1d,w,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1l,18,35,t,s,0,w,1k,t,0,10,t,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,u,0,1b,y,10,u,1m,0,u,y,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,35,0,1l,y,10,t,0,19,u,18,1l,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 7 work
EXT 14,15 1k 14,0
PAL 0
ROOM 1k
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,y,z,s,0,18,1a,1m,0,10,t,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1a,11,y,10,v,t,s,0,18,0,30,1d,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,s,1d,y,1a,0,y,11,0,18,0,1b,y,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,u,1m,0,t,1a,w,1d,10,t,u,x,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,19,10,y,z,1a,1m,t,1m,0,1d,1a,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,v,y,1l,1c,t,w,1d,w,1d,y,1a,6i,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,46,0,46,0,81,81,81,81,81,81,81,81,81,80
80,81,a2,81,a2,81,80,80,80,80,80,80,80,80,80,80
80,80,a1,80,a1,80,80,80,80,80,80,80,80,80,80,80
NAME 8 work
EXT 2,13 1l 2,0
EXT 4,13 1l 4,0
PAL 0
ROOM 1l
80,80,a1,80,a1,80,80,80,80,80,80,80,80,80,80,80
80,80,a1,80,0,0,0,0,0,0,0,a1,80,80,80,80
80,80,a1,80,80,80,80,80,80,80,80,a1,80,80,80,80
80,80,a1,80,80,80,80,0,0,0,0,a1,80,80,80,80
80,80,a1,80,80,80,80,a1,80,80,80,80,80,80,80,80
80,80,a1,80,80,80,80,0,0,a1,80,80,80,80,80,80
80,80,a1,80,80,80,80,80,80,a1,80,80,80,80,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,8p,8q,8e,8f,8t,8w,8z,94,0,0,0,0,0,80
80,0,8n,8s,8c,8g,8u,8x,90,93,0,y,10,0,0,80
80,0,8o,8r,8d,8h,8v,8y,91,92,0,0,0,0,0,80
80,0,0,0,0,86,87,8a,0,8e,8f,8i,8l,0,0,80
80,0,0,0,0,83,84,88,0,8c,8g,8j,8m,0,0,80
80,0,0,0,0,85,0,89,8b,8d,8h,8k,0,0,46,80
80,81,81,81,81,81,81,81,81,81,81,81,81,81,a2,80
80,80,80,80,80,80,80,80,80,80,80,80,80,80,a1,80
NAME 1 game
EXT 14,15 1m 14,0
PAL 0
ROOM 1m
80,80,80,80,80,80,80,80,80,80,80,80,80,80,a1,80
80,80,80,80,0,0,0,0,0,0,0,0,0,0,a1,80
80,80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80
80,80,80,80,0,0,0,a1,80,80,80,80,80,80,80,80
80,80,80,80,80,80,80,a1,80,80,80,80,80,80,80,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,w,1k,t,0,w,t,10,1l,s,0,19,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1l,t,0,18,1a,1m,0,1c,u,18,x,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,10,t,0,w,1b,y,0,s,t,1c,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,10,18,w,t,0,1d,1m,t,18,s,0,1b,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,w,1k,0,10,t,u,18,w,t,1m,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 2 game
EXT 14,15 1n 14,0
PAL 0
ROOM 1n
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,31,z,w,0,1m,1d,s,w,1d,1a,v,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1l,t,18,1a,1d,1a,19,s,6i,0,1d,1a,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,1a,t,0,s,t,1a,s,t,0,1c,u,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,x,0,1d,s,0,18,0,u,18,10,19,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,10,0,w,t,10,1l,0,w,1k,18,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,1a,v,u,z,1m,t,s,0,19,18,1l,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,0,18,s,0,18,0,v,t,10,w,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 3 game
EXT 14,15 1o 14,0
PAL 0
ROOM 1o
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,1a,0,11,y,10,1l,0,y,11,0,1c,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,u,18,x,6i,0,31,z,w,0,18,u,s,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,y,0,w,1k,t,0,10,t,30,t,10,s,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,t,0,1d,s,0,w,10,z,t,3t,0,19,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,1l,t,0,1d,s,0,w,1k,t,0,31,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1d,19,19,t,10,0,w,t,10,1l,0,18,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1a,1m,0,1d,1a,v,u,z,1m,t,s,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,46,80
NAME 4 game
EXT 14,15 1p 14,0
PAL 0
ROOM 1p
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,1c,u,18,x,0,18,s,0,y,1a,t,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,18,s,1c,t,v,w,0,y,11,0,1d,w,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,46,0,0,46,0,0,0,0,0,80
80,81,81,81,81,81,a2,81,81,a2,81,81,81,81,81,80
80,80,80,80,80,80,a1,80,80,a1,80,80,80,80,80,80
80,0,0,0,0,0,a1,80,80,a1,80,80,80,80,80,80
80,a1,80,80,80,80,80,80,80,a1,80,80,80,80,80,80
80,0,0,0,0,0,a1,80,80,a1,80,80,80,80,80,80
80,80,80,80,80,80,a1,80,80,a1,80,0,0,0,0,0
80,80,80,0,0,0,a1,80,80,a1,80,a1,80,80,80,80
80,80,80,a1,80,80,80,80,80,0,0,a1,80,80,80,80
80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80,80
80,80,80,a1,80,80,80,80,80,80,80,80,80,80,80,80
NAME 5 game
EXT 3,15 1q 2,0
EXT 15,11 1q 6,0
PAL 0
ROOM 1q
80,80,a1,80,80,80,a1,80,80,80,80,80,80,80,80,80
80,80,a1,80,80,80,a1,80,80,80,80,80,80,80,80,80
80,80,a1,80,0,0,0,0,0,0,0,0,0,0,0,80
80,80,a1,80,0,86,87,8a,0,8e,8f,8i,8l,0,0,80
80,0,0,0,0,83,84,88,0,8c,8g,8j,8m,0,0,80
80,0,y,10,0,85,0,89,8b,8d,8h,8k,0,0,0,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,8a,0,8z,94,96,98,9b,9e,9j,7c,7e,8z,94,80
80,0,88,0,90,93,95,99,9c,9f,9i,7a,7d,90,93,80
80,0,89,8b,91,92,97,9a,9d,9g,9h,7b,7f,91,92,80
80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80
80,0,0,0,0,0,0,0,0,0,46,0,0,0,0,80
80,81,81,81,81,81,81,81,81,81,a2,81,81,81,81,80
80,80,80,80,80,80,80,80,80,80,a1,80,80,80,80,80
80,80,80,80,80,80,80,80,80,80,0,0,0,0,0,0
80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80
NAME 6 game
PAL 0
TIL 10
01111110
01100110
01100110
01100110
01111100
01101100
01100110
01100110
NAME R
TIL 11
01111110
01111100
01100000
01111000
01110000
01100000
01100000
01100000
NAME F
TIL 12
01111110
01100110
01100110
01111110
00011110
00000110
01100110
01111110
NAME 9
TIL 13
00111000
01110000
01100000
01101100
01111110
01111110
00001100
00001100
NAME 4
TIL 14
11000000
11000000
11000000
11000000
11000000
11000000
11000000
11000000
NAME I
TIL 15
11110000
11010000
11010000
11010000
11010000
11010000
11010000
11110000
NAME II
TIL 16
11111100
11010100
11010100
11010100
11010100
11010100
11010100
11111100
NAME III
TIL 17
01100110
01110110
00111100
00011000
00011100
00111110
01101110
01100110
NAME X
TIL 18
00111100
01101110
01100110
01100110
01111110
01111110
01100110
01100110
NAME A
TIL 19
00111110
01100110
01100000
01100000
01101110
01100110
01100110
00111110
NAME G
TIL 20
01111110
01100110
01100000
01111000
01111110
01100110
01100110
01111110
TIL 21
01111110
01111110
00000110
00000110
00000110
01100110
01101110
01111100
TIL 22
00000000
00000000
00000000
11111000
11111000
00011000
00011000
00011000
>
10101011
10101000
10101011
10100000
00001111
00000000
00001111
00000000
TIL 23
00000000
00000000
00000000
00000111
00000111
00000110
00000110
00000110
>
00101010
00101010
00101010
00000010
00111000
00000000
00111100
00000000
TIL 24
00000111
00000111
00000000
00000000
00000000
00000000
00000000
00000000
>
00111100
00000000
00111010
00000010
00111010
00000000
00000000
00000000
TIL 25
11111000
11111000
00000000
00000000
00000000
00000000
00000000
00000000
>
00001111
10100000
10101011
10101000
10101011
00000000
00000000
00000000
TIL 26
00000110
00000110
00000110
00000110
00000110
00000110
00000110
00000110
>
00111100
00000000
00111100
00000000
00111100
00000000
00111100
00000000
TIL 27
00011000
00011000
00011000
00011000
00011000
00011000
00011000
00011000
>
00001111
00000000
00001111
00000000
00001111
00000000
00001111
00000000
TIL 28
00000000
00000000
00000000
11111111
11111111
00000000
00000000
00000000
>
10101010
10101010
10101010
10101010
00000000
00000000
00000000
00000000
TIL 29
11111111
10001000
10001000
10001000
11111111
10001000
10001000
10001000
>
00000000
10101010
10101010
10101010
10101010
00000000
00000000
00000000
TIL 30
01100100
01100110
01100110
01100110
01100110
01110110
00111100
00011000
TIL 31
01111100
01101110
01100110
01101100
01111110
01100110
01100110
01111110
NAME B
TIL 32
00000000
00011000
00110000
01111110
01111110
00110000
00011000
00000000
>
00000000
00001100
00011000
00111111
00111111
00011000
00001100
00000000
TIL 33
00010000
00011000
00011000
00011000
00011000
00000000
00011000
00011000
NAME !
TIL 34
00001000
00001000
00010000
00010000
00100000
00100000
01000000
01000000
TIL 35
01100110
01100110
01101110
01111100
01111000
01101100
01101110
01100110
TIL 36
11110111
11101011
11110111
11101011
11110111
11101011
11110111
11101011
TIL 37
11111111
00000001
00000001
00000001
00000001
00000001
00000001
00000001
TIL 38
11111111
00000000
00000000
00000000
00000000
00000000
00000000
00000000
TIL 39
00000000
00000000
00000000
00000000
00000000
00000000
00000000
11111111
TIL 40
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000001
TIL 41
11111111
11000111
11011111
11000111
11010111
11000111
11111111
11111111
>
11111111
11111111
11100011
11111011
11100011
11111011
11100011
11111111
TIL 42
11111111
11000111
11011111
11000111
11110111
11000111
11111111
11111111
>
11111111
11111111
11100011
11111011
11100011
11101111
11100011
11111111
TIL 43
11111111
11000111
11010111
11000111
11110111
11000111
11111111
11111111
>
11111111
11101111
11101111
11101111
11101111
11101111
11111111
11111111
TIL 44
11111111
11000111
11010111
11000111
11010111
11000111
11111111
11111111
>
11111111
11000111
11010111
11010111
11010111
11000111
11111111
11111111
TIL 46
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
00011000
00011000
01011010
01111110
00111100
00011000
00000000
00000000
TIL 47
11111110
11000110
10000010
10010010
10000010
11000110
11111110
11111110
TIL 48
01111111
01100011
01000001
01001001
01000001
01100011
01111111
01111111
TIL 49
11111111
00000001
11111101
11111101
11111101
00000001
11111111
11111111
TIL 50
00011111
00011111
00011111
00011111
00011111
00011111
00011111
00011111
TIL 51
00011111
00000111
00000111
00000111
00000111
00011111
00011111
00011111
TIL 52
00000101
00000101
00000101
00000000
00000000
00000000
00000000
00000000
TIL 53
00000000
00000000
00000000
00000000
01000000
01000000
01000000
01000000
TIL 54
11111111
11111111
11111111
11111111
11111111
11111000
01010000
11111111
TIL 55
11111111
11111111
11111111
11111111
11111111
11111111
10111111
11111111
TIL 56
11111101
11111111
11111101
11111111
11111101
11111111
11111111
11111111
TIL 57
11110100
11110100
11111100
11111111
11111111
11111111
11111111
11111111
TIL 58
10100111
10100111
10100111
11100111
11111111
11111111
11111111
11111111
TIL 59
11111111
11111111
10101111
11111111
01111111
11111111
11111111
11111111
TIL 60
11100000
11100000
11100000
11111111
11111111
11111111
11111111
11111111
TIL 61
00000000
00000000
00000000
00000000
00000000
01100000
01100000
00000000
TIL 62
00000000
00000000
00000000
00000000
00000110
00000011
00000011
00000110
TIL 63
10000000
10000000
10000000
10000000
10000000
10000000
10000000
00000000
TIL 64
00100011
10100000
00100011
10100000
00100011
10100000
00100011
10100000
TIL 65
00000111
10100111
00100011
10100000
00100011
10100000
00100011
10100000
TIL 66
00100011
10100000
00100011
10100000
00100011
10100000
00100011
10100011
TIL 67
00000000
00000000
00000000
11111111
00000000
00000000
00000000
00000000
TIL 68
00000010
00000010
00000010
00000001
00000000
00000000
00000000
00000000
TIL 69
10000001
10000001
10000001
00000000
00000000
00000000
00000000
00000000
WAL false
TIL 70
10001001
10001001
10001001
11111111
11110101
11100000
11100000
11110101
TIL 71
11111111
11110000
11110000
11111111
11100000
11100000
11100000
11111111
TIL 72
00000000
00000000
00011000
00111100
01111110
01011010
00011000
00011000
>
00000000
00011000
00111100
01111110
01011010
00011000
00011000
00000000
TIL 73
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME black wall
WAL true
TIL 74
00001111
00011111
00011100
01110000
01110000
01110000
11100000
11100000
NAME O-1
TIL 75
11100000
11100000
11100000
11100000
11100000
11100000
01110000
01110000
NAME O-2
TIL 76
01110001
00111111
00011110
00000000
00000000
00000000
00000000
00000000
NAME O-3
TIL 77
11110000
11000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME O-4
TIL 78
00011000
00011000
00011000
00011000
00111000
00111000
00110000
00110000
NAME O-5
TIL 79
11000000
11100000
01110000
01110000
01110000
00011000
00011000
00011000
NAME O-6
TIL 80
01010101
11111111
01010101
11111111
01010101
11111111
01010101
11111111
>
10101010
00000000
10101010
00000000
10101010
00000000
10101010
00000000
WAL true
TIL 81
01010101
10101010
01010101
10101010
01010101
11111111
01010101
11111111
>
10101010
01010101
10101010
01010101
10101010
00000000
10101010
00000000
WAL true
TIL 82
00011111
00010000
00010000
00010000
00010000
00010000
00010000
00010000
TIL 83
11100000
11100001
01111111
01111110
11100000
11100000
11100000
11100000
NAME P-1
TIL 84
01100000
11100000
11000000
00000000
00000000
00000000
00000000
00000000
NAME P-2
TIL 85
11100000
01110000
01110000
01110000
00000000
00000000
00000000
00000000
NAME P-3
TIL 86
01111111
01111111
11100111
11100000
11100000
11100000
11100000
11100000
NAME P-4
TIL 87
00000000
11000000
11110000
11110000
00110000
00110000
01110000
01110000
NAME P-5
TIL 88
01110000
01110000
11000000
11000000
11000000
11000000
11000000
11000000
NAME L-1
TIL 89
11100000
11100000
01111000
01111111
00000000
00000000
00000000
00000000
NAME L-2
TIL 90
00110000
00110001
00111111
00111110
00111000
00111000
00111000
00111000
NAME E-2
TIL 91
00111000
00111000
00111111
00011111
00000000
00000000
00000000
00000000
NAME E-3
TIL 92
00110000
01110000
11110000
10000000
00000000
00000000
00000000
00000000
NAME E-4
TIL 93
00000000
11100000
11000000
00000000
00000000
00000000
00000000
00000000
NAME E-5
TIL 94
11100000
11110000
00000000
00000000
00000000
00000000
00000000
00000000
NAME E-6
TIL 95
00011100
00011100
00011100
00011100
00011100
00111000
00111000
00111000
NAME I-1
TIL 96
00011100
00011100
00011110
00011110
00001110
00001110
00001110
00011100
NAME I-2
TIL 97
00111000
00111100
00111100
00011100
00000000
00000000
00000000
00000000
NAME I-3
TIL 98
00000011
00000011
00000110
00000110
00011000
00011000
00011000
00011000
NAME S-1
TIL 99
00001100
00001100
00001100
00000011
00000000
00000000
00000000
00000000
NAME S-2
TIL a
10011001
10100000
00000110
10010001
00100110
10001001
00100000
10010101
>
10011001
10100000
00000110
10010001
00100110
10001001
00100000
10010101
WAL true
TIL b
11110111
10101011
11111111
10101101
11110111
01011101
11110111
10111111
NAME tile 3
WAL true
TIL c
11100111
00011011
10100001
11110100
01100111
11011100
01010111
01101101
NAME tile 2
WAL true
TIL d
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME black
WAL false
TIL e
11111110
01101111
11111111
11111101
11111111
11011101
11110111
11111111
NAME tile 4
WAL true
TIL f
00000111
00000011
00000001
00000000
00000000
00000001
00000011
00000111
NAME door right
TIL g
11100111
11000011
10000001
00000000
00000000
00000000
00000000
00000000
NAME door top
TIL h
11100000
11000000
10000000
00000000
00000000
10000000
11000000
11100000
NAME door left
TIL i
00000000
00000000
00000000
00000000
00000000
10000001
11000011
11100111
NAME door bottom
TIL j
00000000
00000000
00000000
00011000
00011000
00000000
00000000
00000000
NAME question mark
TIL k
00111110
01100110
01100110
00001110
00111100
01110000
01100110
01111110
NAME 2
TIL l
00111100
01100110
01100110
01100110
01100110
01100110
01111110
00111100
NAME 0
TIL m
01111110
01111110
01100000
01111100
00001110
00000110
01111110
01111100
NAME 5
TIL n
01111110
01111110
00001110
00011100
00111000
00110000
00110000
00110000
NAME 7
TIL o
00011100
00111100
00001100
00001100
00001100
00001100
00001100
00001100
NAME 1
TIL p
01111110
01100110
01100110
01111110
00111100
01100110
01100110
01111110
NAME 8
TIL q
01111110
01111110
00000110
00011100
00011100
00000110
01111110
01111110
NAME 3
TIL r
00000000
00011000
00001100
01111110
01111110
00001100
00011000
00000000
>
00000000
00110000
00011000
11111100
11111100
00011000
00110000
00000000
NAME arrow
TIL s
01111110
01100110
01100000
01111110
00000110
00000110
01101110
01111110
NAME S
TIL t
01111110
01111100
01100000
01111000
01110000
01100000
01111110
01111110
NAME E
TIL u
01100000
01100000
01100000
01100000
01100000
01100010
01100110
01111110
NAME L
TIL v
01111110
01000110
01000000
01100000
01111000
01111010
01111010
01111110
NAME C
TIL w
01111110
01111110
01011010
00011000
00011000
00011000
00011000
00011000
NAME T
TIL x
01100110
01100110
01100110
01100110
00111100
00011000
01111000
01110000
NAME Y
TIL y
00111100
01111110
01100110
01100110
01100110
01100110
01111110
00111100
NAME O
TIL z
01100100
01100110
01100110
01100110
01100110
01100110
01111110
00111100
NAME U
TIL 1a
01100110
01100110
01110110
01110110
01111110
01101110
01101110
01100110
NAME N
TIL 1b
01100010
01100010
01100010
01100010
01101010
01101010
01111110
01111110
NAME W
TIL 1c
01111110
01100110
01100110
01101110
01111100
01110000
01100000
01100000
NAME P
TIL 1d
00111100
00011000
00011000
00011000
00011000
00011000
00011000
00111100
NAME i
TIL 1e
00000000
00100000
01110000
00100000
00000000
00000000
00000000
00000000
>
00000000
01110000
01000000
01000000
00000000
00000000
00000000
00000000
TIL 1f
00000000
00000100
00001110
00000100
00000000
00000000
00000000
00000000
>
00000000
00001110
00000010
00000010
00000000
00000000
00000000
00000000
TIL 1g
00000000
00000000
00010000
00111000
00010000
00000100
00000000
00000000
>
00000000
00000000
00000000
00001000
00011100
00001000
01000000
00000000
TIL 1h
00000000
00000000
00000000
00000000
00100000
01110000
00100000
00000000
>
00000000
00000000
00000000
00000000
01000000
01000000
01110000
00000000
TIL 1i
00000000
10101010
11111111
00000000
00000000
00000000
00000000
00000000
TIL 1j
00000000
00000000
00000000
00000000
00000000
00000000
01101101
01101101
TIL 1k
01100100
01100110
01100110
01111110
01111110
01100110
01100110
01100110
NAME H
TIL 1l
01111110
01111110
01101010
01101010
01101010
01101010
01101010
01101010
NAME M
TIL 1m
01111100
01100110
01100110
01100110
01100110
01101110
01111100
01111000
NAME D
TIL 1n
01111100
01111100
00001100
00111100
00110000
00000000
00110000
00110000
NAME ?
TIL 1o
10101011
01010101
10101010
01010111
10101010
01010101
10101010
01010101
>
10101011
01010101
10101010
01010101
10101010
01010101
11101010
01010101
TIL 1p
10101010
01010101
10101010
01010101
10101010
01010101
10101010
01010101
TIL 1q
00000000
01010000
00100000
01010000
00000000
00000000
00000000
00000000
TIL 1r
11111111
11111111
11111111
11111111
11111111
11111111
11111111
11111111
NAME green
TIL 1s
10011001
11000011
10000001
01011010
00100100
10100101
10011001
11000011
NAME raspberry
TIL 1t
00000000
01100110
01100110
00000000
01100110
01100110
00000000
00000000
NAME GPIO pins
TIL 1u
00000000
00100010
01110000
00100100
00001110
00000100
01000000
00010000
>
00010000
01000000
11100010
01000000
00001000
00011100
00001000
00000000
TIL 1v
11111100
10001100
10000100
10000100
10000100
10000100
10000100
11111100
TIL 1w
01111100
00000011
01111001
10000101
10000101
10011001
11000010
00111110
>
00111110
00000010
11111001
10000101
10100101
10011001
01000011
01111100
NAME spiral
TIL 1x
00001000
00001000
00001000
00001000
00001000
00001000
00001000
00001000
TIL 1y
00000000
00000000
11111111
00000000
00000000
00000000
00000000
00000000
TIL 1z
00001000
00001000
11111111
00001000
00001000
00001000
00001000
00001000
TIL 2a
00000000
00000000
11111111
11111111
00000000
00000000
00000000
00000000
>
00000000
00000000
10101010
10101010
00000000
00000000
00000000
00000000
TIL 2b
00001100
00001100
00001100
00001100
00001100
00001100
00001100
00001100
>
00001100
00000000
00001100
00000000
00001100
00000000
00001100
00000000
TIL 2c
00001100
00001100
11111111
11111111
00001100
00001100
00001100
00001100
>
00001100
00000000
10100001
10100001
00000000
00001100
00000000
00001100
TIL 2d
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
>
00000000
00111000
00010000
00010000
00010000
00010000
00111000
00000000
TIL 2e
11110000
00011000
00010100
00010010
00011111
00000001
00000001
00000001
TIL 2f
00011111
00010000
00010000
00010000
00010000
00010000
00010000
00010000
TIL 2g
00010000
00010000
00010000
00010000
00010000
00010000
00010000
00011111
TIL 2h
00000001
00000001
00000001
00000001
00000001
00000001
00000001
11111111
TIL 2i
00000000
00000000
10101010
01010111
10101010
01010101
10101010
01010101
>
00000000
00000000
10101010
01010101
10101010
01010101
11101010
01010101
TIL 2j
00001011
00010101
00001010
00010111
00001010
00010101
00001010
00010101
>
00001011
00010101
00001010
00010101
00001010
00010101
00001010
00010101
TIL 2k
00001011
00010101
10101010
01010111
10101010
01010101
10101010
01010101
>
00001011
00010101
10101010
01010101
10101010
01010101
11101010
01010101
TIL 2l
10101011
01010101
10101010
01010111
10101010
01010101
00000000
00000000
>
10101011
01010101
10101010
01010101
10101010
01010101
00000000
00000000
TIL 2m
10101011
01010101
10101010
01010111
10101010
01010101
00101010
00010101
>
10101011
01010101
10101010
01010101
10101010
01010101
00101010
00010101
TIL 2n
11111000
11111000
11111000
11111000
11111000
11111000
11111000
11111000
TIL 2o
11111000
11111000
11111000
11111111
11111111
11111111
11111111
11111111
TIL 2p
00000000
00000000
00000000
11111111
11111111
11111111
11111111
11111111
TIL 2q
00011111
00011111
00011111
11111111
11111111
11111111
11111111
11111111
TIL 2r
00011111
00011111
00011111
00011111
00011111
00011111
00011111
00011111
TIL 2s
11111111
11111111
11111111
11111111
10111111
10111111
00011111
00011111
TIL 2t
11111111
11111111
11111111
00000011
00000001
00000000
00000000
00000000
TIL 2u
11111111
11111111
11111111
11111111
11111110
11111000
11111000
11111000
TIL 2v
00011111
00011111
00011111
00011111
00011111
00000000
00000000
00000000
TIL 2w
11000000
11100000
11110000
11111000
11111100
11111110
01111111
00111111
TIL 2x
00011111
00001111
00000111
00000011
00000001
00000000
00000000
00000000
TIL 2y
00000000
00000000
00000000
00000000
00000000
00000000
00000000
10000000
TIL 2z
00000000
00000000
00000000
01111110
01111110
00000000
00000000
00000000
TIL 3a
00010000
00010000
00010000
00010000
00010000
00010000
00010000
00010000
TIL 3b
00000001
00000001
00000001
00000001
00000001
00000001
00000001
00000001
TIL 3e
11110111
11111111
11111111
10101101
11111111
01111111
11110111
10111111
>
11111111
10111101
11111111
11111111
10111111
11101111
11111101
11111111
WAL true
TIL 3f
11111111
01111111
11111111
11111101
11111111
11111111
11011111
11111111
>
11111110
11111111
11110111
11111111
11111111
11101111
11111101
11110111
WAL true
TIL 3g
11111111
11111111
11111111
11111111
11111111
11111111
11011111
11111111
>
11111111
11111111
11111111
11111101
11111111
11111111
11111111
11111111
WAL true
TIL 3h
11111111
11111101
11111111
11111111
11111111
11111111
11111111
11111111
>
11111111
11111111
11111111
11111111
11111111
11111111
10111111
11111111
WAL true
TIL 3j
00000000
01000000
01011100
01010100
01011100
01000100
01011100
01000000
TIL 3k
00000000
00000000
00000011
00000001
00000001
00000001
00000001
00000000
TIL 3l
00000000
00000000
00000111
00000001
00000111
00000100
00000111
00000000
TIL 3m
00000000
00000000
00000111
00000001
00000111
00000001
00000111
00000000
TIL 3n
00000000
00000000
00000100
00000101
00000111
00000001
00000001
00000000
TIL 3o
00000000
00000000
00000111
00000100
00000111
00000001
00000111
00000000
TIL 3p
00000000
00000000
00000111
00000100
00000111
00000101
00000111
00000000
TIL 3q
00000000
00000000
00000111
00000001
00000011
00000010
00000010
00000000
TIL 3r
00000000
00000000
00000111
00000101
00000111
00000101
00000111
00000000
TIL 3s
00000000
00000000
00000111
00000101
00000111
00000001
00000111
00000000
TIL 3t
00000000
00000000
01100000
01100000
00000000
01100000
01100000
00000000
NAME :
TIL 3u
00110000
01111000
11111100
11111100
01111000
00110000
00000000
00000000
>
00000000
00000000
00001100
00011110
00111111
00111111
00011110
00001100
NAME ball
TIL 3v
00000111
00000111
00011100
11111000
11110000
11110000
11100000
10000000
TIL 3w
00110000
00110000
01100000
01111111
11111111
11111111
11111111
01111111
TIL 3x
01100000
01100000
01100000
01100000
01100000
01100000
01100000
01100000
TIL 3y
00000000
00000000
00000000
00000000
00000000
00000000
10000000
10000000
TIL 3z
00000000
00000000
00000000
00000000
01100000
11100000
11100000
10110000
TIL 4a
11111111
10101100
11111101
10101101
11111101
11111100
11111111
11111111
TIL 4b
11111111
00000000
00110110
00110110
00000000
00110110
00110110
00000000
TIL 4c
11100010
11100010
11100010
11100010
11100010
11100010
11100000
11100000
TIL 4d
11111111
11111111
11111111
11010101
10000000
11000101
10000101
11000001
TIL 4e
11111111
11111111
11111011
01011001
00001100
00011110
00001011
00011001
TIL 4f
10000101
11000101
10000101
11010101
10000000
11010101
11111111
11111111
TIL 4g
00001100
00011110
00001111
00011111
00001011
01011111
11110101
11111111
TIL 4h
00000000
00000000
00000000
00000000
00000000
11111111
11111111
11111111
TIL 4i
11111111
11111111
11111111
01010111
00000011
00000111
00000011
11100111
TIL 4j
10000000
11001111
10000000
11001110
10010010
11010010
10000000
11010101
TIL 4k
00000011
11100111
00000011
01100111
11100011
10100111
00000011
01010111
TIL 4l
00011111
00011111
00011111
00000111
00000111
00010111
00011111
00011111
TIL 4m
00000011
11110011
00001011
00001011
00001011
00001011
00001011
00001011
TIL 4n
00001011
00001011
00001011
00001011
00001011
00001011
00001011
00001011
TIL 4o
00001011
00001011
00001011
00001011
00001011
00001011
11110011
00000011
TIL 4p
01010000
01010000
01010000
01010000
01010000
00000000
11111111
00000000
TIL 4q
01000001
01000001
01000001
01000001
01010001
01000000
00111111
00000000
TIL 4r
01000000
01000000
01000000
01000001
01000001
01000001
01000001
01000001
TIL 4s
00000000
00111111
01000000
01000000
01000000
01000000
01000000
01000000
TIL 4t
00000000
11111111
00000000
00000000
00000000
01000000
01000000
01000000
TIL 4u
01000000
01010000
01010000
01010000
01000000
01010000
01010000
01010000
TIL 4v
00011111
00011111
00011111
00011111
00011111
00011111
00011111
00011111
TIL 4w
01000000
01000000
00000000
00000000
00000000
00000000
00000000
00000000
TIL 4x
00000000
00000000
00000000
00110100
00101000
00010100
00101100
00000000
TIL 4y
00100000
00100000
00100000
00100000
00100101
00100101
00100101
00100101
TIL 4z
00000000
00000000
00100000
00000000
00000000
00000000
00000000
00000000
TIL 5a
01100110
00111100
01111110
10100101
11011011
01011010
01100110
00111100
TIL 5f
00010110
00100011
00100100
00011010
00010001
00001010
00000110
00000001
TIL 5g
00000000
00000000
00000000
00000000
00111000
01010100
10100010
11011100
TIL 5h
00000000
00000000
00000000
00000000
00011100
00101010
01000101
00111011
TIL 5i
01101000
01000100
00100100
01011000
10001000
01010000
01100000
10000000
TIL 5j
01011111
11101111
01101111
10011111
11011111
10111111
01111111
11111111
TIL 5k
10100110
01110010
01101111
10010110
10111001
11010110
11100110
11111001
TIL 5l
11111111
11111111
11111111
11111111
10001111
01010110
11101001
00010000
TIL 5m
11111111
11111111
11111111
11111111
00011111
10101111
01110111
10001111
TIL 5n
11111111
11111111
11111111
11111111
11111111
11111111
11111110
11111111
TIL 5o
00110100
00110100
01111110
01111110
00111100
01111110
01111110
00110100
NAME hash
TIL 5p
00111100
01111110
01111110
01111110
01111110
01111110
01111110
00111100
NAME O filled
TIL 5q
01111100
01111110
01111110
01111100
01111110
01111110
01111110
01111110
NAME B filled
TIL 5r
01111110
01111110
01111110
01111110
01111100
01110000
01100000
01100000
NAME P filled
TIL 5s
00111100
01111110
01111110
01111110
01111110
01111110
01100110
01100110
NAME A filled
TIL 5t
00000000
00011000
00001100
01111110
01111110
00001100
00011000
00000000
>
00000000
00110000
00011000
11111100
11111100
00011000
00110000
00000000
TIL 5u
11100010
11100010
11100010
11100010
11100010
11100010
11100010
11100010
TIL 5v
11100000
11100000
11100000
11100000
11100010
11100010
11100010
11100010
TIL 5w
01001000
01001000
01001000
01001000
01001000
01001000
01001000
01001000
TIL 5x
01001001
01001001
01001001
01001001
01001001
01001001
01001001
01001001
TIL 5y
00000000
00000000
01000000
01001000
01001000
01001000
01001000
01001000
TIL 5z
11000000
11000000
11000000
11111111
11111111
11111111
11111111
11111111
TIL 6a
00010001
00010001
00010001
11100000
00000000
00000000
00000000
00000000
WAL false
TIL 6b
00010000
00010000
00010000
11100000
00000000
00000000
00000000
00000000
WAL false
TIL 6c
00001000
00001000
00001100
00000010
00000010
00000001
00000000
00000000
WAL false
TIL 6d
00000000
00000000
00000000
00000001
00000001
11111110
00000000
00000000
WAL false
TIL 6e
01000000
01000000
11000000
00000000
00000000
00000000
00000000
00000000
WAL false
TIL 6f
00111100
01111110
01100110
01100110
01100110
01100110
01111100
00111110
TIL 6g
01111110
01100110
00000110
00001100
00111000
01110000
01110110
01111110
TIL 6h
00000000
00000000
00000000
01111110
01111110
00000000
00000000
00000000
NAME -
TIL 6i
00000000
00000000
00000000
00000000
00000000
00000000
01100000
01100000
NAME .
TIL 6j
00000000
00000000
00000000
00000000
00000000
01100000
01100000
00100000
TIL 6k
00110000
00110000
00000001
00000011
00000000
00000000
00000000
00000000
TIL 6l
00011000
00011000
00000000
10000000
00000000
00000000
00000000
00000000
TIL 6m
10010010
11110010
10010010
10011111
10011110
10011110
10011110
11111110
TIL 6n
11100111
11111111
11000111
11000101
11111111
01000111
01000101
01111111
TIL 6o
11111111
11111111
11111111
11111111
10101010
10101010
00000000
00000000
TIL 6p
00110001
00110001
00000000
00000000
00000000
00000000
00000000
00010001
TIL 6q
10011111
10011111
00011111
00011111
00011111
00011111
00011111
00011111
TIL 6r
11111111
11111000
11111000
01011000
00001000
00011000
00001111
00011000
TIL 6s
00001000
00011111
00001000
00011000
00001111
01010010
11110010
11110011
TIL 6t
11111111
01111111
00100100
00100100
00100100
00100100
11111111
11111111
TIL 6u
11111111
11111111
11111111
00000000
11111110
10010010
11111110
00000000
TIL 6v
11111111
11111111
11111111
11110000
11110111
11110100
11110111
11110000
TIL 6w
11111111
11111111
11111111
11111111
10011111
10011111
10010011
10010010
TIL 6x
00111111
00111111
00110110
11111111
10101010
10101010
00000000
00000000
TIL 6y
00000011
00000011
01010111
11111100
11111111
11100100
11100111
11100111
TIL 6z
11111111
11010101
11000001
11000001
11000001
11000001
11000001
11010101
TIL 7a
11100000
11100000
11100111
11111111
11111111
01100110
01100111
01100011
NAME R-1
TIL 7b
01110000
01110000
01110000
00000000
00000000
00000000
00000000
00000000
NAME R-2
TIL 7c
00001110
01111111
01111111
01110000
01100000
01100000
11100000
11100000
NAME R-3
TIL 7d
01100000
01100000
11000000
11000000
00000000
00000000
10000000
11000000
NAME R-4
TIL 7e
00000000
10000000
11110000
11110000
00110000
00110000
01110000
01110000
NAME R-5
TIL 7f
11000000
11100000
11100000
00000000
00000000
00000000
00000000
00000000
NAME R-6
TIL 7g
11100011
11100011
11101110
11111110
11111111
11100111
11100111
11100000
NAME K-1
TIL 7h
01100000
01100000
01100000
00000000
00000000
00000000
00000000
00000000
NAME K-2
TIL 7i
00000000
01100000
01110000
01110000
01110000
01110000
01110000
01110000
NAME K-3
TIL 7j
00000000
00110000
01110000
01110000
11100000
11100000
11000000
11000000
NAME K-4
TIL 7k
11100000
11100000
01110000
01110000
00000000
00000000
00000000
00000000
NAME K-5
TIL 7l
11000000
11000000
00000000
00000000
10000000
10000000
11000000
11000000
NAME K-6
TIL 7m
00000011
00000011
00000011
00000110
00001110
00001100
00001100
00001100
NAME W-7
TIL 7n
00001100
00011100
00011100
00011100
00011000
00011000
00011000
00011000
NAME W-6
TIL 7o
11011000
11111000
00110000
00000000
00000000
00000000
00000000
00000000
NAME W-3
TIL 7p
11110011
01110000
01110000
00000000
00000000
00000000
00000000
00000000
NAME W-3
TIL 7q
10001100
10001110
10001110
10001110
10001110
10011110
10011011
10010011
NAME W-5
TIL 7r
00000001
00000001
00000001
00000001
00000001
00000001
00000001
00000001
NAME W-2
TIL 7s
00000001
00000011
00000011
00000011
00000011
00000011
00000011
00000011
NAME W-1
TIL 7t
11111000
00001000
00001000
00001000
00001000
00001000
00001000
00001000
TIL 7u
00001000
00001000
00001000
00001000
00001000
00001000
00001000
00001000
TIL 7v
00010000
00010000
00010000
00010000
00010000
00010000
00010000
00010000
TIL 7w
11111111
00000000
00000000
00000000
00000000
00000000
00000000
00000000
TIL 7x
00001000
00001000
00001000
00001000
00001000
00001000
00001000
11111000
TIL 7y
11111111
11011101
10001111
11011011
11110001
11111011
10111111
11101111
>
11101111
10111111
00011101
10111111
11110111
11100011
11110111
11111111
TIL 7z
11111111
11111111
11101111
11000111
11101111
11111011
11111111
11111111
>
11111111
11111111
11111111
11110111
11100011
11110111
10111111
11111111
TIL 8a
01111000
01111000
01111000
01110000
01110000
01110000
01110000
01110000
NAME L-3
TIL 8b
00110000
11110000
11110000
11110000
00000000
00000000
00000000
00000000
NAME L-4
TIL 8c
01000001
01111111
01111000
11100000
11100000
01110000
01110000
01110000
NAME A-1
TIL 8d
11100000
11100000
11000000
11000000
11000000
10000000
00000000
00000000
NAME A-2
TIL 8e
00011111
00111111
00110000
11110000
11110000
11000000
11000000
11000000
NAME A-3
TIL 8f
11100000
11100000
11100000
11100000
01110000
01110000
01110000
01110000
NAME A-4
TIL 8g
11111000
11111000
01110000
01110000
01110000
01110000
01110000
00110000
NAME A-5
TIL 8h
00110000
00110000
00110000
00000000
00000000
00000000
00000000
00000000
NAME A-6
TIL 8i
11100000
11100000
01100000
01100000
01111000
01111000
00011100
00011100
NAME Y-1
TIL 8j
00011100
00001100
00001100
00001111
00000011
00000011
00001110
00001110
NAME Y-2
TIL 8k
00011110
00011110
00011110
00001110
00001100
00000000
00000000
00000000
NAME Y-3
TIL 8l
00011100
00011100
00011100
00011100
00111000
00111000
01110000
01110000
NAME Y-4
TIL 8m
01110000
01110000
01100000
11100000
10000000
10000000
00000000
00000000
NAME Y-5
TIL 8n
01110000
01110000
01110000
01110000
01110000
01110000
01110000
01110000
NAME G-1
TIL 8o
00111000
00111000
00111111
00001111
00000000
00000000
00000000
00000000
NAME G-2
TIL 8p
00000111
00000111
00011100
00011100
00011100
00011100
01110000
01110000
NAME G-3
TIL 8q
11110000
11111100
00001100
00011100
00011100
00011100
00000000
00000000
NAME G-4
TIL 8r
00111000
01100000
11100000
11000000
00000000
00000000
00000000
00000000
NAME G-5
TIL 8s
00000000
00000000
11111100
01111100
00001100
00001100
00111000
00111000
NAME G-6
TIL 8t
00111000
00111000
00111000
00111111
00111111
01110011
01110011
01110011
NAME M-1
TIL 8u
00111000
00111000
00111000
00111000
00111000
01110000
01110000
01110000
NAME M-2
TIL 8v
01110000
01110000
01110000
01110000
00000000
00000000
00000000
00000000
NAME M-3
TIL 8w
00001110
00001110
00111110
00111110
00110110
00110110
11100110
11100110
NAME M-4
TIL 8x
11101110
11001110
11001110
00001110
00001110
00000110
00000110
00000110
NAME M-5
TIL 8y
00001100
00001100
00001100
00000100
00000000
00000000
00000000
00000000
NAME M-6
TIL 8z
00001111
00001111
00111110
00110000
00110000
00110000
00110000
00110000
NAME E-1
TIL 9a
00000000
00110000
00110001
00011111
00000000
00000000
00000000
00000000
NAME S-3
TIL 9b
11111000
11111110
00000110
00000010
00000000
00000000
00000000
00000000
NAME S-4
TIL 9c
00000000
00000000
11111000
11111000
00001100
00001100
00001100
00011100
NAME S-5
TIL 9d
00011000
00111000
11100000
11100000
00000000
00000000
00000000
00000000
NAME S-6
TIL 9e
00111000
00110000
00110000
00110000
01110000
01110000
01110000
01110000
NAME U-1
TIL 9f
01110000
01110000
01110000
01110000
01110000
01110000
00110000
00110000
NAME U-2
TIL 9g
00110001
00111111
00001110
00000000
00000000
00000000
00000000
00000000
NAME U-3
TIL 9h
11111000
11000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME U-4
TIL 9i
00011100
00011100
00011100
00011100
00011100
00011100
00111000
00111000
NAME U-5
TIL 9j
00000100
00001100
00001100
00001100
00001100
00001100
00001100
00001100
NAME U-6
TIL 9k
01110001
01111111
01111110
01100000
01100000
01100000
01100000
01100000
NAME B-1
TIL 9l
00111111
00111111
01100000
01100000
01100000
01100000
01100000
01100000
NAME B-2
TIL 9m
01100000
01100011
01111111
01111100
00000000
00000000
00000000
00000000
NAME B-3
TIL 9n
11100000
11100000
00011000
00011000
00001100
00001100
00111000
00111000
NAME B-4
TIL 9o
11100000
11111000
00011000
00011100
00001100
00001100
00111000
00111000
NAME B-5
TIL 9p
11110000
10000000
10000000
00000000
00000000
00000000
00000000
00000000
NAME B-6
TIL 9q
00111111
00111111
00110000
00110000
01110000
01110000
01110000
01110000
NAME F-1
TIL 9r
01110011
00111111
00111100
00110000
00110000
01100000
01100000
01100000
NAME F-2
TIL 9s
01110000
01110000
01110000
00110000
00000000
00000000
00000000
00000000
NAME F-3
TIL 9t
11000000
11111000
00111000
00000000
00000000
00000000
00000000
00000000
NAME F-4
TIL 9u
11000000
10000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME F-5
TIL 9v
00001111
00011000
00110000
00110000
01100000
01100000
01100000
01100000
NAME C-1
TIL 9w
11100000
01110000
01110000
00000000
00000000
00000000
00000000
00000000
NAME C-2
TIL 9x
11000000
11000000
11000000
11000000
11000000
11000000
11000000
11000000
NAME C-3
TIL 9y
01100011
00111111
00111110
00000000
00000000
00000000
00000000
00000000
NAME C-4
TIL 9z
00000000
00000000
00000000
00000000
00000000
00000000
01100000
11100000
NAME C-5
TIL a0
11100000
10000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME C-6
TIL a1
00000001
00000001
00000001
00000001
00000001
00000001
00000001
00000001
>
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME door
WAL false
TIL a2
01010101
00000000
00000001
00000000
01010101
11111111
01010101
11111111
>
10101010
00000001
00000000
00000001
10101010
00000000
10101010
00000000
NAME tunnel
WAL false
TIL a3
00000000
00000000
00011110
11111111
11111000
00000000
00000000
00000000
NAME dash
TIL a4
11111111
11111111
11111111
11111111
11111111
00000000
00000000
00000000
NAME ghost files
TIL a5
11111111
11111111
11111111
11111111
11111111
00011111
00011111
00011111
NAME ghost files
TIL a6
11111111
11111111
11111111
11111111
11111111
11111000
11111000
11111000
NAME ghost files
TIL a7
01110111
01000111
01110000
01000000
01110000
00000000
00000000
00000000
>
00000000
10101010
10101010
10101010
10101010
00000000
00000000
00000000
TIL a8
01111101
01000101
01101100
01000100
01101100
01000100
01111100
00000000
>
00000000
10101010
10101010
10101010
10101010
00000000
00000000
00000000
TIL a9
10111110
10100010
00101110
00100010
00101010
00100010
00111110
00000000
>
00000000
10101010
10101010
10101010
10101010
00000000
00000000
00000000
TIL aa
00001100
00001100
00001100
00001100
00001100
00001100
00001100
00001100
>
00000000
00001100
00000000
00001100
00000000
00001100
00000000
00001100
TIL ab
00011111
00001111
00000111
00000011
00000001
00000000
00000000
00000000
>
00011111
00001111
01000111
01000011
00000001
00000000
00000000
00000000
TIL ac
10101010
00000000
10101010
00000000
10101010
00000000
10101010
00000000
WAL true
TIL ad
11111111
00000000
11111111
00000000
11111111
00000000
11111111
00000000
WAL false
TIL ae
11111100
00000100
11111100
00000100
11111100
00000100
11111100
00000100
WAL false
TIL af
00111111
00100000
00111111
00100000
00111111
00100000
00111111
00100000
WAL false
TIL ag
00000011
01010000
00100011
01010000
00000011
00000000
11111111
00000100
TIL ah
00000000
00000000
11110000
00000000
00000000
00000000
00000000
00000000
TIL ai
11111111
00000000
11111111
00000000
11111111
00000000
11111111
00000100
WAL false
TIL aj
11111111
00000000
11111111
00000000
11111111
00000000
11111111
00100000
WAL false
SPR A
00000000
00111100
00100100
00111100
00010000
00011000
00010000
00011000
POS g 13,2
SPR a
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
>
00000000
00111100
00000100
00011100
00010000
00000000
00010000
00000000
NAME friend
ITM 0
00000000
00000100
00000000
00000000
00000000
00000000
00000000
00000000
>
00000000
00000000
00001000
00000000
00000000
00000000
00000000
00000000
NAME item 1
ITM 1
00000000
00000000
00000000
00000000
00000000
00001000
00000000
00000000
>
00000000
00000000
00000000
00000000
00000000
00000000
00000100
00000000
NAME item 2
ITM 2
00000000
01111110
00111100
00011000
00011000
00111100
01111110
00000000
NAME item 3
ITM 3
00000000
01111000
01100110
01011010
01011010
01100110
00011110
00000000
ITM 4
01010100
10101010
00000001
00101010
01010100
10000000
01010101
00101010
>
10101010
01010101
00000010
01010100
10101000
01000000
00101010
00010101
DLG a
ITM 5
01111100
01111100
00001100
00111000
00110000
00000000
00110000
00110000
>
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME ?
DLG b
ITM 6
00111110
00111110
00000110
00011100
00011000
00000000
00011000
00000000
>
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME ? time
DLG g
ITM 7
00000000
00111110
00111110
00000110
00011100
00011000
00000000
00011000
>
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
NAME ? now
DLG l
ITM 8
00011111
00000001
00111101
00000101
01110101
00010100
11010000
01000000
>
00000000
00111110
00000010
01111010
00001010
11101010
00101000
00100000
NAME echo
DLG 1a
ITM 9
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME CPU
DLG z
ITM a
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME RAM
DLG y
ITM b
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME WIFI BLE
DLG x
ITM c
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME GPIO
DLG w
ITM d
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME ETHERNET
DLG v
ITM e
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME USB 2
DLG 18
ITM f
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME AUDIO
DLG t
ITM g
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME CAMERA
DLG s
ITM h
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME MICRO HDMI
DLG r
ITM i
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME POWER
DLG q
ITM j
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME MICRO SD
DLG p
ITM k
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME USB 3
DLG 10
ITM l
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME POE Pins
DLG 11
ITM m
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME DISPLAY
DLG 12
ITM n
00000000
00011000
00011000
01011010
01111110
00111100
00011000
00000000
>
11111111
11100111
11100111
10100101
10000001
11000011
11100111
11111111
NAME MOTHERBOARD
DLG 17
DLG 8
"""
{
- {item "4"} < 82 ?
Clear your Thoughts!{property locked true}
- {item "4"} == 82 ?
Scattered Thoughts, they feel like Ghosts haunting me.{property locked false}
}
"""
NAME Fog Room Exit
DLG 10
USB 3{pg}The USB (Universal Serial Bus) port enables a connection between the Raspberry Pi and other external devices.{pg}Via the USB port data from an input device (e.g. keyboard, mouse ...) can be sent to the computer or information from the computer can be sent to an output device (e.g. printer).
NAME USB 3 dialog
DLG 11
POE Pins{pg}The Power over Ethernet (PoE) pins allow the Ethernet network cable to carry not only data, but also electric power.{pg}With a separate PoE HAT the Ethernet connection becomes the new lifeline of the Raspberry Pi. The traditional POWER supply is no longer needed.
NAME POE Pins dialog
DLG 12
MIPI DSI{pg}The MIPI DSI (Mobile Industry Processor Interface Display Serial Interface) is a display port that enables the connection from the Raspberry Pi to a display.
NAME MIPI DSI dialog
DLG 13
... Here is a little fun fact before you leave ...{pg}This game you are playing right now is stored and in this very moment running on a Raspberry Pi.
NAME ending exit 3D pi
DLG 14
This is a Raspberry Pi 4B.{pg}A Raspberry Pi is a tiny and low-cost single-board computer that enables people to explore computing and to learn how to program in languages like Python.{pg}A computer receives data through an input unit and after it processes the data, it sends it back through output devices.{pg}The Raspberry Pi runs Linux and provides a set of GPIO (general purpose input/output) pins, allowing you to control electronic components for physical computing and explore the Internet of Things (IoT).{pg}Welcome inside the Raspberry Pi!
NAME exit 2D enter pi3D
DLG 15
NAME ending 1
DLG 16
pi3D
NAME pi3D
DLG 17
MOTHERBOARD{pg}The Motherboard acts as the home for all other components of the computer. It provides the physical connection that allows them to communicate with each other and gives them power in order to function. Without a Motherboard, there can't be a computer.
NAME MOTHERBOARD dialog
DLG 18
USB 2{pg}The USB (Universal Serial Bus) port enables a connection between the Raspberry Pi and other external devices.{pg}Via the USB port data from an input device (e.g. keyboard, mouse ...) can be sent to the computer or information from the computer can be sent to an output device (e.g. printer).
NAME USB 2 dialog
DLG 19
The end
NAME ending 2
DLG a
"""
{
- {item "4"} == 1 ?
The Future is foggy. Scary, but also exciting, right? You will see more clearly soon ...
- {item "4"} == 34 ?
Oh ... It is January 2026 and nothing changed? Do we all agree on this reality? Is there no way out?
}
"""
NAME Foggy Future Dialogue
DLG b
Yes ...{pg}but ...{pg}... is there a way to escape the system? Or is each loophole part of it?
NAME ? dialog
DLG c
You walk through the doorway
NAME exit narration 1
DLG d
"""
{
- {item "5"} >= 1 ?
{property locked false}Lets try to find a way out!
- else ?
{property locked true}...
}
"""
NAME locked exit 1
DLG g
This is a tricky choice! Here is a rough thought: Is the Past behind us, while we look ahead watching an unknown Future unfold? Or is the Past rather in front of us (where we can see it) and we are walking backwards into an unknown Future? Where is the Present? What if Time is not linear? {printTile "1w"}
NAME ? time
DLG h
"""
{
- {item "6"} >= 1 ?
{property locked false}You chose the past ...
- else ?
{property locked true}...
}
"""
NAME locked exit to past
DLG j
"""
{
- {item "6"} >= 1 ?
{property locked false}You chose the future ...
- else ?
{property locked true}...
}
"""
NAME locked exit to future
DLG k
"""
{
- {item "4"} >= 90 ?
{property locked false}
- else ?
{property locked true}What does the future hold?
}
"""
NAME locked exit from foggy future
DLG l
So, this is Now? It looks incredibly sad ... So many walls that cannot be overcome. This path is predetermined by the system ... Is an exit that is part of the system an opportunity to escape the system ...?{pg}... 1, 2, 3 or 4?
NAME now dialog
DLG m
"""
{
- {item "6"} >= 1 ?
{property locked false}You chose the present ...
- else ?
{property locked true}...
}
"""
NAME locked exit to present
DLG n
"""
{
- {item "7"} >= 1 ?
{property locked false}
- else ?
{property locked true}...
}
"""
NAME locked exit from present
DLG o
In this room you only encounter information and opinions that reflect and reinforce their own. Your thoughts are amplified inside this closed system. Is history repeating itself?{pg}This is the center of the echo chamber. {pg}You are the center of the echo chamber.{pg}This is the only way out ...
NAME exit narration from past
DLG p
MICRO SD{pg}The MICRO SD is the long term memory of the Raspberry Pi. This is where the Operating System (OS), software and data are stored permanently.{pg}The MICRO SD slot sits on the bottom side of the Motherboard and the card can be changed manually.
NAME MICRO SD dialog
DLG q
POWER{pg}The Raspberry Pi is an electronic device that requires a POWER supply to work. It is the lifeline of this computer.
NAME POWER dialog
DLG r
MICRO HDMI{pg}The MICRO HDMI (High Definition Multimedia Interface) port enables the connection from the Raspberry Pi to external devices such as SmartPhones and Action Cameras.
NAME MICRO HDMI dialog
DLG s
MIPI CSI{pg}The MIPI CSI (Mobile Industry Processor Interface Camera Serial Interface) is a camera port that enables the connection from the Raspberry Pi to a camera.
NAME MIPI CSI dialog
DLG t
AUDIO / VIDEO{pg}The AUDIO and VIDEO (A/V) Output Jack enables the connection from the Raspberry Pi to an audio or video output device.
NAME AUDIO dialog
DLG v
ETHERNET{pg}The ETHERNET network adapter is the connection to the outside world as it provides access to the internet or other local networks.
NAME ETHERNET dialog
DLG w
GPIO{pg}A general-purpose input/output (GPIO) is a digital signal pin that can be used as an input or output, or both.{pg}The 40 GPIO pins on the Raspberry Pi can be used to connect the computer to other electronic input and output devices (e.g. sensors, motors, lights, buttons and other peripherals).
NAME GPIO dialog
DLG x
WIFI / BLE{pg}The WIFI / BLE (Bluetooth Low Energy) component enables a wireless connection to the outside world as it provides access to the internet or other local networks and devices.
NAME WIFI BLE dialog
DLG y
RAM{pg}The RAM (Random-Access Memory) is a short term memory and together with the CPU builds the brain of the Raspberry Pi.{pg}It stores data that is used by the Operating System (OS) and software so that the CPU can process them quickly.{pg}Every time the computer is shut off, all information stored on RAM is lost.
NAME RAM dialog
DLG z
CPU{pg}The CPU (Central Processing Unit) is the processor and together with the RAM builds the brain of the Raspberry Pi.{pg}It is considered the most important component in a computer since it handles most operations that make it function:{pg}Receiving data from an input unit, processing the information and then sending it back through an output device.
NAME CPU dialog
DLG 1a
"""
{sequence
- Hello? ... Hello? Hello?
- ...Is this an echo chamber? ... chamber? chamber?
- What is this? is this? is this? ...
- Who's here? ... here? here?
- Hello? ... Hello? Hello?
- Echo! ... Echoe! Echoe!
- Hello? ... Hello? Hello?
- ...Is this an echo chamber? ... chamber? chamber?
- What is this? is this? is this? ...
- Who's here? ... here? here?
- Hello? ... Hello? Hello?
}
"""
NAME echo
DLG 1b
Yes, you are allowed to leave the Imaginary Library of Ghost Files. But be aware:{pg}{shk}Some ghosts could get stuck in your mind and haunt you.{shk}
NAME ending ghost files
VAR a
42
</script>
<style>
html {
margin:0px;
padding:0px;
}
body {
margin:0px;
padding:0px;
overflow:hidden;
background:#000000;
}
#game {
background:black;
width:100vw;
max-width:100vh;
margin:auto;
display:block;
}
</style>
<!-- SCRIPTS -->
<script>
function startExportedGame() {
var gameCanvas = document.getElementById("game");
var gameData = document.getElementById("exportedGameData").text.slice(1);
var defaultFontData = document.getElementById(defaultFontName).text.slice(1)
attachCanvas(gameCanvas);
loadGame(gameData, defaultFontData);
}
</script>
<script>
/* logging */
var DebugLogCategory = {
system: false,
bitsy : false,
editor : false,
};
var isLoggingVerbose = false;
/* input */
var key = {
left : 37,
right : 39,
up : 38,
down : 40,
space : 32,
enter : 13,
w : 87,
a : 65,
s : 83,
d : 68,
r : 82,
shift : 16,
ctrl : 17,
alt : 18,
cmd : 224
};
var InputManager = function() {
var self = this;
var pressed;
var ignored;
var touchState;
var isRestartComboPressed = false;
var SwipeDir = {
None : -1,
Up : 0,
Down : 1,
Left : 2,
Right : 3,
};
function resetAll() {
isRestartComboPressed = false;
pressed = {};
ignored = {};
touchState = {
isDown : false,
startX : 0,
startY : 0,
curX : 0,
curY : 0,
swipeDistance : 30,
swipeDirection : SwipeDir.None,
tapReleased : false
};
}
resetAll();
function stopWindowScrolling(e) {
if(e.keyCode == key.left || e.keyCode == key.right || e.keyCode == key.up || e.keyCode == key.down || !isPlayerEmbeddedInEditor)
e.preventDefault();
}
function isRestartCombo(e) {
return (e.keyCode === key.r && (e.getModifierState("Control")|| e.getModifierState("Meta")));
}
function eventIsModifier(event) {
return (event.keyCode == key.shift || event.keyCode == key.ctrl || event.keyCode == key.alt || event.keyCode == key.cmd);
}
function isModifierKeyDown() {
return ( self.isKeyDown(key.shift) || self.isKeyDown(key.ctrl) || self.isKeyDown(key.alt) || self.isKeyDown(key.cmd) );
}
this.ignoreHeldKeys = function() {
for (var key in pressed) {
if (pressed[key]) { // only ignore keys that are actually held
ignored[key] = true;
// bitsyLog("IGNORE -- " + key, "system");
}
}
}
this.onkeydown = function(event) {
// bitsyLog("KEYDOWN -- " + event.keyCode, "system");
stopWindowScrolling(event);
isRestartComboPressed = isRestartCombo(event);
// Special keys being held down can interfere with keyup events and lock movement
// so just don't collect input when they're held
{
if (isModifierKeyDown()) {
return;
}
if (eventIsModifier(event)) {
resetAll();
}
}
if (ignored[event.keyCode]) {
return;
}
pressed[event.keyCode] = true;
ignored[event.keyCode] = false;
}
this.onkeyup = function(event) {
// bitsyLog("KEYUP -- " + event.keyCode, "system");
pressed[event.keyCode] = false;
ignored[event.keyCode] = false;
}
this.ontouchstart = function(event) {
event.preventDefault();
if( event.changedTouches.length > 0 ) {
touchState.isDown = true;
touchState.startX = touchState.curX = event.changedTouches[0].clientX;
touchState.startY = touchState.curY = event.changedTouches[0].clientY;
touchState.swipeDirection = SwipeDir.None;
}
}
this.ontouchmove = function(event) {
event.preventDefault();
if( touchState.isDown && event.changedTouches.length > 0 ) {
touchState.curX = event.changedTouches[0].clientX;
touchState.curY = event.changedTouches[0].clientY;
var prevDirection = touchState.swipeDirection;
if( touchState.curX - touchState.startX <= -touchState.swipeDistance ) {
touchState.swipeDirection = SwipeDir.Left;
}
else if( touchState.curX - touchState.startX >= touchState.swipeDistance ) {
touchState.swipeDirection = SwipeDir.Right;
}
else if( touchState.curY - touchState.startY <= -touchState.swipeDistance ) {
touchState.swipeDirection = SwipeDir.Up;
}
else if( touchState.curY - touchState.startY >= touchState.swipeDistance ) {
touchState.swipeDirection = SwipeDir.Down;
}
if( touchState.swipeDirection != prevDirection ) {
// reset center so changing directions is easier
touchState.startX = touchState.curX;
touchState.startY = touchState.curY;
}
}
}
this.ontouchend = function(event) {
event.preventDefault();
touchState.isDown = false;
if( touchState.swipeDirection == SwipeDir.None ) {
// tap!
touchState.tapReleased = true;
}
touchState.swipeDirection = SwipeDir.None;
}
this.isKeyDown = function(keyCode) {
return pressed[keyCode] != null && pressed[keyCode] == true && (ignored[keyCode] == null || ignored[keyCode] == false);
}
this.anyKeyDown = function() {
var anyKey = false;
for (var key in pressed) {
if (pressed[key] && (ignored[key] == null || ignored[key] == false) &&
!(key === key.up || key === key.down || key === key.left || key === key.right) &&
!(key === key.w || key === key.s || key === key.a || key === key.d)) {
// detected that a key other than the d-pad keys are down!
anyKey = true;
}
}
return anyKey;
}
this.isRestartComboPressed = function() {
return isRestartComboPressed;
}
this.swipeLeft = function() {
return touchState.swipeDirection == SwipeDir.Left;
}
this.swipeRight = function() {
return touchState.swipeDirection == SwipeDir.Right;
}
this.swipeUp = function() {
return touchState.swipeDirection == SwipeDir.Up;
}
this.swipeDown = function() {
return touchState.swipeDirection == SwipeDir.Down;
}
this.isTapReleased = function() {
return touchState.tapReleased;
}
this.resetTapReleased = function() {
touchState.tapReleased = false;
}
this.onblur = function() {
// bitsyLog("~~~ BLUR ~~", "system");
resetAll();
}
this.resetAll = resetAll;
}
var input = new InputManager();
/* events */
var onLoadFunction = null;
var onQuitFunction = null;
var onUpdateFunction = null;
var updateInterval = null;
function initSystem() {
// temp hack for the editor? unless??
drawingBuffers[screenBufferId] = createDrawingBuffer(width, height, scale);
drawingBuffers[textboxBufferId] = createDrawingBuffer(0, 0, textScale);
}
function loadGame(gameData, defaultFontData) {
drawingBuffers[screenBufferId] = createDrawingBuffer(width, height, scale);
drawingBuffers[textboxBufferId] = createDrawingBuffer(0, 0, textScale);
document.addEventListener('keydown', input.onkeydown);
document.addEventListener('keyup', input.onkeyup);
if (isPlayerEmbeddedInEditor) {
canvas.addEventListener('touchstart', input.ontouchstart, {passive:false});
canvas.addEventListener('touchmove', input.ontouchmove, {passive:false});
canvas.addEventListener('touchend', input.ontouchend, {passive:false});
}
else {
// creates a 'touchTrigger' element that covers the entire screen and can universally have touch event listeners added w/o issue.
// we're checking for existing touchTriggers both at game start and end, so it's slightly redundant.
var existingTouchTrigger = document.querySelector('#touchTrigger');
if (existingTouchTrigger === null) {
var touchTrigger = document.createElement("div");
touchTrigger.setAttribute("id","touchTrigger");
// afaik css in js is necessary here to force a fullscreen element
touchTrigger.setAttribute(
"style","position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; overflow: hidden;"
);
document.body.appendChild(touchTrigger);
touchTrigger.addEventListener('touchstart', input.ontouchstart);
touchTrigger.addEventListener('touchmove', input.ontouchmove);
touchTrigger.addEventListener('touchend', input.ontouchend);
}
}
window.onblur = input.onblur;
if (onLoadFunction) {
// todo : is this the right place to supply default font data?
onLoadFunction(gameData, defaultFontData);
}
updateInterval = setInterval(
function() {
if (onUpdateFunction) {
onUpdateFunction();
}
renderGame();
input.resetTapReleased();
if (bitsyGetButton(5)) {
if (confirm("Restart the game?")) {
input.resetAll();
reset_cur_game();
}
return;
}
},
16);
}
function renderGame() {
// bitsyLog("render game mode=" + curGraphicsMode, "system");
bitsyLog(systemPalette.length, "system");
var startIndex = curGraphicsMode === 0 ? screenBufferId : (drawingBuffers.length - 1);
for (var i = startIndex; i >= 0; i--) {
var buffer = drawingBuffers[i];
if (buffer && buffer.canvas === null) {
bitsyLog("render buffer " + i, "system");
renderDrawingBuffer(i, buffer);
}
}
// show screen buffer
var screenBuffer = drawingBuffers[screenBufferId];
ctx.drawImage(
screenBuffer.canvas,
0,
0,
screenBuffer.width * screenBuffer.scale,
screenBuffer.height * screenBuffer.scale);
}
function quitGame() {
document.removeEventListener('keydown', input.onkeydown);
document.removeEventListener('keyup', input.onkeyup);
if (isPlayerEmbeddedInEditor) {
canvas.removeEventListener('touchstart', input.ontouchstart);
canvas.removeEventListener('touchmove', input.ontouchmove);
canvas.removeEventListener('touchend', input.ontouchend);
}
else {
//check for touchTrigger and removes it
var existingTouchTrigger = document.querySelector('#touchTrigger');
if (existingTouchTrigger !== null) {
existingTouchTrigger.removeEventListener('touchstart', input.ontouchstart);
existingTouchTrigger.removeEventListener('touchmove', input.ontouchmove);
existingTouchTrigger.removeEventListener('touchend', input.ontouchend);
existingTouchTrigger.parentElement.removeChild(existingTouchTrigger);
}
}
window.onblur = null;
if (onQuitFunction) {
onQuitFunction();
}
clearInterval(updateInterval);
}
/* graphics */
var canvas;
var ctx;
var textScale = 2; // todo : move tile scale into here too?
var curGraphicsMode = 0;
var systemPalette = [[0, 0, 0]];
var curBufferId = -1; // note: -1 is invalid
var drawingBuffers = [];
var screenBufferId = 0;
var textboxBufferId = 1;
var tileStartBufferId = 2;
var nextBufferId = tileStartBufferId;
var DrawingInstruction = {
Pixel : 0,
Tile : 1,
Clear : 2,
Textbox : 3,
PixelIndex : 4,
};
function attachCanvas(c) {
canvas = c;
canvas.width = width * scale;
canvas.height = width * scale;
ctx = canvas.getContext("2d");
}
function createDrawingBuffer(width, height, scale) {
var buffer = {
width : width,
height : height,
scale : scale, // logical-pixel to display-pixel scale
instructions : [], // drawing instructions
canvas : null,
}
return buffer;
}
function renderPixelInstruction(bufferId, buffer, paletteIndex, x, y) {
if (bufferId === screenBufferId && curGraphicsMode != 0) {
return;
}
if (!systemPalette[paletteIndex]) {
// bitsyLog("invalid index " + paletteIndex + " @ " + x + "," + y, "system");
return;
}
var color = systemPalette[paletteIndex];
if (buffer.imageData) {
for (var sy = 0; sy < buffer.scale; sy++) {
for (var sx = 0; sx < buffer.scale; sx++) {
var pixelIndex = (((y * buffer.scale) + sy) * buffer.width * buffer.scale * 4) + (((x * buffer.scale) + sx) * 4);
buffer.imageData.data[pixelIndex + 0] = color[0];
buffer.imageData.data[pixelIndex + 1] = color[1];
buffer.imageData.data[pixelIndex + 2] = color[2];
buffer.imageData.data[pixelIndex + 3] = 255;
}
}
}
else {
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.fillStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
bufferContext.fillRect(x * buffer.scale, y * buffer.scale, buffer.scale, buffer.scale);
}
}
function renderPixelAtIndexInstruction(bufferId, buffer, paletteIndex, index) {
if (bufferId === screenBufferId && curGraphicsMode != 0) {
return;
}
if (!systemPalette[paletteIndex]) {
// bitsyLog("invalid index " + paletteIndex + " @ " + x + "," + y, "system");
return;
}
var color = systemPalette[paletteIndex];
if (buffer.imageData) {
for (var sy = 0; sy < buffer.scale; sy++) {
for (var sx = 0; sx < buffer.scale; sx++) {
var pixelIndex = index * 4;
buffer.imageData.data[pixelIndex + 0] = color[0];
buffer.imageData.data[pixelIndex + 1] = color[1];
buffer.imageData.data[pixelIndex + 2] = color[2];
buffer.imageData.data[pixelIndex + 3] = 255;
}
}
}
else {
var y = Math.floor(index / buffer.width);
var x = index - (y * buffer.width);
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.fillStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
bufferContext.fillRect(x * buffer.scale, y * buffer.scale, buffer.scale, buffer.scale);
}
}
function renderTileInstruction(bufferId, buffer, tileId, x, y) {
if (bufferId != screenBufferId || curGraphicsMode != 1) {
return;
}
if (!drawingBuffers[tileId]) {
return;
}
var tileBuffer = drawingBuffers[tileId];
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.drawImage(
tileBuffer.canvas,
x * tilesize * buffer.scale,
y * tilesize * buffer.scale,
tilesize * buffer.scale,
tilesize * buffer.scale);
}
function renderClearInstruction(bufferId, buffer, paletteIndex) {
var color = systemPalette[paletteIndex];
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.fillStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
bufferContext.fillRect(0, 0, buffer.canvas.width, buffer.canvas.height);
}
function renderTextboxInstruction(bufferId, buffer, x, y) {
if (bufferId != screenBufferId || curGraphicsMode != 1) {
return;
}
if (!drawingBuffers[textboxBufferId]) {
return;
}
var textboxBuffer = drawingBuffers[textboxBufferId];
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.drawImage(
textboxBuffer.canvas,
x * buffer.scale,
y * buffer.scale,
textboxBuffer.canvas.width,
textboxBuffer.canvas.height);
}
function renderDrawingBuffer(bufferId, buffer) {
// bitsyLog("render buffer " + bufferId, "system");
// if (bufferId === 0) {
// bitsyLog("instructions " + buffer.instructions.length, "system");
// }
buffer.canvas = document.createElement("canvas");
buffer.canvas.width = buffer.width * buffer.scale;
buffer.canvas.height = buffer.height * buffer.scale;
for (var i = 0; i < buffer.instructions.length; i++) {
var instruction = buffer.instructions[i];
switch (instruction.type) {
case DrawingInstruction.Pixel:
renderPixelInstruction(bufferId, buffer, instruction.id, instruction.x, instruction.y);
break;
case DrawingInstruction.Tile:
renderTileInstruction(bufferId, buffer, instruction.id, instruction.x, instruction.y);
break;
case DrawingInstruction.Clear:
renderClearInstruction(bufferId, buffer, instruction.id);
break;
case DrawingInstruction.Textbox:
renderTextboxInstruction(bufferId, buffer, instruction.x, instruction.y);
break;
case DrawingInstruction.PixelIndex:
renderPixelAtIndexInstruction(bufferId, buffer, instruction.id, instruction.index);
}
}
if (buffer.imageData) {
var bufferContext = buffer.canvas.getContext("2d");
bufferContext.putImageData(buffer.imageData, 0, 0);
}
}
function invalidateDrawingBuffer(buffer) {
buffer.canvas = null;
}
function hackForEditor_GetImageFromTileId(tileId) {
if (tileId === undefined || !drawingBuffers[tileId]) {
bitsyLog("editor hack::invalid tile id!", "system");
return null;
}
// force render the buffer if it hasn't been
if (drawingBuffers[tileId].canvas === null) {
renderDrawingBuffer(tileId, drawingBuffers[tileId]);
}
return drawingBuffers[tileId].canvas;
}
/* ==== */
function bitsyLog(message, category) {
if (!category) {
category = "bitsy";
}
var summary = category + "::" + message;
if (DebugLogCategory[category] === true) {
if (isLoggingVerbose) {
console.group(summary);
console.dir(message);
console.group("stack")
console.trace();
console.groupEnd();
console.groupEnd();
}
else {
console.log(summary);
}
}
}
function bitsyGetButton(buttonCode) {
switch (buttonCode) {
case 0: // UP
return (input.isKeyDown(key.up) || input.isKeyDown(key.w) || input.swipeUp());
case 1: // DOWN
return (input.isKeyDown(key.down) || input.isKeyDown(key.s) || input.swipeDown());
case 2: // LEFT
return (input.isKeyDown(key.left) || input.isKeyDown(key.a) || input.swipeLeft());
case 3: // RIGHT
return ((input.isKeyDown(key.right) || input.isKeyDown(key.d) || input.swipeRight()));
case 4: // OK (equivalent to "any key" on the keyboard or "tap" on touch screen)
return (input.anyKeyDown() || input.isTapReleased());
case 5: // MENU / RESTART (restart the game: "ctrl+r" on keyboard, no touch control yet)
return input.isRestartComboPressed();
}
return false;
}
// two modes (0 == pixel mode, 1 == tile mode)
function bitsySetGraphicsMode(mode) {
curGraphicsMode = mode;
var screenBuffer = drawingBuffers[screenBufferId];
if (curGraphicsMode === 0) {
screenBuffer.imageData = ctx.createImageData(screenBuffer.width * screenBuffer.scale, screenBuffer.height * screenBuffer.scale);
}
else {
screenBuffer.imageData = undefined;
}
}
function bitsySetColor(paletteIndex, r, g, b) {
systemPalette[paletteIndex] = [r, g, b];
// invalidate all drawing buffers
for (var i = 0; i < drawingBuffers.length; i++) {
if (drawingBuffers[i]) {
invalidateDrawingBuffer(drawingBuffers[i]);
}
}
}
function bitsyResetColors() {
systemPalette = [[0, 0, 0]];
// invalidate all drawing buffers
for (var i = 0; i < drawingBuffers.length; i++) {
if (drawingBuffers[i]) {
invalidateDrawingBuffer(drawingBuffers[i]);
}
}
}
function bitsyDrawBegin(bufferId) {
curBufferId = bufferId;
var buffer = drawingBuffers[curBufferId];
invalidateDrawingBuffer(buffer);
}
function bitsyDrawEnd() {
curBufferId = -1;
}
function bitsyDrawPixel(paletteIndex, x, y) {
if (curBufferId === screenBufferId && curGraphicsMode != 0) {
return;
}
// avoid trying to render out-of-bounds colors
if (paletteIndex >= systemPalette.length) {
bitsyLog("invalid color! " + paletteIndex, "system");
paletteIndex = systemPalette.length - 1;
}
var buffer = drawingBuffers[curBufferId];
buffer.instructions.push({ type: DrawingInstruction.Pixel, id: paletteIndex, x: x, y: y, });
}
// todo : name is too long :(
// todo : merge with function above?
function bitsySetPixelAtIndex(paletteIndex, pixelIndex) {
if (curBufferId === screenBufferId && curGraphicsMode != 0) {
return;
}
// avoid trying to render out-of-bounds colors
if (paletteIndex >= systemPalette.length) {
bitsyLog("invalid color! " + paletteIndex, "system");
paletteIndex = systemPalette.length - 1;
}
var buffer = drawingBuffers[curBufferId];
buffer.instructions.push({ type: DrawingInstruction.PixelIndex, id: paletteIndex, index: pixelIndex, });
}
function bitsyDrawTile(tileId, x, y) {
if (curBufferId != screenBufferId || curGraphicsMode != 1) {
return;
}
var buffer = drawingBuffers[curBufferId];
buffer.instructions.push({ type: DrawingInstruction.Tile, id: tileId, x: x, y: y, });
}
function bitsyDrawTextbox(x, y) {
if (curBufferId != screenBufferId || curGraphicsMode != 1) {
return;
}
var buffer = drawingBuffers[curBufferId];
buffer.instructions.push({ type: DrawingInstruction.Textbox, x: x, y: y, });
}
function bitsyClear(paletteIndex) {
// avoid trying to render out-of-bounds colors
if (paletteIndex >= systemPalette.length) {
paletteIndex = systemPalette.length - 1;
}
drawingBuffers[curBufferId].instructions = []; // reset instructions
drawingBuffers[curBufferId].instructions.push({ type: DrawingInstruction.Clear, id: paletteIndex, });
}
// allocates a tile buffer and returns the ID
function bitsyAddTile() {
var tileBufferId = nextBufferId;
nextBufferId++;
drawingBuffers[tileBufferId] = createDrawingBuffer(tilesize, tilesize, scale);
return tileBufferId;
}
// clears all tile buffers
function bitsyResetTiles() {
bitsyLog("RESET TILES", "system");
// bitsyLog(drawingBuffers, "system");
// bitsyLog(tileStartBufferId, "system");
// bitsyLog(drawingBuffers.slice(tileStartBufferId), "system");
drawingBuffers = drawingBuffers.slice(0, tileStartBufferId);
}
// note: width and height are in text scale pixels
function bitsySetTextboxSize(w, h) {
drawingBuffers[textboxBufferId] = createDrawingBuffer(w, h, textScale);
}
function bitsyOnLoad(fn) {
onLoadFunction = fn;
}
function bitsyOnQuit(fn) {
onQuitFunction = fn;
}
function bitsyOnUpdate(fn) {
onUpdateFunction = fn;
}
</script>
<script>
var TransitionManager = function() {
var transitionStart = null;
var transitionEnd = null;
var isTransitioning = false;
var transitionTime = 0; // milliseconds
var minStepTime = 125; // cap the frame rate
var curStep = 0;
this.BeginTransition = function(startRoom, startX, startY, endRoom, endX, endY, effectName) {
bitsyLog("--- START ROOM TRANSITION ---");
curEffect = effectName;
var tmpRoom = player().room;
var tmpX = player().x;
var tmpY = player().y;
if (transitionEffects[curEffect].showPlayerStart) {
player().room = startRoom;
player().x = startX;
player().y = startY;
}
else {
player().room = "_transition_none"; // kind of hacky!!
}
var startRoomPixels = createRoomPixelBuffer(room[startRoom]);
var startPalette = getPal(room[startRoom].pal);
var startImage = new PostProcessImage(startRoomPixels);
transitionStart = new TransitionInfo(startImage, startPalette, startX, startY);
if (transitionEffects[curEffect].showPlayerEnd) {
player().room = endRoom;
player().x = endX;
player().y = endY;
}
else {
player().room = "_transition_none";
}
var endRoomPixels = createRoomPixelBuffer(room[endRoom]);
var endPalette = getPal(room[endRoom].pal);
var endImage = new PostProcessImage(endRoomPixels);
transitionEnd = new TransitionInfo(endImage, endPalette, endX, endY);
isTransitioning = true;
transitionTime = 0;
curStep = 0;
player().room = tmpRoom;
player().x = tmpX;
player().y = tmpY;
}
this.UpdateTransition = function(dt) {
if (!isTransitioning) {
return;
}
// todo : shouldn't need to set this every frame!
bitsySetGraphicsMode(0);
transitionTime += dt;
var maxStep = transitionEffects[curEffect].stepCount;
if (transitionTime >= minStepTime) {
curStep++;
var step = curStep;
bitsyLog("transition step " + step);
if (transitionEffects[curEffect].paletteEffectFunc) {
var colors = transitionEffects[curEffect].paletteEffectFunc(transitionStart, transitionEnd, (step / maxStep));
updatePaletteWithTileColors(colors);
}
bitsyDrawBegin(0);
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var color = transitionEffects[curEffect].pixelEffectFunc(transitionStart, transitionEnd, x, y, (step / maxStep));
bitsyDrawPixel(color, x, y);
}
}
bitsyDrawEnd();
transitionTime = 0;
}
if (curStep >= (maxStep - 1)) {
isTransitioning = false;
transitionTime = 0;
transitionStart = null;
transitionEnd = null;
curStep = 0;
if (transitionCompleteCallback != null) {
transitionCompleteCallback();
}
transitionCompleteCallback = null;
}
}
this.IsTransitionActive = function() {
return isTransitioning;
}
// todo : should this be part of the constructor?
var transitionCompleteCallback = null;
this.OnTransitionComplete = function(callback) {
if (isTransitioning) { // TODO : safety check necessary?
transitionCompleteCallback = callback;
}
}
var transitionEffects = {};
var curEffect = "none";
this.RegisterTransitionEffect = function(name, effect) {
transitionEffects[name] = effect;
}
this.RegisterTransitionEffect("none", {
showPlayerStart : false,
showPlayerEnd : false,
paletteEffectFunc : function() {},
pixelEffectFunc : function() {},
});
this.RegisterTransitionEffect("fade_w", { // TODO : have it linger on full white briefly?
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 6,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
return delta < 0.5 ? start.Image.GetPixel(pixelX, pixelY) : end.Image.GetPixel(pixelX, pixelY);
},
paletteEffectFunc : function(start, end, delta) {
var colors = [];
if (delta < 0.5) {
delta = delta / 0.5;
for (var i = 0; i < start.Palette.length; i++) {
colors.push(lerpColor(start.Palette[i], [255, 255, 255], delta));
}
}
else {
delta = ((delta - 0.5) / 0.5);
for (var i = 0; i < end.Palette.length; i++) {
colors.push(lerpColor([255, 255, 255], end.Palette[i], delta));
}
}
return colors;
},
});
this.RegisterTransitionEffect("fade_b", {
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 6,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
return delta < 0.5 ? start.Image.GetPixel(pixelX, pixelY) : end.Image.GetPixel(pixelX, pixelY);
},
paletteEffectFunc : function(start, end, delta) {
var colors = [];
if (delta < 0.5) {
delta = delta / 0.5;
for (var i = 0; i < start.Palette.length; i++) {
colors.push(lerpColor(start.Palette[i], [0, 0, 0], delta));
}
}
else {
delta = ((delta - 0.5) / 0.5);
for (var i = 0; i < end.Palette.length; i++) {
colors.push(lerpColor([0, 0, 0], end.Palette[i], delta));
}
}
return colors;
},
});
this.RegisterTransitionEffect("wave", {
showPlayerStart : true,
showPlayerEnd : true,
stepCount : 12,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
var waveDelta = delta < 0.5 ? delta / 0.5 : 1 - ((delta - 0.5) / 0.5);
var offset = (pixelY + (waveDelta * waveDelta * 0.2 * start.Image.Height));
var freq = 4;
var size = 2 + (14 * waveDelta);
pixelX += Math.floor(Math.sin(offset / freq) * size);
if (pixelX < 0) {
pixelX += start.Image.Width;
}
else if (pixelX >= start.Image.Width) {
pixelX -= start.Image.Width;
}
var curImage = delta < 0.5 ? start.Image : end.Image;
return curImage.GetPixel(pixelX, pixelY);
},
paletteEffectFunc : function(start, end, delta) {
return delta < 0.5 ? start.Palette : end.Palette;
},
});
this.RegisterTransitionEffect("tunnel", {
showPlayerStart : true,
showPlayerEnd : true,
stepCount : 12,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
if (delta <= 0.4) {
var tunnelDelta = 1 - (delta / 0.4);
var xDist = start.PlayerCenter.x - pixelX;
var yDist = start.PlayerCenter.y - pixelY;
var dist = Math.sqrt((xDist * xDist) + (yDist * yDist));
if (dist > start.Image.Width * tunnelDelta) {
return 0;
}
else {
return start.Image.GetPixel(pixelX, pixelY);
}
}
else if (delta <= 0.6) {
return 0;
}
else {
var tunnelDelta = (delta - 0.6) / 0.4;
var xDist = end.PlayerCenter.x - pixelX;
var yDist = end.PlayerCenter.y - pixelY;
var dist = Math.sqrt((xDist * xDist) + (yDist * yDist));
if (dist > end.Image.Width * tunnelDelta) {
return 0;
}
else {
return end.Image.GetPixel(pixelX, pixelY);
}
}
},
paletteEffectFunc : function(start, end, delta) {
return delta < 0.5 ? start.Palette : end.Palette;
},
});
function lerpPalettes(start, end, delta) {
var colors = [];
var maxLength = (start.Palette.length > end.Palette.length) ?
start.Palette.length : end.Palette.length;
for (var i = 0; i < maxLength; i++) {
if (i < start.Palette.length && i < end.Palette.length) {
colors.push(lerpColor(start.Palette[i], end.Palette[i], delta));
}
else if (i < start.Palette.length) {
colors.push(lerpColor(
start.Palette[i],
end.Palette[end.Palette.length - 1],
delta));
}
else if (i < end.Palette.length) {
colors.push(lerpColor(
start.Palette[start.Palette.length - 1],
end.Palette[i],
delta));
}
}
return colors;
}
this.RegisterTransitionEffect("slide_u", {
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 8,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
var pixelOffset = -1 * Math.floor(start.Image.Height * delta);
var slidePixelY = pixelY + pixelOffset;
if (slidePixelY >= 0) {
return start.Image.GetPixel(pixelX, slidePixelY);
}
else {
slidePixelY += start.Image.Height;
return end.Image.GetPixel(pixelX, slidePixelY);
}
},
paletteEffectFunc : lerpPalettes,
});
this.RegisterTransitionEffect("slide_d", {
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 8,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
var pixelOffset = Math.floor(start.Image.Height * delta);
var slidePixelY = pixelY + pixelOffset;
if (slidePixelY < start.Image.Height) {
return start.Image.GetPixel(pixelX, slidePixelY);
}
else {
slidePixelY -= start.Image.Height;
return end.Image.GetPixel(pixelX, slidePixelY);
}
},
paletteEffectFunc : lerpPalettes,
});
this.RegisterTransitionEffect("slide_l", {
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 8,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
var pixelOffset = -1 * Math.floor(start.Image.Width * delta);
var slidePixelX = pixelX + pixelOffset;
if (slidePixelX >= 0) {
return start.Image.GetPixel(slidePixelX, pixelY);
}
else {
slidePixelX += start.Image.Width;
return end.Image.GetPixel(slidePixelX, pixelY);
}
},
paletteEffectFunc : lerpPalettes,
});
this.RegisterTransitionEffect("slide_r", {
showPlayerStart : false,
showPlayerEnd : true,
stepCount : 8,
pixelEffectFunc : function(start, end, pixelX, pixelY, delta) {
var pixelOffset = Math.floor(start.Image.Width * delta);
var slidePixelX = pixelX + pixelOffset;
if (slidePixelX < start.Image.Width) {
return start.Image.GetPixel(slidePixelX, pixelY);
}
else {
slidePixelX -= start.Image.Width;
return end.Image.GetPixel(slidePixelX, pixelY);
}
},
paletteEffectFunc : lerpPalettes,
});
// todo : move to Renderer()?
function createRoomPixelBuffer(room) {
var pixelBuffer = [];
for (var i = 0; i < width * height; i++) {
pixelBuffer.push(tileColorStartIndex);
}
var drawTileInPixelBuffer = function(sourceData, frameIndex, colorIndex, tx, ty, pixelBuffer) {
var frameData = sourceData[frameIndex];
for (var y = 0; y < tilesize; y++) {
for (var x = 0; x < tilesize; x++) {
var color = tileColorStartIndex + (frameData[y][x] === 1 ? colorIndex : 0);
pixelBuffer[(((ty * tilesize) + y) * width) + ((tx * tilesize) + x)] = color;
}
}
}
//draw tiles
for (i in room.tilemap) {
for (j in room.tilemap[i]) {
var id = room.tilemap[i][j];
var x = parseInt(j);
var y = parseInt(i);
if (id != "0" && tile[id] != null) {
drawTileInPixelBuffer(
renderer.GetDrawingSource(tile[id].drw),
tile[id].animation.frameIndex,
tile[id].col,
x,
y,
pixelBuffer);
}
}
}
//draw items
for (var i = 0; i < room.items.length; i++) {
var itm = room.items[i];
drawTileInPixelBuffer(
renderer.GetDrawingSource(item[itm.id].drw),
item[itm.id].animation.frameIndex,
item[itm.id].col,
itm.x,
itm.y,
pixelBuffer);
}
//draw sprites
for (id in sprite) {
var spr = sprite[id];
if (spr.room === room.id) {
drawTileInPixelBuffer(
renderer.GetDrawingSource(spr.drw),
spr.animation.frameIndex,
spr.col,
spr.x,
spr.y,
pixelBuffer);
}
}
return pixelBuffer;
}
function lerpColor(colorA, colorB, t) {
return [
colorA[0] + ((colorB[0] - colorA[0]) * t),
colorA[1] + ((colorB[1] - colorA[1]) * t),
colorA[2] + ((colorB[2] - colorA[2]) * t),
];
};
}; // TransitionManager()
// todo : is this wrapper still useful?
var PostProcessImage = function(imageData) {
this.Width = width;
this.Height = height;
this.GetPixel = function(x, y) {
return imageData[(y * width) + x];
};
this.GetData = function() {
return imageData;
};
};
var TransitionInfo = function(image, palette, playerX, playerY) {
this.Image = image;
this.Palette = palette;
this.PlayerTilePos = { x: playerX, y: playerY };
this.PlayerCenter = { x: Math.floor((playerX * tilesize) + (tilesize / 2)), y: Math.floor((playerY * tilesize) + (tilesize / 2)) };
};
</script>
<script>
/*
TODO:
- can I simplify this more now that I've removed the external resources stuff?
*/
function FontManager(packagedFontNames) {
var self = this;
var fontExtension = ".bitsyfont";
this.GetExtension = function() {
return fontExtension;
}
// place to store font data
var fontResources = {};
// load fonts from the editor
if (packagedFontNames != undefined && packagedFontNames != null && packagedFontNames.length > 0
&& Resources != undefined && Resources != null) {
for (var i = 0; i < packagedFontNames.length; i++) {
var filename = packagedFontNames[i];
fontResources[filename] = Resources[filename];
}
}
// manually add resource
this.AddResource = function(filename, fontdata) {
fontResources[filename] = fontdata;
}
this.ContainsResource = function(filename) {
return fontResources[filename] != null;
}
function GetData(fontName) {
return fontResources[fontName + fontExtension];
}
this.GetData = GetData;
function Create(fontData) {
return new Font(fontData);
}
this.Create = Create;
this.Get = function(fontName) {
var fontData = self.GetData(fontName);
return self.Create(fontData);
}
function Font(fontData) {
var name = "unknown";
var width = 6; // default size so if you have NO font or an invalid font it displays boxes
var height = 8;
var chardata = {};
// create invalid char data at default size in case the font is missing
var invalidCharData = {};
updateInvalidCharData();
this.getName = function() {
return name;
}
this.getData = function() {
return chardata;
}
this.getWidth = function() {
return width;
}
this.getHeight = function() {
return height;
}
this.hasChar = function(char) {
var codepoint = char.charCodeAt(0);
return chardata[codepoint] != null;
}
this.getChar = function(char) {
var codepoint = char.charCodeAt(0);
if (chardata[codepoint] != null) {
return chardata[codepoint];
}
else {
return invalidCharData;
}
}
this.allCharCodes = function() {
var codeList = [];
for (var code in chardata) {
codeList.push(code);
}
return codeList;
}
function createCharData() {
return {
width: width,
height: height,
offset: {
x: 0,
y: 0
},
spacing: width,
data: [],
};
}
function updateInvalidCharData() {
invalidCharData = createCharData();
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (x < width-1 && y < height-1) {
invalidCharData.data.push(1);
}
else {
invalidCharData.data.push(0);
}
}
}
}
function parseFont(fontData) {
if (fontData == null) {
return;
}
var lines = fontData.split("\n");
var isReadingChar = false;
var isReadingCharProperties = false;
var curCharLineCount = 0;
var curCharCode = 0;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line[0] === "#") {
continue; // skip comment lines
}
if (!isReadingChar) {
// READING NON CHARACTER DATA LINE
var args = line.split(" ");
if (args[0] == "FONT") {
name = args[1];
}
else if (args[0] == "SIZE") {
width = parseInt(args[1]);
height = parseInt(args[2]);
}
else if (args[0] == "CHAR") {
isReadingChar = true;
isReadingCharProperties = true;
curCharLineCount = 0;
curCharCode = parseInt(args[1]);
chardata[curCharCode] = createCharData();
}
}
else {
// CHAR PROPERTIES
if (isReadingCharProperties) {
var args = line.split(" ");
if (args[0].indexOf("CHAR_") == 0) { // Sub-properties start with "CHAR_"
if (args[0] == "CHAR_SIZE") {
// Custom character size - overrides the default character size for the font
chardata[curCharCode].width = parseInt(args[1]);
chardata[curCharCode].height = parseInt(args[2]);
chardata[curCharCode].spacing = parseInt(args[1]); // HACK : assumes CHAR_SIZE is always declared first
}
else if (args[0] == "CHAR_OFFSET") {
// Character offset - shift the origin of the character on the X or Y axis
chardata[curCharCode].offset.x = parseInt(args[1]);
chardata[curCharCode].offset.y = parseInt(args[2]);
}
else if (args[0] == "CHAR_SPACING") {
// Character spacing:
// specify total horizontal space taken up by the character
// lets chars take up more or less space on a line than its bitmap does
chardata[curCharCode].spacing = parseInt(args[1]);
}
}
else {
isReadingCharProperties = false;
}
}
// CHAR DATA
if (!isReadingCharProperties) {
// READING CHARACTER DATA LINE
for (var j = 0; j < chardata[curCharCode].width; j++)
{
chardata[curCharCode].data.push( parseInt(line[j]) );
}
curCharLineCount++;
if (curCharLineCount >= height) {
isReadingChar = false;
}
}
}
}
// re-init invalid character box at the actual font size once it's loaded
updateInvalidCharData();
}
parseFont(fontData);
}
} // FontManager
</script>
<script>
function Script() {
this.CreateInterpreter = function() {
return new Interpreter();
};
this.CreateUtils = function() {
return new Utils();
};
var Interpreter = function() {
var env = new Environment();
var parser = new Parser( env );
this.SetDialogBuffer = function(buffer) { env.SetDialogBuffer( buffer ); };
// TODO -- maybe this should return a string instead othe actual script??
this.Compile = function(scriptName, scriptStr) {
// bitsyLog("COMPILE");
var script = parser.Parse(scriptStr, scriptName);
env.SetScript(scriptName, script);
}
this.Run = function(scriptName, exitHandler, objectContext) { // Runs pre-compiled script
var localEnv = new LocalEnvironment(env);
if (objectContext) {
localEnv.SetObject(objectContext); // PROTO : should this be folded into the constructor?
}
var script = env.GetScript(scriptName);
script.Eval( localEnv, function(result) { OnScriptReturn(localEnv, exitHandler); } );
}
this.Interpret = function(scriptStr, exitHandler, objectContext) { // Compiles and runs code immediately
// bitsyLog("INTERPRET");
var localEnv = new LocalEnvironment(env);
if (objectContext) {
localEnv.SetObject(objectContext); // PROTO : should this be folded into the constructor?
}
var script = parser.Parse(scriptStr, "anonymous");
script.Eval( localEnv, function(result) { OnScriptReturn(localEnv, exitHandler); } );
}
this.HasScript = function(name) { return env.HasScript(name); };
this.ResetEnvironment = function() {
env = new Environment();
parser = new Parser( env );
}
this.Parse = function(scriptStr, rootId) { // parses a script but doesn't save it
return parser.Parse(scriptStr, rootId);
}
// TODO : add back in if needed later...
// this.CompatibilityParse = function(scriptStr, compatibilityFlags) {
// env.compatibilityFlags = compatibilityFlags;
// var result = parser.Parse(scriptStr);
// delete env.compatibilityFlags;
// return result;
// }
this.Eval = function(scriptTree, exitHandler) { // runs a script stored externally
var localEnv = new LocalEnvironment(env); // TODO : does this need an object context?
scriptTree.Eval(
localEnv,
function(result) {
OnScriptReturn(result, exitHandler);
});
}
function OnScriptReturn(result, exitHandler) {
if (exitHandler != null) {
exitHandler(result);
}
}
this.CreateExpression = function(expStr) {
return parser.CreateExpression(expStr);
}
this.SetVariable = function(name,value,useHandler) {
env.SetVariable(name,value,useHandler);
}
this.DeleteVariable = function(name,useHandler) {
env.DeleteVariable(name,useHandler);
}
this.HasVariable = function(name) {
return env.HasVariable(name);
}
this.SetOnVariableChangeHandler = function(onVariableChange) {
env.SetOnVariableChangeHandler(onVariableChange);
}
this.GetVariableNames = function() {
return env.GetVariableNames();
}
this.GetVariable = function(name) {
return env.GetVariable(name);
}
function DebugVisualizeScriptTree(scriptTree) {
var printVisitor = {
Visit : function(node,depth) {
bitsyLog("-".repeat(depth) + "- " + node.ToString());
},
};
scriptTree.VisitAll( printVisitor );
}
this.DebugVisualizeScriptTree = DebugVisualizeScriptTree;
this.DebugVisualizeScript = function(scriptName) {
DebugVisualizeScriptTree(env.GetScript(scriptName));
}
}
var Utils = function() {
// for editor ui
this.CreateDialogBlock = function(children,doIndentFirstLine) {
if (doIndentFirstLine === undefined) {
doIndentFirstLine = true;
}
var block = new DialogBlockNode(doIndentFirstLine);
for (var i = 0; i < children.length; i++) {
block.AddChild(children[i]);
}
return block;
}
this.CreateOptionBlock = function() {
var block = new DialogBlockNode(false);
block.AddChild(new FuncNode("print", [new LiteralNode(" ")]));
return block;
}
this.CreateItemConditionPair = function() {
var itemFunc = this.CreateFunctionBlock("item", ["0"]);
var condition = new ExpNode("==", itemFunc, new LiteralNode(1));
var result = new DialogBlockNode(true);
result.AddChild(new FuncNode("print", [new LiteralNode(" ")]));
var conditionPair = new ConditionPairNode(condition, result);
return conditionPair;
}
this.CreateVariableConditionPair = function() {
var varNode = this.CreateVariableNode("a");
var condition = new ExpNode("==", varNode, new LiteralNode(1));
var result = new DialogBlockNode(true);
result.AddChild(new FuncNode("print", [new LiteralNode(" ")]));
var conditionPair = new ConditionPairNode(condition, result);
return conditionPair;
}
this.CreateDefaultConditionPair = function() {
var condition = this.CreateElseNode();
var result = new DialogBlockNode(true);
result.AddChild(new FuncNode("print", [new LiteralNode(" ")]));
var conditionPair = new ConditionPairNode(condition, result);
return conditionPair;
}
this.CreateEmptyPrintFunc = function() {
return new FuncNode("print", [new LiteralNode("...")]);
}
this.CreateFunctionBlock = function(name, initParamValues) {
var parameters = [];
for (var i = 0; i < initParamValues.length; i++) {
parameters.push(new LiteralNode(initParamValues[i]));
}
var node = new FuncNode(name, parameters);
var block = new CodeBlockNode();
block.AddChild(node);
return block;
}
// TODO : rename ParseStringToLiteralNode?
this.CreateLiteralNode = function(str) {
if (str === "true") {
return new LiteralNode(true);
}
else if (str === "false") {
return new LiteralNode(false);
}
else if (!isNaN(parseFloat(str))) {
return new LiteralNode(parseFloat(str));
}
else {
return new LiteralNode(str);
}
}
this.CreateVariableNode = function(variableName) {
return new VarNode(variableName);
}
this.CreatePropertyNode = function(propertyName, literalValue) {
var varNode = new VarNode(propertyName);
var valNode = new LiteralNode(literalValue);
var node = new FuncNode("property", [varNode, valNode]);
var block = new CodeBlockNode();
block.AddChild(node);
return block;
}
this.CreateElseNode = function() {
return new ElseNode();
}
this.CreateStringLiteralNode = function(str) {
return new LiteralNode(str);
}
// TODO : need to split up code & dialog blocks :|
this.CreateCodeBlock = function() {
return new CodeBlockNode();
}
this.ChangeSequenceType = function(oldSequence, type) {
if(type === "sequence") {
return new SequenceNode(oldSequence.children);
}
else if(type === "cycle") {
return new CycleNode(oldSequence.children);
}
else if(type === "shuffle") {
return new ShuffleNode(oldSequence.children);
}
return oldSequence;
}
this.CreateSequenceBlock = function() {
var option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option1.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option2.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var sequence = new SequenceNode( [ option1, option2 ] );
var block = new CodeBlockNode();
block.AddChild( sequence );
return block;
}
this.CreateCycleBlock = function() {
var option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option1.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option2.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var sequence = new CycleNode( [ option1, option2 ] );
var block = new CodeBlockNode();
block.AddChild( sequence );
return block;
}
this.CreateShuffleBlock = function() {
var option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option1.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );
option2.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var sequence = new ShuffleNode( [ option1, option2 ] );
var block = new CodeBlockNode();
block.AddChild( sequence );
return block;
}
this.CreateIfBlock = function() {
var leftNode = new CodeBlockNode();
leftNode.AddChild( new FuncNode("item", [new LiteralNode("0")] ) );
var rightNode = new LiteralNode( 1 );
var condition1 = new ExpNode("==", leftNode, rightNode );
var condition2 = new ElseNode();
var result1 = new DialogBlockNode();
result1.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var result2 = new DialogBlockNode();
result2.AddChild(new FuncNode("print", [new LiteralNode("...")]));
var ifNode = new IfNode( [ condition1, condition2 ], [ result1, result2 ] );
var block = new CodeBlockNode();
block.AddChild( ifNode );
return block;
}
this.ReadDialogScript = function(lines, i) {
var scriptStr = "";
if (lines[i] === Sym.DialogOpen) {
scriptStr += lines[i] + "\n";
i++;
while(lines[i] != Sym.DialogClose) {
scriptStr += lines[i] + "\n";
i++;
}
scriptStr += lines[i];
i++;
}
else {
scriptStr += lines[i];
i++;
}
return { script:scriptStr, index:i };
}
// TODO this.ReadCodeScript (reads through code open and close symbols), and this.ReadScript
this.EnsureDialogBlockFormat = function(dialogStr) {
// TODO -- what if it's already enclosed in dialog symbols??
if(dialogStr.indexOf('\n') > -1) {
dialogStr = Sym.DialogOpen + "\n" + dialogStr + "\n" + Sym.DialogClose;
}
return dialogStr;
}
this.RemoveDialogBlockFormat = function(source) {
var sourceLines = source.split("\n");
var dialogStr = "";
if(sourceLines[0] === Sym.DialogOpen) {
// multi line
var i = 1;
while (i < sourceLines.length && sourceLines[i] != Sym.DialogClose) {
dialogStr += sourceLines[i] + (sourceLines[i+1] != Sym.DialogClose ? '\n' : '');
i++;
}
}
else {
// single line
dialogStr = source;
}
return dialogStr;
}
this.SerializeDialogNodeList = function(nodeList) {
var tempBlock = new DialogBlockNode(false);
// set children directly to avoid breaking the parenting chain for this temp operation
tempBlock.children = nodeList;
return tempBlock.Serialize();
}
this.GetOperatorList = function() {
return [Sym.Set].concat(Sym.Operators);
}
this.IsInlineCode = function(node) {
return isInlineCode(node);
}
}
/* BUILT-IN FUNCTIONS */ // TODO: better way to encapsulate these?
function deprecatedFunc(environment,parameters,onReturn) {
bitsyLog("BITSY SCRIPT WARNING: Tried to use deprecated function");
onReturn(null);
}
function printFunc(environment, parameters, onReturn) {
if (parameters[0] != undefined && parameters[0] != null) {
var textStr = "" + parameters[0];
environment.GetDialogBuffer().AddText(textStr);
environment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });
}
else {
onReturn(null);
}
}
function linebreakFunc(environment, parameters, onReturn) {
// bitsyLog("LINEBREAK FUNC");
environment.GetDialogBuffer().AddLinebreak();
environment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });
}
function pagebreakFunc(environment, parameters, onReturn) {
environment.GetDialogBuffer().AddPagebreak(function() { onReturn(null); });
}
function printDrawingFunc(environment, parameters, onReturn) {
var drawingId = parameters[0];
environment.GetDialogBuffer().AddDrawing(drawingId);
environment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });
}
function printSpriteFunc(environment,parameters,onReturn) {
var spriteId = parameters[0];
if(names.sprite[spriteId] != undefined) spriteId = names.sprite[spriteId]; // id is actually a name
var drawingId = sprite[spriteId].drw;
printDrawingFunc(environment, [drawingId], onReturn);
}
function printTileFunc(environment,parameters,onReturn) {
var tileId = parameters[0];
if(names.tile[tileId] != undefined) tileId = names.tile[tileId]; // id is actually a name
var drawingId = tile[tileId].drw;
printDrawingFunc(environment, [drawingId], onReturn);
}
function printItemFunc(environment,parameters,onReturn) {
var itemId = parameters[0];
if(names.item[itemId] != undefined) itemId = names.item[itemId]; // id is actually a name
var drawingId = item[itemId].drw;
printDrawingFunc(environment, [drawingId], onReturn);
}
function printFontFunc(environment, parameters, onReturn) {
var allCharacters = "";
var font = fontManager.Get(fontName);
var codeList = font.allCharCodes();
for (var i = 0; i < codeList.length; i++) {
allCharacters += String.fromCharCode(codeList[i]) + " ";
}
printFunc(environment, [allCharacters], onReturn);
}
function itemFunc(environment,parameters,onReturn) {
var itemId = parameters[0];
if (names.item[itemId] != undefined) {
// id is actually a name
itemId = names.item[itemId];
}
var curItemCount = player().inventory[itemId] ? player().inventory[itemId] : 0;
if (parameters.length > 1) {
// TODO : is it a good idea to force inventory to be >= 0?
player().inventory[itemId] = Math.max(0, parseInt(parameters[1]));
curItemCount = player().inventory[itemId];
if (onInventoryChanged != null) {
onInventoryChanged(itemId);
}
}
onReturn(curItemCount);
}
function addOrRemoveTextEffect(environment,name) {
if( environment.GetDialogBuffer().HasTextEffect(name) )
environment.GetDialogBuffer().RemoveTextEffect(name);
else
environment.GetDialogBuffer().AddTextEffect(name);
}
function rainbowFunc(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"rbw");
onReturn(null);
}
// TODO : should the colors use a parameter instead of special names?
function color1Func(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"clr1");
onReturn(null);
}
function color2Func(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"clr2");
onReturn(null);
}
function color3Func(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"clr3");
onReturn(null);
}
function wavyFunc(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"wvy");
onReturn(null);
}
function shakyFunc(environment,parameters,onReturn) {
addOrRemoveTextEffect(environment,"shk");
onReturn(null);
}
function propertyFunc(environment, parameters, onReturn) {
var outValue = null;
if (parameters.length > 0 && parameters[0]) {
var propertyName = parameters[0];
if (environment.HasProperty(propertyName)) {
// TODO : in a future update I can handle the case of initializing a new property
// after which we can move this block outside the HasProperty check
if (parameters.length > 1) {
var inValue = parameters[1];
environment.SetProperty(propertyName, inValue);
}
outValue = environment.GetProperty(propertyName);
}
}
bitsyLog("PROPERTY! " + propertyName + " " + outValue);
onReturn(outValue);
}
function endFunc(environment,parameters,onReturn) {
isEnding = true;
isNarrating = true;
dialogRenderer.SetCentered(true);
onReturn(null);
}
function exitFunc(environment,parameters,onReturn) {
var destRoom = parameters[0];
if (names.room[destRoom] != undefined) {
// it's a name, not an id! (note: these could cause trouble if people names things weird)
destRoom = names.room[destRoom];
}
var destX = parseInt(parameters[1]);
var destY = parseInt(parameters[2]);
if (parameters.length >= 4) {
var transitionEffect = parameters[3];
transition.BeginTransition(
player().room,
player().x,
player().y,
destRoom,
destX,
destY,
transitionEffect);
transition.UpdateTransition(0);
}
var movePlayerAndResumeScript = function() {
// update world state
player().room = destRoom;
player().x = destX;
player().y = destY;
curRoom = destRoom;
// update game state
initRoom(curRoom);
// resume dialog script
onReturn(null);
};
// TODO : this doesn't play nice with pagebreak because it thinks the dialog is finished!
if (transition.IsTransitionActive()) {
transition.OnTransitionComplete(movePlayerAndResumeScript);
}
else {
movePlayerAndResumeScript();
}
}
/* BUILT-IN OPERATORS */
function setExp(environment,left,right,onReturn) {
// bitsyLog("SET " + left.name);
if(left.type != "variable") {
// not a variable! return null and hope for the best D:
onReturn( null );
return;
}
right.Eval(environment,function(rVal) {
environment.SetVariable( left.name, rVal );
// bitsyLog("VAL " + environment.GetVariable( left.name ) );
left.Eval(environment,function(lVal) {
onReturn( lVal );
});
});
}
function equalExp(environment,left,right,onReturn) {
// bitsyLog("EVAL EQUAL");
// bitsyLog(left);
// bitsyLog(right);
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal === rVal );
});
});
}
function greaterExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal > rVal );
});
});
}
function lessExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal < rVal );
});
});
}
function greaterEqExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal >= rVal );
});
});
}
function lessEqExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal <= rVal );
});
});
}
function multExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal * rVal );
});
});
}
function divExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal / rVal );
});
});
}
function addExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal + rVal );
});
});
}
function subExp(environment,left,right,onReturn) {
right.Eval(environment,function(rVal){
left.Eval(environment,function(lVal){
onReturn( lVal - rVal );
});
});
}
/* ENVIRONMENT */
var Environment = function() {
var dialogBuffer = null;
this.SetDialogBuffer = function(buffer) { dialogBuffer = buffer; };
this.GetDialogBuffer = function() { return dialogBuffer; };
var functionMap = {};
functionMap["print"] = printFunc;
functionMap["say"] = printFunc;
functionMap["br"] = linebreakFunc;
functionMap["item"] = itemFunc;
functionMap["rbw"] = rainbowFunc;
functionMap["clr1"] = color1Func;
functionMap["clr2"] = color2Func;
functionMap["clr3"] = color3Func;
functionMap["wvy"] = wavyFunc;
functionMap["shk"] = shakyFunc;
functionMap["printSprite"] = printSpriteFunc;
functionMap["printTile"] = printTileFunc;
functionMap["printItem"] = printItemFunc;
functionMap["debugOnlyPrintFont"] = printFontFunc; // DEBUG ONLY
functionMap["end"] = endFunc;
functionMap["exit"] = exitFunc;
functionMap["pg"] = pagebreakFunc;
functionMap["property"] = propertyFunc;
this.HasFunction = function(name) { return functionMap[name] != undefined; };
this.EvalFunction = function(name,parameters,onReturn,env) {
if (env == undefined || env == null) {
env = this;
}
functionMap[name](env, parameters, onReturn);
}
var variableMap = {};
this.HasVariable = function(name) { return variableMap[name] != undefined; };
this.GetVariable = function(name) { return variableMap[name]; };
this.SetVariable = function(name,value,useHandler) {
// bitsyLog("SET VARIABLE " + name + " = " + value);
if(useHandler === undefined) useHandler = true;
variableMap[name] = value;
if(onVariableChangeHandler != null && useHandler){
onVariableChangeHandler(name);
}
};
this.DeleteVariable = function(name,useHandler) {
if(useHandler === undefined) useHandler = true;
if(variableMap[name] != undefined) {
variableMap.delete(name);
if(onVariableChangeHandler != null && useHandler) {
onVariableChangeHandler(name);
}
}
};
var operatorMap = {};
operatorMap["="] = setExp;
operatorMap["=="] = equalExp;
operatorMap[">"] = greaterExp;
operatorMap["<"] = lessExp;
operatorMap[">="] = greaterEqExp;
operatorMap["<="] = lessEqExp;
operatorMap["*"] = multExp;
operatorMap["/"] = divExp;
operatorMap["+"] = addExp;
operatorMap["-"] = subExp;
this.HasOperator = function(sym) { return operatorMap[sym] != undefined; };
this.EvalOperator = function(sym,left,right,onReturn) {
operatorMap[ sym ]( this, left, right, onReturn );
}
var scriptMap = {};
this.HasScript = function(name) { return scriptMap[name] != undefined; };
this.GetScript = function(name) { return scriptMap[name]; };
this.SetScript = function(name,script) { scriptMap[name] = script; };
var onVariableChangeHandler = null;
this.SetOnVariableChangeHandler = function(onVariableChange) {
onVariableChangeHandler = onVariableChange;
}
this.GetVariableNames = function() {
var variableNames = [];
for (var key in variableMap) {
variableNames.push(key);
}
return variableNames;
}
}
// Local environment for a single run of a script: knows local context
var LocalEnvironment = function(parentEnvironment) {
// this.SetDialogBuffer // not allowed in local environment?
this.GetDialogBuffer = function() { return parentEnvironment.GetDialogBuffer(); };
this.HasFunction = function(name) { return parentEnvironment.HasFunction(name); };
this.EvalFunction = function(name,parameters,onReturn,env) {
if (env == undefined || env == null) {
env = this;
}
parentEnvironment.EvalFunction(name,parameters,onReturn,env);
}
this.HasVariable = function(name) { return parentEnvironment.HasVariable(name); };
this.GetVariable = function(name) { return parentEnvironment.GetVariable(name); };
this.SetVariable = function(name,value,useHandler) { parentEnvironment.SetVariable(name,value,useHandler); };
// this.DeleteVariable // not needed in local environment?
this.HasOperator = function(sym) { return parentEnvironment.HasOperator(sym); };
this.EvalOperator = function(sym,left,right,onReturn,env) {
if (env == undefined || env == null) {
env = this;
}
parentEnvironment.EvalOperator(sym,left,right,onReturn,env);
};
// TODO : I don't *think* any of this is required by the local environment
// this.HasScript
// this.GetScript
// this.SetScript
// TODO : pretty sure these debug methods aren't required by the local environment either
// this.SetOnVariableChangeHandler
// this.GetVariableNames
/* Here's where specific local context data goes:
* this includes access to the object running the script
* and any properties it may have (so far only "locked")
*/
// The local environment knows what object called it -- currently only used to access properties
var curObject = null;
this.HasObject = function() { return curObject != undefined && curObject != null; }
this.SetObject = function(object) { curObject = object; }
this.GetObject = function() { return curObject; }
// accessors for properties of the object that's running the script
this.HasProperty = function(name) {
if (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {
return true;
}
else {
return false;
}
};
this.GetProperty = function(name) {
if (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {
return curObject.property[name]; // TODO : should these be getters and setters instead?
}
else {
return null;
}
};
this.SetProperty = function(name, value) {
// NOTE : for now, we need to gaurd against creating new properties
if (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {
curObject.property[name] = value;
}
};
}
function leadingWhitespace(depth) {
var str = "";
for(var i = 0; i < depth; i++) {
str += " "; // two spaces per indent
}
// bitsyLog("WHITESPACE " + depth + " ::" + str + "::");
return str;
}
/* NODES */
var TreeRelationship = function() {
this.parent = null;
this.children = [];
this.AddChild = function(node) {
this.children.push(node);
node.parent = this;
};
this.AddChildren = function(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
this.AddChild(nodeList[i]);
}
};
this.SetChildren = function(nodeList) {
this.children = [];
this.AddChildren(nodeList);
};
this.VisitAll = function(visitor, depth) {
if (depth == undefined || depth == null) {
depth = 0;
}
visitor.Visit(this, depth);
for (var i = 0; i < this.children.length; i++) {
this.children[i].VisitAll( visitor, depth + 1 );
}
};
this.rootId = null; // for debugging
this.GetId = function() {
// bitsyLog(this);
if (this.rootId != null) {
return this.rootId;
}
else if (this.parent != null) {
var parentId = this.parent.GetId();
if (parentId != null) {
return parentId + "_" + this.parent.children.indexOf(this);
}
}
else {
return null;
}
}
}
var DialogBlockNode = function(doIndentFirstLine) {
Object.assign( this, new TreeRelationship() );
// Object.assign( this, new Runnable() );
this.type = "dialog_block";
this.Eval = function(environment, onReturn) {
// bitsyLog("EVAL BLOCK " + this.children.length);
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_enter", { id: this.GetId() });
}
var lastVal = null;
var i = 0;
function evalChildren(children, done) {
if (i < children.length) {
// bitsyLog(">> CHILD " + i);
children[i].Eval(environment, function(val) {
// bitsyLog("<< CHILD " + i);
lastVal = val;
i++;
evalChildren(children,done);
});
}
else {
done();
}
};
var self = this;
evalChildren(this.children, function() {
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_exit", { id: self.GetId() });
}
onReturn(lastVal);
});
}
if (doIndentFirstLine === undefined) {
doIndentFirstLine = true; // This is just for serialization
}
this.Serialize = function(depth) {
if (depth === undefined) {
depth = 0;
}
var str = "";
var lastNode = null;
for (var i = 0; i < this.children.length; i++) {
var curNode = this.children[i];
var shouldIndentFirstLine = (i == 0 && doIndentFirstLine);
var shouldIndentAfterLinebreak = (lastNode && lastNode.type === "function" && lastNode.name === "br");
if (shouldIndentFirstLine || shouldIndentAfterLinebreak) {
str += leadingWhitespace(depth);
}
str += curNode.Serialize(depth);
lastNode = curNode;
}
return str;
}
this.ToString = function() {
return this.type + " " + this.GetId();
};
}
var CodeBlockNode = function() {
Object.assign( this, new TreeRelationship() );
this.type = "code_block";
this.Eval = function(environment, onReturn) {
// bitsyLog("EVAL BLOCK " + this.children.length);
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_enter", { id: this.GetId() });
}
var lastVal = null;
var i = 0;
function evalChildren(children, done) {
if (i < children.length) {
// bitsyLog(">> CHILD " + i);
children[i].Eval(environment, function(val) {
// bitsyLog("<< CHILD " + i);
lastVal = val;
i++;
evalChildren(children,done);
});
}
else {
done();
}
};
var self = this;
evalChildren(this.children, function() {
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_exit", { id: self.GetId() });
}
onReturn(lastVal);
});
}
this.Serialize = function(depth) {
if(depth === undefined) {
depth = 0;
}
// bitsyLog("SERIALIZE BLOCK!!!");
// bitsyLog(depth);
// bitsyLog(doIndentFirstLine);
var str = "{"; // todo: increase scope of Sym?
// TODO : do code blocks ever have more than one child anymore????
for (var i = 0; i < this.children.length; i++) {
var curNode = this.children[i];
str += curNode.Serialize(depth);
}
str += "}";
return str;
}
this.ToString = function() {
return this.type + " " + this.GetId();
};
}
function isInlineCode(node) {
return isTextEffectBlock(node) || isUndefinedBlock(node) || isMultilineListBlock(node);
}
function isUndefinedBlock(node) {
return node.type === "code_block" && node.children.length > 0 && node.children[0].type === "undefined";
}
var textEffectBlockNames = ["clr1", "clr2", "clr3", "wvy", "shk", "rbw", "printSprite", "printItem", "printTile", "print", "say", "br"];
function isTextEffectBlock(node) {
if (node.type === "code_block") {
if (node.children.length > 0 && node.children[0].type === "function") {
var func = node.children[0];
return textEffectBlockNames.indexOf(func.name) != -1;
}
}
return false;
}
var listBlockTypes = ["sequence", "cycle", "shuffle", "if"];
function isMultilineListBlock(node) {
if (node.type === "code_block") {
if (node.children.length > 0) {
var child = node.children[0];
return listBlockTypes.indexOf(child.type) != -1;
}
}
return false;
}
// for round-tripping undefined code through the parser (useful for hacks!)
var UndefinedNode = function(sourceStr) {
Object.assign(this, new TreeRelationship());
this.type = "undefined";
this.source = sourceStr;
this.Eval = function(environment,onReturn) {
addOrRemoveTextEffect(environment, "_debug_highlight");
printFunc(environment, ["{" + sourceStr + "}"], function() {
onReturn(null);
});
addOrRemoveTextEffect(environment, "_debug_highlight");
}
this.Serialize = function(depth) {
return this.source;
}
this.ToString = function() {
return "undefined" + " " + this.GetId();
}
}
var FuncNode = function(name,args) {
Object.assign( this, new TreeRelationship() );
// Object.assign( this, new Runnable() );
this.type = "function";
this.name = name;
this.args = args;
this.Eval = function(environment,onReturn) {
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_enter", { id: this.GetId() });
}
var self = this; // hack to deal with scope (TODO : move up higher?)
var argumentValues = [];
var i = 0;
function evalArgs(args, done) {
// TODO : really hacky way to make we get the first
// symbol's NAME instead of its variable value
// if we are trying to do something with a property
if (self.name === "property" && i === 0 && i < args.length) {
if (args[i].type === "variable") {
argumentValues.push(args[i].name);
i++;
}
else {
// first argument for a property MUST be a variable symbol
// -- so skip everything if it's not!
i = args.length;
}
}
if (i < args.length) {
// Evaluate each argument
args[i].Eval(
environment,
function(val) {
argumentValues.push(val);
i++;
evalArgs(args, done);
});
}
else {
done();
}
};
evalArgs(
this.args,
function() {
if (isPlayerEmbeddedInEditor && events != undefined && events != null) {
events.Raise("script_node_exit", { id: self.GetId() });
}
environment.EvalFunction(self.name, argumentValues, onReturn);
});
}
this.Serialize = function(depth) {
var isDialogBlock = this.parent.type === "dialog_block";
if (isDialogBlock && this.name === "print") {
// TODO this could cause problems with "real" print functions
return this.args[0].value; // first argument should be the text of the {print} func
}
else if (isDialogBlock && this.name === "br") {
return "\n";
}
else {
var str = "";
str += this.name;
for(var i = 0; i < this.args.length; i++) {
str += " ";
str += this.args[i].Serialize(depth);
}
return str;
}
}
this.ToString = function() {
return this.type + " " + this.name + " " + this.GetId();
};
}
var LiteralNode = function(value) {
Object.assign( this, new TreeRelationship() );
// Object.assign( this, new Runnable() );
this.type = "literal";
this.value = value;
this.Eval = function(environment,onReturn) {
onReturn(this.value);
}
this.Serialize = function(depth) {
var str = "";
if (this.value === null) {
return str;
}
if (typeof this.value === "string") {
str += '"';
}
str += this.value;
if (typeof this.value === "string") {
str += '"';
}
return str;
}
this.ToString = function() {
return this.type + " " + this.value + " " + this.GetId();
};
}
var VarNode = function(name) {
Object.assign( this, new TreeRelationship() );
// Object.assign( this, new Runnable() );
this.type = "variable";
this.name = name;
this.Eval = function(environment,onReturn) {
// bitsyLog("EVAL " + this.name + " " + environment.HasVariable(this.name) + " " + environment.GetVariable(this.name));
if( environment.HasVariable(this.name) )
onReturn( environment.GetVariable( this.name ) );
else
onReturn(null); // not a valid variable -- return null and hope that's ok
} // TODO: might want to store nodes in the variableMap instead of values???
this.Serialize = function(depth) {
var str = "" + this.name;
return str;
}
this.ToString = function() {
return this.type + " " + this.name + " " + this.GetId();
};
}
var ExpNode = function(operator, left, right) {
Object.assign( this, new TreeRelationship() );
this.type = "operator";
this.operator = operator;
this.left = left;
this.right = right;
this.Eval = function(environment,onReturn) {
// bitsyLog("EVAL " + this.operator);
var self = this; // hack to deal with scope
environment.EvalOperator( this.operator, this.left, this.right,
function(val){
// bitsyLog("EVAL EXP " + self.operator + " " + val);
onReturn(val);
} );
// NOTE : sadly this pushes a lot of complexity down onto the actual operator methods
}
this.Serialize = function(depth) {
var isNegativeNumber = this.operator === "-" && this.left.type === "literal" && this.left.value === null;
if (!isNegativeNumber) {
var str = "";
if (this.left != undefined && this.left != null) {
str += this.left.Serialize(depth) + " ";
}
str += this.operator;
if (this.right != undefined && this.right != null) {
str += " " + this.right.Serialize(depth);
}
return str;
}
else {
return this.operator + this.right.Serialize(depth); // hacky but seems to work
}
}
this.VisitAll = function(visitor, depth) {
if (depth == undefined || depth == null) {
depth = 0;
}
visitor.Visit( this, depth );
if(this.left != null)
this.left.VisitAll( visitor, depth + 1 );
if(this.right != null)
this.right.VisitAll( visitor, depth + 1 );
};
this.ToString = function() {
return this.type + " " + this.operator + " " + this.GetId();
};
}
var SequenceBase = function() {
this.Serialize = function(depth) {
var str = "";
str += this.type + "\n";
for (var i = 0; i < this.children.length; i++) {
str += leadingWhitespace(depth + 1) + Sym.List + " ";
str += this.children[i].Serialize(depth + 2);
str += "\n";
}
str += leadingWhitespace(depth);
return str;
}
this.VisitAll = function(visitor, depth) {
if (depth == undefined || depth == null) {
depth = 0;
}
visitor.Visit(this, depth);
for (var i = 0; i < this.children.length; i++) {
this.children[i].VisitAll( visitor, depth + 1 );
}
};
this.ToString = function() {
return this.type + " " + this.GetId();
};
}
var SequenceNode = function(options) {
Object.assign(this, new TreeRelationship());
Object.assign(this, new SequenceBase());
this.type = "sequence";
this.AddChildren(options);
var index = 0;
this.Eval = function(environment, onReturn) {
// bitsyLog("SEQUENCE " + index);
this.children[index].Eval(environment, onReturn);
var next = index + 1;
if (next < this.children.length) {
index = next;
}
}
}
var CycleNode = function(options) {
Object.assign(this, new TreeRelationship());
Object.assign(this, new SequenceBase());
this.type = "cycle";
this.AddChildren(options);
var index = 0;
this.Eval = function(environment, onReturn) {
// bitsyLog("CYCLE " + index);
this.children[index].Eval(environment, onReturn);
var next = index + 1;
if (next < this.children.length) {
index = next;
}
else {
index = 0;
}
}
}
var ShuffleNode = function(options) {
Object.assign(this, new TreeRelationship());
Object.assign(this, new SequenceBase());
this.type = "shuffle";
this.AddChildren(options);
var optionsShuffled = [];
function shuffle(options) {
optionsShuffled = [];
var optionsUnshuffled = options.slice();
while (optionsUnshuffled.length > 0) {
var i = Math.floor(Math.random() * optionsUnshuffled.length);
optionsShuffled.push(optionsUnshuffled.splice(i,1)[0]);
}
}
shuffle(this.children);
var index = 0;
this.Eval = function(environment, onReturn) {
optionsShuffled[index].Eval(environment, onReturn);
index++;
if (index >= this.children.length) {
shuffle(this.children);
index = 0;
}
}
}
// TODO : rename? ConditionalNode?
var IfNode = function(conditions, results, isSingleLine) {
Object.assign(this, new TreeRelationship());
this.type = "if";
for (var i = 0; i < conditions.length; i++) {
this.AddChild(new ConditionPairNode(conditions[i], results[i]));
}
var self = this;
this.Eval = function(environment, onReturn) {
// bitsyLog("EVAL IF");
var i = 0;
function TestCondition() {
self.children[i].Eval(environment, function(result) {
if (result.conditionValue == true) {
onReturn(result.resultValue);
}
else if (i+1 < self.children.length) {
i++;
TestCondition();
}
else {
onReturn(null);
}
});
};
TestCondition();
}
if (isSingleLine === undefined) {
isSingleLine = false; // This is just for serialization
}
this.Serialize = function(depth) {
var str = "";
if(isSingleLine) {
// HACKY - should I even keep this mode???
str += this.children[0].children[0].Serialize() + " ? " + this.children[0].children[1].Serialize();
if (this.children.length > 1 && this.children[1].children[0].type === Sym.Else) {
str += " " + Sym.ElseExp + " " + this.children[1].children[1].Serialize();
}
}
else {
str += "\n";
for (var i = 0; i < this.children.length; i++) {
str += this.children[i].Serialize(depth);
}
str += leadingWhitespace(depth);
}
return str;
}
this.IsSingleLine = function() {
return isSingleLine;
}
this.VisitAll = function(visitor, depth) {
if (depth == undefined || depth == null) {
depth = 0;
}
visitor.Visit(this, depth);
for (var i = 0; i < this.children.length; i++) {
this.children[i].VisitAll(visitor, depth + 1);
}
};
this.ToString = function() {
return this.type + " " + this.mode + " " + this.GetId();
};
}
var ConditionPairNode = function(condition, result) {
Object.assign(this, new TreeRelationship());
this.type = "condition_pair";
this.AddChild(condition);
this.AddChild(result);
var self = this;
this.Eval = function(environment, onReturn) {
self.children[0].Eval(environment, function(conditionSuccess) {
if (conditionSuccess) {
self.children[1].Eval(environment, function(resultValue) {
onReturn({ conditionValue:true, resultValue:resultValue });
});
}
else {
onReturn({ conditionValue:false });
}
});
}
this.Serialize = function(depth) {
var str = "";
str += leadingWhitespace(depth + 1);
str += Sym.List + " " + this.children[0].Serialize(depth) + " " + Sym.ConditionEnd + Sym.Linebreak;
str += this.children[1].Serialize(depth + 2) + Sym.Linebreak;
return str;
}
this.VisitAll = function(visitor, depth) {
if (depth == undefined || depth == null) {
depth = 0;
}
visitor.Visit(this, depth);
for (var i = 0; i < this.children.length; i++) {
this.children[i].VisitAll(visitor, depth + 1);
}
}
this.ToString = function() {
return this.type + " " + this.GetId();
}
}
var ElseNode = function() {
Object.assign( this, new TreeRelationship() );
this.type = Sym.Else;
this.Eval = function(environment, onReturn) {
onReturn(true);
}
this.Serialize = function() {
return Sym.Else;
}
this.ToString = function() {
return this.type + " " + this.mode + " " + this.GetId();
};
}
var Sym = {
DialogOpen : '"""',
DialogClose : '"""',
CodeOpen : "{",
CodeClose : "}",
Linebreak : "\n", // just call it "break" ?
Separator : ":",
List : "-",
String : '"',
ConditionEnd : "?",
Else : "else",
ElseExp : ":", // special shorthand for expressions (deprecate?)
Set : "=",
Operators : ["==", ">=", "<=", ">", "<", "-", "+", "/", "*"], // operators need to be in reverse order of precedence
};
var Parser = function(env) {
var environment = env;
this.Parse = function(scriptStr, rootId) {
var rootNode = new DialogBlockNode();
rootNode.rootId = rootId;
var state = new ParserState(rootNode, scriptStr);
bitsyLog(scriptStr);
bitsyLog(state.Source());
if (state.MatchAhead(Sym.DialogOpen)) {
// multi-line dialog block
var dialogStr = state.ConsumeBlock(Sym.DialogOpen + Sym.Linebreak, Sym.Linebreak + Sym.DialogClose);
rootNode = new DialogBlockNode();
rootNode.rootId = rootId; // hacky!!
state = new ParserState(rootNode, dialogStr);
state = ParseDialog(state);
}
else {
// single-line dialog block
state = ParseDialog(state);
}
return state.rootNode;
};
var ParserState = function( rootNode, str ) {
this.rootNode = rootNode;
this.curNode = this.rootNode;
var sourceStr = str;
var i = 0;
this.Index = function() { return i; };
this.Count = function() { return sourceStr.length; };
this.Done = function() { return i >= sourceStr.length; };
this.Char = function() { return sourceStr[i]; };
this.Step = function(n) { if(n===undefined) n=1; i += n; };
this.MatchAhead = function(str) {
// bitsyLog(str);
str = "" + str; // hack to turn single chars into strings
// bitsyLog(str);
// bitsyLog(str.length);
for (var j = 0; j < str.length; j++) {
if (i + j >= sourceStr.length) {
return false;
}
else if (str[j] != sourceStr[i+j]) {
return false;
}
}
return true;
}
this.Peak = function(end) {
var str = "";
var j = i;
// bitsyLog(j);
while (j < sourceStr.length && end.indexOf(sourceStr[j]) == -1) {
str += sourceStr[j];
j++;
}
// bitsyLog("PEAK ::" + str + "::");
return str;
}
this.ConsumeBlock = function(open, close, includeSymbols) {
if (includeSymbols === undefined || includeSymbols === null) {
includeSymbols = false;
}
var startIndex = i;
var matchCount = 0;
if (this.MatchAhead(open)) {
matchCount++;
this.Step(open.length);
}
while (matchCount > 0 && !this.Done()) {
if (this.MatchAhead(close)) {
matchCount--;
this.Step( close.length );
}
else if (this.MatchAhead(open)) {
matchCount++;
this.Step(open.length);
}
else {
this.Step();
}
}
if (includeSymbols) {
return sourceStr.slice(startIndex, i);
}
else {
return sourceStr.slice(startIndex + open.length, i - close.length);
}
}
this.Print = function() { bitsyLog(sourceStr); };
this.Source = function() { return sourceStr; };
};
/*
ParseDialog():
This function adds {print} nodes and linebreak {br} nodes to display text,
interleaved with bracketed code nodes for functions and flow control,
such as text effects {shk} {wvy} or sequences like {cycle} and {shuffle}.
The parsing of those code blocks is handled by ParseCode.
Note on parsing newline characters:
- there should be an implicit linebreak {br} after each dialog line
- a "dialog line" is defined as any line that either:
- 1) contains dialog text (any text outside of a code block)
- 2) is entirely empty (no text, no code)
- *or* 3) contains a list block (sequence, cycle, shuffle, or conditional)
- lines *only* containing {code} blocks are not dialog lines
NOTE TO SELF: all the state I'm storing in here feels like
evidence that the parsing system kind of broke down at this point :(
Maybe it would feel better if I move into the "state" object
*/
function ParseDialog(state) {
var curLineNodeList = [];
var curText = "";
var curLineIsEmpty = true;
var curLineContainsDialogText = false;
var prevLineIsDialogLine = false;
var curLineIsDialogLine = function() {
return curLineContainsDialogText || curLineIsEmpty;
}
var resetLineStateForNewLine = function() {
prevLineIsDialogLine = curLineIsDialogLine();
curLineContainsDialogText = false;
curLineIsEmpty = true;
curText = "";
curLineNodeList = [];
}
var tryAddTextNodeToList = function() {
if (curText.length > 0) {
var printNode = new FuncNode("print", [new LiteralNode(curText)]);
curLineNodeList.push(printNode);
curText = "";
curLineIsEmpty = false;
curLineContainsDialogText = true;
}
}
var addCodeNodeToList = function() {
var codeSource = state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose);
var codeState = new ParserState(new CodeBlockNode(), codeSource);
codeState = ParseCode(codeState);
var codeBlockNode = codeState.rootNode;
curLineNodeList.push(codeBlockNode);
curLineIsEmpty = false;
// lists count as dialog text, because they can contain it
if (isMultilineListBlock(codeBlockNode)) {
curLineContainsDialogText = true;
}
}
var tryAddLinebreakNodeToList = function() {
if (prevLineIsDialogLine) {
var linebreakNode = new FuncNode("br", []);
curLineNodeList.unshift(linebreakNode);
}
}
var addLineNodesToParent = function() {
for (var i = 0; i < curLineNodeList.length; i++) {
state.curNode.AddChild(curLineNodeList[i]);
}
}
while (!state.Done()) {
if (state.MatchAhead(Sym.CodeOpen)) { // process code block
// add any buffered text to a print node, and parse the code
tryAddTextNodeToList();
addCodeNodeToList();
}
else if (state.MatchAhead(Sym.Linebreak)) { // process new line
// add any buffered text to a print node,
// and add a linebreak if we are between two dialog lines
tryAddTextNodeToList();
tryAddLinebreakNodeToList();
// since we've reached the end of a line
// add stored nodes for this line to the parent node we are building,
// and reset state for the next line
addLineNodesToParent();
resetLineStateForNewLine();
state.Step();
}
else {
// continue adding text to the current text buffer
curText += state.Char();
state.Step();
}
}
// to make sure we don't leave anything behind:
// add buffered text to a print node and add all nodes
// to the current parent node
tryAddTextNodeToList();
tryAddLinebreakNodeToList();
addLineNodesToParent();
return state;
}
function ParseDialogBlock(state) {
var dialogStr = state.ConsumeBlock( Sym.DialogOpen, Sym.DialogClose );
var dialogState = new ParserState(new DialogBlockNode(), dialogStr);
dialogState = ParseDialog( dialogState );
state.curNode.AddChild( dialogState.rootNode );
return state;
}
/*
ParseConditional():
A conditional contains a list of conditions that can be
evaluated to true or false, followed by more dialog
that will be evaluated if the condition is true. The first
true condition is the one that gets evaluated.
*/
function ParseConditional(state) {
var conditionStrings = [];
var resultStrings = [];
var curIndex = -1;
var requiredLeadingWhitespace = -1;
// TODO : very similar to sequence parsing - can we share anything?
function parseConditionalItemLine(state) {
var lineText = "";
var whitespaceCount = 0;
var isNewCondition = false;
var encounteredNonWhitespace = false;
var encounteredConditionEnd = false;
while (!state.Done() && !(state.Char() === Sym.Linebreak)) {
// count whitespace until we hit the first non-whitespace character
if (!encounteredNonWhitespace) {
if (state.Char() === " " || state.Char() === "\t") {
whitespaceCount++;
}
else {
encounteredNonWhitespace = true;
if (state.Char() === Sym.List) {
isNewCondition = true;
whitespaceCount += 2; // count the list seperator AND the following extra space
}
}
}
// if this is the condition, we need to track whether we've
// reached the end of the condition
if (isNewCondition && !encounteredConditionEnd) {
if (state.Char() === Sym.ConditionEnd) {
encounteredConditionEnd = true;
}
}
// add characters one at a time, unless it's a code block
// since code blocks can contain additional sequences inside
// them that will mess up our list item detection
if (state.Char() === Sym.CodeOpen) {
lineText += state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose, true /*includeSymbols*/);
}
else {
if (!encounteredConditionEnd) { // skip all characters including & after the condition end
lineText += state.Char();
}
state.Step();
}
}
if (state.Char() === Sym.Linebreak) {
state.Step();
}
return { text:lineText, whitespace:whitespaceCount, isNewCondition:isNewCondition };
}
// TODO : this is copied from sequence parsing; share?
function trimLeadingWhitespace(text, trimLength) {
var textSplit = text.split(Sym.linebreak);
textSplit = textSplit.map(function(line) { return line.slice(trimLength) });
return textSplit.join(Sym.linebreak);
}
while (!state.Done()) {
var lineResults = parseConditionalItemLine(state);
if (lineResults.isNewCondition) {
requiredLeadingWhitespace = lineResults.whitespace;
curIndex++;
conditionStrings[curIndex] = "";
resultStrings[curIndex] = "";
}
// to avoid extra newlines in nested conditionals, only count lines
// that at least match the whitespace count of the initial line
// NOTE: see the comment in sequence parsing for more details
if (lineResults.whitespace >= requiredLeadingWhitespace) {
var trimmedText = trimLeadingWhitespace(lineResults.text, requiredLeadingWhitespace);
if (lineResults.isNewCondition) {
conditionStrings[curIndex] += trimmedText;
}
else {
resultStrings[curIndex] += trimmedText + Sym.Linebreak;
}
}
}
// hack: cut off the trailing newlines from all the result strings
resultStrings = resultStrings.map(function(result) { return result.slice(0,-1); });
var conditions = [];
for (var i = 0; i < conditionStrings.length; i++) {
var str = conditionStrings[i].trim();
if (str === Sym.Else) {
conditions.push(new ElseNode());
}
else {
var exp = CreateExpression(str);
conditions.push(exp);
}
}
var results = [];
for (var i = 0; i < resultStrings.length; i++) {
var str = resultStrings[i];
var dialogBlockState = new ParserState(new DialogBlockNode(), str);
dialogBlockState = ParseDialog(dialogBlockState);
var dialogBlock = dialogBlockState.rootNode;
results.push(dialogBlock);
}
state.curNode.AddChild(new IfNode(conditions, results));
return state;
}
function IsSequence(str) {
// bitsyLog("IsSequence? " + str);
return str === "sequence" || str === "cycle" || str === "shuffle";
}
/*
ParseSequence():
Sequence nodes contain a list of dialog block nodes. The order those
nodes are evaluated is determined by the type of sequence:
- sequence: each child node evaluated once in order
- cycle: repeats from the beginning after all nodes evaluate
- shuffle: evaluate in a random order
Each item in a sequence is sepearated by a "-" character.
The seperator must come at the beginning of the line,
but may be preceded by whitespace (in any amount).
About whitespace: Whitespace at the start of a line
is ignored if it less than or equal to the count of
whitespace that preceded the list separator ("-") at
the start of that item. (The count also includes the
seperator and the extra space after the seperator.)
*/
function ParseSequence(state, sequenceType) {
var itemStrings = [];
var curItemIndex = -1; // -1 indicates not reading an item yet
var requiredLeadingWhitespace = -1;
function parseSequenceItemLine(state) {
var lineText = "";
var whitespaceCount = 0;
var isNewListItem = false;
var encounteredNonWhitespace = false;
while (!state.Done() && !(state.Char() === Sym.Linebreak)) {
// count whitespace until we hit the first non-whitespace character
if (!encounteredNonWhitespace) {
if (state.Char() === " " || state.Char() === "\t") {
whitespaceCount++;
}
else {
encounteredNonWhitespace = true;
if (state.Char() === Sym.List) {
isNewListItem = true;
whitespaceCount += 2; // count the list seperator AND the following extra space
}
}
}
// add characters one at a time, unless it's a code block
// since code blocks can contain additional sequences inside
// them that will mess up our list item detection
if (state.Char() === Sym.CodeOpen) {
lineText += state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose, true /*includeSymbols*/);
}
else {
lineText += state.Char();
state.Step();
}
}
if (state.Char() === Sym.Linebreak) {
state.Step();
}
return { text:lineText, whitespace:whitespaceCount, isNewListItem:isNewListItem };
}
function trimLeadingWhitespace(text, trimLength) {
// the split and join is necessary because a single "line"
// can contain sequences that may contain newlines of their own
// (we treat them all as one "line" for sequence parsing purposes)
var textSplit = text.split(Sym.linebreak);
textSplit = textSplit.map(function(line) { return line.slice(trimLength) });
return textSplit.join(Sym.linebreak);
}
while (!state.Done()) {
var lineResults = parseSequenceItemLine(state);
if (lineResults.isNewListItem) {
requiredLeadingWhitespace = lineResults.whitespace;
curItemIndex++;
itemStrings[curItemIndex] = "";
}
// to avoid double counting closing lines (empty ones ending in a curly brace)
// we only allow lines that have at least as much whitespace as the start of the list item
// TODO : I think right now this leads to a bug if the list item's indentation is less than
// its parent code block... hopefully that won't be a big deal for now
// (NOTE: I think the bug could be fixed by only applying this to the FINAL line of an item, but
// that would require more consideration and testing)
if (lineResults.whitespace >= requiredLeadingWhitespace) {
var trimmedText = trimLeadingWhitespace(lineResults.text, requiredLeadingWhitespace);
itemStrings[curItemIndex] += trimmedText + Sym.Linebreak;
}
}
// a bit hacky: cut off the trailing newlines from all the items
itemStrings = itemStrings.map(function(item) { return item.slice(0,-1); });
var options = [];
for (var i = 0; i < itemStrings.length; i++) {
var str = itemStrings[i];
var dialogBlockState = new ParserState(new DialogBlockNode(false /* doIndentFirstLine */), str);
dialogBlockState = ParseDialog(dialogBlockState);
var dialogBlock = dialogBlockState.rootNode;
options.push(dialogBlock);
}
if (sequenceType === "sequence") {
state.curNode.AddChild(new SequenceNode(options));
}
else if (sequenceType === "cycle") {
state.curNode.AddChild(new CycleNode(options));
}
else if (sequenceType === "shuffle") {
state.curNode.AddChild(new ShuffleNode(options));
}
return state;
}
function ParseFunction(state, funcName) {
bitsyLog("~~~ PARSE FUNCTION " + funcName);
var args = [];
var curSymbol = "";
function OnSymbolEnd() {
curSymbol = curSymbol.trim();
// bitsyLog("PARAMTER " + curSymbol);
args.push( StringToValue(curSymbol) );
// bitsyLog(args);
curSymbol = "";
}
while( !( state.Char() === "\n" || state.Done() ) ) {
if( state.MatchAhead(Sym.CodeOpen) ) {
var codeBlockState = new ParserState(new CodeBlockNode(), state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose));
codeBlockState = ParseCode( codeBlockState );
var codeBlock = codeBlockState.rootNode;
args.push( codeBlock );
curSymbol = "";
}
else if( state.MatchAhead(Sym.String) ) {
/* STRING LITERAL */
var str = state.ConsumeBlock(Sym.String, Sym.String);
// bitsyLog("STRING " + str);
args.push( new LiteralNode(str) );
curSymbol = "";
}
else if(state.Char() === " " && curSymbol.length > 0) {
OnSymbolEnd();
}
else {
curSymbol += state.Char();
}
state.Step();
}
if(curSymbol.length > 0) {
OnSymbolEnd();
}
state.curNode.AddChild( new FuncNode( funcName, args ) );
return state;
}
function IsValidVariableName(str) {
var reg = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
var isValid = reg.test(str);
// bitsyLog("VALID variable??? " + isValid);
return isValid;
}
function StringToValue(valStr) {
if(valStr[0] === Sym.CodeOpen) {
// CODE BLOCK!!!
var codeStr = (new ParserState( null, valStr )).ConsumeBlock(Sym.CodeOpen, Sym.CodeClose); //hacky
var codeBlockState = new ParserState(new CodeBlockNode(), codeStr);
codeBlockState = ParseCode( codeBlockState );
return codeBlockState.rootNode;
}
else if(valStr[0] === Sym.String) {
// STRING!!
// bitsyLog("STRING");
var str = "";
var i = 1;
while (i < valStr.length && valStr[i] != Sym.String) {
str += valStr[i];
i++;
}
// bitsyLog(str);
return new LiteralNode( str );
}
else if(valStr === "true") {
// BOOL
return new LiteralNode( true );
}
else if(valStr === "false") {
// BOOL
return new LiteralNode( false );
}
else if( !isNaN(parseFloat(valStr)) ) {
// NUMBER!!
// bitsyLog("NUMBER!!! " + valStr);
return new LiteralNode( parseFloat(valStr) );
}
else if(IsValidVariableName(valStr)) {
// VARIABLE!!
// bitsyLog("VARIABLE");
return new VarNode(valStr); // TODO : check for valid potential variables
}
else {
// uh oh
return new LiteralNode(null);
}
}
function CreateExpression(expStr) {
expStr = expStr.trim();
function IsInsideString(index) {
var inString = false;
for(var i = 0; i < expStr.length; i++) {
if(expStr[i] === Sym.String)
inString = !inString;
if(index === i)
return inString;
}
return false;
}
function IsInsideCode(index) {
var count = 0;
for(var i = 0; i < expStr.length; i++) {
if(expStr[i] === Sym.CodeOpen)
count++;
else if(expStr[i] === Sym.CodeClose)
count--;
if(index === i)
return count > 0;
}
return false;
}
var operator = null;
// set is special because other operator can look like it, and it has to go first in the order of operations
var setIndex = expStr.indexOf(Sym.Set);
if( setIndex > -1 && !IsInsideString(setIndex) && !IsInsideCode(setIndex) ) { // it might be a set operator
if( expStr[setIndex+1] != "=" && expStr[setIndex-1] != ">" && expStr[setIndex-1] != "<" ) {
// ok it actually IS a set operator and not ==, >=, or <=
operator = Sym.Set;
var variableName = expStr.substring(0,setIndex).trim(); // TODO : valid variable name testing
var left = IsValidVariableName(variableName) ? new VarNode( variableName ) : new LiteralNode(null);
var right = CreateExpression( expStr.substring(setIndex+Sym.Set.length) );
var exp = new ExpNode( operator, left, right );
return exp;
}
}
// special if "expression" for single-line if statements
var ifIndex = expStr.indexOf(Sym.ConditionEnd);
if( ifIndex > -1 && !IsInsideString(ifIndex) && !IsInsideCode(ifIndex) ) {
operator = Sym.ConditionEnd;
var conditionStr = expStr.substring(0,ifIndex).trim();
var conditions = [ CreateExpression(conditionStr) ];
var resultStr = expStr.substring(ifIndex+Sym.ConditionEnd.length);
var results = [];
function AddResult(str) {
var dialogBlockState = new ParserState(new DialogBlockNode(), str);
dialogBlockState = ParseDialog( dialogBlockState );
var dialogBlock = dialogBlockState.rootNode;
results.push( dialogBlock );
}
var elseIndex = resultStr.indexOf(Sym.ElseExp); // does this need to test for strings?
if(elseIndex > -1) {
conditions.push( new ElseNode() );
var elseStr = resultStr.substring(elseIndex+Sym.ElseExp.length);
var resultStr = resultStr.substring(0,elseIndex);
AddResult( resultStr.trim() );
AddResult( elseStr.trim() );
}
else {
AddResult( resultStr.trim() );
}
return new IfNode( conditions, results, true /*isSingleLine*/ );
}
for( var i = 0; (operator == null) && (i < Sym.Operators.length); i++ ) {
var opSym = Sym.Operators[i];
var opIndex = expStr.indexOf( opSym );
if( opIndex > -1 && !IsInsideString(opIndex) && !IsInsideCode(opIndex) ) {
operator = opSym;
var left = CreateExpression( expStr.substring(0,opIndex) );
var right = CreateExpression( expStr.substring(opIndex+opSym.length) );
var exp = new ExpNode( operator, left, right );
return exp;
}
}
if( operator == null ) {
return StringToValue(expStr);
}
}
this.CreateExpression = CreateExpression;
function IsWhitespace(str) {
return ( str === " " || str === "\t" || str === "\n" );
}
function IsExpression(str) {
var tempState = new ParserState(null, str); // hacky
var textOutsideCodeBlocks = "";
while (!tempState.Done()) {
if (tempState.MatchAhead(Sym.CodeOpen)) {
tempState.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose);
}
else {
textOutsideCodeBlocks += tempState.Char();
tempState.Step();
}
}
var containsAnyExpressionOperators = (textOutsideCodeBlocks.indexOf(Sym.ConditionEnd) != -1) ||
(textOutsideCodeBlocks.indexOf(Sym.Set) != -1) ||
(Sym.Operators.some(function(opSym) { return textOutsideCodeBlocks.indexOf(opSym) != -1; }));
return containsAnyExpressionOperators;
}
function IsLiteral(str) {
var isBool = str === "true" || str === "false";
var isNum = !isNaN(parseFloat(str));
var isStr = str[0] === '"' && str[str.length-1] === '"';
var isVar = IsValidVariableName(str);
var isEmpty = str.length === 0;
return isBool || isNum || isStr || isVar || isEmpty;
}
function ParseExpression(state) {
var line = state.Source(); // state.Peak( [Sym.Linebreak] ); // TODO : remove the linebreak thing
// bitsyLog("EXPRESSION " + line);
var exp = CreateExpression(line);
// bitsyLog(exp);
state.curNode.AddChild(exp);
state.Step(line.length);
return state;
}
function IsConditionalBlock(state) {
var peakToFirstListSymbol = state.Peak([Sym.List]);
var foundListSymbol = peakToFirstListSymbol < state.Source().length;
var areAllCharsBeforeListWhitespace = true;
for (var i = 0; i < peakToFirstListSymbol.length; i++) {
if (!IsWhitespace(peakToFirstListSymbol[i])) {
areAllCharsBeforeListWhitespace = false;
}
}
var peakToFirstConditionSymbol = state.Peak([Sym.ConditionEnd]);
peakToFirstConditionSymbol = peakToFirstConditionSymbol.slice(peakToFirstListSymbol.length);
var hasNoLinebreakBetweenListAndConditionEnd = peakToFirstConditionSymbol.indexOf(Sym.Linebreak) == -1;
return foundListSymbol &&
areAllCharsBeforeListWhitespace &&
hasNoLinebreakBetweenListAndConditionEnd;
}
function ParseCode(state) {
if (IsConditionalBlock(state)) {
state = ParseConditional(state);
}
else if (environment.HasFunction(state.Peak([" "]))) { // TODO --- what about newlines???
var funcName = state.Peak([" "]);
state.Step(funcName.length);
state = ParseFunction(state, funcName);
}
else if (IsSequence(state.Peak([" ", Sym.Linebreak]))) {
var sequenceType = state.Peak([" ", Sym.Linebreak]);
state.Step(sequenceType.length);
state = ParseSequence(state, sequenceType);
}
else if (IsLiteral(state.Source()) || IsExpression(state.Source())) {
state = ParseExpression(state);
}
else {
var undefinedSrc = state.Peak([]);
var undefinedNode = new UndefinedNode(undefinedSrc);
state.curNode.AddChild(undefinedNode);
}
// just go to the end now
while (!state.Done()) {
state.Step();
}
return state;
}
function ParseCodeBlock(state) {
var codeStr = state.ConsumeBlock( Sym.CodeOpen, Sym.CodeClose );
var codeState = new ParserState(new CodeBlockNode(), codeStr);
codeState = ParseCode( codeState );
state.curNode.AddChild( codeState.rootNode );
return state;
}
}
} // Script()
</script>
<script>
function Dialog() {
this.CreateRenderer = function() {
return new DialogRenderer();
};
this.CreateBuffer = function() {
return new DialogBuffer();
};
var DialogRenderer = function() {
// TODO : refactor this eventually? remove everything from struct.. avoid the defaults?
var textboxInfo = {
width : 104,
height : 8+4+2+5, //8 for text, 4 for top-bottom padding, 2 for line padding, 5 for arrow
top : 12,
left : 12,
bottom : 12, //for drawing it from the bottom
font_scale : 0.5, // we draw font at half-size compared to everything else
padding_vert : 2,
padding_horz : 4,
arrow_height : 5,
};
var font = null;
this.SetFont = function(f) {
font = f;
textboxInfo.height = (textboxInfo.padding_vert * 3) + (relativeFontHeight() * 2) + textboxInfo.arrow_height;
// todo : clean up all the scale stuff
var textboxScaleW = textboxInfo.width / textboxInfo.font_scale;
var textboxScaleH = textboxInfo.height / textboxInfo.font_scale;
bitsySetTextboxSize(textboxScaleW, textboxScaleH);
}
function textScale() {
return scale * textboxInfo.font_scale;
}
function relativeFontWidth() {
return Math.ceil( font.getWidth() * textboxInfo.font_scale );
}
function relativeFontHeight() {
return Math.ceil( font.getHeight() * textboxInfo.font_scale );
}
this.ClearTextbox = function() {
bitsyDrawBegin(1);
bitsyClear(textBackgroundIndex);
bitsyDrawEnd();
};
var isCentered = false;
this.SetCentered = function(centered) {
isCentered = centered;
};
this.DrawTextbox = function() {
bitsyDrawBegin(0);
if (isCentered) {
// todo : will the height calculations always work?
bitsyDrawTextbox(textboxInfo.left, ((height / 2) - (textboxInfo.height / 2)));
}
else if (player().y < (mapsize / 2)) {
// bottom
bitsyDrawTextbox(textboxInfo.left, (height - textboxInfo.bottom - textboxInfo.height));
}
else {
// top
bitsyDrawTextbox(textboxInfo.left, textboxInfo.top);
}
bitsyDrawEnd();
};
var arrowdata = [
1,1,1,1,1,
0,1,1,1,0,
0,0,1,0,0
];
this.DrawNextArrow = function() {
// bitsyLog("draw arrow!");
bitsyDrawBegin(1);
var top = (textboxInfo.height - 5) * text_scale;
var left = (textboxInfo.width - (5 + 4)) * text_scale;
if (textDirection === TextDirection.RightToLeft) { // RTL hack
left = 4 * text_scale;
}
for (var y = 0; y < 3; y++) {
for (var x = 0; x < 5; x++) {
var i = (y * 5) + x;
if (arrowdata[i] == 1) {
//scaling nonsense
for (var sy = 0; sy < text_scale; sy++) {
for (var sx = 0; sx < text_scale; sx++) {
bitsyDrawPixel(textArrowIndex, left + (x * text_scale) + sx, top + (y * text_scale) + sy);
}
}
}
}
}
bitsyDrawEnd();
};
var text_scale = 2; //using a different scaling factor for text feels like cheating... but it looks better
this.DrawChar = function(char, row, col, leftPos) {
bitsyDrawBegin(1);
char.offset = {
x: char.base_offset.x,
y: char.base_offset.y
}; // compute render offset *every* frame
char.SetPosition(row,col);
char.ApplyEffects(effectTime);
var charData = char.bitmap;
var top = (4 * text_scale) + (row * 2 * text_scale) + (row * font.getHeight()) + Math.floor(char.offset.y);
var left = (4 * text_scale) + leftPos + Math.floor(char.offset.x);
for (var y = 0; y < char.height; y++) {
for (var x = 0; x < char.width; x++) {
var i = (y * char.width) + x;
if (charData[i] == 1) {
// todo : other colors
bitsySetPixelAtIndex(char.color, ((top + y) * (textboxInfo.width * text_scale)) + (left + x));
}
}
}
bitsyDrawEnd();
// call printHandler for character
char.OnPrint();
};
var effectTime = 0; // TODO this variable should live somewhere better
this.Draw = function(buffer, dt) {
effectTime += dt;
this.ClearTextbox();
buffer.ForEachActiveChar(this.DrawChar);
if (buffer.CanContinue()) {
this.DrawNextArrow();
}
this.DrawTextbox();
if (buffer.DidPageFinishThisFrame() && onPageFinish != null) {
onPageFinish();
}
};
/* this is a hook for GIF rendering */
var onPageFinish = null;
this.SetPageFinishHandler = function(handler) {
onPageFinish = handler;
};
this.Reset = function() {
effectTime = 0;
// TODO - anything else?
}
// this.CharsPerRow = function() {
// return textboxInfo.charsPerRow;
// }
}
var DialogBuffer = function() {
var buffer = [[[]]]; // holds dialog in an array buffer
var pageIndex = 0;
var rowIndex = 0;
var charIndex = 0;
var nextCharTimer = 0;
var nextCharMaxTime = 50; // in milliseconds
var isDialogReadyToContinue = false;
var activeTextEffects = [];
var font = null;
var arabicHandler = new ArabicHandler();
var onDialogEndCallbacks = [];
this.SetFont = function(f) {
font = f;
}
this.CurPage = function() { return buffer[ pageIndex ]; };
this.CurRow = function() { return this.CurPage()[ rowIndex ]; };
this.CurChar = function() { return this.CurRow()[ charIndex ]; };
this.CurPageCount = function() { return buffer.length; };
this.CurRowCount = function() { return this.CurPage().length; };
this.CurCharCount = function() { return this.CurRow().length; };
this.ForEachActiveChar = function(handler) { // Iterates over visible characters on the active page
var rowCount = rowIndex + 1;
for (var i = 0; i < rowCount; i++) {
var row = this.CurPage()[i];
var charCount = (i == rowIndex) ? charIndex+1 : row.length;
// bitsyLog(charCount);
var leftPos = 0;
if (textDirection === TextDirection.RightToLeft) {
leftPos = 24 * 8; // hack -- I think this is correct?
}
for(var j = 0; j < charCount; j++) {
var char = row[j];
if(char) {
if (textDirection === TextDirection.RightToLeft) {
leftPos -= char.spacing;
}
// bitsyLog(j + " " + leftPos);
// handler( char, i /*rowIndex*/, j /*colIndex*/ );
handler(char, i /*rowIndex*/, j /*colIndex*/, leftPos)
if (textDirection === TextDirection.LeftToRight) {
leftPos += char.spacing;
}
}
}
}
}
this.Reset = function() {
buffer = [[[]]];
pageIndex = 0;
rowIndex = 0;
charIndex = 0;
isDialogReadyToContinue = false;
afterManualPagebreak = false;
activeTextEffects = [];
onDialogEndCallbacks = [];
isActive = false;
};
this.DoNextChar = function() {
nextCharTimer = 0; //reset timer
//time to update characters
if (charIndex + 1 < this.CurCharCount()) {
//add char to current row
charIndex++;
}
else if (rowIndex + 1 < this.CurRowCount()) {
//start next row
rowIndex++;
charIndex = 0;
}
else {
//the page is full!
isDialogReadyToContinue = true;
didPageFinishThisFrame = true;
}
if (this.CurChar() != null) {
if (this.CurChar().isPageBreak) {
// special case for page break marker character!
isDialogReadyToContinue = true;
didPageFinishThisFrame = true;
}
this.CurChar().OnPrint(); // make sure we hit the callback before we run out of text
}
};
this.Update = function(dt) {
didPageFinishThisFrame = false;
didFlipPageThisFrame = false;
// this.Draw(dt); // TODO move into a renderer object
if (isDialogReadyToContinue) {
return; //waiting for dialog to be advanced by player
}
nextCharTimer += dt; //tick timer
if (nextCharTimer > nextCharMaxTime) {
this.DoNextChar();
}
};
this.Skip = function() {
bitsyLog("SKIPPP");
didPageFinishThisFrame = false;
didFlipPageThisFrame = false;
// add new characters until you get to the end of the current line of dialog
while (rowIndex < this.CurRowCount()) {
this.DoNextChar();
if(isDialogReadyToContinue) {
//make sure to push the rowIndex past the end to break out of the loop
rowIndex++;
charIndex = 0;
}
}
rowIndex = this.CurRowCount()-1;
charIndex = this.CurCharCount()-1;
};
this.FlipPage = function() {
didFlipPageThisFrame = true;
isDialogReadyToContinue = false;
pageIndex++;
rowIndex = 0;
charIndex = 0;
}
this.EndDialog = function() {
isActive = false; // no more text to show... this should be a sign to stop rendering dialog
for (var i = 0; i < onDialogEndCallbacks.length; i++) {
onDialogEndCallbacks[i]();
}
}
var afterManualPagebreak = false; // is it bad to track this state like this?
this.Continue = function() {
bitsyLog("CONTINUE");
// if we used a page break character to continue we need
// to run whatever is in the script afterwards! // TODO : make this comment better
if (this.CurChar().isPageBreak) {
// hacky: always treat a page break as the end of dialog
// if there's more dialog later we re-activate the dialog buffer
this.EndDialog();
afterManualPagebreak = true;
this.CurChar().OnContinue();
return false;
}
if (pageIndex + 1 < this.CurPageCount()) {
bitsyLog("FLIP PAGE!");
//start next page
this.FlipPage();
return true; /* hasMoreDialog */
}
else {
bitsyLog("END DIALOG!");
//end dialog mode
this.EndDialog();
return false; /* hasMoreDialog */
}
};
var isActive = false;
this.IsActive = function() { return isActive; };
this.OnDialogEnd = function(callback) {
if (!isActive) {
callback();
}
else {
onDialogEndCallbacks.push(callback);
}
}
this.CanContinue = function() { return isDialogReadyToContinue; };
function DialogChar(effectList) {
this.effectList = effectList.slice(); // clone effect list (since it can change between chars)
this.color = textColorIndex; // white
this.offset = { x:0, y:0 }; // in pixels (screen pixels?)
this.col = 0;
this.row = 0;
this.SetPosition = function(row,col) {
// bitsyLog("SET POS");
// bitsyLog(this);
this.row = row;
this.col = col;
}
this.ApplyEffects = function(time) {
// bitsyLog("APPLY EFFECTS! " + time);
for(var i = 0; i < this.effectList.length; i++) {
var effectName = this.effectList[i];
// bitsyLog("FX " + effectName);
TextEffects[ effectName ].DoEffect( this, time );
}
}
var printHandler = null; // optional function to be called once on printing character
this.SetPrintHandler = function(handler) {
printHandler = handler;
}
this.OnPrint = function() {
if (printHandler != null) {
// bitsyLog("PRINT HANDLER ---- DIALOG BUFFER");
printHandler();
printHandler = null; // only call handler once (hacky)
}
}
this.bitmap = [];
this.width = 0;
this.height = 0;
this.base_offset = { // hacky name
x: 0,
y: 0
};
this.spacing = 0;
}
function DialogFontChar(font, char, effectList) {
Object.assign(this, new DialogChar(effectList));
var charData = font.getChar(char);
this.bitmap = charData.data;
this.width = charData.width;
this.height = charData.height;
this.base_offset.x = charData.offset.x;
this.base_offset.y = charData.offset.y;
this.spacing = charData.spacing;
}
function DialogDrawingChar(drawingId, effectList) {
Object.assign(this, new DialogChar(effectList));
// get the first frame of the drawing and flatten it
var drawingData = renderer.GetDrawingSource(drawingId)[0];
var drawingDataFlat = [];
for (var i = 0; i < drawingData.length; i++) {
drawingDataFlat = drawingDataFlat.concat(drawingData[i]);
}
this.bitmap = drawingDataFlat;
this.width = 8;
this.height = 8;
this.spacing = 8;
}
function DialogScriptControlChar() {
Object.assign(this, new DialogChar([]));
this.width = 0;
this.height = 0;
this.spacing = 0;
}
// is a control character really the best way to handle page breaks?
function DialogPageBreakChar() {
Object.assign(this, new DialogChar([]));
this.width = 0;
this.height = 0;
this.spacing = 0;
this.isPageBreak = true;
var continueHandler = null;
this.SetContinueHandler = function(handler) {
continueHandler = handler;
}
this.OnContinue = function() {
if (continueHandler) {
continueHandler();
}
}
}
function AddWordToCharArray(charArray,word,effectList) {
for(var i = 0; i < word.length; i++) {
charArray.push( new DialogFontChar( font, word[i], effectList ) );
}
return charArray;
}
function GetCharArrayWidth(charArray) {
var width = 0;
for(var i = 0; i < charArray.length; i++) {
width += charArray[i].spacing;
}
return width;
}
function GetStringWidth(str) {
var width = 0;
for (var i = 0; i < str.length; i++) {
var charData = font.getChar(str[i]);
width += charData.spacing;
}
return width;
}
var pixelsPerRow = 192; // hard-coded fun times!!!
this.AddScriptReturn = function(onReturnHandler) {
var curPageIndex = buffer.length - 1;
var curRowIndex = buffer[curPageIndex].length - 1;
var curRowArr = buffer[curPageIndex][curRowIndex];
var controlChar = new DialogScriptControlChar();
controlChar.SetPrintHandler(onReturnHandler);
curRowArr.push(controlChar);
isActive = true;
}
this.AddDrawing = function(drawingId) {
// bitsyLog("DRAWING ID " + drawingId);
var curPageIndex = buffer.length - 1;
var curRowIndex = buffer[curPageIndex].length - 1;
var curRowArr = buffer[curPageIndex][curRowIndex];
var drawingChar = new DialogDrawingChar(drawingId, activeTextEffects);
var rowLength = GetCharArrayWidth(curRowArr);
// TODO : clean up copy-pasted code here :/
if (afterManualPagebreak) {
this.FlipPage(); // hacky
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer.push([]);
curPageIndex++;
buffer[curPageIndex].push([]);
curRowIndex = 0;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr.push(drawingChar);
afterManualPagebreak = false;
}
else if (rowLength + drawingChar.spacing <= pixelsPerRow || rowLength <= 0) {
//stay on same row
curRowArr.push(drawingChar);
}
else if (curRowIndex == 0) {
//start next row
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer[curPageIndex].push([]);
curRowIndex++;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr.push(drawingChar);
}
else {
//start next page
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer.push([]);
curPageIndex++;
buffer[curPageIndex].push([]);
curRowIndex = 0;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr.push(drawingChar);
}
isActive = true; // this feels like a bad way to do this???
}
// TODO : convert this into something that takes DialogChar arrays
this.AddText = function(textStr) {
bitsyLog("ADD TEXT " + textStr);
//process dialog so it's easier to display
var words = textStr.split(" ");
// var curPageIndex = this.CurPageCount() - 1;
// var curRowIndex = this.CurRowCount() - 1;
// var curRowArr = this.CurRow();
var curPageIndex = buffer.length - 1;
var curRowIndex = buffer[curPageIndex].length - 1;
var curRowArr = buffer[curPageIndex][curRowIndex];
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (arabicHandler.ContainsArabicCharacters(word)) {
word = arabicHandler.ShapeArabicCharacters(word);
}
var wordWithPrecedingSpace = ((i == 0) ? "" : " ") + word;
var wordLength = GetStringWidth(wordWithPrecedingSpace);
var rowLength = GetCharArrayWidth(curRowArr);
if (afterManualPagebreak) {
this.FlipPage();
// hacky copied bit for page breaks
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer.push([]);
curPageIndex++;
buffer[curPageIndex].push([]);
curRowIndex = 0;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects);
afterManualPagebreak = false;
}
else if (rowLength + wordLength <= pixelsPerRow || rowLength <= 0) {
//stay on same row
curRowArr = AddWordToCharArray(curRowArr, wordWithPrecedingSpace, activeTextEffects);
}
else if (curRowIndex == 0) {
//start next row
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer[curPageIndex].push([]);
curRowIndex++;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects);
}
else {
//start next page
buffer[curPageIndex][curRowIndex] = curRowArr;
buffer.push([]);
curPageIndex++;
buffer[curPageIndex].push([]);
curRowIndex = 0;
curRowArr = buffer[curPageIndex][curRowIndex];
curRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects);
}
}
//destroy any empty stuff
var lastPage = buffer[buffer.length-1];
var lastRow = lastPage[lastPage.length-1];
if (lastRow.length == 0) {
lastPage.splice(lastPage.length-1, 1);
}
if (lastPage.length == 0) {
buffer.splice(buffer.length-1, 1);
}
//finish up
lastPage = buffer[buffer.length-1];
lastRow = lastPage[lastPage.length-1];
if (lastRow.length > 0) {
var lastChar = lastRow[lastRow.length-1];
}
// bitsyLog(buffer);
isActive = true;
};
this.AddLinebreak = function() {
var lastPage = buffer[buffer.length-1];
if (lastPage.length <= 1) {
// bitsyLog("LINEBREAK - NEW ROW ");
// add new row
lastPage.push([]);
}
else {
// add new page
buffer.push([[]]);
}
// bitsyLog(buffer);
isActive = true;
}
this.AddPagebreak = function(onReturnHandler) {
var curPageIndex = buffer.length - 1;
var curRowIndex = buffer[curPageIndex].length - 1;
var curRowArr = buffer[curPageIndex][curRowIndex];
// need to actually create a whole new page if following another pagebreak character
if (this.CurChar() && this.CurChar().isPageBreak) {
buffer.push([]);
curPageIndex++;
buffer[curPageIndex].push([]);
curRowIndex = 0;
curRowArr = buffer[curPageIndex][curRowIndex];
}
var pagebreakChar = new DialogPageBreakChar();
pagebreakChar.SetContinueHandler(onReturnHandler);
curRowArr.push(pagebreakChar);
isActive = true;
}
/* new text effects */
this.HasTextEffect = function(name) {
return activeTextEffects.indexOf( name ) > -1;
}
this.AddTextEffect = function(name) {
activeTextEffects.push( name );
}
this.RemoveTextEffect = function(name) {
activeTextEffects.splice( activeTextEffects.indexOf( name ), 1 );
}
/* this is a hook for GIF rendering */
var didPageFinishThisFrame = false;
this.DidPageFinishThisFrame = function(){ return didPageFinishThisFrame; };
var didFlipPageThisFrame = false;
this.DidFlipPageThisFrame = function(){ return didFlipPageThisFrame; };
// this.SetCharsPerRow = function(num){ charsPerRow = num; }; // hacky
};
/* ARABIC */
var ArabicHandler = function() {
var arabicCharStart = 0x0621;
var arabicCharEnd = 0x064E;
var CharacterForm = {
Isolated : 0,
Final : 1,
Initial : 2,
Middle : 3
};
// map glyphs to their character forms
var glyphForms = {
/* Isolated, Final, Initial, Middle Forms */
0x0621: [0xFE80,0xFE80,0xFE80,0xFE80], /* HAMZA */
0x0622: [0xFE81,0xFE82,0xFE81,0xFE82], /* ALEF WITH MADDA ABOVE */
0x0623: [0xFE83,0xFE84,0xFE83,0xFE84], /* ALEF WITH HAMZA ABOVE */
0x0624: [0xFE85,0xFE86,0xFE85,0xFE86], /* WAW WITH HAMZA ABOVE */
0x0625: [0xFE87,0xFE88,0xFE87,0xFE88], /* ALEF WITH HAMZA BELOW */
0x0626: [0xFE89,0xFE8A,0xFE8B,0xFE8C], /* YEH WITH HAMZA ABOVE */
0x0627: [0xFE8D,0xFE8E,0xFE8D,0xFE8E], /* ALEF */
0x0628: [0xFE8F,0xFE90,0xFE91,0xFE92], /* BEH */
0x0629: [0xFE93,0xFE94,0xFE93,0xFE94], /* TEH MARBUTA */
0x062A: [0xFE95,0xFE96,0xFE97,0xFE98], /* TEH */
0x062B: [0xFE99,0xFE9A,0xFE9B,0xFE9C], /* THEH */
0x062C: [0xFE9D,0xFE9E,0xFE9F,0xFEA0], /* JEEM */
0x062D: [0xFEA1,0xFEA2,0xFEA3,0xFEA4], /* HAH */
0x062E: [0xFEA5,0xFEA6,0xFEA7,0xFEA8], /* KHAH */
0x062F: [0xFEA9,0xFEAA,0xFEA9,0xFEAA], /* DAL */
0x0630: [0xFEAB,0xFEAC,0xFEAB,0xFEAC], /* THAL */
0x0631: [0xFEAD,0xFEAE,0xFEAD,0xFEAE], /* RAA */
0x0632: [0xFEAF,0xFEB0,0xFEAF,0xFEB0], /* ZAIN */
0x0633: [0xFEB1,0xFEB2,0xFEB3,0xFEB4], /* SEEN */
0x0634: [0xFEB5,0xFEB6,0xFEB7,0xFEB8], /* SHEEN */
0x0635: [0xFEB9,0xFEBA,0xFEBB,0xFEBC], /* SAD */
0x0636: [0xFEBD,0xFEBE,0xFEBF,0xFEC0], /* DAD */
0x0637: [0xFEC1,0xFEC2,0xFEC3,0xFEC4], /* TAH */
0x0638: [0xFEC5,0xFEC6,0xFEC7,0xFEC8], /* ZAH */
0x0639: [0xFEC9,0xFECA,0xFECB,0xFECC], /* AIN */
0x063A: [0xFECD,0xFECE,0xFECF,0xFED0], /* GHAIN */
0x063B: [0x0000,0x0000,0x0000,0x0000], /* space */
0x063C: [0x0000,0x0000,0x0000,0x0000], /* space */
0x063D: [0x0000,0x0000,0x0000,0x0000], /* space */
0x063E: [0x0000,0x0000,0x0000,0x0000], /* space */
0x063F: [0x0000,0x0000,0x0000,0x0000], /* space */
0x0640: [0x0640,0x0640,0x0640,0x0640], /* TATWEEL */
0x0641: [0xFED1,0xFED2,0xFED3,0xFED4], /* FAA */
0x0642: [0xFED5,0xFED6,0xFED7,0xFED8], /* QAF */
0x0643: [0xFED9,0xFEDA,0xFEDB,0xFEDC], /* KAF */
0x0644: [0xFEDD,0xFEDE,0xFEDF,0xFEE0], /* LAM */
0x0645: [0xFEE1,0xFEE2,0xFEE3,0xFEE4], /* MEEM */
0x0646: [0xFEE5,0xFEE6,0xFEE7,0xFEE8], /* NOON */
0x0647: [0xFEE9,0xFEEA,0xFEEB,0xFEEC], /* HEH */
0x0648: [0xFEED,0xFEEE,0xFEED,0xFEEE], /* WAW */
0x0649: [0xFEEF,0xFEF0,0xFBE8,0xFBE9], /* ALEF MAKSURA */
0x064A: [0xFEF1,0xFEF2,0xFEF3,0xFEF4], /* YEH */
0x064B: [0xFEF5,0xFEF6,0xFEF5,0xFEF6], /* LAM ALEF MADD*/
0x064C: [0xFEF7,0xFEF8,0xFEF7,0xFEF8], /* LAM ALEF HAMZA ABOVE*/
0x064D: [0xFEF9,0xFEFa,0xFEF9,0xFEFa], /* LAM ALEF HAMZA BELOW*/
0x064E: [0xFEFb,0xFEFc,0xFEFb,0xFEFc], /* LAM ALEF */
};
var disconnectedCharacters = [0x0621,0x0622,0x0623,0x0624,0x0625,0x0627,0x062f,0x0630,0x0631,0x0632,0x0648,0x0649,0x064b,0x064c,0x064d,0x064e];
function IsArabicCharacter(char) {
var code = char.charCodeAt(0);
return (code >= arabicCharStart && code <= arabicCharEnd);
}
function ContainsArabicCharacters(word) {
for (var i = 0; i < word.length; i++) {
if (IsArabicCharacter(word[i])) {
return true;
}
}
return false;
}
function IsDisconnectedCharacter(char) {
var code = char.charCodeAt(0);
return disconnectedCharacters.indexOf(code) != -1;
}
function ShapeArabicCharacters(word) {
var shapedWord = "";
for (var i = 0; i < word.length; i++) {
if (!IsArabicCharacter(word[i])) {
shapedWord += word[i];
continue;
}
var connectedToPreviousChar = i-1 >= 0 && IsArabicCharacter(word[i-1]) && !IsDisconnectedCharacter(word[i-1]);
var connectedToNextChar = i+1 < word.length && IsArabicCharacter(word[i+1]) && !IsDisconnectedCharacter(word[i]);
var form;
if (!connectedToPreviousChar && !connectedToNextChar) {
form = CharacterForm.Isolated;
}
else if (connectedToPreviousChar && !connectedToNextChar) {
form = CharacterForm.Final;
}
else if (!connectedToPreviousChar && connectedToNextChar) {
form = CharacterForm.Initial;
}
else if (connectedToPreviousChar && connectedToNextChar) {
form = CharacterForm.Middle;
}
var code = word[i].charCodeAt(0);
// handle lam alef special case
if (code == 0x0644 && connectedToNextChar) {
var nextCode = word[i+1].charCodeAt(0);
var specialCode = null;
if (nextCode == 0x0622) {
// alef madd
specialCode = glyphForms[0x064b][form];
}
else if (nextCode == 0x0623) {
// hamza above
specialCode = glyphForms[0x064c][form];
}
else if (nextCode == 0x0625) {
// hamza below
specialCode = glyphForms[0x064d][form];
}
else if (nextCode == 0x0627) {
// alef
specialCode = glyphForms[0x064e][form];
}
if (specialCode != null) {
shapedWord += String.fromCharCode(specialCode);
i++; // skip a step
continue;
}
}
// hacky?
if (form === CharacterForm.Isolated) {
shapedWord += word[i];
continue;
}
var shapedCode = glyphForms[code][form];
shapedWord += String.fromCharCode(shapedCode);
}
return shapedWord;
}
this.ContainsArabicCharacters = ContainsArabicCharacters;
this.ShapeArabicCharacters = ShapeArabicCharacters;
}
/* NEW TEXT EFFECTS */
var TextEffects = {};
function positiveModulo(number, divisor) {
return ((number % divisor) + divisor) % divisor;
}
var RainbowEffect = function() {
this.DoEffect = function(char, time) {
char.color = rainbowColorStartIndex + Math.floor(positiveModulo((time / 100) - char.col * 0.5, rainbowColorCount));
}
};
TextEffects["rbw"] = new RainbowEffect();
var ColorEffect = function(index) {
this.DoEffect = function(char) {
char.color = tileColorStartIndex + index;
}
};
TextEffects["clr1"] = new ColorEffect(0);
TextEffects["clr2"] = new ColorEffect(1); // TODO : should I use parameters instead of special names?
TextEffects["clr3"] = new ColorEffect(2);
var WavyEffect = function() {
this.DoEffect = function(char,time) {
char.offset.y += Math.sin((time / 250) - (char.col / 2)) * 2;
}
};
TextEffects["wvy"] = new WavyEffect();
var ShakyEffect = function() {
function disturb(func, time, offset, mult1, mult2) {
return func((time * mult1) - (offset * mult2));
}
this.DoEffect = function(char,time) {
char.offset.y += 1.5
* disturb(Math.sin, time, char.col, 0.1, 0.5)
* disturb(Math.cos, time, char.col, 0.3, 0.2)
* disturb(Math.sin, time, char.row, 2.0, 1.0);
char.offset.x += 1.5
* disturb(Math.cos, time, char.row, 0.1, 1.0)
* disturb(Math.sin, time, char.col, 3.0, 0.7)
* disturb(Math.cos, time, char.col, 0.2, 0.3);
}
};
TextEffects["shk"] = new ShakyEffect();
var DebugHighlightEffect = function() {
this.DoEffect = function(char) {
char.color = tileColorStartIndex;
}
}
TextEffects["_debug_highlight"] = new DebugHighlightEffect();
} // Dialog()
</script>
<script>
function TileRenderer(tilesize) {
// todo : do I need to pass in tilesize? or can I use the global value?
bitsyLog("!!!!! NEW TILE RENDERER");
var drawingCache = {
source: {},
render: {},
};
// var debugRenderCount = 0;
function createRenderCacheId(drawingId, colorIndex) {
return drawingId + "_" + colorIndex;
}
function renderDrawing(drawing) {
// debugRenderCount++;
// bitsyLog("RENDER COUNT " + debugRenderCount);
var col = drawing.col;
var drwId = drawing.drw;
var drawingFrames = drawingCache.source[drwId];
// initialize render cache entry
var cacheId = createRenderCacheId(drwId, col);
if (drawingCache.render[cacheId] === undefined) {
// initialize array of frames for drawing
drawingCache.render[cacheId] = [];
}
for (var i = 0; i < drawingFrames.length; i++) {
var frameData = drawingFrames[i];
var frameTileId = renderTileFromDrawingData(frameData, col);
drawingCache.render[cacheId].push(frameTileId);
}
}
function renderTileFromDrawingData(drawingData, col) {
var tileId = bitsyAddTile();
var backgroundColor = tileColorStartIndex + 0;
var foregroundColor = tileColorStartIndex + col;
bitsyDrawBegin(tileId);
for (var y = 0; y < tilesize; y++) {
for (var x = 0; x < tilesize; x++) {
var px = drawingData[y][x];
if (px === 1) {
bitsyDrawPixel(foregroundColor, x, y);
}
else {
bitsyDrawPixel(backgroundColor, x, y);
}
}
}
bitsyDrawEnd();
return tileId;
}
// TODO : move into core
function undefinedOrNull(x) {
return x === undefined || x === null;
}
function isDrawingRendered(drawing) {
var cacheId = createRenderCacheId(drawing.drw, drawing.col);
return drawingCache.render[cacheId] != undefined;
}
function getRenderedDrawingFrames(drawing) {
var cacheId = createRenderCacheId(drawing.drw, drawing.col);
return drawingCache.render[cacheId];
}
function getDrawingFrameTileId(drawing, frameOverride) {
var frameIndex = 0;
if (drawing != null && drawing.animation.isAnimated) {
if (frameOverride != undefined && frameOverride != null) {
frameIndex = frameOverride;
}
else {
frameIndex = drawing.animation.frameIndex;
}
}
return getRenderedDrawingFrames(drawing)[frameIndex];
}
function getOrRenderDrawingFrame(drawing, frameOverride) {
// bitsyLog("frame render: " + drawing.type + " " + drawing.id + " f:" + frameOverride);
if (!isDrawingRendered(drawing)) {
// bitsyLog("frame render: doesn't exist");
renderDrawing(drawing);
}
return getDrawingFrameTileId(drawing, frameOverride);
}
/* PUBLIC INTERFACE */
this.GetDrawingFrame = getOrRenderDrawingFrame;
this.SetDrawingSource = function(drawingId, drawingData) {
drawingCache.source[drawingId] = drawingData;
// TODO : reset render cache for this image
}
this.GetDrawingSource = function(drawingId) {
return drawingCache.source[drawingId];
}
this.GetFrameCount = function(drawingId) {
return drawingCache.source[drawingId].length;
}
this.ClearCache = function() {
bitsyResetTiles();
drawingCache.render = {};
}
} // Renderer()
</script>
<script>
var room = {};
var tile = {};
var sprite = {};
var item = {};
var dialog = {};
var palette = { //start off with a default palette
"default" : {
name : "default",
colors : [[0,0,0],[255,255,255],[255,255,255]]
}
};
var variable = {}; // these are starting variable values -- they don't update (or I don't think they will)
var playerId = "A";
var titleDialogId = "title";
function getTitle() {
return dialog[titleDialogId].src;
}
function setTitle(titleSrc) {
dialog[titleDialogId] = { src:titleSrc, name:null };
}
var defaultFontName = "ascii_small";
var fontName = defaultFontName;
var TextDirection = {
LeftToRight : "LTR",
RightToLeft : "RTL"
};
var textDirection = TextDirection.LeftToRight;
/* NAME-TO-ID MAPS */
var names = {
room : {},
tile : {},
sprite : {},
item : {},
dialog : {},
};
function updateNamesFromCurData() {
function createNameMap(objectStore) {
var map = {};
for (id in objectStore) {
if (objectStore[id].name != undefined && objectStore[id].name != null) {
map[objectStore[id].name] = id;
}
}
return map;
}
names.room = createNameMap(room);
names.tile = createNameMap(tile);
names.sprite = createNameMap(sprite);
names.item = createNameMap(item);
names.dialog = createNameMap(dialog);
}
var spriteStartLocations = {};
/* VERSION */
var version = {
major: 7, // major changes
minor: 12, // smaller changes
devBuildPhase: "RELEASE",
};
function getEngineVersion() {
return version.major + "." + version.minor;
}
/* FLAGS */
var flags;
function resetFlags() {
flags = {
ROOM_FORMAT : 0 // 0 = non-comma separated, 1 = comma separated
};
}
resetFlags(); //init flags on load script
// SUPER hacky location... :/
var editorDevFlags = {
// NONE right now!
};
function clearGameData() {
room = {};
tile = {};
sprite = {};
item = {};
dialog = {};
palette = { //start off with a default palette
"default" : {
name : "default",
colors : [[0,0,0],[255,255,255],[255,255,255]]
}
};
isEnding = false; //todo - correct place for this?
variable = {};
// TODO RENDERER : clear data?
spriteStartLocations = {};
updateNamesFromCurData();
fontName = defaultFontName; // TODO : reset font manager too?
textDirection = TextDirection.LeftToRight;
}
var scale = 4; //this is stupid but necessary
var tilesize = 8;
var mapsize = 16;
var width = mapsize * tilesize;
var height = mapsize * tilesize;
var curRoom = "0";
var prevTime = 0;
var deltaTime = 0;
// engine event hooks for the editor
var onInventoryChanged = null;
var onVariableChanged = null;
var onGameReset = null;
var onInitRoom = null;
var isPlayerEmbeddedInEditor = false;
var renderer = new TileRenderer(tilesize);
var curGameData = null;
var curDefaultFontData = null;
function load_game(gameData, defaultFontData, startWithTitle) {
curGameData = gameData; //remember the current game (used to reset the game)
dialogBuffer.Reset();
scriptInterpreter.ResetEnvironment(); // ensures variables are reset -- is this the best way?
parseWorld(gameData);
if (!isPlayerEmbeddedInEditor && defaultFontData) {
curDefaultFontData = defaultFontData; // store for resetting game
// todo : consider replacing this with a more general system for requesting resources from the system?
// hack to ensure default font is available
fontManager.AddResource(defaultFontName + fontManager.GetExtension(), defaultFontData);
}
var font = fontManager.Get( fontName );
dialogBuffer.SetFont(font);
dialogRenderer.SetFont(font);
setInitialVariables();
onready(startWithTitle);
}
function reset_cur_game() {
if (curGameData == null) {
return; //can't reset if we don't have the game data
}
stopGame();
clearGameData();
load_game(curGameData, curDefaultFontData);
if (isPlayerEmbeddedInEditor && onGameReset != null) {
onGameReset();
}
}
function onready(startWithTitle) {
if (startWithTitle === undefined || startWithTitle === null) {
startWithTitle = true;
}
if (startWithTitle) { // used by editor
startNarrating(getTitle());
}
}
function setInitialVariables() {
for(id in variable) {
var value = variable[id]; // default to string
if(value === "true") {
value = true;
}
else if(value === "false") {
value = false;
}
else if(!isNaN(parseFloat(value))) {
value = parseFloat(value);
}
scriptInterpreter.SetVariable(id,value);
}
scriptInterpreter.SetOnVariableChangeHandler( onVariableChanged );
}
function getOffset(evt) {
var offset = { x:0, y:0 };
var el = evt.target;
var rect = el.getBoundingClientRect();
offset.x += rect.left + el.scrollLeft;
offset.y += rect.top + el.scrollTop;
offset.x = evt.clientX - offset.x;
offset.y = evt.clientY - offset.y;
return offset;
}
function stopGame() {
bitsyLog("stop GAME!");
}
function update() {
var curTime = Date.now();
deltaTime = curTime - prevTime;
if (curRoom == null) {
// in the special case where there is no valid room, end the game
startNarrating( "", true /*isEnding*/ );
}
if (!transition.IsTransitionActive()) {
updateInput();
}
if (transition.IsTransitionActive()) {
// transition animation takes over everything!
transition.UpdateTransition(deltaTime);
}
else {
bitsySetGraphicsMode(1);
if (!isNarrating && !isEnding) {
updateAnimation();
drawRoom(room[curRoom]); // draw world if game has begun
}
else {
clearRoom();
}
// if (isDialogMode) { // dialog mode
if(dialogBuffer.IsActive()) {
dialogRenderer.Draw( dialogBuffer, deltaTime );
dialogBuffer.Update( deltaTime );
}
// keep moving avatar if player holds down button
if( !dialogBuffer.IsActive() && !isEnding )
{
if( curPlayerDirection != Direction.None ) {
playerHoldToMoveTimer -= deltaTime;
if( playerHoldToMoveTimer <= 0 )
{
movePlayer( curPlayerDirection );
playerHoldToMoveTimer = 150;
}
}
}
}
prevTime = curTime;
}
var isAnyButtonHeld = false;
var isIgnoringInput = false;
function isAnyButtonDown() {
return bitsyGetButton(0) || bitsyGetButton(1) || bitsyGetButton(2) || bitsyGetButton(3) || bitsyGetButton(4);
}
function updateInput() {
if (dialogBuffer.IsActive()) {
if (!isAnyButtonHeld && isAnyButtonDown()) {
/* CONTINUE DIALOG */
if (dialogBuffer.CanContinue()) {
var hasMoreDialog = dialogBuffer.Continue();
if (!hasMoreDialog) {
// ignore currently held keys UNTIL they are released (stops player from insta-moving)
isIgnoringInput = true;
curPlayerDirection = Direction.None;
}
}
else {
dialogBuffer.Skip();
}
}
}
else if ( isEnding ) {
/* BITSY MUSEUM HACK:
instead of reseting on ending it takes player back to the museum
also removes need to click button to reset */
if (!redirectBitsy){
redirectBitsy = true;
window.location.href = "lootboxes.html";
}
}
else if (!isIgnoringInput) {
/* WALK */
var prevPlayerDirection = curPlayerDirection;
if (bitsyGetButton(0)) {
curPlayerDirection = Direction.Up;
}
else if (bitsyGetButton(1)) {
curPlayerDirection = Direction.Down;
}
else if (bitsyGetButton(2)) {
curPlayerDirection = Direction.Left;
}
else if (bitsyGetButton(3)) {
curPlayerDirection = Direction.Right;
}
else {
curPlayerDirection = Direction.None;
}
if (curPlayerDirection != Direction.None && curPlayerDirection != prevPlayerDirection) {
movePlayer(curPlayerDirection);
playerHoldToMoveTimer = 500;
}
}
if (!isAnyButtonDown()) {
isIgnoringInput = false;
}
isAnyButtonHeld = isAnyButtonDown();
}
var animationCounter = 0;
var animationTime = 400;
function updateAnimation() {
animationCounter += deltaTime;
if ( animationCounter >= animationTime ) {
// animate sprites
for (id in sprite) {
var spr = sprite[id];
if (spr.animation.isAnimated) {
spr.animation.frameIndex = ( spr.animation.frameIndex + 1 ) % spr.animation.frameCount;
}
}
// animate tiles
for (id in tile) {
var til = tile[id];
if (til.animation.isAnimated) {
til.animation.frameIndex = ( til.animation.frameIndex + 1 ) % til.animation.frameCount;
}
}
// animate items
for (id in item) {
var itm = item[id];
if (itm.animation.isAnimated) {
itm.animation.frameIndex = ( itm.animation.frameIndex + 1 ) % itm.animation.frameCount;
}
}
// reset counter
animationCounter = 0;
}
}
function resetAllAnimations() {
for (id in sprite) {
var spr = sprite[id];
if (spr.animation.isAnimated) {
spr.animation.frameIndex = 0;
}
}
for (id in tile) {
var til = tile[id];
if (til.animation.isAnimated) {
til.animation.frameIndex = 0;
}
}
for (id in item) {
var itm = item[id];
if (itm.animation.isAnimated) {
itm.animation.frameIndex = 0;
}
}
}
function getSpriteAt(x,y) {
for (id in sprite) {
var spr = sprite[id];
if (spr.room === curRoom) {
if (spr.x == x && spr.y == y) {
return id;
}
}
}
return null;
}
var Direction = {
None : -1,
Up : 0,
Down : 1,
Left : 2,
Right : 3
};
var curPlayerDirection = Direction.None;
var playerHoldToMoveTimer = 0;
function movePlayer(direction) {
var roomIds = Object.keys(room);
if (player().room == null || roomIds.indexOf(player().room) < 0) {
return; // player room is missing or invalid.. can't move them!
}
var spr = null;
if ( curPlayerDirection == Direction.Left && !(spr = getSpriteLeft()) && !isWallLeft()) {
player().x -= 1;
}
else if ( curPlayerDirection == Direction.Right && !(spr = getSpriteRight()) && !isWallRight()) {
player().x += 1;
}
else if ( curPlayerDirection == Direction.Up && !(spr = getSpriteUp()) && !isWallUp()) {
player().y -= 1;
}
else if ( curPlayerDirection == Direction.Down && !(spr = getSpriteDown()) && !isWallDown()) {
player().y += 1;
}
var ext = getExit( player().room, player().x, player().y );
var end = getEnding( player().room, player().x, player().y );
var itmIndex = getItemIndex( player().room, player().x, player().y );
// do items first, because you can pick up an item AND go through a door
if (itmIndex > -1) {
var itm = room[player().room].items[itmIndex];
var itemRoom = player().room;
startItemDialog(itm.id, function() {
// remove item from room
room[itemRoom].items.splice(itmIndex, 1);
// update player inventory
if (player().inventory[itm.id]) {
player().inventory[itm.id] += 1;
}
else {
player().inventory[itm.id] = 1;
}
// show inventory change in UI
if (onInventoryChanged != null) {
onInventoryChanged(itm.id);
}
});
}
if (end) {
startEndingDialog(end);
}
else if (ext) {
movePlayerThroughExit(ext);
}
else if (spr) {
startSpriteDialog(spr /*spriteId*/);
}
}
var transition = new TransitionManager();
function movePlayerThroughExit(ext) {
var GoToDest = function() {
if (ext.transition_effect != null) {
transition.BeginTransition(
player().room,
player().x,
player().y,
ext.dest.room,
ext.dest.x,
ext.dest.y,
ext.transition_effect);
transition.UpdateTransition(0);
transition.OnTransitionComplete(function() {
player().room = ext.dest.room;
player().x = ext.dest.x;
player().y = ext.dest.y;
curRoom = ext.dest.room;
initRoom(curRoom);
});
}
else {
player().room = ext.dest.room;
player().x = ext.dest.x;
player().y = ext.dest.y;
curRoom = ext.dest.room;
initRoom(curRoom);
}
};
if (ext.dlg != undefined && ext.dlg != null) {
// TODO : I need to simplify dialog code,
// so I don't have to get the ID and the source str
// every time!
startDialog(
dialog[ext.dlg].src,
ext.dlg,
function(result) {
var isLocked = ext.property && ext.property.locked === true;
if (!isLocked) {
GoToDest();
}
},
ext);
}
else {
GoToDest();
}
}
/* PALETTE INDICES */
var textBackgroundIndex = 0;
var textArrowIndex = 1;
var textColorIndex = 2;
// precalculated rainbow colors
var rainbowColorStartIndex = 3;
var rainbowColorCount = 10;
var rainbowColors = [
[255,0,0],
[255,217,0],
[78,255,0],
[0,255,125],
[0,192,255],
[0,18,255],
[136,0,255],
[255,0,242],
[255,0,138],
[255,0,61],
];
// todo : where should this be stored?
var tileColorStartIndex = 16;
function updatePaletteWithTileColors(tileColors) {
// clear existing colors
bitsyResetColors();
// textbox colors
bitsySetColor(textBackgroundIndex, 0, 0, 0); // black
bitsySetColor(textArrowIndex, 255, 255, 255); // white
bitsySetColor(textColorIndex, 255, 255, 255); // white
// todo : move this to game init?
// rainbow colors
for (var i = 0; i < rainbowColorCount; i++) {
var color = rainbowColors[i];
bitsySetColor(rainbowColorStartIndex + i, color[0], color[1], color[2]);
}
// tile colors
for (var i = 0; i < tileColors.length; i++) {
var color = tileColors[i];
bitsySetColor(tileColorStartIndex + i, color[0], color[1], color[2]);
}
}
function updatePalette(palId) {
var pal = palette[palId];
bitsyLog(pal.colors.length, "editor");
updatePaletteWithTileColors(pal.colors);
}
function initRoom(roomId) {
bitsyLog("init room " + roomId);
updatePalette(curPal());
renderer.ClearCache();
// init exit properties
for (var i = 0; i < room[roomId].exits.length; i++) {
room[roomId].exits[i].property = { locked:false };
}
// init ending properties
for (var i = 0; i < room[roomId].endings.length; i++) {
room[roomId].endings[i].property = { locked:false };
}
if (onInitRoom) {
onInitRoom(roomId);
}
}
function getItemIndex( roomId, x, y ) {
for( var i = 0; i < room[roomId].items.length; i++ ) {
var itm = room[roomId].items[i];
if ( itm.x == x && itm.y == y)
return i;
}
return -1;
}
function getSpriteLeft() { //repetitive?
return getSpriteAt( player().x - 1, player().y );
}
function getSpriteRight() {
return getSpriteAt( player().x + 1, player().y );
}
function getSpriteUp() {
return getSpriteAt( player().x, player().y - 1 );
}
function getSpriteDown() {
return getSpriteAt( player().x, player().y + 1 );
}
function isWallLeft() {
return (player().x - 1 < 0) || isWall( player().x - 1, player().y );
}
function isWallRight() {
return (player().x + 1 >= mapsize) || isWall( player().x + 1, player().y );
}
function isWallUp() {
return (player().y - 1 < 0) || isWall( player().x, player().y - 1 );
}
function isWallDown() {
return (player().y + 1 >= mapsize) || isWall( player().x, player().y + 1 );
}
function isWall(x,y,roomId) {
if(roomId == undefined || roomId == null)
roomId = curRoom;
var tileId = getTile( x, y );
if( tileId === '0' )
return false; // Blank spaces aren't walls, ya doofus
if( tile[tileId].isWall === undefined || tile[tileId].isWall === null ) {
// No wall-state defined: check room-specific walls
var i = room[roomId].walls.indexOf( getTile(x,y) );
return i > -1;
}
// Otherwise, use the tile's own wall-state
return tile[tileId].isWall;
}
function getItem(roomId,x,y) {
for (i in room[roomId].items) {
var item = room[roomId].items[i];
if (x == item.x && y == item.y) {
return item;
}
}
return null;
}
function getExit(roomId,x,y) {
for (i in room[roomId].exits) {
var e = room[roomId].exits[i];
if (x == e.x && y == e.y) {
return e;
}
}
return null;
}
function getEnding(roomId,x,y) {
for (i in room[roomId].endings) {
var e = room[roomId].endings[i];
if (x == e.x && y == e.y) {
return e;
}
}
return null;
}
function getTile(x,y) {
// bitsyLog(x + " " + y);
var t = getRoom().tilemap[y][x];
return t;
}
function player() {
return sprite[playerId];
}
// Sort of a hack for legacy palette code (when it was just an array)
function getPal(id) {
if (palette[id] === undefined) {
id = "default";
}
return palette[ id ].colors;
}
function getRoom() {
return room[curRoom];
}
function isSpriteOffstage(id) {
return sprite[id].room == null;
}
function parseWorld(file) {
spriteStartLocations = {};
resetFlags();
var versionNumber = 0;
// flags to keep track of which compatibility conversions
// need to be applied to this game data
var compatibilityFlags = {
convertSayToPrint : false,
combineEndingsWithDialog : false,
convertImplicitSpriteDialogIds : false,
};
var lines = file.split("\n");
var i = 0;
while (i < lines.length) {
var curLine = lines[i];
// bitsyLog(lines[i]);
if (i == 0) {
i = parseTitle(lines, i);
}
else if (curLine.length <= 0 || curLine.charAt(0) === "#") {
// collect version number (from a comment.. hacky I know)
if (curLine.indexOf("# BITSY VERSION ") != -1) {
versionNumber = parseFloat(curLine.replace("# BITSY VERSION ", ""));
if (versionNumber < 5.0) {
compatibilityFlags.convertSayToPrint = true;
}
if (versionNumber < 7.0) {
compatibilityFlags.combineEndingsWithDialog = true;
compatibilityFlags.convertImplicitSpriteDialogIds = true;
}
}
//skip blank lines & comments
i++;
}
else if (getType(curLine) == "PAL") {
i = parsePalette(lines, i);
}
else if (getType(curLine) === "ROOM" || getType(curLine) === "SET") { //SET for back compat
i = parseRoom(lines, i, compatibilityFlags);
}
else if (getType(curLine) === "TIL") {
i = parseTile(lines, i);
}
else if (getType(curLine) === "SPR") {
i = parseSprite(lines, i);
}
else if (getType(curLine) === "ITM") {
i = parseItem(lines, i);
}
else if (getType(curLine) === "DRW") {
i = parseDrawing(lines, i);
}
else if (getType(curLine) === "DLG") {
i = parseDialog(lines, i, compatibilityFlags);
}
else if (getType(curLine) === "END" && compatibilityFlags.combineEndingsWithDialog) {
// parse endings for back compat
i = parseEnding(lines, i, compatibilityFlags);
}
else if (getType(curLine) === "VAR") {
i = parseVariable(lines, i);
}
else if (getType(curLine) === "DEFAULT_FONT") {
i = parseFontName(lines, i);
}
else if (getType(curLine) === "TEXT_DIRECTION") {
i = parseTextDirection(lines, i);
}
else if (getType(curLine) === "FONT") {
i = parseFontData(lines, i);
}
else if (getType(curLine) === "!") {
i = parseFlag(lines, i);
}
else {
i++;
}
}
placeSprites();
var roomIds = Object.keys(room);
if (player() != undefined && player().room != null && roomIds.indexOf(player().room) != -1) {
// player has valid room
curRoom = player().room;
}
else if (roomIds.length > 0) {
// player not in any room! what the heck
curRoom = roomIds[0];
}
else {
// uh oh there are no rooms I guess???
curRoom = null;
}
if (curRoom != null) {
initRoom(curRoom);
}
scriptCompatibility(compatibilityFlags);
return versionNumber;
}
function scriptCompatibility(compatibilityFlags) {
if (compatibilityFlags.convertSayToPrint) {
bitsyLog("CONVERT SAY TO PRINT!");
var PrintFunctionVisitor = function() {
var didChange = false;
this.DidChange = function() { return didChange; };
this.Visit = function(node) {
if (node.type != "function") {
return;
}
if (node.name === "say") {
node.name = "print";
didChange = true;
}
};
};
for (dlgId in dialog) {
var dialogScript = scriptInterpreter.Parse(dialog[dlgId].src);
var visitor = new PrintFunctionVisitor();
dialogScript.VisitAll(visitor);
if (visitor.DidChange()) {
var newDialog = dialogScript.Serialize();
if (newDialog.indexOf("\n") > -1) {
newDialog = '"""\n' + newDialog + '\n"""';
}
dialog[dlgId].src = newDialog;
}
}
}
}
//TODO this is in progress and doesn't support all features
function serializeWorld(skipFonts) {
if (skipFonts === undefined || skipFonts === null)
skipFonts = false;
var worldStr = "";
/* TITLE */
worldStr += getTitle() + "\n";
worldStr += "\n";
/* VERSION */
worldStr += "# BITSY VERSION " + getEngineVersion() + "\n"; // add version as a comment for debugging purposes
if (version.devBuildPhase != "RELEASE") {
worldStr += "# DEVELOPMENT BUILD -- " + version.devBuildPhase;
}
worldStr += "\n";
/* FLAGS */
for (f in flags) {
worldStr += "! " + f + " " + flags[f] + "\n";
}
worldStr += "\n"
/* FONT */
if (fontName != defaultFontName) {
worldStr += "DEFAULT_FONT " + fontName + "\n";
worldStr += "\n"
}
if (textDirection != TextDirection.LeftToRight) {
worldStr += "TEXT_DIRECTION " + textDirection + "\n";
worldStr += "\n"
}
/* PALETTE */
for (id in palette) {
if (id != "default") {
worldStr += "PAL " + id + "\n";
if( palette[id].name != null )
worldStr += "NAME " + palette[id].name + "\n";
for (i in getPal(id)) {
for (j in getPal(id)[i]) {
worldStr += getPal(id)[i][j];
if (j < 2) worldStr += ",";
}
worldStr += "\n";
}
worldStr += "\n";
}
}
/* ROOM */
for (id in room) {
worldStr += "ROOM " + id + "\n";
if ( flags.ROOM_FORMAT == 0 ) {
// old non-comma separated format
for (i in room[id].tilemap) {
for (j in room[id].tilemap[i]) {
worldStr += room[id].tilemap[i][j];
}
worldStr += "\n";
}
}
else if ( flags.ROOM_FORMAT == 1 ) {
// new comma separated format
for (i in room[id].tilemap) {
for (j in room[id].tilemap[i]) {
worldStr += room[id].tilemap[i][j];
if (j < room[id].tilemap[i].length-1) worldStr += ","
}
worldStr += "\n";
}
}
if (room[id].name != null) {
/* NAME */
worldStr += "NAME " + room[id].name + "\n";
}
if (room[id].walls.length > 0) {
/* WALLS */
worldStr += "WAL ";
for (j in room[id].walls) {
worldStr += room[id].walls[j];
if (j < room[id].walls.length-1) {
worldStr += ",";
}
}
worldStr += "\n";
}
if (room[id].items.length > 0) {
/* ITEMS */
for (j in room[id].items) {
var itm = room[id].items[j];
worldStr += "ITM " + itm.id + " " + itm.x + "," + itm.y;
worldStr += "\n";
}
}
if (room[id].exits.length > 0) {
/* EXITS */
for (j in room[id].exits) {
var e = room[id].exits[j];
if ( isExitValid(e) ) {
worldStr += "EXT " + e.x + "," + e.y + " " + e.dest.room + " " + e.dest.x + "," + e.dest.y;
if (e.transition_effect != undefined && e.transition_effect != null) {
worldStr += " FX " + e.transition_effect;
}
if (e.dlg != undefined && e.dlg != null) {
worldStr += " DLG " + e.dlg;
}
worldStr += "\n";
}
}
}
if (room[id].endings.length > 0) {
/* ENDINGS */
for (j in room[id].endings) {
var e = room[id].endings[j];
// todo isEndingValid
worldStr += "END " + e.id + " " + e.x + "," + e.y;
worldStr += "\n";
}
}
if (room[id].pal != null && room[id].pal != "default") {
/* PALETTE */
worldStr += "PAL " + room[id].pal + "\n";
}
worldStr += "\n";
}
/* TILES */
for (id in tile) {
worldStr += "TIL " + id + "\n";
worldStr += serializeDrawing( "TIL_" + id );
if (tile[id].name != null && tile[id].name != undefined) {
/* NAME */
worldStr += "NAME " + tile[id].name + "\n";
}
if (tile[id].isWall != null && tile[id].isWall != undefined) {
/* WALL */
worldStr += "WAL " + tile[id].isWall + "\n";
}
if (tile[id].col != null && tile[id].col != undefined && tile[id].col != 1) {
/* COLOR OVERRIDE */
worldStr += "COL " + tile[id].col + "\n";
}
worldStr += "\n";
}
/* SPRITES */
for (id in sprite) {
worldStr += "SPR " + id + "\n";
worldStr += serializeDrawing( "SPR_" + id );
if (sprite[id].name != null && sprite[id].name != undefined) {
/* NAME */
worldStr += "NAME " + sprite[id].name + "\n";
}
if (sprite[id].dlg != null) {
worldStr += "DLG " + sprite[id].dlg + "\n";
}
if (sprite[id].room != null) {
/* SPRITE POSITION */
worldStr += "POS " + sprite[id].room + " " + sprite[id].x + "," + sprite[id].y + "\n";
}
if (sprite[id].inventory != null) {
for(itemId in sprite[id].inventory) {
worldStr += "ITM " + itemId + " " + sprite[id].inventory[itemId] + "\n";
}
}
if (sprite[id].col != null && sprite[id].col != undefined && sprite[id].col != 2) {
/* COLOR OVERRIDE */
worldStr += "COL " + sprite[id].col + "\n";
}
worldStr += "\n";
}
/* ITEMS */
for (id in item) {
worldStr += "ITM " + id + "\n";
worldStr += serializeDrawing( "ITM_" + id );
if (item[id].name != null && item[id].name != undefined) {
/* NAME */
worldStr += "NAME " + item[id].name + "\n";
}
if (item[id].dlg != null) {
worldStr += "DLG " + item[id].dlg + "\n";
}
if (item[id].col != null && item[id].col != undefined && item[id].col != 2) {
/* COLOR OVERRIDE */
worldStr += "COL " + item[id].col + "\n";
}
worldStr += "\n";
}
/* DIALOG */
for (id in dialog) {
if (id != titleDialogId) {
worldStr += "DLG " + id + "\n";
worldStr += dialog[id].src + "\n";
if (dialog[id].name != null) {
worldStr += "NAME " + dialog[id].name + "\n";
}
worldStr += "\n";
}
}
/* VARIABLES */
for (id in variable) {
worldStr += "VAR " + id + "\n";
worldStr += variable[id] + "\n";
worldStr += "\n";
}
/* FONT */
// TODO : support multiple fonts
if (fontName != defaultFontName && !skipFonts) {
worldStr += fontManager.GetData(fontName);
}
return worldStr;
}
function serializeDrawing(drwId) {
var drawingData = renderer.GetDrawingSource(drwId);
var drwStr = "";
for (f in drawingData) {
for (y in drawingData[f]) {
var rowStr = "";
for (x in drawingData[f][y]) {
rowStr += drawingData[f][y][x];
}
drwStr += rowStr + "\n";
}
if (f < (drawingData.length-1)) drwStr += ">\n";
}
return drwStr;
}
function isExitValid(e) {
var hasValidStartPos = e.x >= 0 && e.x < mapsize && e.y >= 0 && e.y < mapsize;
var hasDest = e.dest != null;
var hasValidRoomDest = (e.dest.room != null && e.dest.x >= 0 && e.dest.x < mapsize && e.dest.y >= 0 && e.dest.y < mapsize);
return hasValidStartPos && hasDest && hasValidRoomDest;
}
function placeSprites() {
for (id in spriteStartLocations) {
//bitsyLog(id);
//bitsyLog( spriteStartLocations[id] );
//bitsyLog(sprite[id]);
sprite[id].room = spriteStartLocations[id].room;
sprite[id].x = spriteStartLocations[id].x;
sprite[id].y = spriteStartLocations[id].y;
//bitsyLog(sprite[id]);
}
}
/* ARGUMENT GETTERS */
function getType(line) {
return getArg(line,0);
}
function getId(line) {
return getArg(line,1);
}
function getArg(line,arg) {
return line.split(" ")[arg];
}
function getCoord(line,arg) {
return getArg(line,arg).split(",");
}
function parseTitle(lines, i) {
var results = scriptUtils.ReadDialogScript(lines,i);
setTitle(results.script);
i = results.index;
i++;
return i;
}
function parseRoom(lines, i, compatibilityFlags) {
var id = getId(lines[i]);
room[id] = {
id : id,
tilemap : [],
walls : [],
exits : [],
endings : [],
items : [],
pal : null,
name : null
};
i++;
// create tile map
if ( flags.ROOM_FORMAT == 0 ) {
// old way: no commas, single char tile ids
var end = i + mapsize;
var y = 0;
for (; i<end; i++) {
room[id].tilemap.push( [] );
for (x = 0; x<mapsize; x++) {
room[id].tilemap[y].push( lines[i].charAt(x) );
}
y++;
}
}
else if ( flags.ROOM_FORMAT == 1 ) {
// new way: comma separated, multiple char tile ids
var end = i + mapsize;
var y = 0;
for (; i<end; i++) {
room[id].tilemap.push( [] );
var lineSep = lines[i].split(",");
for (x = 0; x<mapsize; x++) {
room[id].tilemap[y].push( lineSep[x] );
}
y++;
}
}
while (i < lines.length && lines[i].length > 0) { //look for empty line
// bitsyLog(getType(lines[i]));
if (getType(lines[i]) === "SPR") {
/* NOTE SPRITE START LOCATIONS */
var sprId = getId(lines[i]);
if (sprId.indexOf(",") == -1 && lines[i].split(" ").length >= 3) { //second conditional checks for coords
/* PLACE A SINGLE SPRITE */
var sprCoord = lines[i].split(" ")[2].split(",");
spriteStartLocations[sprId] = {
room : id,
x : parseInt(sprCoord[0]),
y : parseInt(sprCoord[1])
};
}
else if ( flags.ROOM_FORMAT == 0 ) { // TODO: right now this shortcut only works w/ the old comma separate format
/* PLACE MULTIPLE SPRITES*/
//Does find and replace in the tilemap (may be hacky, but its convenient)
var sprList = sprId.split(",");
for (row in room[id].tilemap) {
for (s in sprList) {
var col = room[id].tilemap[row].indexOf( sprList[s] );
//if the sprite is in this row, replace it with the "null tile" and set its starting position
if (col != -1) {
room[id].tilemap[row][col] = "0";
spriteStartLocations[ sprList[s] ] = {
room : id,
x : parseInt(col),
y : parseInt(row)
};
}
}
}
}
}
else if (getType(lines[i]) === "ITM") {
var itmId = getId(lines[i]);
var itmCoord = lines[i].split(" ")[2].split(",");
var itm = {
id: itmId,
x : parseInt(itmCoord[0]),
y : parseInt(itmCoord[1])
};
room[id].items.push( itm );
}
else if (getType(lines[i]) === "WAL") {
/* DEFINE COLLISIONS (WALLS) */
room[id].walls = getId(lines[i]).split(",");
}
else if (getType(lines[i]) === "EXT") {
/* ADD EXIT */
var exitArgs = lines[i].split(" ");
//arg format: EXT 10,5 M 3,2 [AVA:7 LCK:a,9] [AVA 7 LCK a 9]
var exitCoords = exitArgs[1].split(",");
var destName = exitArgs[2];
var destCoords = exitArgs[3].split(",");
var ext = {
x : parseInt(exitCoords[0]),
y : parseInt(exitCoords[1]),
dest : {
room : destName,
x : parseInt(destCoords[0]),
y : parseInt(destCoords[1])
},
transition_effect : null,
dlg: null,
};
// optional arguments
var exitArgIndex = 4;
while (exitArgIndex < exitArgs.length) {
if (exitArgs[exitArgIndex] == "FX") {
ext.transition_effect = exitArgs[exitArgIndex+1];
exitArgIndex += 2;
}
else if (exitArgs[exitArgIndex] == "DLG") {
ext.dlg = exitArgs[exitArgIndex+1];
exitArgIndex += 2;
}
else {
exitArgIndex += 1;
}
}
room[id].exits.push(ext);
}
else if (getType(lines[i]) === "END") {
/* ADD ENDING */
var endId = getId(lines[i]);
// compatibility with when endings were stored separate from other dialog
if (compatibilityFlags.combineEndingsWithDialog) {
endId = "end_" + endId;
}
var endCoords = getCoord(lines[i], 2);
var end = {
id : endId,
x : parseInt(endCoords[0]),
y : parseInt(endCoords[1])
};
room[id].endings.push(end);
}
else if (getType(lines[i]) === "PAL") {
/* CHOOSE PALETTE (that's not default) */
room[id].pal = getId(lines[i]);
}
else if (getType(lines[i]) === "NAME") {
var name = lines[i].split(/\s(.+)/)[1];
room[id].name = name;
names.room[name] = id;
}
i++;
}
return i;
}
function parsePalette(lines,i) { //todo this has to go first right now :(
var id = getId(lines[i]);
i++;
var colors = [];
var name = null;
while (i < lines.length && lines[i].length > 0) { //look for empty line
var args = lines[i].split(" ");
if (args[0] === "NAME") {
name = lines[i].split(/\s(.+)/)[1];
}
else {
var col = [];
lines[i].split(",").forEach(function(i) {
col.push(parseInt(i));
});
colors.push(col);
}
i++;
}
palette[id] = {
id : id,
name : name,
colors : colors
};
return i;
}
function parseTile(lines, i) {
var id = getId(lines[i]);
var tileData = createDrawingData("TIL", id);
i++;
// read & store tile image source
i = parseDrawingCore(lines, i, tileData.drw);
// update animation info
tileData.animation.frameCount = renderer.GetFrameCount(tileData.drw);
tileData.animation.isAnimated = tileData.animation.frameCount > 1;
// read other properties
while (i < lines.length && lines[i].length > 0) { // look for empty line
if (getType(lines[i]) === "COL") {
tileData.col = parseInt(getId(lines[i]));
}
else if (getType(lines[i]) === "NAME") {
/* NAME */
tileData.name = lines[i].split(/\s(.+)/)[1];
names.tile[tileData.name] = id;
}
else if (getType(lines[i]) === "WAL") {
var wallArg = getArg(lines[i], 1);
if (wallArg === "true") {
tileData.isWall = true;
}
else if (wallArg === "false") {
tileData.isWall = false;
}
}
i++;
}
// store tile data
tile[id] = tileData;
return i;
}
function parseSprite(lines, i) {
var id = getId(lines[i]);
var type = (id === "A") ? "AVA" : "SPR";
var spriteData = createDrawingData(type, id);
bitsyLog(spriteData);
i++;
// read & store sprite image source
i = parseDrawingCore(lines, i, spriteData.drw);
// update animation info
spriteData.animation.frameCount = renderer.GetFrameCount(spriteData.drw);
spriteData.animation.isAnimated = spriteData.animation.frameCount > 1;
// read other properties
while (i < lines.length && lines[i].length > 0) { // look for empty line
if (getType(lines[i]) === "COL") {
/* COLOR OFFSET INDEX */
spriteData.col = parseInt(getId(lines[i]));
}
else if (getType(lines[i]) === "POS") {
/* STARTING POSITION */
var posArgs = lines[i].split(" ");
var roomId = posArgs[1];
var coordArgs = posArgs[2].split(",");
spriteStartLocations[id] = {
room : roomId,
x : parseInt(coordArgs[0]),
y : parseInt(coordArgs[1])
};
}
else if(getType(lines[i]) === "DLG") {
spriteData.dlg = getId(lines[i]);
}
else if (getType(lines[i]) === "NAME") {
/* NAME */
spriteData.name = lines[i].split(/\s(.+)/)[1];
names.sprite[spriteData.name] = id;
}
else if (getType(lines[i]) === "ITM") {
/* ITEM STARTING INVENTORY */
var itemId = getId(lines[i]);
var itemCount = parseFloat(getArg(lines[i], 2));
spriteData.inventory[itemId] = itemCount;
}
i++;
}
// store sprite data
sprite[id] = spriteData;
return i;
}
function parseItem(lines, i) {
var id = getId(lines[i]);
var itemData = createDrawingData("ITM", id);
i++;
// read & store item image source
i = parseDrawingCore(lines, i, itemData.drw);
// update animation info
itemData.animation.frameCount = renderer.GetFrameCount(itemData.drw);
itemData.animation.isAnimated = itemData.animation.frameCount > 1;
// read other properties
while (i < lines.length && lines[i].length > 0) { // look for empty line
if (getType(lines[i]) === "COL") {
/* COLOR OFFSET INDEX */
itemData.col = parseInt(getArg(lines[i], 1));
}
else if (getType(lines[i]) === "DLG") {
itemData.dlg = getId(lines[i]);
}
else if (getType(lines[i]) === "NAME") {
/* NAME */
itemData.name = lines[i].split(/\s(.+)/)[1];
names.item[itemData.name] = id;
}
i++;
}
// store item data
item[id] = itemData;
return i;
}
function parseDrawing(lines, i) {
// store drawing source
var drwId = getId( lines[i] );
return parseDrawingCore( lines, i, drwId );
}
function parseDrawingCore(lines, i, drwId) {
var frameList = []; //init list of frames
frameList.push( [] ); //init first frame
var frameIndex = 0;
var y = 0;
while ( y < tilesize ) {
var l = lines[i+y];
var row = [];
for (x = 0; x < tilesize; x++) {
row.push( parseInt( l.charAt(x) ) );
}
frameList[frameIndex].push( row );
y++;
if (y === tilesize) {
i = i + y;
if ( lines[i] != undefined && lines[i].charAt(0) === ">" ) {
// start next frame!
frameList.push( [] );
frameIndex++;
//start the count over again for the next frame
i++;
y = 0;
}
}
}
renderer.SetDrawingSource(drwId, frameList);
return i;
}
// creates a drawing data structure with default property values for the type
function createDrawingData(type, id) {
// the avatar's drawing id still uses the sprite prefix (for back compat)
var drwId = (type === "AVA" ? "SPR" : type) + "_" + id;
var drawingData = {
type : type,
id : id,
name : null,
drw : drwId,
col : (type === "TIL") ? 1 : 2,
animation : {
isAnimated : false,
frameIndex : 0,
frameCount : 1,
},
};
// add type specific properties
if (type === "TIL") {
// default null value indicates it can vary from room to room (original version)
drawingData.isWall = null;
}
if (type === "AVA" || type === "SPR") {
// default sprite location is "offstage"
drawingData.room = null;
drawingData.x = -1;
drawingData.y = -1;
drawingData.inventory = {};
}
if (type === "AVA" || type === "SPR" || type === "ITM") {
drawingData.dlg = null;
}
return drawingData;
}
function parseScript(lines, i, backCompatPrefix, compatibilityFlags) {
var id = getId(lines[i]);
id = backCompatPrefix + id;
i++;
var results = scriptUtils.ReadDialogScript(lines,i);
dialog[id] = { src: results.script, name: null, id: id, };
if (compatibilityFlags.convertImplicitSpriteDialogIds) {
// explicitly hook up dialog that used to be implicitly
// connected by sharing sprite and dialog IDs in old versions
if (sprite[id]) {
if (sprite[id].dlg === undefined || sprite[id].dlg === null) {
sprite[id].dlg = id;
}
}
}
i = results.index;
return i;
}
function parseDialog(lines, i, compatibilityFlags) {
// hacky but I need to store this so I can set the name below
var id = getId(lines[i]);
i = parseScript(lines, i, "", compatibilityFlags);
if (lines[i].length > 0 && getType(lines[i]) === "NAME") {
dialog[id].name = lines[i].split(/\s(.+)/)[1]; // TODO : hacky to keep copying this regex around...
names.dialog[dialog[id].name] = id;
i++;
}
return i;
}
// keeping this around to parse old files where endings were separate from dialogs
function parseEnding(lines, i, compatibilityFlags) {
return parseScript(lines, i, "end_", compatibilityFlags);
}
function parseVariable(lines, i) {
var id = getId(lines[i]);
i++;
var value = lines[i];
i++;
variable[id] = value;
return i;
}
function parseFontName(lines, i) {
fontName = getArg(lines[i], 1);
i++;
return i;
}
function parseTextDirection(lines, i) {
textDirection = getArg(lines[i], 1);
i++;
return i;
}
function parseFontData(lines, i) {
// NOTE : we're not doing the actual parsing here --
// just grabbing the block of text that represents the font
// and giving it to the font manager to use later
var localFontName = getId(lines[i]);
var localFontData = lines[i];
i++;
while (i < lines.length && lines[i] != "") {
localFontData += "\n" + lines[i];
i++;
}
var localFontFilename = localFontName + fontManager.GetExtension();
fontManager.AddResource( localFontFilename, localFontData );
return i;
}
function parseFlag(lines, i) {
var id = getId(lines[i]);
var valStr = lines[i].split(" ")[2];
flags[id] = parseInt( valStr );
i++;
return i;
}
function drawTile(tileId, x, y) {
bitsyDrawBegin(0);
bitsyDrawTile(tileId, x, y);
bitsyDrawEnd();
}
function drawSprite(tileId, x, y) {
drawTile(tileId, x, y);
}
function drawItem(tileId, x, y) {
drawTile(tileId, x, y);
}
// var debugLastRoomDrawn = "0";
function clearRoom() {
var paletteId = "default";
if (room === undefined) {
// protect against invalid rooms
return;
}
if (room.pal != null && palette[paletteId] != undefined) {
paletteId = room.pal;
}
bitsyDrawBegin(0);
bitsyClear(tileColorStartIndex);
bitsyDrawEnd();
}
function drawRoom(room, frameIndex) { // frameIndex is optional
// if (room.id != debugLastRoomDrawn) {
// debugLastRoomDrawn = room.id;
// bitsyLog("DRAW ROOM " + debugLastRoomDrawn);
// }
if (room === undefined) {
// protect against invalid rooms
return;
}
// clear the screen buffer
bitsyDrawBegin(0);
bitsyClear(tileColorStartIndex);
bitsyDrawEnd();
//draw tiles
for (i in room.tilemap) {
for (j in room.tilemap[i]) {
var id = room.tilemap[i][j];
var x = parseInt(j);
var y = parseInt(i);
if (id != "0") {
//bitsyLog(id);
if (tile[id] == null) { // hack-around to avoid corrupting files (not a solution though!)
id = "0";
room.tilemap[i][j] = id;
}
else {
// bitsyLog(id);
drawTile(getTileFrame(tile[id], frameIndex), x, y);
}
}
}
}
//draw items
for (var i = 0; i < room.items.length; i++) {
var itm = room.items[i];
drawItem(getItemFrame(item[itm.id], frameIndex), itm.x, itm.y);
}
//draw sprites
for (id in sprite) {
var spr = sprite[id];
if (spr.room === room.id) {
drawSprite(getSpriteFrame(spr, frameIndex), spr.x, spr.y);
}
}
}
// TODO : remove these get*Image methods
function getTileFrame(t, frameIndex) {
return renderer.GetDrawingFrame(t, frameIndex);
}
function getSpriteFrame(s, frameIndex) {
return renderer.GetDrawingFrame(s, frameIndex);
}
function getItemFrame(itm, frameIndex) {
return renderer.GetDrawingFrame(itm, frameIndex);
}
function curPal() {
return getRoomPal(curRoom);
}
function getRoomPal(roomId) {
var defaultId = "default";
if (roomId == null) {
return defaultId;
}
else if (room[roomId].pal != null) {
//a specific palette was chosen
return room[roomId].pal;
}
else {
if (roomId in palette) {
//there is a palette matching the name of the room
return roomId;
}
else {
//use the default palette
return defaultId;
}
}
return defaultId;
}
var isDialogMode = false;
var isNarrating = false;
var isEnding = false;
var redirectBitsy = false;
var dialogModule = new Dialog();
var dialogRenderer = dialogModule.CreateRenderer();
var dialogBuffer = dialogModule.CreateBuffer();
var fontManager = new FontManager();
// TODO : is this scriptResult thing being used anywhere???
function onExitDialog(scriptResult, dialogCallback) {
bitsyLog("EXIT DIALOG!");
isDialogMode = false;
if (isNarrating) {
isNarrating = false;
}
if (isDialogPreview) {
isDialogPreview = false;
if (onDialogPreviewEnd != null) {
onDialogPreviewEnd();
}
}
if (dialogCallback != undefined && dialogCallback != null) {
dialogCallback(scriptResult);
}
}
/*
TODO
- titles and endings should also take advantage of the script pre-compilation if possible??
- could there be a namespace collision?
- what about dialog NAMEs vs IDs?
- what about a special script block separate from DLG?
*/
function startNarrating(dialogStr,end) {
bitsyLog("NARRATE " + dialogStr);
if(end === undefined) {
end = false;
}
isNarrating = true;
isEnding = end;
startDialog(dialogStr);
}
function startEndingDialog(ending) {
isNarrating = true;
isEnding = true;
startDialog(
dialog[ending.id].src,
ending.id,
function() {
var isLocked = ending.property && ending.property.locked === true;
if (isLocked) {
isEnding = false;
}
},
ending);
}
function startItemDialog(itemId, dialogCallback) {
var dialogId = item[itemId].dlg;
// bitsyLog("START ITEM DIALOG " + dialogId);
if (dialog[dialogId]) {
var dialogStr = dialog[dialogId].src;
startDialog(dialogStr, dialogId, dialogCallback);
}
else {
dialogCallback();
}
}
function startSpriteDialog(spriteId) {
var spr = sprite[spriteId];
var dialogId = spr.dlg;
// bitsyLog("START SPRITE DIALOG " + dialogId);
if (dialog[dialogId]){
var dialogStr = dialog[dialogId].src;
startDialog(dialogStr,dialogId);
}
}
function startDialog(dialogStr, scriptId, dialogCallback, objectContext) {
// bitsyLog("START DIALOG ");
if (dialogStr.length <= 0) {
// bitsyLog("ON EXIT DIALOG -- startDialog 1");
onExitDialog(null, dialogCallback);
return;
}
isDialogMode = true;
dialogRenderer.Reset();
dialogRenderer.SetCentered(isNarrating /*centered*/);
dialogBuffer.Reset();
scriptInterpreter.SetDialogBuffer(dialogBuffer);
var onScriptEnd = function(scriptResult) {
dialogBuffer.OnDialogEnd(function() {
onExitDialog(scriptResult, dialogCallback);
});
};
if (scriptId === undefined) { // TODO : what's this for again?
scriptInterpreter.Interpret(dialogStr, onScriptEnd);
}
else {
if (!scriptInterpreter.HasScript(scriptId)) {
scriptInterpreter.Compile(scriptId, dialogStr);
}
// scriptInterpreter.DebugVisualizeScript(scriptId);
scriptInterpreter.Run(scriptId, onScriptEnd, objectContext);
}
}
var isDialogPreview = false;
function startPreviewDialog(script, dialogCallback) {
isNarrating = true;
isDialogMode = true;
isDialogPreview = true;
dialogRenderer.Reset();
dialogRenderer.SetCentered(true);
dialogBuffer.Reset();
scriptInterpreter.SetDialogBuffer(dialogBuffer);
// TODO : do I really need a seperate callback for this debug mode??
onDialogPreviewEnd = dialogCallback;
var onScriptEndCallback = function(scriptResult) {
dialogBuffer.OnDialogEnd(function() {
onExitDialog(scriptResult, null);
});
};
scriptInterpreter.Eval(script, onScriptEndCallback);
}
/* NEW SCRIPT STUFF */
var scriptModule = new Script();
var scriptInterpreter = scriptModule.CreateInterpreter();
var scriptUtils = scriptModule.CreateUtils(); // TODO: move to editor.js?
// scriptInterpreter.SetDialogBuffer( dialogBuffer );
/* EVENTS */
bitsyOnUpdate(update);
bitsyOnQuit(stopGame);
bitsyOnLoad(load_game);
</script>
<!-- store default font in separate script tag for back compat-->
<script type="text/bitsyFontData" id="ascii_small">
FONT ascii_small
SIZE 6 8
CHAR 0
000000
000000
000000
000000
000000
000000
000000
000000
CHAR 1
001110
010001
011011
010001
010101
010001
001110
000000
CHAR 2
001110
011111
010101
011111
010001
011111
001110
000000
CHAR 3
000000
001010
011111
011111
011111
001110
000100
000000
CHAR 4
000000
000000
001010
001110
001110
000100
000000
000000
CHAR 5
000100
001110
001110
000100
011111
011111
000100
000000
CHAR 6
000000
000100
001110
011111
011111
000100
001110
000000
CHAR 7
000000
000000
000000
001100
001100
000000
000000
000000
CHAR 8
111111
111111
111111
110011
110011
111111
111111
111111
CHAR 9
000000
000000
011110
010010
010010
011110
000000
000000
CHAR 10
111111
111111
100001
101101
101101
100001
111111
111111
CHAR 11
000000
000111
000011
001101
010010
010010
001100
000000
CHAR 12
001110
010001
010001
001110
000100
001110
000100
000000
CHAR 13
000100
000110
000101
000100
001100
011100
011000
000000
CHAR 14
000011
001101
001011
001101
001011
011011
011000
000000
CHAR 15
000000
010101
001110
011011
001110
010101
000000
000000
CHAR 16
001000
001100
001110
001111
001110
001100
001000
000000
CHAR 17
000010
000110
001110
011110
001110
000110
000010
000000
CHAR 18
000100
001110
011111
000100
011111
001110
000100
000000
CHAR 19
001010
001010
001010
001010
001010
000000
001010
000000
CHAR 20
001111
010101
010101
001101
000101
000101
000101
000000
CHAR 21
001110
010001
001100
001010
000110
010001
001110
000000
CHAR 22
000000
000000
000000
000000
000000
011110
011110
000000
CHAR 23
000100
001110
011111
000100
011111
001110
000100
001110
CHAR 24
000100
001110
011111
000100
000100
000100
000100
000000
CHAR 25
000100
000100
000100
000100
011111
001110
000100
000000
CHAR 26
000000
000100
000110
011111
000110
000100
000000
000000
CHAR 27
000000
000100
001100
011111
001100
000100
000000
000000
CHAR 28
000000
000000
000000
010000
010000
010000
011111
000000
CHAR 29
000000
001010
001010
011111
001010
001010
000000
000000
CHAR 30
000100
000100
001110
001110
011111
011111
000000
000000
CHAR 31
011111
011111
001110
001110
000100
000100
000000
000000
CHAR 32
000000
000000
000000
000000
000000
000000
000000
000000
CHAR 33
000100
001110
001110
000100
000100
000000
000100
000000
CHAR 34
011011
011011
010010
000000
000000
000000
000000
000000
CHAR 35
000000
001010
011111
001010
001010
011111
001010
000000
CHAR 36
001000
001110
010000
001100
000010
011100
000100
000000
CHAR 37
011001
011001
000010
000100
001000
010011
010011
000000
CHAR 38
001000
010100
010100
001000
010101
010010
001101
000000
CHAR 39
001100
001100
001000
000000
000000
000000
000000
000000
CHAR 40
000100
001000
001000
001000
001000
001000
000100
000000
CHAR 41
001000
000100
000100
000100
000100
000100
001000
000000
CHAR 42
000000
001010
001110
011111
001110
001010
000000
000000
CHAR 43
000000
000100
000100
011111
000100
000100
000000
000000
CHAR 44
000000
000000
000000
000000
000000
001100
001100
001000
CHAR 45
000000
000000
000000
011111
000000
000000
000000
000000
CHAR 46
000000
000000
000000
000000
000000
001100
001100
000000
CHAR 47
000000
000001
000010
000100
001000
010000
000000
000000
CHAR 48
001110
010001
010011
010101
011001
010001
001110
000000
CHAR 49
000100
001100
000100
000100
000100
000100
001110
000000
CHAR 50
001110
010001
000001
000110
001000
010000
011111
000000
CHAR 51
001110
010001
000001
001110
000001
010001
001110
000000
CHAR 52
000010
000110
001010
010010
011111
000010
000010
000000
CHAR 53
011111
010000
010000
011110
000001
010001
001110
000000
CHAR 54
000110
001000
010000
011110
010001
010001
001110
000000
CHAR 55
011111
000001
000010
000100
001000
001000
001000
000000
CHAR 56
001110
010001
010001
001110
010001
010001
001110
000000
CHAR 57
001110
010001
010001
001111
000001
000010
001100
000000
CHAR 58
000000
000000
001100
001100
000000
001100
001100
000000
CHAR 59
000000
000000
001100
001100
000000
001100
001100
001000
CHAR 60
000010
000100
001000
010000
001000
000100
000010
000000
CHAR 61
000000
000000
011111
000000
000000
011111
000000
000000
CHAR 62
001000
000100
000010
000001
000010
000100
001000
000000
CHAR 63
001110
010001
000001
000110
000100
000000
000100
000000
CHAR 64
001110
010001
010111
010101
010111
010000
001110
000000
CHAR 65
001110
010001
010001
010001
011111
010001
010001
000000
CHAR 66
011110
010001
010001
011110
010001
010001
011110
000000
CHAR 67
001110
010001
010000
010000
010000
010001
001110
000000
CHAR 68
011110
010001
010001
010001
010001
010001
011110
000000
CHAR 69
011111
010000
010000
011110
010000
010000
011111
000000
CHAR 70
011111
010000
010000
011110
010000
010000
010000
000000
CHAR 71
001110
010001
010000
010111
010001
010001
001111
000000
CHAR 72
010001
010001
010001
011111
010001
010001
010001
000000
CHAR 73
001110
000100
000100
000100
000100
000100
001110
000000
CHAR 74
000001
000001
000001
000001
010001
010001
001110
000000
CHAR 75
010001
010010
010100
011000
010100
010010
010001
000000
CHAR 76
010000
010000
010000
010000
010000
010000
011111
000000
CHAR 77
010001
011011
010101
010001
010001
010001
010001
000000
CHAR 78
010001
011001
010101
010011
010001
010001
010001
000000
CHAR 79
001110
010001
010001
010001
010001
010001
001110
000000
CHAR 80
011110
010001
010001
011110
010000
010000
010000
000000
CHAR 81
001110
010001
010001
010001
010101
010010
001101
000000
CHAR 82
011110
010001
010001
011110
010010
010001
010001
000000
CHAR 83
001110
010001
010000
001110
000001
010001
001110
000000
CHAR 84
011111
000100
000100
000100
000100
000100
000100
000000
CHAR 85
010001
010001
010001
010001
010001
010001
001110
000000
CHAR 86
010001
010001
010001
010001
010001
001010
000100
000000
CHAR 87
010001
010001
010101
010101
010101
010101
001010
000000
CHAR 88
010001
010001
001010
000100
001010
010001
010001
000000
CHAR 89
010001
010001
010001
001010
000100
000100
000100
000000
CHAR 90
011110
000010
000100
001000
010000
010000
011110
000000
CHAR 91
001110
001000
001000
001000
001000
001000
001110
000000
CHAR 92
000000
010000
001000
000100
000010
000001
000000
000000
CHAR 93
001110
000010
000010
000010
000010
000010
001110
000000
CHAR 94
000100
001010
010001
000000
000000
000000
000000
000000
CHAR 95
000000
000000
000000
000000
000000
000000
000000
111111
CHAR 96
001100
001100
000100
000000
000000
000000
000000
000000
CHAR 97
000000
000000
001110
000001
001111
010001
001111
000000
CHAR 98
010000
010000
011110
010001
010001
010001
011110
000000
CHAR 99
000000
000000
001110
010001
010000
010001
001110
000000
CHAR 100
000001
000001
001111
010001
010001
010001
001111
000000
CHAR 101
000000
000000
001110
010001
011110
010000
001110
000000
CHAR 102
000110
001000
001000
011110
001000
001000
001000
000000
CHAR 103
000000
000000
001111
010001
010001
001111
000001
001110
CHAR 104
010000
010000
011100
010010
010010
010010
010010
000000
CHAR 105
000100
000000
000100
000100
000100
000100
000110
000000
CHAR 106
000010
000000
000110
000010
000010
000010
010010
001100
CHAR 107
010000
010000
010010
010100
011000
010100
010010
000000
CHAR 108
000100
000100
000100
000100
000100
000100
000110
000000
CHAR 109
000000
000000
011010
010101
010101
010001
010001
000000
CHAR 110
000000
000000
011100
010010
010010
010010
010010
000000
CHAR 111
000000
000000
001110
010001
010001
010001
001110
000000
CHAR 112
000000
000000
011110
010001
010001
010001
011110
010000
CHAR 113
000000
000000
001111
010001
010001
010001
001111
000001
CHAR 114
000000
000000
010110
001001
001000
001000
011100
000000
CHAR 115
000000
000000
001110
010000
001110
000001
001110
000000
CHAR 116
000000
001000
011110
001000
001000
001010
000100
000000
CHAR 117
000000
000000
010010
010010
010010
010110
001010
000000
CHAR 118
000000
000000
010001
010001
010001
001010
000100
000000
CHAR 119
000000
000000
010001
010001
010101
011111
001010
000000
CHAR 120
000000
000000
010010
010010
001100
010010
010010
000000
CHAR 121
000000
000000
010010
010010
010010
001110
000100
011000
CHAR 122
000000
000000
011110
000010
001100
010000
011110
000000
CHAR 123
000110
001000
001000
011000
001000
001000
000110
000000
CHAR 124
000100
000100
000100
000100
000100
000100
000100
000100
CHAR 125
001100
000010
000010
000011
000010
000010
001100
000000
CHAR 126
001010
010100
000000
000000
000000
000000
000000
000000
CHAR 127
000100
001110
011011
010001
010001
011111
000000
000000
CHAR 128
001110
010001
010000
010000
010001
001110
000100
001100
CHAR 129
010010
000000
010010
010010
010010
010110
001010
000000
CHAR 130
000011
000000
001110
010001
011110
010000
001110
000000
CHAR 131
001110
000000
001110
000001
001111
010001
001111
000000
CHAR 132
001010
000000
001110
000001
001111
010001
001111
000000
CHAR 133
001100
000000
001110
000001
001111
010001
001111
000000
CHAR 134
001110
001010
001110
000001
001111
010001
001111
000000
CHAR 135
000000
001110
010001
010000
010001
001110
000100
001100
CHAR 136
001110
000000
001110
010001
011110
010000
001110
000000
CHAR 137
001010
000000
001110
010001
011110
010000
001110
000000
CHAR 138
001100
000000
001110
010001
011110
010000
001110
000000
CHAR 139
001010
000000
000100
000100
000100
000100
000110
000000
CHAR 140
000100
001010
000000
000100
000100
000100
000110
000000
CHAR 141
001000
000000
000100
000100
000100
000100
000110
000000
CHAR 142
001010
000000
000100
001010
010001
011111
010001
000000
CHAR 143
001110
001010
001110
011011
010001
011111
010001
000000
CHAR 144
000011
000000
011111
010000
011110
010000
011111
000000
CHAR 145
000000
000000
011110
000101
011111
010100
001111
000000
CHAR 146
001111
010100
010100
011111
010100
010100
010111
000000
CHAR 147
001110
000000
001100
010010
010010
010010
001100
000000
CHAR 148
001010
000000
001100
010010
010010
010010
001100
000000
CHAR 149
011000
000000
001100
010010
010010
010010
001100
000000
CHAR 150
001110
000000
010010
010010
010010
010110
001010
000000
CHAR 151
011000
000000
010010
010010
010010
010110
001010
000000
CHAR 152
001010
000000
010010
010010
010010
001110
000100
011000
CHAR 153
010010
001100
010010
010010
010010
010010
001100
000000
CHAR 154
001010
000000
010010
010010
010010
010010
001100
000000
CHAR 155
000000
000100
001110
010000
010000
001110
000100
000000
CHAR 156
000110
001001
001000
011110
001000
001001
010111
000000
CHAR 157
010001
001010
000100
011111
000100
011111
000100
000000
CHAR 158
011000
010100
010100
011010
010111
010010
010010
000000
CHAR 159
000010
000101
000100
001110
000100
000100
010100
001000
CHAR 160
000110
000000
001110
000001
001111
010001
001111
000000
CHAR 161
000110
000000
000100
000100
000100
000100
000110
000000
CHAR 162
000110
000000
001100
010010
010010
010010
001100
000000
CHAR 163
000110
000000
010010
010010
010010
010110
001010
000000
CHAR 164
001010
010100
000000
011100
010010
010010
010010
000000
CHAR 165
001010
010100
000000
010010
011010
010110
010010
000000
CHAR 166
001110
000001
001111
010001
001111
000000
001111
000000
CHAR 167
001100
010010
010010
010010
001100
000000
011110
000000
CHAR 168
000100
000000
000100
001100
010000
010001
001110
000000
CHAR 169
000000
000000
011111
010000
010000
010000
000000
000000
CHAR 170
000000
000000
111111
000001
000001
000000
000000
000000
CHAR 171
010000
010010
010100
001110
010001
000010
000111
000000
CHAR 172
010000
010010
010100
001011
010101
000111
000001
000000
CHAR 173
000100
000000
000100
000100
001110
001110
000100
000000
CHAR 174
000000
000000
001001
010010
001001
000000
000000
000000
CHAR 175
000000
000000
010010
001001
010010
000000
000000
000000
CHAR 176
010101
000000
101010
000000
010101
000000
101010
000000
CHAR 177
010101
101010
010101
101010
010101
101010
010101
101010
CHAR 178
101010
111111
010101
111111
101010
111111
010101
111111
CHAR 179
000100
000100
000100
000100
000100
000100
000100
000100
CHAR 180
000100
000100
000100
111100
000100
000100
000100
000100
CHAR 181
000000
000000
010010
010010
010010
011100
010000
010000
CHAR 182
010100
010100
010100
110100
010100
010100
010100
010100
CHAR 183
000000
000000
000000
111100
010100
010100
010100
010100
CHAR 184
000000
111100
000100
111100
000100
000100
000100
000100
CHAR 185
010100
110100
000100
110100
010100
010100
010100
010100
CHAR 186
010100
010100
010100
010100
010100
010100
010100
010100
CHAR 187
000000
111100
000100
110100
010100
010100
010100
010100
CHAR 188
010100
110100
000100
111100
000000
000000
000000
000000
CHAR 189
010100
010100
010100
111100
000000
000000
000000
000000
CHAR 190
000100
111100
000100
111100
000000
000000
000000
000000
CHAR 191
000000
000000
000000
111100
000100
000100
000100
000100
CHAR 192
000100
000100
000100
000111
000000
000000
000000
000000
CHAR 193
000100
000100
000100
111111
000000
000000
000000
000000
CHAR 194
000000
000000
000000
111111
000100
000100
000100
000100
CHAR 195
000100
000100
000100
000111
000100
000100
000100
000100
CHAR 196
000000
000000
000000
111111
000000
000000
000000
000000
CHAR 197
000100
000100
000100
111111
000100
000100
000100
000100
CHAR 198
000100
000111
000100
000111
000100
000100
000100
000100
CHAR 199
010100
010100
010100
010111
010100
010100
010100
010100
CHAR 200
010100
010111
010000
011111
000000
000000
000000
000000
CHAR 201
000000
011111
010000
010111
010100
010100
010100
010100
CHAR 202
010100
110111
000000
111111
000000
000000
000000
000000
CHAR 203
000000
111111
000000
110111
010100
010100
010100
010100
CHAR 204
010100
010111
010000
010111
010100
010100
010100
010100
CHAR 205
000000
111111
000000
111111
000000
000000
000000
000000
CHAR 206
010100
110111
000000
110111
010100
010100
010100
010100
CHAR 207
000100
111111
000000
111111
000000
000000
000000
000000
CHAR 208
010100
010100
010100
111111
000000
000000
000000
000000
CHAR 209
000000
111111
000000
111111
000100
000100
000100
000100
CHAR 210
000000
000000
000000
111111
010100
010100
010100
010100
CHAR 211
010100
010100
010100
011111
000000
000000
000000
000000
CHAR 212
000000
000000
000000
000000
000000
000000
000000
111111
CHAR 213
000000
000000
000000
000000
000000
000000
111111
111111
CHAR 214
000000
000000
000000
000000
000000
111111
111111
111111
CHAR 215
000000
000000
000000
000000
111111
111111
111111
111111
CHAR 216
000000
000000
000000
111111
111111
111111
111111
111111
CHAR 217
000000
000000
111111
111111
111111
111111
111111
111111
CHAR 218
000000
111111
111111
111111
111111
111111
111111
111111
CHAR 219
111111
111111
111111
111111
111111
111111
111111
111111
CHAR 220
100000
100000
100000
100000
100000
100000
100000
100000
CHAR 221
110000
110000
110000
110000
110000
110000
110000
110000
CHAR 222
111000
111000
111000
111000
111000
111000
111000
111000
CHAR 223
111100
111100
111100
111100
111100
111100
111100
111100
CHAR 224
111110
111110
111110
111110
111110
111110
111110
111110
CHAR 225
000000
011100
010010
011100
010010
010010
011100
010000
CHAR 226
011110
010010
010000
010000
010000
010000
010000
000000
CHAR 227
000000
011111
001010
001010
001010
001010
001010
000000
CHAR 228
001010
000000
001110
000001
001111
010001
001111
000000
CHAR 229
000000
000000
001111
010010
010010
001100
000000
000000
CHAR 230
000000
000000
010010
010010
010010
011100
010000
010000
CHAR 231
000000
000000
001010
010100
000100
000100
000100
000000
CHAR 232
001110
000100
001110
010001
001110
000100
001110
000000
CHAR 233
001100
010010
010010
011110
010010
010010
001100
000000
CHAR 234
000000
001110
010001
010001
001010
001010
011011
000000
CHAR 235
001100
010000
001000
000100
001110
010010
001100
000000
CHAR 236
000000
000000
001010
010101
010101
001010
000000
000000
CHAR 237
000000
000100
001110
010101
010101
001110
000100
000000
CHAR 238
000000
001110
010000
011110
010000
001110
000000
000000
CHAR 239
000000
001100
010010
010010
010010
010010
000000
000000
CHAR 240
000000
011110
000000
011110
000000
011110
000000
000000
CHAR 241
000000
000100
001110
000100
000000
001110
000000
000000
CHAR 242
010000
001100
000010
001100
010000
000000
011110
000000
CHAR 243
000000
000000
111111
111000
100110
100001
100000
111111
CHAR 244
000000
000000
111111
000111
011001
100001
000001
111111
CHAR 245
000100
000100
000100
000100
000100
010100
001000
000000
CHAR 246
001010
000000
001110
010001
010001
010001
001110
000000
CHAR 247
111110
111110
111110
111110
111110
111110
111110
111110
CHAR 248
111100
111100
111100
111100
111100
111100
111100
111100
CHAR 249
111000
111000
111000
111000
111000
111000
111000
111000
CHAR 250
110000
110000
110000
110000
110000
110000
110000
110000
CHAR 251
100000
100000
100000
100000
100000
100000
100000
100000
CHAR 252
001010
000000
010010
010010
010010
010110
001010
000000
CHAR 253
011000
000100
001000
011100
000000
000000
000000
000000
CHAR 254
000000
000000
000000
011110
110010
110011
111110
001111
CHAR 255
010010
111111
010010
010010
111111
010010
000000
000000
</script>
</script>
</script>
</script>
</script>
</script>
</script>
</script>
</head>
<!-- DOCUMENT BODY -->
<body onload='startExportedGame()'>
<!-- GAME CANVAS -->
<canvas id='game'></canvas>
</body>
</html>