diff --git a/CHANGELOG.md b/CHANGELOG.md
index 57b07b08..24d77ff2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 8.11
+
+- game tool: combines the features of the download, settings, and game data tools into one tool
+
## 8.10
- fix: fix bug rendering exits and endings
diff --git a/dev/icon_generator/icons.bitsy b/dev/icon_generator/icons.bitsy
index ed92ac77..4823237a 100644
--- a/dev/icon_generator/icons.bitsy
+++ b/dev/icon_generator/icons.bitsy
@@ -1,9 +1,9 @@
icons
-# BITSY VERSION 7.12
-# DEVELOPMENT BUILD -- BITSY-WORKSPACE--SOUND
-! VER_MAJ 7
-! VER_MIN 12
+# BITSY VERSION 8.9
+
+! VER_MAJ 8
+! VER_MIN 9
! ROOM_FORMAT 1
! DLG_COMPAT 1
! TXT_MODE 0
@@ -21,7 +21,7 @@ q,r,s,t,u,v,w,x,y,z,10,11,12,13,14,15
16,17,18,19,1a,1b,1c,1d,1e,1f,1g,1h,1i,1j,1k,1l
1m,1n,1o,1p,1q,1r,1s,1t,1u,1v,1w,1x,1y,1z,20,21
23,29,2a,2b,26,27,28,2c,2d,2e,2f,2g,2h,2j,2k,2l
-24,25,2m,2n,2o,2p,2v,2x,2y,2z,30,31,32,0,0,0
+24,25,2m,2n,2o,2p,2v,2x,2y,2z,30,31,32,33,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
@@ -288,6 +288,17 @@ TIL 32
11000000
NAME eyedropper
+TIL 33
+11111100
+10101010
+10111001
+10000001
+10111101
+10111101
+10111101
+11111111
+NAME save
+
TIL a
00000011
00000101
@@ -1225,18 +1236,3 @@ NAME key dialog
VAR a
42
-TUNE 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
-NAME test song
-KEY C,C,C,C,C,C,C,C
-TMP M
-SQR P8 P8
-
-BLIP 0
-C3,C3,C3
-ENV 6 7 9 6 3
-BEAT 0 0
-SQR P8
-SLD 1
-RPT 1
\ No newline at end of file
diff --git a/dev/resources/icons/icon_save.svg b/dev/resources/icons/icon_save.svg
new file mode 100644
index 00000000..36fecb6e
--- /dev/null
+++ b/dev/resources/icons/icon_save.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/dev/resources/localization.tsv b/dev/resources/localization.tsv
index ffb2e3c3..afbf2278 100644
--- a/dev/resources/localization.tsv
+++ b/dev/resources/localization.tsv
@@ -43,7 +43,7 @@ condition_type_default default par défault padrão por defecto defailt по у
cycle_list_description repeat items in a _: frasi in _ che si ripetono: повторить объекты в _: Alle Blöcke werden in _ eingeblendet. تكرار البنود في _: endurtaktu hluti í _: ismételd a tárgyakat ebben: _ powtarzaj pozycje zapętlone w _: повторювати пункти з _: repetir itens em _:
cycle_list_name cycle list nom de la boucle lista serie список циклов Schleifen-Liste قائمة دائرية lotu listi ismétlés lista lista cyklu список-цикл lista de ciclo
cycle_name cycle boucle serie цикл Schleife دائري lota ismétlés cykl цикл ciclo
-data_tool_name game data données du jeu dados do jogo datos del juego dati del gioco игровые данные speldata Spieldaten بيانات اللعبة leikjagögn játékadatok ludodosiero mängu andmed 游戏数据 dane ゲームデータ hihi kod kód hry дані гри código de jogo
+data_tool_name data données du jeu dados do jogo datos del juego dati del gioco игровые данные speldata Spieldaten بيانات اللعبة leikjagögn játékadatok ludodosiero mängu andmed 游戏数据 dane ゲームデータ hihi kod kód hry дані гри código de jogo
default_end_dlg The end Fin Fim Fin Fine конец slut Ende النهاية Endir Itt a vége, fuss el véle! Ludofino 结束 Koniec alta kopit hihi Кінець Fim
default_item_dlg You found a nice warm cup of tea Vous avez trouvé une bonne tasse de thé chaud Encontraste um copo com chá quentinho Encontraste una buena taza de té Hai trovato una buona tazza di tè caldo вы нашли чашку тёплого чая du hittade en varm kopp te Du hast einen schönen warmen Tee gefunden لقد وجدت كوب دافئ من الشاي Þú fannst heitann bolla af te Találtál egy bögre forró teát Vi trovis belan, varman tason da teo 你找到了一杯暖心的茶 Znajdujesz kubek ciepłej herbaty naika tlap 1 uskan wam tipso-chuk! Ви знайшли чашку запашного гарячого чаю Você encontrou um chá quentinho
default_sprite_dlg I'm a cat Je suis un chat Sou um gato Sono un gatto я котейка jag är en katt Ich bin eine Katze أنا قطة Ég er köttur Én egy cica vagyok Mi estas kato 我是一只猫 Jestem kotem puspus naika Já jsem kočka. Я киця Meow. Que foi?
@@ -80,12 +80,12 @@ dialog_no_selection Select a sprite or item to edit its dialog. sélectionner un
dialog_show_code show code afficher le code mostrar códiogo mostrar código mostra codice показать код visa kod Code zeigen أظهر شفرة البرنامج sýna kóða forráskód mutatása vidu kodon näita koodi 显示代码 pokaż kod コード表示 nanich kod zobrazit kód показати код mostrar código
dialog_start_preview preview aperçu pré-vizualizar previsualizar anteprima предпросмотр förhandsvisning Vorschau معاينة sýnishorn előnézet antaŭvidu eelvaade 预览 podgląd プレビュー tenas nanich náhled rozhovoru перегляд prévia
dialog_tool_name dialog dialogue diálogo diálogo dialogo диалог dialog Dialoge حوار samtöl párbeszéd skribejo dialoog 对话 dialogi ダイアログ wawa rozhovor діалог diálogo
-download_data download data télécharger les données descarregar data descargar data scarica dati скачать данные ladda ned data Daten herunterladen تحميل المعلومات hala niður gögnum adatok letöltése prenu dosieron lae alla andmed 下载数据 pobierz dane データをダウンロード iskum hihi kod stáhnout kód завантажити дані baixar código
-download_font download font télécharger la police descarregar fonte descargar fuente scarica font скачать шрифт ladda ned typsnitt Schriftart herunterladen تحميل الخط hala niður leturgerð betűtípus letöltése prenu tiparon lae alla font 下载字体 pobierz czcionkę フォントをダウンロード iskum .bitsyfont stáhnout font завантажити шрифт baixar fonte
-download_game download game télécharger le jeu descarregar jogo descargar juego como html scarica gioco скачать игру ladda ned Spiel herunterladen تحميل اللعبة hala niður leik játék letöltése prenu ludon lae alla mäng 下载游戏 pobierz grę ゲームをダウンロード iskum hihi stáhnout hru завантажити гру baixar o jogo
+download_data save data télécharger les données descarregar data descargar data scarica dati скачать данные ladda ned data Daten herunterladen تحميل المعلومات hala niður gögnum adatok letöltése prenu dosieron lae alla andmed 下载数据 pobierz dane データをダウンロード iskum hihi kod stáhnout kód завантажити дані baixar código
+download_font save font télécharger la police descarregar fonte descargar fuente scarica font скачать шрифт ladda ned typsnitt Schriftart herunterladen تحميل الخط hala niður leturgerð betűtípus letöltése prenu tiparon lae alla font 下载字体 pobierz czcionkę フォントをダウンロード iskum .bitsyfont stáhnout font завантажити шрифт baixar fonte
+download_game save game télécharger le jeu descarregar jogo descargar juego como html scarica gioco скачать игру ladda ned Spiel herunterladen تحميل اللعبة hala niður leik játék letöltése prenu ludon lae alla mäng 下载游戏 pobierz grę ゲームをダウンロード iskum hihi stáhnout hru завантажити гру baixar o jogo
download_html download game as html file: télécharger en fichier html descarregar jogo como ficheiro html: descargar juego como html scarica il gioco come html скачать игру как html файл: ladda ned spelet som html-fil: Lade das Spiel als HTML Datei herunter: تحميل اللعبة كملف html hala niður leik sem html skjal játék letöltése html fájlként: prenu la ludon kiel HTML-dosieron: lae alla mäng html failina: 下载游戏为 html 文件: pobierz grę jako plik html: ゲームをhtmlファイルとしてダウンロード iskum hihi kakwa .html: stáhnout hru v .html завантажити гру як файл HTML: baixar o jogo como arquivo html:
-download_tool_name download télécharger descarregar descargar scarica скачать ladda ned Herunterladen تحميل niðurhal letöltés prenu (elŝutu) lae alla 下载 pobierz ダウンロード munk-iskum hihi stáhnout завантажити baixar
-editor_settings editor settings paramètres de l'éditeur definições do editor ajustes del editor impostazioni dell'editor настройки редактора editor-inställningar Editor-Einstellungen إعدادات المحرر stillingar á tóli szerkesztő beállításai kreila elektaro bitsy seaded 编辑器设置 ustawienia edytora エディター設定 edita setins nastavení editoru налаштування редактору definições de editor
+download_tool_name save télécharger descarregar descargar scarica скачать ladda ned Herunterladen تحميل niðurhal letöltés prenu (elŝutu) lae alla 下载 pobierz ダウンロード munk-iskum hihi stáhnout завантажити baixar
+editor_settings tool settings paramètres de l'éditeur definições do editor ajustes del editor impostazioni dell'editor настройки редактора editor-inställningar Editor-Einstellungen إعدادات المحرر stillingar á tóli szerkesztő beállításai kreila elektaro bitsy seaded 编辑器设置 ustawienia edytora エディター設定 edita setins nastavení editoru налаштування редактору definições de editor
ending_dialog ending dialog fin de dialogue diálogo de final dialógo de final dialogo del finale текст концовки slut-dialog End-Nachricht حوار النهاية enda samtal befejező párbeszéd finluda parolo 结局对话 dialog zakończenia kopit-hihi taim wawa діалог закінчення diálogo de final
ending_label ending fin final final finale концовка slut Ende نهاية endir befejezés ludofino lõpp 结局 zakończenie エンディング kopit-skwil konec hry закінчення final
ending_place place ending ajouter une fin colocar final colocar final piazza finale расположить концовку placera slut Platziere Ende مكان النهاية staðsetja enda befejezés elhelyezése metu finon paiguta lõpp 安排结局 umieść zakończenie エンディング配置 munk-mitlait kopit-skwil umístit konec hry розмістити закінчення colocar final
@@ -240,8 +240,8 @@ transition_tunnel tunnel tunnel tunnel тоннель Tunnel نفق göng ala
transition_wave wave vague onda волна Welle موجي alda hullám falowanie волна ondular
unsupported_features uh oh ~ your browser doesn't support all of bitsy's features, so some things might not work right. maybe try another browser, such as: oh oh ~ ton navigateur internet ne supporte pas toutes les fonctionnalités de Bitsy, certaines choses pourraient ne pas fonctionner correctement, essaie un autre navigateur, comme : uh oh - o teu explorador não suporta todas as funcionalidades do Bitsy, por isso algumas coisas poderão não funcionar correctamente. talvez queiras tentar outro explorador como: oh oh! tu navegador no es compatible con todas las funcionalidades de Bitsy y es posible que algunas no funcionen correctamente. Puedes intentar utilizar otros como: oh no! il tuo browser non è compatibile con tutte le funzionalità di Bitsy, ed è possibile che alcune non funzionino bene. Puoi usarne altri come: упс ~ ваш браузер не поддерживает все функции битси, поэтому что-то работать не будет. попробуйте другой браузер, например: oj då ~ bitsy har vissa funktioner som inte stöds av din webbläsare, så allt kanske inte fungerar som det ska. du kan prova med en annan webbläsare, till exempel: Oh oh - Dein Browser unterstützt nicht alle Funktionen von Bitsy. Manche Dinge könnten nicht funktionieren, probiere doch einen der anderen Browser aus: متصفحك لا يدعم جميع ميزات بيتسي، و لهذا، بعض الوظائف قد لا تعمل. من الأفضل أن تجرب أحد المتصفحات التالية: æji nei ~ vafrinn sem þú notar styður ekki alla eiginleika bitsy, þannig að nokkrir hlutir munu ef til vill ekki virka. Ef þú getur prófaðu að nota aðra vafra eins og: ajjajj ~ a böngésződ nem támogatja az összes bitsy funkciót, próbálj ki egy másik böngészőt, mondjuk ezeket: ho ve ~ via retvidilo ne povas ĉiujn aferojn de Bitsy, do eble iuj aferoj ne funkcios ĝuste. vi povas provi alian retvidilon, ekzemple: ups ~ sinu brauser ei toeta kõiki bitsy funktsioone, nii et mõned asjad ei pruugi õigesti töötada. Võibolla proovi teist brauserit, nagu näiteks: 啊噢~你的浏览器不完全支持 Bitsy ,有些功能无法正确运行。试试其他的浏览器吧,比如: o nie ~ twoja przeglądarka nie wspiera wszystkich funkcjonalności bitsy, więc niektóre rzeczy mogą nie działać prawidłowo. możesz wypróbować inną przeglądarkę, taką jak: あらら、残念ながら使用中のブラウザでは一部のBitsyの機能が使用できないようです。次のブラウザを使ってみるといいかもしれません: aaana ~ wik maika nanich-inanet ikta klosh kopa nanich kanawei bitsy iktas. klosh maika tlai huloima nanich-inanet ikta, kakwa: йой ~ ваш браузер не підтримує всі функції Бітсі, щось може не працювати. Пропоную спробувати інший браузер, такий як: vish ~ o seu navegador não suporta todas as funções do bitsy, então algumas coisas não funcionam. tente um desses navegadores:
unsupported_features_dismiss I'll be ok, hide this message j'ai compris, cacher ce message eu compreendo, esconde esta mensagem Entiendo. Oculta este mensaje. Capito. Nascondi questo messaggio. со мной всё будет в порядке, спрятать это сообщение det går bra, göm det här meddelandet Ist in Ordnung. Verberge diese Nachricht سأكون بخير، أخفي هذه الرسالة ekkert mál, feldu þessi skilaboð köszi, de megoldom, üzenet elrejtése mi ne zorgas. malvidu ĉi informon kõik on korras, peida see sõnum 没关系,隐藏这条消息 Wszystko w porządku, ukryj tę wiadomość なんとかなるので、このメッセージを隠す klosh naika. ipui okok sisim. най буде, приховати повідомлення Tudo certo, pode esconder a mensagem
-upload_font upload font charger une police d'écriture carregar uma fonte para utilização subir fuente Carica font загрузить шрифт ladda upp typsnitt Schrift hochladen أرفع الخط hala upp letri betűtípus feltöltése legu tiparon 上传字体 wyślij czcionkę mash pipa-tsum nahrát font відвантажити шрифт carregar fonte
-upload_game upload game charger un jeu carregar um jogo para edição subir juego Carica gioco загрузить игру ladda upp spel Spiel hochladen أرفع اللعبة hala upp leik játék feltöltése legu (alŝutu) ludon 上传游戏 wyślij grę mash hihi nahrát hru відвантажити гру carregar jogo
+upload_font load font charger une police d'écriture carregar uma fonte para utilização subir fuente Carica font загрузить шрифт ladda upp typsnitt Schrift hochladen أرفع الخط hala upp letri betűtípus feltöltése legu tiparon 上传字体 wyślij czcionkę mash pipa-tsum nahrát font відвантажити шрифт carregar fonte
+upload_game load game charger un jeu carregar um jogo para edição subir juego Carica gioco загрузить игру ladda upp spel Spiel hochladen أرفع اللعبة hala upp leik játék feltöltése legu (alŝutu) ludon 上传游戏 wyślij grę mash hihi nahrát hru відвантажити гру carregar jogo
value_type_text text texte testo текст Text نص texti szöveg tekst текст texto
variable_add add variable ajouter une variable adicionar variável añadir variable aggiungi variabile добавить переменную lägg till variabel Variable hinzufügen إضافة متغير ný breyta változó hozzáadása aldonu ŝangeblan numeron lisa muutuja 加入变量 dodaj zmienną 変数の追加 mash huyhuy-nampa přidat proměnnou додати змінну colocar variável
variable_label variable variable variável variables variabili переменная variabel Variablen متغير breyta változó ŝangebla numero muutuja 变量 zmienna 変数 huyhuy-nampa proměnná змінна variável
diff --git a/docs/docs/tools/download/.images/downloadDiagram.JPG b/docs/docs/tools/download/.images/downloadDiagram.JPG
deleted file mode 100644
index c4791009..00000000
Binary files a/docs/docs/tools/download/.images/downloadDiagram.JPG and /dev/null differ
diff --git a/docs/docs/tools/download/index.md b/docs/docs/tools/download/index.md
deleted file mode 100644
index 5d6b919f..00000000
--- a/docs/docs/tools/download/index.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Download
-
-## Description
-
-The download tool can be used to export your Bitsy game as an .html file. While your internet browser should remember the settings in your current game (unless you delete your internet cache), this can be useful for backing up your work.
-
-An .html file can be opened in any internet browser to play the game. You could also send the .html to other people for them to play your game.
-
-Websites such as [itch.io](https://itch.io) also allow you to create an account and upload your game for free if you want a permanent place to host your game. [Click here](/faq/uploadToItch) for a tutorial on uploading games to Itch.io.
-
-This tool also allows you to import an existing .html format Bitsy game so that you can edit it. You could import a game of your own that you have previously exported, or you can import anyone else's game that you have the .html format of. If you do want to re-use someone else's game or the assets they have created you should always seek their permission first.
-
-An alternative way to import and export Bitsy games is through the [game data tool](../gamedata).
-
-## Features
-
-![download tool diagram](.images/downloadDiagram.JPG)
-
-1. **Download game.** Downloads your Bitsy game as an .html file, which can be opened in any internet browser to play, or uploaded to a game hosting platform such as itch.io to share with other people.
-
-2. **Upload game.** Upload a Bitsy game from .html format into the Bitsy editor to edit it.
diff --git a/docs/docs/tools/game/.images/gameDataDiagram.png b/docs/docs/tools/game/.images/gameDataDiagram.png
new file mode 100644
index 00000000..36987938
Binary files /dev/null and b/docs/docs/tools/game/.images/gameDataDiagram.png differ
diff --git a/docs/docs/tools/game/.images/gameSaveDiagram.png b/docs/docs/tools/game/.images/gameSaveDiagram.png
new file mode 100644
index 00000000..f05a7f2b
Binary files /dev/null and b/docs/docs/tools/game/.images/gameSaveDiagram.png differ
diff --git a/docs/docs/tools/game/.images/gameSettingsDiagram.png b/docs/docs/tools/game/.images/gameSettingsDiagram.png
new file mode 100644
index 00000000..9af48efc
Binary files /dev/null and b/docs/docs/tools/game/.images/gameSettingsDiagram.png differ
diff --git a/docs/docs/tools/game/data.md b/docs/docs/tools/game/data.md
new file mode 100644
index 00000000..c7348a41
--- /dev/null
+++ b/docs/docs/tools/game/data.md
@@ -0,0 +1,20 @@
+# Data
+
+:::caution
+Editing your game data directly may result in the game becoming unplayable or having unwanted effects.
+:::
+
+## Description
+
+The data menu allows you to view your Bitsy game's raw data in text format. All images, dialogs, rooms etc. that you have created are represented here and can be edited.
+
+However, it is a good idea to create a backup of your game before doing this in case any changes you make have unwanted effects, such as the game becoming unplayable.
+
+Create a backup using the [save menu](save).
+
+## Features
+
+![game data menu screenshot](.images/gameDataDiagram.png)
+
+- **Raw text of the Bitsy game data file.** This text box displays all the data in the Bitsy game in text format. Be cautious if you edit this, as changing this data can potentially make your game unplayable.
+ - **Note:** By default, font data is hidden due to its large size. If you need to view this data, you can make it visible in the [settings menu](settings).
\ No newline at end of file
diff --git a/docs/docs/tools/game/index.md b/docs/docs/tools/game/index.md
new file mode 100644
index 00000000..e7fcea15
--- /dev/null
+++ b/docs/docs/tools/game/index.md
@@ -0,0 +1,15 @@
+# Game
+
+## Description
+
+The game tool is where you manage your current project file, settings, and game data.
+
+## Features
+
+![save menu screenshot](.images/gameSaveDiagram.png)
+
+The game tool has three sub-menus:
+
+- **Save.** The [save menu](save) is where you export and import your game, or start a new project.
+- **Settings.** The [settings menu](settings) contain configuration options for the editor tools, the game engine, and exported .html files. Language and font settings are located here.
+- **Data.** The [data menu](data) lets you view or edit the raw data for your game in a plaintext format.
\ No newline at end of file
diff --git a/docs/docs/tools/game/save.md b/docs/docs/tools/game/save.md
new file mode 100644
index 00000000..b4f4abe8
--- /dev/null
+++ b/docs/docs/tools/game/save.md
@@ -0,0 +1,25 @@
+# Save
+
+## Description
+
+The save menu can be used to export your Bitsy game as an .html file. While your internet browser should remember the settings in your current game (unless you delete your internet cache), this can be useful for backing up your work.
+
+An .html file can be opened in any internet browser to play the game. You could also send the .html to other people for them to play your game.
+
+Websites such as [itch.io](https://itch.io) also allow you to create an account and upload your game for free if you want a permanent place to host your game. [Click here](/faq/uploadToItch) for a tutorial on uploading games to Itch.io.
+
+This menu also allows you to import an existing .html format Bitsy game so that you can edit it. You could import a game of your own that you have previously exported, or you can import anyone else's game that you have the .html format of. If you do want to re-use someone else's game or the assets they have created you should always seek their permission first.
+
+In this menu you can also import and export Bitsy games as a .bitsy file, or start a new project.
+
+## Features
+
+![save menu screenshot](.images/gameSaveDiagram.png)
+
+- **Game file tools.** Save / load .html game files.
+ - **Save game.** Save your Bitsy game as an .html file, which can be opened in any internet browser to play, or uploaded to a game hosting platform such as itch.io to share with other people.
+ - **Load game.** Import a Bitsy game from an .html file into the Bitsy editor to edit it.
+- **Data file tools.** Save / load .bitsy data files.
+ - **Save data.** This button will save the game data for your current Bitsy game in a .bitsy format. This is a text-based data format that can be opened in a program such as Notepad.
+ - **Load data.** Opens a file browser window to allow you to import pre-existing game data in .bitsy format. Be aware that this will override the current game data in your game, so you may want to create a backup of your work before you do this using 'save data' or 'save game'.
+- **New game.** Start a new project containing only the default Bitsy room. (*This will delete your current game!* Save a backup if you wish to keep a copy of your work.)
\ No newline at end of file
diff --git a/docs/docs/tools/game/settings.md b/docs/docs/tools/game/settings.md
new file mode 100644
index 00000000..78d584fd
--- /dev/null
+++ b/docs/docs/tools/game/settings.md
@@ -0,0 +1,45 @@
+# Settings
+
+## Description
+
+The settings tool contains several useful customisations to do with font, language, and export settings.
+
+## Features
+
+![settings menu screenshot](.images/gameSettingsDiagram.png)
+
+- **Tool settings.** Options for configuring the Bitsy editor tools.
+
+ - **Language.** Choose from the dropdown menu to set the editor default language. Translations are created manually by the community. If you notice something that is missing in your language please feel free to [contribute](/contributing#translating-editor-text).
+
+ - **Font data.** In the [data menu](data) the font used for your text in Bitsy is hidden by default due to its large size (especially for languages with many characters). Leave this as hidden unless you need to view the font data specifically.
+
+- **Game settings.** Options for configuring the Bitsy game engine.
+
+ - **Game font.** Choose a font from the dropdown menu that your in-game dialog will be displayed in. Generally this choice will depend on the language you are writing in.
+
+ - ASCII is a set of 128 characters from the English language, but does not include characters from European languages, such as é, á etc.
+
+ - Unicode includes several thousand more characters than ASCII, and is recommended if you are writing in a European language. (There are small and large versions of this font.)
+
+ - Unicode Asian includes additional characters for Asian languages such as Chinese, Japanese, and Korean.
+
+ - Arabic is a pixel font with Arabic characters.
+
+ - Custom Font allows you to use your own custom font. This must be a file in the .bitsyfont format, which can be imported with the 'load font' button.
+
+ - **Font file tools.** Save / load .bitsyfont files.
+
+ - **Save font.** Clicking this saves a .bitsyfont file of the font selected in the dropdown menu above. This plaintext file can be edited in external tools.
+
+ - **Load font.** Import a .bitsyfont file you've made or found on the web to use via the 'Custom Font' option in the font dropdown (see above).
+
+ - **Text direction.** This changes whether text appears on screen from left to right, or from right to left in the game.
+
+ - **Text mode.** Choose from 'default' or 'chunky' to set the scale of the text.
+
+- **Export settings.** These settings are related to games exported as HTML files.
+
+ - **HTML page color.** Opens a color picker that changes the background color outside of the game. This is visible when the game is downloaded and opened in an internet browser, or as the background color if you [upload your game to Itch.io](/faq/uploadToItch).
+
+ - **Game window size.** When set to 'full page', a bitsy game that is downloaded and opened in an internet browser will be scaled at full-screen. 'Fixed size' allows you to set a custom size in pixels that the game will display at. Since Bitsy games have a square aspect ratio, the width and height will always be equal.
\ No newline at end of file
diff --git a/docs/docs/tools/gamedata/.images/gameDataDiagram.JPG b/docs/docs/tools/gamedata/.images/gameDataDiagram.JPG
deleted file mode 100644
index 63ba9cdb..00000000
Binary files a/docs/docs/tools/gamedata/.images/gameDataDiagram.JPG and /dev/null differ
diff --git a/docs/docs/tools/gamedata/index.md b/docs/docs/tools/gamedata/index.md
deleted file mode 100644
index 3b8b386e..00000000
--- a/docs/docs/tools/gamedata/index.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Game Data
-
-:::caution
-Editing your game data directly may result in the game becoming unplayable or having unwanted effects.
-:::
-
-## Description
-
-The Game Data tool allows you to view your Bitsy game's raw data in text format. All images, dialogs, rooms etc. that you have created are represented here and can be edited.
-However, it is a good idea to create a backup of your game before doing this in case any changes you make have unwanted effects, such as the game becoming unplayable.
-Create a backup by downloading the game data in this tool or by using the [download tool](../download).
-
-## Features
-
-![game data tool diagram](.images/gameDataDiagram.JPG)
-
-1. **Raw text of the Bitsy game data file.** This text box displays all the data in the Bitsy game in text format. Be cautious if you edit this, as changing this data can potentially make your game unplayable.
-
-2. **Font data.** Data about the font used for your text in Bitsy is contained in the game data but is hidden by default due to its large size (especially for languages with many characters). Leave this as hidden unless you need to view the font data specifically.
-
-3. **Download data.** This button will download the game data for your current Bitsy game in a .bitsy format. This is a text-based data format that can be opened in a program such as Notepad.
-
-4. **Upload data.** Opens a file browser window to allow you to import pre-existing game data in .bitsy format. Be aware that this will override the current game data in your game, so you may want to create a backup of your work before you do this using 'download data' or the download tool.
\ No newline at end of file
diff --git a/docs/docs/tools/settings/.images/settingsDiagram.JPG b/docs/docs/tools/settings/.images/settingsDiagram.JPG
deleted file mode 100644
index d4fb44ef..00000000
Binary files a/docs/docs/tools/settings/.images/settingsDiagram.JPG and /dev/null differ
diff --git a/docs/docs/tools/settings/index.md b/docs/docs/tools/settings/index.md
deleted file mode 100644
index 48db8c70..00000000
--- a/docs/docs/tools/settings/index.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Settings
-
-## Description
-
-The settings tool contains several useful customisations to do with font, language, and export settings.
-
-## Features
-
-![settings diagram](.images/settingsDiagram.JPG)
-
-1. **Language.** Choose from the dropdown menu to set the editor default language. Translations are created manually by the community. If you notice something that is missing in your language please feel free to [contribute](/contributing#translating-editor-text).
-
-2. **Game font.** Choose a font from the dropdown menu that your in-game dialog will be displayed in. Generally this choice will depend on the language you are writing in.
-
- ASCII is a set of 128 characters from the English language, but does not include characters from European languages, such as é, á etc.
-
- Unicode includes several thousand more characters than ASCII, and is recommended if you are writing in a European language.
-
- Unicode Asian includes additional characters for Asian languages such as Chinese, Japanese, and Korean.
-
- Arabic is a pixel font with Arabic characters.
-
- Custom Font allows you to upload your own custom font. This must be in the .bitsyfont format.
-
-3. **Download font.** Clicking this downloads a .bitsyfont file of the font selected in the dropdown menu above.
-
-4. **Text direction.** This changes whether text appears on screen from left to right, or from right to left in the game.
-
-5. **Text mode.** Choose from 'default' or 'chunky' to set the scale of the text.
-
-6. **HTML page color.** Opens a color picker that changes the background color outside of the game. This is visible when the game is downloaded and opened in an internet browser, or as the background color if you [upload your game to Itch.io](/faq/uploadToItch). A preview is also visible in the [room tool](../room).
-
-7. **Game window size.** When set to 'full page', a bitsy game that is downloaded and opened in an internet browser will be scaled at full-screen. 'Fixed size' allows you to set a custom size in pixels that the game will display at. Since Bitsy games have a square aspect ratio, the width and height will always be equal.
\ No newline at end of file
diff --git a/editor/index.html b/editor/index.html
index dbd68492..68db2026 100644
--- a/editor/index.html
+++ b/editor/index.html
@@ -69,6 +69,7 @@
+
@@ -157,63 +158,11 @@
inventory
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- uh oh ~ your browser doesn't support all of bitsy's features, so some things might not work right. maybe try another browser, such as:
-
- chrome
- ,
-
- firefox
-
-
-
-
-
-
- The current font is missing some of the characters used in your dialog. Not all text in your game will display correctly. Consider picking a different font in the settings.
-
-
-
-
@@ -858,57 +807,6 @@
-
-
- file
-
-
- download
-
-
-
-
-
-
-
-
-
- download game as html file:
-
-
-
-
-
- import game from html file:
-
-
-
-
-
-
-
- Note: your browser doesn't support
- direct downloads, so sharing requires
- some extra steps:
- - press share
- - a new tab will open
- - save that tab
- - name it "mygame.html"
- - share your game as a web page!
-
-
-
-
record
@@ -973,175 +871,6 @@
-
-
- game_data
-
-
- game data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- settings
-
-
- settings
-
-
-
-
-
-
-
-
-
-
-
- editor settings
-
-
- language:
-
-
- English text by Adam Le Doux
-
-
- game settings
-
-
- font:
-
-
- Small font limited to ASCII, which includes English characters and some symbols.
-
-
- Small font with some unicode support. Includes characters for most European languages.
-
-
- Large font with more unicode support. Includes characters for all European languages.
-
-
- Large font which includes characters for Asian languages such as Chinese, Japanese, and Korean.
-
-
- Pixel font with Arabic characters
-
-
- Upload your own custom font in the ".bitsyfont" format!
-
-
-
-
-
-
-
-
- text direction:
-
-
-
- Option for languages that read right to left
-
-
- text_edit
- text mode:
-
-
-
-
-
- export settings
-
-
- html page color:
-
-
-
-
-
-
-
-
-
-
-
diff --git a/editor/script/card.js b/editor/script/card.js
index 823e5cab..c6320b98 100644
--- a/editor/script/card.js
+++ b/editor/script/card.js
@@ -101,14 +101,17 @@ function makeToolCard(processName, initFunction) {
card.nav = navControls;
}
- var cardCanvas = document.createElement("canvas");
- cardCanvas.classList.add("bitsy-card-nothing-hide");
- cardCanvas.width = 512;
- cardCanvas.height = 512;
+ var cardCanvas = null;
+ if (card.disableCanvas != true) {
+ cardCanvas = document.createElement("canvas");
+ cardCanvas.classList.add("bitsy-card-nothing-hide");
+ cardCanvas.width = 512;
+ cardCanvas.height = 512;
- card.system._attachCanvas(cardCanvas);
+ card.system._attachCanvas(cardCanvas);
- mainDiv.appendChild(cardCanvas);
+ mainDiv.appendChild(cardCanvas);
+ }
var menuDiv = document.createElement("div");
menuDiv.classList.add("bitsy-menu");
@@ -131,7 +134,7 @@ function makeToolCard(processName, initFunction) {
togglePanelAnimated(e);
},
}),
- document.getElementById(card.insertBefore != undefined ? card.insertBefore : "resetGameButton"));
+ document.getElementById(card.insertBefore));
// todo : feels like kind of a disorganized structure right now..
card.rootElement = cardDiv;
@@ -220,22 +223,24 @@ function makeToolCard(processName, initFunction) {
onMouseMove(e);
}
- card.canvasElement.onmousedown = onMouseDown;
- card.canvasElement.onmouseup = onMouseUp;
- card.canvasElement.onmousemove = onMouseMove;
+ if (card.canvasElement != null) {
+ card.canvasElement.onmousedown = onMouseDown;
+ card.canvasElement.onmouseup = onMouseUp;
+ card.canvasElement.onmousemove = onMouseMove;
- card.canvasElement.onmouseenter = function(e) {
- card.mouseState.hover = true;
- };
+ card.canvasElement.onmouseenter = function(e) {
+ card.mouseState.hover = true;
+ };
- card.canvasElement.onmouseleave = function(e) {
- card.mouseState.hover = false;
- card.canvasElement.onmouseup(e);
- };
+ card.canvasElement.onmouseleave = function(e) {
+ card.mouseState.hover = false;
+ card.canvasElement.onmouseup(e);
+ };
- card.canvasElement.addEventListener('touchstart', onTouchStart, { passive: false });
- card.canvasElement.addEventListener('touchmove', onTouchMove, { passive: false });
- card.canvasElement.addEventListener('touchend', onTouchEnd, { passive: false });
+ card.canvasElement.addEventListener('touchstart', onTouchStart, { passive: false });
+ card.canvasElement.addEventListener('touchmove', onTouchMove, { passive: false });
+ card.canvasElement.addEventListener('touchend', onTouchEnd, { passive: false });
+ }
// hacky way to respond to changes outside the tool??
events.Listen("paint_edit", function() {
@@ -246,13 +251,15 @@ function makeToolCard(processName, initFunction) {
card.mouse = new MouseInterface(card);
- // card.menu.update();
if (card.selectAtIndex) {
card.selectAtIndex(0);
}
card.system._startNoInput();
+ // initialize the menu
+ card.menu.update();
+
return card;
}
diff --git a/editor/script/editor.js b/editor/script/editor.js
index 1c0aa1ba..3949d0ff 100644
--- a/editor/script/editor.js
+++ b/editor/script/editor.js
@@ -536,70 +536,19 @@ function getCurDialogId() {
}
function setDefaultGameState() {
+ // initialize game with default data
var defaultData = Resources["defaultGameData.bitsy"];
- // bitsyLog("DEFAULT DATA \n" + defaultData, "editor");
- document.getElementById("game_data").value = defaultData;
- Store.set('game_data', document.getElementById("game_data").value); // save game
- clearGameData();
- loadWorldFromGameData(document.getElementById("game_data").value); // load game
-
- // refresh images
- renderer.ClearCache();
-}
-
-function newGameDialog() {
- var resetMessage = localization.GetStringOrFallback("reset_game_message", "Starting a new game will erase your old data. Consider exporting your work first! Are you sure you want to start over?");
- if (confirm(resetMessage)) {
- resetGameData();
- }
-}
-
-function resetGameData() {
- setDefaultGameState();
-
- // TODO : localize default_title
- setTitle(localization.GetStringOrFallback("default_title", "Write your game's title here"));
- dialog["0"] = {
- src: localization.GetStringOrFallback("default_sprite_dlg", "I'm a cat"), // hacky to do this in two places :(
- name: "cat dialog", // todo : localize
- };
- dialog["1"] = {
- src: localization.GetStringOrFallback("default_item_dlg", "You found a nice warm cup of tea"),
- name: "tea dialog", // todo : localize
- };
-
- pickDefaultFontForLanguage(localization.GetLanguage());
+ Store.set("game_data", defaultData);
- // todo wrap these variable resets in a function
- tileIndex = 0;
- spriteIndex = 0;
+ // reset game state
+ clearGameData();
- refreshGameData();
+ // load the game
+ var gamedataStorage = Store.get("game_data");
+ loadWorldFromGameData(gamedataStorage); // load game
// refresh images
renderer.ClearCache();
-
- updateExitOptionsFromGameData();
- updateInventoryUI();
- updateFontSelectUI(); // hmm is this really the place for this?
-
- on_paint_avatar();
- document.getElementById('paintOptionAvatar').checked = true;
-
- paintTool.updateCanvas();
- markerTool.Clear(); // hacky -- should combine more of this stuff together
- markerTool.Refresh();
-
- roomTool.selectAtIndex(0);
- tuneTool.selectAtIndex(0);
- blipTool.selectAtIndex(0);
-
- events.Raise("game_data_change"); // TODO -- does this need to have a specific reset event or flag?
-
- // reset find tool (a bit heavy handed?)
- findTool = new FindTool({
- mainElement : document.getElementById("findPanelMain"),
- });
}
function refreshGameData() {
@@ -609,15 +558,7 @@ function refreshGameData() {
flags.ROOM_FORMAT = 1; // always save out comma separated format, even if the old format is read in
- // var gameData = serializeWorld();
-
- // document.getElementById("game_data").value = gameData; // TODO : this is where the slow down is
-
var gameDataNoFonts = serializeWorld(true);
- document.getElementById("game_data").value = showFontDataInGameData ? serializeWorld() : gameDataNoFonts;
-
- // Store.set("game_data", gameData); //auto-save
-
Store.set("game_data", gameDataNoFonts);
}
@@ -720,19 +661,6 @@ function detectBrowserFeatures() {
}
}
-function hasUnsupportedFeatures() {
- return /*!browserFeatures.colorPicker ||*/ !browserFeatures.fileDownload;
-}
-// NOTE: No longer relying on color picker feature
-
-function showUnsupportedFeatureWarning() {
- document.getElementById("unsupportedFeatures").style.display = "block";
-}
-
-function hideUnsupportedFeatureWarning() {
- document.getElementById("unsupportedFeatures").style.display = "none";
-}
-
// This is the panel arrangement you get if you are new or your editor settings are out-of-date
var defaultPanelPrefs = {
workspace : [
@@ -740,16 +668,14 @@ var defaultPanelPrefs = {
{ id:"roomPanel", visible:true, position:1 },
{ id:"paintPanel", visible:true, position:2 },
{ id:"colorsPanel", visible:true, position:3 },
- { id:"downloadPanel", visible:true, position:4 },
+ { id:"gamePanel", visible:true, position:4 },
{ id:"gifPanel", visible:false, position:5 },
- { id:"dataPanel", visible:false, position:6 },
- { id:"exitsPanel", visible:false, position:7 },
- { id:"dialogPanel", visible:false, position:8 },
- { id:"findPanel", visible:false, position:9 },
- { id:"inventoryPanel", visible:false, position:10 },
- { id:"settingsPanel", visible:false, position:11 },
- { id:"tunePanel", visible:false, position:12 },
- { id:"blipPanel", visible:false, position:13 },
+ { id:"exitsPanel", visible:false, position:6 },
+ { id:"dialogPanel", visible:false, position:7 },
+ { id:"findPanel", visible:false, position:8 },
+ { id:"inventoryPanel", visible:false, position:9 },
+ { id:"tunePanel", visible:false, position:10 },
+ { id:"blipPanel", visible:false, position:11 },
]
};
// bitsyLog(defaultPanelPrefs, "editor");
@@ -759,7 +685,7 @@ function getPanelPrefs() {
var storedEngineVersion = Store.get('engine_version');
var useDefaultPrefs = (!storedEngineVersion) ||
(storedEngineVersion.major < 8) ||
- (storedEngineVersion.minor < 0);
+ (storedEngineVersion.minor < 11);
var prefs = useDefaultPrefs ? defaultPanelPrefs : Store.get('panel_prefs', defaultPanelPrefs);
// add missing panel prefs (if any)
@@ -877,11 +803,8 @@ function start() {
}
//load last auto-save
- var gamedataStorage = Store.get('game_data');
+ var gamedataStorage = Store.get("game_data");
if (gamedataStorage) {
- //bitsyLog("~~~ found old save data! ~~~", "editor");
- //bitsyLog(gamedataStorage, "editor");
- document.getElementById("game_data").value = gamedataStorage;
on_game_data_change_core();
}
else {
@@ -911,10 +834,6 @@ function start() {
refreshGameData();
});
- //unsupported feature stuff
- if (hasUnsupportedFeatures() && !isPortraitOrientation()) {
- showUnsupportedFeatureWarning();
- }
if (!browserFeatures.fileDownload) {
document.getElementById("downloadHelp").style.display = "block";
}
@@ -978,33 +897,9 @@ function start() {
}
}
- //color testing
- // on_change_color_bg();
- // on_change_color_tile();
- // on_change_color_sprite();
-
// save latest version used by editor (for compatibility)
Store.set('engine_version', version);
- // load saved export settings
- export_settings = Store.get('export_settings', export_settings);
- if (export_settings) {
- document.getElementById("pageColor").value = export_settings.page_color;
- }
-
- // TODO : interesting idea but needs work!
- // // try to honor state of all checkboxes from previous session
- // var inputElements = document.getElementsByTagName("input");
- // for (var i in inputElements) {
- // if (inputElements[i].type === "checkbox") {
- // var checkbox = inputElements[i];
- // if (checkbox.checked) {
- // bitsyLog(checkbox, "editor");
- // checkbox.dispatchEvent(new Event("click"));
- // }
- // }
- // }
-
// create title widgets
var titleTextWidgets = document.getElementsByClassName("titleWidgetContainer");
for (var i = 0; i < titleTextWidgets.length; i++) {
@@ -1016,8 +911,6 @@ function start() {
openDialogTool(titleDialogId, undefined, false); // start with the title open
alwaysShowDrawingDialog = document.getElementById("dialogAlwaysShowDrawingCheck").checked;
- initLanguageOptions();
-
// find tool
findTool = new FindTool({
mainElement : document.getElementById("findPanelMain"),
@@ -1057,6 +950,9 @@ function start() {
}
}
+ // game tool
+ gameTool = makeGameTool();
+
// about tool
initAbout();
}
@@ -1475,12 +1371,6 @@ function toggleDownloadOptions(e) {
}
}
-// hacky - part of hiding font data from the game data
-function getFullGameData() {
- // return document.getElementById("game_data").value + fontManager.GetData(fontName);
- return serializeWorld();
-}
-
function togglePlayMode(e) {
if (e.target.checked) {
on_play_mode();
@@ -1505,7 +1395,7 @@ function on_play_mode() {
roomTool.menu.update();
document.getElementById("appRoot").classList.add("bitsy-playmode");
// todo : I feel likef I need to take a look at the font manager and simplify things there
- loadGame(roomTool.canvasElement, getFullGameData(), fontManager.GetData(defaultFontName));
+ loadGame(roomTool.canvasElement, serializeWorld(), fontManager.GetData(defaultFontName));
}
function on_edit_mode() {
@@ -1513,11 +1403,11 @@ function on_edit_mode() {
document.getElementById("appRoot").classList.remove("bitsy-playmode");
- // stopGame();
quitGame();
- // TODO I should really do more to separate the editor's game-data from the engine's game-data
- loadWorldFromGameData(document.getElementById("game_data").value); //reparse world to account for any changes during gameplay
+ // reparse world to reset any changes from gameplay
+ var gamedataStorage = Store.get("game_data");
+ loadWorldFromGameData(gamedataStorage);
state.room = sortedRoomIdList()[roomIndex]; //restore current room to pre-play state
@@ -1582,13 +1472,6 @@ function updatePaintGridCheck(checked) {
iconUtils.LoadIcon(document.getElementById("paintGridIcon"), checked ? "visibility" : "visibility_off");
}
-var showFontDataInGameData = false;
-function toggleFontDataVisibility(e) {
- showFontDataInGameData = e.target.checked;
- iconUtils.LoadIcon(document.getElementById("fontDataIcon"), e.target.checked ? "visibility" : "visibility_off");
- refreshGameData(); // maybe a bit expensive
-}
-
/* PALETTE STUFF */
var colorPicker = null;
var paletteTool = null;
@@ -1828,13 +1711,21 @@ function on_change_adv_dialog() {
function on_game_data_change() {
on_game_data_change_core();
refreshGameData();
+
+ // reset find tool (a bit heavy handed?)
+ findTool = new FindTool({
+ mainElement : document.getElementById("findPanelMain"),
+ });
}
function on_game_data_change_core() {
- bitsyLog(document.getElementById("game_data").value, "editor");
+ var gamedataStorage = Store.get("game_data");
+ bitsyLog(gamedataStorage, "editor");
clearGameData();
- loadWorldFromGameData(document.getElementById("game_data").value); //reparse world if user directly manipulates game data
+
+ // reparse world if user directly manipulates game data
+ loadWorldFromGameData(gamedataStorage);
if (roomTool) {
roomTool.selectAtIndex(0);
@@ -1907,33 +1798,10 @@ function on_game_data_change_core() {
updateInventoryUI();
- updateFontSelectUI();
-
// TODO -- start using this for more things
events.Raise("game_data_change");
}
-function updateFontSelectUI() {
- var fontStorage = Store.get('custom_font', null);
-
- var fontSelect = document.getElementById("fontSelect");
-
- for (var i in fontSelect.options) {
- var fontOption = fontSelect.options[i];
- var fontOptionName = (fontOption.value === "custom" && fontStorage != null) ? fontStorage.name : fontOption.value;
- fontOption.selected = fontOptionName === fontName;
-
- if (fontOption.value === "custom" && fontStorage != null) {
- var textSplit = fontOption.text.split("-");
- fontOption.text = textSplit[0] + "- " + fontStorage.name;
- }
- }
-
- updateFontDescriptionUI();
- updateTextDirectionSelectUI(); // a bit hacky but probably ok?
- updateEditorTextDirection(textDirection); // EXTREMELY hack :(
-}
-
function updateFontDescriptionUI() {
for (var i in fontSelect.options) {
var fontOption = fontSelect.options[i];
@@ -1946,10 +1814,6 @@ function updateFontDescriptionUI() {
}
}
-function updateExitOptionsFromGameData() {
- // TODO ???
-}
-
function on_toggle_wall(e) {
paintTool.toggleWall( e.target.checked );
}
@@ -1958,45 +1822,6 @@ function toggleWallUI(checked) {
iconUtils.LoadIcon(document.getElementById("wallCheckboxIcon"), checked ? "wall_on" : "wall_off");
}
-function filenameFromGameTitle() {
- var filename = getTitle().replace(/[^a-zA-Z]/g, "_"); // replace non alphabet characters
- filename = filename.toLowerCase();
- filename = filename.substring(0,32); // keep it from getting too long
- return filename;
-}
-
-function exportGame() {
- if (isPlayMode) {
- alert("You can't download your game while you're playing it! Sorry :(");
- return;
- }
-
- refreshGameData(); //just in case
- // var gameData = document.getElementById("game_data").value; //grab game data
- var gameData = getFullGameData();
- var size = document.getElementById("exportSizeFixedInput").value;
- //download as html file
- exporter.exportGame(
- gameData,
- getTitle(),
- export_settings.page_color,
- filenameFromGameTitle() + ".html",
- isFixedSize,
- size);
-}
-
-function exportGameData() {
- refreshGameData(); //just in case
- // var gameData = document.getElementById("game_data").value; //grab game data
- var gameData = getFullGameData();
- ExporterUtils.DownloadFile(filenameFromGameTitle() + ".bitsy", gameData);
-}
-
-function exportFont() {
- var fontData = fontManager.GetData(fontName);
- ExporterUtils.DownloadFile( fontName + ".bitsyfont", fontData );
-}
-
function hideAbout() {
document.getElementById("aboutPanel").setAttribute("style","display:none;");
}
@@ -2455,94 +2280,6 @@ function finishRecordingGif(gif) {
}, 10);
}
-/* LOAD FROM FILE */
-function importGameFromFile(e) {
- if (isPlayMode) {
- alert("You can't upload a game while you're playing one! Sorry :(");
- return;
- }
-
- resetGameData();
-
- // load file chosen by user
- var files = e.target.files;
- var file = files[0];
- var reader = new FileReader();
- reader.readAsText( file );
-
- reader.onloadend = function() {
- var fileText = reader.result;
- gameDataStr = exporter.importGame( fileText );
-
- // change game data & reload everything
- document.getElementById("game_data").value = gameDataStr;
- on_game_data_change();
-
- // reset find tool (a bit heavy handed?)
- findTool = new FindTool({
- mainElement : document.getElementById("findPanelMain"),
- });
- }
-}
-
-function importFontFromFile(e) {
- // load file chosen by user
- var files = e.target.files;
- var file = files[0];
- var reader = new FileReader();
- reader.readAsText( file );
-
- reader.onloadend = function() {
- var fileText = reader.result;
- bitsyLog(fileText, "editor");
-
- var customFontName = (fontManager.Create(fileText)).getName();
-
- fontManager.AddResource(customFontName + fontManager.GetExtension(), fileText);
- switchFont(customFontName); // bitsy engine setting
-
- var fontStorage = {
- name : customFontName,
- fontdata : fileText
- };
- Store.set('custom_font', fontStorage);
-
- refreshGameData();
- updateFontSelectUI();
-
- // TODO
- // fontLoadSettings.resources.set("custom.txt", fileText); // hacky!!!
- }
-}
-
-function importGameDataFromFile(e) {
- if (isPlayMode) {
- alert("You can't upload a game while you're playing one! Sorry :(");
- return;
- }
-
- resetGameData();
-
- // load file chosen by user
- var files = e.target.files;
- var file = files[0];
- var reader = new FileReader();
- reader.readAsText(file);
-
- reader.onloadend = function() {
- var gameDataStr = reader.result;
-
- // change game data & reload everything
- document.getElementById("game_data").value = gameDataStr;
- on_game_data_change();
-
- // reset find tool (a bit heavy handed?)
- findTool = new FindTool({
- mainElement : document.getElementById("findPanelMain"),
- });
- }
-}
-
/* ANIMATION EDITING*/
function on_toggle_animated() {
bitsyLog("ON TOGGLE ANIMATED", "editor");
@@ -2791,21 +2528,6 @@ function on_paint_frame2() {
paintTool.reloadDrawing();
}
-var export_settings = {
- page_color : "#ffffff"
-};
-
-function on_change_color_page() {
- var hex = document.getElementById("pageColor").value;
- //bitsyLog(hex, "editor");
- var rgb = hexToRgb( hex );
- // document.body.style.background = hex;
- document.getElementById("roomPanel").style.background = hex;
- export_settings.page_color = hex;
-
- Store.set('export_settings', export_settings);
-}
-
function getComplimentingColor(palId) {
if (!palId) palId = curDefaultPal();
var hsl = rgbToHsl( getPal(palId)[0][0], getPal(palId)[0][1], getPal(palId)[0][2] );
@@ -3056,47 +2778,17 @@ function chooseExportSizeFixed() {
// LOCALIZATION
var localization;
-function on_change_language(e) {
- var language = e.target.value;
- pickDefaultFontForLanguage(language);
- on_change_language_inner(language);
-}
-
-function on_change_language_inner(language) {
- changeLnaguageStyle(language); // TODO : misspelled funciton name
-
- localization.ChangeLanguage(language);
- updateInventoryUI();
- reloadDialogUI();
- hackyUpdatePlaceholderText();
-
- // update title in new language IF the user hasn't made any changes to the default title
- if (localization.LocalizationContains("default_title", getTitle())) {
- setTitle(localization.GetStringOrFallback("default_title", "Write your game's title here"));
- // make sure all editors with a title know to update
- events.Raise("dialog_update", { dialogId:titleDialogId, editorId:null });
- }
-
- // update default sprite
- var defaultSpriteDlgExists = dialog["0"] != null && localization.LocalizationContains("default_sprite_dlg", dialog["0"]);
- if (defaultSpriteDlgExists) {
- dialog["0"] = {
- src: localization.GetStringOrFallback("default_sprite_dlg", "I'm a cat"),
- name: null,
- };
- paintTool.reloadDrawing();
- }
- // update default item
- var defaultItemDlgExists = dialog["1"] != null && localization.LocalizationContains("default_item_dlg", dialog["1"]);
- if (defaultItemDlgExists) {
- dialog["1"] = {
- src: localization.GetStringOrFallback("default_item_dlg", "You found a nice warm cup of tea"),
- name: null,
- };
- paintTool.reloadDrawing(); // hacky to do this twice
+// TODO : create a system for placeholder text like I have for innerText
+function hackUpdatePlaceholderText() {
+ var titlePlaceholder = localization.GetStringOrFallback("title_placeholder", "Title");
+ var titleTextBoxes = document.getElementsByClassName("titleTextBox");
+ for (var i = 0; i < titleTextBoxes.length; i++) {
+ titleTextBoxes[i].placeholder = titlePlaceholder;
}
+}
+function hackUpdateEditorToolMenusOnLanguageChange() {
// hack : manually update tool menus & titles
if (roomTool) {
roomTool.resetTitlebar();
@@ -3116,133 +2808,32 @@ function on_change_language_inner(language) {
document.getElementById(tuneTool.id + "CheckLabelText").innerText = tuneTool.name();
}
- refreshGameData();
-}
-
-// TODO : create a system for placeholder text like I have for innerText
-function hackyUpdatePlaceholderText() {
- var titlePlaceholder = localization.GetStringOrFallback("title_placeholder", "Title");
- var titleTextBoxes = document.getElementsByClassName("titleTextBox");
- for (var i = 0; i < titleTextBoxes.length; i++) {
- titleTextBoxes[i].placeholder = titlePlaceholder;
+ // do this in case the the current sprite dialog changed
+ if (paintTool) {
+ paintTool.reloadDrawing();
}
+
+ // TODO : test - is this necessary still? we already call "reloadDialogUI" in the settings tool
+ // make sure all editors with a title know to update
+ events.Raise("dialog_update", { dialogId:titleDialogId, editorId:null });
}
var curEditorLanguageCode = "en";
-function changeLnaguageStyle(newCode) { // TODO : fix function name
+function updateEditorLanguageStyle(newCode) {
document.body.classList.remove("lang_" + curEditorLanguageCode);
curEditorLanguageCode = newCode;
document.body.classList.add("lang_" + curEditorLanguageCode);
}
-function pickDefaultFontForLanguage(lang) {
- // TODO : switch to asian characters when we get asian language translations of editor
- if (lang === "en") {
- switchFont("ascii_small", true /*doPickTextDirection*/);
- }
- else if (lang === "ar") {
- switchFont("arabic", true /*doPickTextDirection*/);
- }
- else if (lang === "zh" || lang === "ja") {
- switchFont("unicode_asian", true /*doPickTextDirection*/);
- }
- else {
- switchFont("unicode_european_small", true /*doPickTextDirection*/);
- }
- updateFontSelectUI();
-}
-
-function on_change_font(e) {
- if (e.target.value != "custom") {
- switchFont(e.target.value, true /*doPickTextDirection*/);
- }
- else {
- var fontStorage = Store.get('custom_font', { name: 'ascii_small' });
- switchFont(fontStorage.name, true /*doPickTextDirection*/);
- }
- updateFontDescriptionUI();
- // updateEditorTextDirection();
-}
-
-function switchFont(newFontName, doPickTextDirection) {
- if (doPickTextDirection === undefined || doPickTextDirection === null) {
- doPickTextDirection = false;
- }
-
- fontName = newFontName;
-
- if (doPickTextDirection) {
- bitsyLog("PICK TEXT DIR", "editor");
- pickDefaultTextDirectionForFont(newFontName);
- }
-
- refreshGameData()
-}
-
-function initLanguageOptions() {
- localization.Localize();
-
- var languageSelect = document.getElementById("languageSelect");
- languageSelect.innerHTML = "";
-
- var languageList = localization.GetLanguageList();
- for (var i = 0; i < languageList.length; i++) {
- var option = document.createElement("option");
- option.innerText = languageList[i].name;
- option.value = languageList[i].id;
- option.selected = languageList[i].id === localization.GetLanguage();
- languageSelect.add(option);
- }
-
- // is this doing duplicate work??
- on_change_language_inner( localization.GetLanguage() );
-}
-
-function on_change_text_direction(e) {
- bitsyLog("CHANGE TEXT DIR " + e.target.value, "editor");
- updateEditorTextDirection(e.target.value);
- refreshGameData();
-}
-
-function onChangeTextMode(e) {
- flags.TXT_MODE = bitsy[e.target.value];
- refreshGameData();
-}
-
-function pickDefaultTextDirectionForFont(newFontName) {
- var newTextDirection = TextDirection.LeftToRight;
- if (newFontName === "arabic") {
- newTextDirection = TextDirection.RightToLeft;
- }
- updateEditorTextDirection(newTextDirection);
- updateTextDirectionSelectUI();
-}
-
function updateEditorTextDirection(newTextDirection) {
var prevTextDirection = textDirection;
- textDirection = newTextDirection;
- bitsyLog("TEXT BOX TEXT DIR " + textDirection, "editor");
+ bitsyLog("TEXT BOX TEXT DIR " + newTextDirection, "editor");
if (prevTextDirection != null) {
document.body.classList.remove("dir_" + prevTextDirection.toLowerCase());
}
- document.body.classList.add("dir_" + textDirection.toLowerCase());
-}
-
-function updateTextDirectionSelectUI() {
- var textDirSelect = document.getElementById("textDirectionSelect");
- for (var i in textDirSelect.options) {
- var option = textDirSelect.options[i];
- option.selected = (option.value === textDirection);
- }
-
- // hacky to stick this here..
- var textModeSelect = document.getElementById("textModeSelect");
- for (var i in textModeSelect.options) {
- var option = textModeSelect.options[i];
- option.selected = (bitsy[option.value] === flags.TXT_MODE);
- }
+ document.body.classList.add("dir_" + newTextDirection.toLowerCase());
}
/* UTILS (todo : move into utils.js after merge) */
@@ -3324,6 +2915,9 @@ function openFindToolWithCurrentPaintCategory() {
openFindTool(categoryId, "paintPanel");
}
+/* GAME TOOL */
+var gameTool;
+
/* SOUND TOOLS */
var tuneTool;
var blipTool;
\ No newline at end of file
diff --git a/editor/script/engine/world.js b/editor/script/engine/world.js
index f5d83ff4..79d9c0ce 100644
--- a/editor/script/engine/world.js
+++ b/editor/script/engine/world.js
@@ -2,7 +2,7 @@
// is this the right place for this to live?
var version = {
major: 8, // major changes
- minor: 10, // smaller changes
+ minor: 11, // smaller changes
devBuildPhase: "RELEASE",
};
function getEngineVersion() {
diff --git a/editor/script/generated/resources.js b/editor/script/generated/resources.js
index 418f64b8..7061ee79 100644
--- a/editor/script/generated/resources.js
+++ b/editor/script/generated/resources.js
@@ -1,5 +1,5 @@
var Resources = {
- "localization.tsv": "id\ten\tfr\tpt\tes\tit\tru\tsv\tde\tar\tis\thu\teo\tet\tzh\tpl\tja\tchn\tcze\tuk\tbr\nabout_tool_name\tabout\tà propos\tsobre\tsobre\tA proposito di\tо битси\tinfo\tÜber\tعن\tupplýsingar\ta bitsyről\tpri\tbitsy kohta\t相关\tinfo\t説明\tikta bitsy?\tO bitsy\tпро Бітсі\tsobre\nabout_tool_title\tabout bitsy\tà propos de Bitsy\tsobre o Bitsy\tSobre Bitsy\tA proposito di Bitsy\tо битси\tom bitsy\tÜber Bitsy\tعن بيتسي\tum bitsy\ta bitsyről\tpri Bitsy\tbitsy kohta\t关于 Bitsy\to bitsy\tBitsyについて\tikta bitsy?\tO Bisty\tпро Бітсі\tsobre o bitsy\nabout1\thi! bitsy is a little editor for little games or worlds. the goal is to make it easy to make games where you can walk around and talk to people and be somewhere.\tSalut ! Bitsy est un petit moteur de jeu vidéo pour des petits jeux ou des petits mondes. Le but est de rendre accessible la création de jeux dans lesquels tu peux te promener, parler à des personnes et découvrir de nouveaux environnements.\tolá! o bitsy é um pequeno editor para pequenos jogos ou mundos. o objectivo é tornar fácil a criação de jogos onde podes mover-te, falar com pessoas e estar em algum lado.\t¡Hola! Bitsy es un motor para crear pequeños juegos y mundos. El objetivo es crear juegos fácilmente en cualquier lugar.\tCiao! Bitsy è un piccolo motore, concepito per creare piccoli giochi e mondi. Lo scopo è creare giochi dove puoi gironzolare, parlare con qualcuno, ed essere da qualche parte.\tпривет! битси это небольшой редактор для маленьких игр или игровых миров. главная цель этой программы - сделать простым и доступным процесс создания игры, в которой ты можешь ходить, разговаривать и просто быть где-то.\thej! bitsy är ett litet verktyg för små spel eller världar. syftet är att det ska vara lätt att göra spel där du kan gå omkring, prata med folk och vara någonstans.\tHi! Bitsy ist ein kleiner Editor für kleine Spiele oder Welten. Ziel ist, möglichst einfach Spiele machen zu können, in denen man umherlaufen, mit Figuren reden und sich irgendwo wiederfinden kann.\tأهلاً! بيتسي عبارة عن محرر لتصميم الألعاب الصغيرة و العوالم. الهدف تسهيل عملية تصميم الألعاب التي يمكن أن تتحرك وتتحدث مع شخصيات مختلفة في عالمها.\thæ! bitsy er tól til að búa til litla leiki eða heima. tilgangur þess er að auðvelda gerð leikja þar sem þú getur gengið um og talað við fólk og verið til staðar.\tszia! a bitsy egy kicsi fejlesztői eszköz pici játékokhoz és apró világokhoz. a cél, hogy segítségével könnyen létrehozhass olyan játékokat, ahol barangolhatsz, beszélgethetsz emberekkel és felfedezhetsz új világokat.\tsaluton! Bitsy estas eta kreilo por ludetoj kaj mondetoj. Bitsy celas faciligi la kreadon de ludoj pri marŝadi tien-ĉi-tien, paroli ludulojn kaj ĝui la ludomondon.\ttere! bitsy on väike programm millega saab luua väikseid mänge või maailmaid. bitsy eesmärk on teha lihtsaks mängude tegemise, kus saab ringi jalutada, inimestega rääkida ja olla kuskil.\t你好!Bitsy 是一个为小游戏和小世界制作的小编辑器,目的是为了使得制作游戏更容易,你可以在这些游戏中随处走走,随意聊聊,或者去到某个地方。\tCześć! Bitsy to mały edytor do małych gier lub światów. Powstał by ułatwić tworzenie gier, w których możesz chodzić, rozmawiać z ludźmi, i sobie przebywać.\tこんにちは!bitsyはちょっとしたゲームや世界のための編集ツール。歩き回って人と話たり、いろんな場所に行ったりするようなゲームを簡単に作れるようにする事を目指しています。\ttlahowya! maika mamook hihi kopa okok bitsy. bitsy mamook tenas hihi, okok hihi klosh spos maika tiki tlatawa pi wawa tilikum, spos maika tiki wawa yahim.\t\tпривіт! бітсі це невеличкий редактор для маленьких ігор або світів. його мета - спростити створення ігр, де ти можеш ходити, розмовляти з людьми, та просто бути.\tolá! o bitsy é um pequeno editor para pequenos mundos ou jogos. a ideia é facilitar a criação de um jogo onde você possa andar, explorar e conversar em um lugarzinho só seu.\nabout10_link_twitter\tadam\tadam\tadam\tadam\tadam\tадам\tadam\tadam\tآدم\tadam\tadam\tAdam\tadam\tadam\tadam\tadam\tadam\t\tадам\tadam\nabout2\tnot sure where to start? try this helpful\tTu ne sais pas par où commencer ? Essaie ce\tsem certeza por onde começar? tenta este\t¿No sabes por dónde empezar? ¡Prueba este\tNon sai da dove cominciare? Da un occhiata a questo utile\tне знаете с чего начать? прочитайте эту прекрасную обучающую\tVet du inte var du ska börja? här finns en hjälpsam\tNicht sicher wie du loslegen kannst? Probiere diese hilfreiche\tلا تدري من أين تبدأ؟ يمكنك تجربة\tekki viss um hvar best er að byrja? prófaðu þessa hjálplegu\tnem tudod hogyan kezdj neki? akkor kövesd ezt a hasznos\tĉu ne scias vi kion fari je la komenco? se scipovas la anglan, legu ĉi utilan\tpole kindel kust alustada? proovi seda suurepärast\t不知道从哪里开始?这个或许会帮到你。\tnie wiesz gdzie zacząć? wypróbuj ten pomocny\tどこから始めればいいかわからないんですか?じゃあこれをみてください!\twik maika komtuks kata maika chi? klosh maika nanich okok klosh\t\tне знаєш з чого почати? спробуй цей корисний\tsem ideia de por onde começar? tente o nosso\nabout3_link_tutorial\tbitsy tutorial!\ttutoriel Bitsy !\ttutorial do bitsy!\ttutorial de Bitsy!\ttutorial di Bitsy!\tстатью про битси!\tbitsy-guide!\tBitsy Anleitung!\tدورة بيتسي التعليمية\tbitsy kennslu!\tbitsy segédletet!\tBitsy-helposkribo!\tbitsy õpetust!\tBitsy 教程!\tsamouczek bitsy!\tBitsyチュートリアル(英語)。\tbitsy hilp-pipa!\t\tпідручнік по Бітсі!\ttutorial do bitsy!\nabout4\tif you need inspiration, you can\tSi tu as besoin d'inspiration, tu peux\tse precisares de inspiração, podes\tSi necesitas inspiración, puedes\tSe hai bisogno d'ispirazione puoi\tесли вы ищите вдохновения,\tför att få mer inspiration kan du\tFalls du Inspiration suchst, kannst du\tتبحث عن الإلهام؟ لما لا\tef þig vantar innblástur geturðu\tha inspirációra van szükséged,\tse vi volas ideojn por kian ludon krei, provu\tkui vajad inspiratsiooni, võid\t如果你需要灵感,你可以\tjeśli szukasz inspiracji, możesz\tもしインスピレーションが欲しいならこれをみてください→\tspos wik maika komtuks ikta hihi maika mamook, klosh maika\t\tбракує натхнення? можеш\tse precisar se inspirar, você pode\nabout5_link_games\tplay other bitsy games.\tjouer à ces jeux Bitsy.\tjogar outros jogos do bitsy.\tjugar a otros juegos de Bitsy\tgiocare altri giochi creati con Bitsy\tсыграйте в другие битси-игры.\tspela andra bitsy-spel.\tandere Bitsy Spiele spielen\tتلعب بعض ألعاب بيتسي الآخرى\tspilað aðra bitsy leiki.\tjátssz más bitsy játékokkal,\tludi aliajn Bitsy-ludojn.\tmängida teisi bitsy mänge.\t试试其它用 Bitsy 制作的游戏\tzagrać w inne gry bitsy.\t他のBitsyで作られたゲーム。\tkultus hihi kopa huloima tilikum tlaska bitsy hihi.\t\tпограти в інші ігри з Бітсі.\tjogar outros jogos feitos com bitsy.\nabout6\tyou can find many on\tTu peux aussi en trouver plein d'autres sur\tpodes encontrar muitos no\t¡Puedes encontrar otros títulos en\tPuoi trovarne altri su\tмногие из них можно найти на сайте\tdu kan hitta många på\tDu kannst viele von ihnen auf\tيمكنك أن تجد الكثير على\tþú finnur marga slíka á\tvan egy csomó itt:\tekzistas multaj ludoj ĉe\tmitmeid võib leida\t你可以在这里找到很多\tznajdziesz je na\t多くのゲームはここで見つけられます→\thayu bitsy hihi mitlayt kopa\t\tБагато з них можна знайти на\tvocê pode encontrar vários em\nabout7_link_itchio\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\nabout8\t, which is a great place to share games!\t, qui est un endroit fantastique pour partager des jeux!\t, um sítio fantástico para partilhar jogos!\tque es un gran lugar para compartir tus creaciones!\tche è un bellissimo luogo per condividere le tue creazioni!\t, это отличное место для того, чтобы делиться своими играми!\t, ett bra ställe att dela sina spel!\tfinden, was auch ein toller Ort ist um deine Spiele zu teilen!\tفأنه المكان الأمثل لمشاركة الألعاب!\t, sem er frábær staður til að deila leikjum!\t- ez egy tök jó oldal ahol megoszthatod a játékaidat!\t, kiu estas bonega retejo por disdoni ludojn!\tlehelt - üks imepärane koht, kus jagada mänge!\t,这是个很棒的分享游戏的地方!\t, które jest świetnym miejscem do dzielenia się grami!\t、このサイトは作ったゲームをシェアするのにおすすめです!\t, okok itch.io klosh twa-pipa spos maika tiki munk-nanich huloima tilikum maika bitsy hihi!\t\t, це чудове місце для поширення ігор!\t, um ótimo lugar para compartilhar jogos!\nabout9\thave fun making things with bitsy!\tAmuse-toi bien avec Bitsy!\tdiverte-te a criar coisas com o bitsy!\t¡Diviértete creando en Bitsy!\tDivertiti creando con Bitsy!\tи пусть творческий процесс будет весёлым и захватывающим!\thoppas du har roligt med bitsy!\tViel Spaß beim Basteln mit Bitsy!\tو لا تنسى أن تستمتع و أنت تستخدم بيتسي!\tskemmtu þér vel við að skapa með bitsy!\tjó szórakozást a bitsyhez!\tĝuu kreadi per Bitsy!\tlõbutse bitsyga luues!\t希望你用 Bitsy 玩的开心!\tbaw się dobrze, tworząc z bitsy!\tBitsyでのゲームづくりを楽しんでください!\tnaika tiki spos maika hayu klosh-tumtum kopa mamook hihi kopa bitsy!\t\tНасолоджуйся творінням з Бітсі!\tdivirta-se criando com bitsy!\naction_add_new\tadd\tajouter\t\t\taggiungi\tдобавить\t\tHinzufügen\tإضافة\tbæta við\thozzáadás\t\t\t\tdodaj\t\t\t\tдодати\tcolocar\naction_back\tback\tretour\t\t\tindietro\tназад\t\tZurück\tرجوع\tbakka\tvissza\t\t\t\twstecz\t\t\tzpět\tназад\tvoltar\naction_cancel\tcancel\tannuler\t\t\tcancella\tотмена\t\tAbbrechen\tإلغاء\thætta við\tmégsem\t\t\t\tanuluj\t\t\t\tскасувати\tcancelar\naction_save\tsave\tsauvegarder\t\t\tsalva\tсохранить\t\tSpeichern\tحفظ\tvista\tmentés\t\t\t\tzapisz\t\t\tuložit\tзберегти\tsalvar\nanimation_frame1\tframe 1\tcalque 1\tframe 1\tframe 1\tframe 1\tкадр 1\tbild 1\tFrame 1\tإطار ١\trammi 1\t1. képkocka\tmovo 1\tkaader 1\t第1帧\tklatka 1\tフレーム1\thulel 1\tframe 1\tкадр 1\tquadro 1\nanimation_frame2\tframe 2\tcalque 2\tframe 2\tframe 2\tframe 2\tкадр 2\tbild 2\tFrame 2\tإطار ٢\trammi 2\t2. képkocka\tmovo 2\tkaader 2\t第2帧\tklatka 2\tフレーム2\thulel 2\tframe 2\tкадр 2\tquadro 2\nanimation_preview\tpreview\taperçu\tpré-vizualizar\tprevisualizar\tanteprima\tпредпросмотр\tförhandsvisning\tVorschau\tمعاينة\tsýnishorn\telőnézet\tantaŭvido\teelvaade\t预览\tpodgląd\tプレビュー\ttenas nanich\tnáhled\tперегляд\tprévia\nanimation_title\tanimation\tanimation\tanimação\tanimación\tanimazione\tанимация\tanimation\tAnimation\tالرسوم المتحركة\threyfimynd\tanimáció\tmovanta bildo\tanimatsioon\t动画\tanimacja\tアニメーション\thulel-picha\tanimace\tанімація\tanimação\nauthor_by\tby\tpar\tde\tpor\tdi\tот\tav\tvon\tبواسطة\teftir\tkészítette:\tde\tautor:\t来自\tautor\tby\tkopa\tautor\tвід\tpor\nauthor_twitter\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\navatar_label\tavatar\tavatar\tavatar\tavatar\tavatar\tаватар\tavatar\tAvatar\tالشخصية الممثل\tleikpeð\tavatár\tmovulo\tavatar\t人物\tawatar\tアバター\ttayi picha\tavatar\tаватар\tavatar\naxis_x_label\tx\tx\t\t\tx\tx\t\tx\tx\tx\tx\t\t\t\tx\t\t\tx\tx\tx\naxis_y_label\ty\ty\t\t\ty\ty\t\ty\ty\ty\ty\t\t\t\ty\t\t\ty\ty\ty\nbitsy_title\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tБитси\tBitsy\tBitsy\tبيتسي\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tБітсі\tBitsy\nbranch_add\tadd branch\tajouter un choix\t\t\taggiungi ramificazione\tдобавить ветвь\t\tVerzweigung hinzufügen\tإضافة فرع\tbæta við grein\telágazás hozzáadása\t\t\t\tdodaj gałąź\t\t\t\tдодати гілку\tcolocar opção\nbranch_type_default\tdefault branch\tchoix par défaut\t\t\tramificazione di default\tветвь по умолчанию\t\tdann\tفرع\tsjálfgefin grein\talapértelmezett elágazás\t\t\t\tgałąź domyślna\t\t\t\tгілка за замовчуванням\topção padrão\nbranch_type_item\titem branch\tchoix avec un objet\t\t\tramificazione con oggetti\tтип ветви объекта\t\tWenn Objekt\tفرع معتمد على أداة\thluta grein\ttárgy elágazás\t\t\t\tgałąź przedmiotu\t\t\t\tгілка з предметом\topção de item\nbranch_type_variable\tvariable branch\tchoix avec une variable\t\t\tramificazione con variabili\tпеременная ветви\t\tWenn Variable\tفرع معتمد على متغير\tbreytu grein\tváltozó elágazás\t\t\t\tgałąź zmiennej\t\t\t\tгілка зі змінною\topção de variável\nbranching_list_description\tgo to the first branch whose condition is true:\taller au premier choix dont la condition est vraie\t\t\tsegui la prima ramificazione la cui condizione è vera:\tперейдите к первой ветви, условие которой истинно:\t\tZeige den ersten Block in dem eine Bedingung erfüllt ist:\tالإتجاة إلى الفرع الأول إذا كان الشرط صحيح:\tfarðu að fyrstu greininni þar sem aðstæður eru sannar:\tmenj az első elágazáshoz, ami igaz:\t\t\t\tprzejdź do pierwszej gałęzi, której warunek jest spełniony:\t\t\t\tпіти на першу гілку, де умова справджується:\tir à primeira opção de condição verdadeira:\nbranching_list_name\tbranching list\tliste à choix\t\t\tlista ramificata\tветвящийся список\t\tVerzweigte Liste\tقائمة متفرعة\tgreinar listi\tszétágazó lista\t\t\t\tlista rozgałęzień\t\t\t\tсписок-розгалуження\tlista de opções\nbrowser_chrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tChrome\tكروم\tchrome\tchrome\tChrome\tChrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\nbrowser_firefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tFirefox\tفيرفوكس\tfirefox\tfirefox\tFirefox\tFirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\nclose_label\tclose\tfermer\tfechar\tcerrar\tchiudi\tзакрыть\tstäng\tSchließen\tأغلق\tloka\tbezárás\tmalvidu\tsulge\t关闭\tzamknij\t\tipui\tzavřít\tзакрити\tfechar\ncondition_else_if_label\telse if\tsinon si\t\t\taltrimenti se\tиначе, если\t\tsonst - Wenn\telse if\tannars ef\tkülönben, ha\t\t\t\tw przeciwnym wypadku, jeżeli\t\t\t\tінакше, якщо\tou se\ncondition_else_label\telse\tsinon\t\t\taltrimenti\tиначе\t\tsonst\telse\tannars\tkülönben\t\t\t\tw przeciwnym wypadku\t\t\t\tінакше\tou\ncondition_if_label\tif\tsi\t\t\tse\tесли\t\tWenn\tif\tef\tha\t\t\t\tjeżeli\t\t\t\tякщо\tse\ncondition_then_label\tthen\tpuis\t\t\tallora\tзатем\t\tdann\tthen\tþá\takkor\t\t\t\twtedy\t\t\t\tто\tentão\ncondition_type_custom\tcustom\tcustomisée\tcostumizado\tpersonalizado\tpersonalizzato\tспециальное условие\tanpassa\tBenutzerdefiniert\tمعدل\tsérval\tegyedi\tlaŭmia\tmäära oma tingimus\t自定义\tużytkownika\tカスタム\thuloima\tvlastní\tвласний\tcustom\ncondition_type_default\tdefault\tpar défault\tpadrão\tpor defecto\tdefailt\tпо умолчанию\tstandard\tStandard\tافتراضي\tsjálfgefið\talapértelmezett\tnormala\tvaikevalik\t默认\tdomyślna\tデフォルト\tkwan\tvýchozí\tза замовчуванням\tpadrão\ncycle_list_description\trepeat items in a _:\t\t\t\tfrasi in _ che si ripetono:\tповторить объекты в _:\t\tAlle Blöcke werden in _ eingeblendet.\tتكرار البنود في _:\tendurtaktu hluti í _:\tismételd a tárgyakat ebben: _\t\t\t\tpowtarzaj pozycje zapętlone w _:\t\t\t\tповторювати пункти з _:\trepetir itens em _:\ncycle_list_name\tcycle list\tnom de la boucle\t\t\tlista serie\tсписок циклов\t\tSchleifen-Liste\tقائمة دائرية\tlotu listi\tismétlés lista\t\t\t\tlista cyklu\t\t\t\tсписок-цикл\tlista de ciclo\ncycle_name\tcycle\tboucle\t\t\tserie\tцикл\t\tSchleife\tدائري\tlota\tismétlés\t\t\t\tcykl\t\t\t\tцикл\tciclo\ndata_tool_name\tgame data\tdonnées du jeu\tdados do jogo\tdatos del juego\tdati del gioco\tигровые данные\tspeldata\tSpieldaten\tبيانات اللعبة\tleikjagögn\tjátékadatok\tludodosiero\tmängu andmed\t游戏数据\tdane\tゲームデータ\thihi kod\tkód hry\tдані гри\tcódigo de jogo\ndefault_end_dlg\tThe end\tFin\tFim\tFin\tFine\tконец\tslut\tEnde\tالنهاية\tEndir\tItt a vége, fuss el véle!\tLudofino\t\t结束\tKoniec\t\talta kopit hihi\t\tКінець\tFim\ndefault_item_dlg\tYou found a nice warm cup of tea\tVous avez trouvé une bonne tasse de thé chaud\tEncontraste um copo com chá quentinho\tEncontraste una buena taza de té\tHai trovato una buona tazza di tè caldo\tвы нашли чашку тёплого чая\tdu hittade en varm kopp te\tDu hast einen schönen warmen Tee gefunden\tلقد وجدت كوب دافئ من الشاي\tÞú fannst heitann bolla af te\tTaláltál egy bögre forró teát\tVi trovis belan, varman tason da teo\t\t你找到了一杯暖心的茶\tZnajdujesz kubek ciepłej herbaty\t\tnaika tlap 1 uskan wam tipso-chuk!\t\tВи знайшли чашку запашного гарячого чаю\tVocê encontrou um chá quentinho\ndefault_sprite_dlg\tI'm a cat\tJe suis un chat\tSou um gato\t\tSono un gatto\tя котейка\tjag är en katt\tIch bin eine Katze\tأنا قطة\tÉg er köttur\tÉn egy cica vagyok\tMi estas kato\t\t我是一只猫\tJestem kotem\t\tpuspus naika\tJá jsem kočka.\tЯ киця\tMeow. Que foi?\ndefault_title\tWrite your game's title here\tEcris le nom de ton jeu ici\tEscreve o título do teu jogo aqui\tEscribe el título de tú juego aqui\tScrivi qui il titolo del tuo gioco\tНапишите название вашей игры здесь\tSkriv spelets titel här\tHier solltest du deinen Spieltitel hinschreiben\tأكتب عنوان لعبتك هنا\tSkrifaðu heiti leiksins þíns hér\tÍrd ide a játékod címét\tSkribu vian ludonomon\tKirjuta oma mängu pealkiri siia\t在这里写下你的游戏标题\tWpisz tutaj tytuł swojej gry\tゲームのタイトルを入力\thihi nim\tZde napište název vaší hry.\tНапиши назву гри тут\tDigite o título do seu jogo aqui\ndestination_label\tdestination\tdestination\tdestino\tdestino\tdestinazione\tточка прибытия\tdestination\tAusgang\tالمقصد\tákvörðunarstaður\térkezés\tcelo\t\t目的地\tcel przejścia\t\tko\t\tпризначення\tdestino\ndialog_action_category_dialog\tdialog\tdialogue\t\t\tdialogo\tдиалог\t\tDialog\tحوار\tsamtal\tpárbeszéd\t\t\t\tdialog\t\t\trozhovor\tдіалог\tdiálogo\ndialog_action_category_exit\texit and ending actions\t\t\t\tazioni di uscita e finale\tвыход и завершение действий\t\tAktionen: Übergänge & Enden\tتصرفات الخروج و النهاية\taðgerðir fyrir útgöngleiðir og endalok\tkijárat és befejezés akciók\t\t\t\takcje przejścia i zakończenia\t\t\t\tдії виходу та закінчень\tações de saída e final\ndialog_action_category_item\titem and variable actions\t\t\t\tazioni di variabili e oggetti\tдействия с объектами и переменными\t\tAktionen: Objekte & Variablen\tتصرفات الأدوات و المتغيرات\taðgerðir fyrir hluti og breytur\ttárgy és változó akciók\t\t\t\takcje przedmiotów i zmiennych\t\t\t\tдії предметів та змінних\tações de item e variável\ndialog_action_category_list\tlists\tlistes\t\t\tliste\tсписки\t\tListen\tقائمة\tlistar\tlisták\t\t\t\tkategorie list\t\t\tseznam\tсписки\tlistas\ndialog_action_item_decrease\tdecrease item count\t\t\t\tdiminuisci conteggio oggetti\tуменьшить счётчик объекта\t\tObjekt-Anzahl verringern\tتقليص عدد الأدوات\tfækka tölu hluta\ttárgyak számának csökkentése\t\t\t\tzmniejsz liczbę przedmiotów\t\t\t\tзменшити кількість предмету\tdiminuir itens\ndialog_action_item_increase\tincrease item count\t\t\t\taumenta conteggio oggetti\tувеличить счётчик объекта\t\tObjekt-Anzahl erhöhen\tزيادة عدد الأداوات\tfjölga tölu hluta\ttárgyak számának növelése\t\t\t\tzwiększ liczbę przedmiotów\t\t\t\tзбільшити кількість предмету\taumentar itens\ndialog_action_item_set\tset item count\t\t\t\timposta conteggio oggetti\tзадать значение счётчика\t\tObjekt-Anzahl festlegen\tتعيين عدد الأدوات\tsetja tölu hluta\ttárgyak számának beálltása\t\t\t\tustaw liczbę przedmiotów\t\t\t\tвстановити кількість предмету\tdefinir itens\ndialog_action_locked_set\tlock / unlock\tverrouiller/déverrouiller\t\t\tblocca/sblocca\tзаблокировать / разблокировать\t\tsperren / entsperren\tقفل / فتح\tlæsa / aflæsa\tnyitás / zárás\t\t\t\tzablokuj / odblokuj\t\t\t\tзамкнути / відімкнути\ttrancar / destrancar\ndialog_action_variable_change\tchange variable value\t\t\t\tcambia valore variabile\tизменить значение переменной\t\tVariablen-Wert ändern\tتغيير قيمة المتغير\tbreyta gildi í breytu\tváltozó értékének módoítása\t\t\t\tzmień wartość zmiennej\t\t\t\tзмінити значення змінної\tmudar valor de variável\ndialog_action_variable_set\tset variable value\t\t\t\timposta valore variabile\tзадать значение переменной\t\tVariablen-Wert festlegen\tتعيين قيمة المتغير\tsetja gildi í breytu\tváltozó értékének beálltása\t\t\t\tustaw wartość zmiennej\t\t\t\tвстановити значення змінної\tdefinir valor de variável\ndialog_block_basic\tdialog\tdialogue\tdiálogo\tdiálogo\tdialogo\tфаза\tdialog\tDialog\tحوار\tsamtal\tpárbeszéd\tparolo\tdialoog\t对话\tdialog\tダイアログ\twawa\trozhovor\tдіалог\tdiálogo\ndialog_block_conditional\tconditional\tconditions\tcondicional\tcondicional\tcondizionale\tусловие\tvillkor\tBedingungen\tشرطي\tskilyrt\tfeltételes\tse...do kodo\ttingimusega\t条件\twarunek\t条件\tspos\tpodmínka\tумова\tcondicional\ndialog_block_list\tlist\tliste\tlista\tlista\tlista\tсписок\tlista\tListe\tقائمة\tlisti\tlista\tlisto\tlist\t列表\tlista\tリスト\tlist\tseznam\tсписок\tlista\ndialog_block_new\tnew section\tnouvelle section\tnova secção\tnueva sección\tnuova sezione\tновая секция\tny replik\tNeuer Absatz\tقسم جديد\tnýr hluti\túj szakasz\tnovan parton\tlisa dialoogi kast\t新部分\tnowa sekcja\t新規セクション\tchi wawa sitkum\tnová část\tновий розділ\tnova seção\ndialog_conditional_add\tadd option\tajouter une option\tadicionar opção\tañadir opción\taggiungi opzione\tдобавить опцию\tlägg till alternativ\tOption hinzufügen\tإضافة أختيار\tnýr liður\topció hozzáadása\taldonu elekton\tlisa võimalus\t加入选项\tdodaj opcję\tオプション追加\tchi spos\t\tдодати опцію\tcolocar opção\ndialog_conditional_when\twhen\tquand\tquando\tcuando\tquando\tкогда\tom\tWenn\tمتى\tþegar\tha\tse\tkui\t当\tjeżeli\tいつ\tspos\t\tколи\tquando\ndialog_effect_color1\tcolor 1\tcouleur 1\tcôr 1\tcolor 1\tcolore 1\tцвет 1\tfärg 1\tFarbe 1\tلون ١\tlitur 1\tszín 1\tkoloro 1\tvärv 1\t颜色1\tkolor 1\t色1\ttsum 1\tbarva 1\tколір 1\tcor 1\ndialog_effect_color2\tcolor 2\tcouleur 2\tcôr 2\tcolor 2\tcolore 2\tцвет 2\tfärg 2\tFarbe 2\tلون ٢\tlitur 2\tszín 2\tkoloro 2\tvärv 2\t颜色2\tkolor 2\t色2\ttsum 2\tbarva 2\tколір 2\tcor 2\ndialog_effect_color3\tcolor 3\tcouleur 3\tcôr 3\tcolor 3\tcolore 3\tцвет 3\tfärg 3\tFarbe 3\tلون ٣\tlitur 3\tszín 3\tkoloro 3\tvärv 3\t颜色3\tkolor 3\t色3\ttsum 3\tbarva 3\tколір 3\tcor 3\ndialog_effect_drawing\tinsert drawing\tinsérer un dessin\t\t\tinserisci disegno\tвставить рисунок\t\tZeichnung einfügen\tإضافة رسم\tsetja inn teikningu\trajz beillesztése\t\t\t\twstaw rysunek\t\t\t\tвставити малюнок\tinserir desenho\ndialog_effect_new\ttext effects\teffets de texte\tefeitos do texto\tefectos de texto\teffetti di testo\tэффекты текста\ttexteffekter\tText Effekte\tمؤثرات كتابية\ttextaáhrif\tszövegeffektek\tskribostiloj\tteksti efektid\t文字效果\tefekt tekstu\tテキストエフェクト\tala pipa\ttextové efekty\tефекти тексту\tefeitos de texto\ndialog_effect_rainbow\trainbow\tarc-en-ciel\tarco-íris\tarcoiris\tarcobaleno\tрадужный\tregnbåge\tRegenbogen\tقوس قزح\tregnbogi\tszivárványos\tmultkolora\tvikerkaar\t彩虹\ttęcza\tレインボー\tolawtet\tduha\tвеселка\tarco-íris\ndialog_effect_shaky\tshaky\ttremblement\ttremido\ttembloroso\ttremante\tдрожащий\tskakig\tzitternd\tمهزوز\thristist\tremegő\tmovada\tvärisev\t晃动\tdrżenie\t振動\tshuk-shuk\ttřas\tтремтіння\ttremer\ndialog_effect_wavy\twavy\tvague\tondulado\tondulado\tondulato\tволнистый\tvågor\twellig\tمموج\tbylgjað\thullámos\tondeca\tlainetav\t波纹\tfala\t波紋\thantli\tvlnění\tволна\tondular\ndialog_hide_code\thide code\tcacher le code\tesconder código\tesconder código\tnascondi codice\tспрятать код\tgöm kod\tCode verbergen\tأخفي شفرة البرنامج\tfela kóða\tforráskód elrejtése\tmalvidu kodon\tpeida kood\t隐藏代码\tukryj kod\tコード格納\tipui kod\tskrýt kód\tприховати код\tesconder código\ndialog_list_add\tadd line\tajouter une ligne\tadicionar fala\tañadir línea\taggiungere frase\tдобавить линию\tlägg till replik\tZeile hinzufügen\tإضافة سطر\tný setning\tsor hozzáadása\taldonu skribon\tlisa rida\t加入行\tdodaj linijkę\t行の追加\tchi layn\tpřidat řádek\tдодати рядок\tcolocar linha\ndialog_list_order\torder:\tordre :\tordem:\torden:\tordine\tпорядок:\tordning:\tReihenfolge\tالترتيب:\tröð:\tsorrend:\tordo:\tjärjekord:\t次序:\tkolejność:\t順序:\tiht-iht:\tpořadí:\tпорядок:\tordem:\ndialog_no_selection\tSelect a sprite or item to edit its dialog.\tsélectionner un sprite ou un item pour éditer ses lignes de dialogue\tseleciona um sprite ou item para editares o seu diálogo\tSelecciona un sprite u objeto para editar este diálogo\tSeleziona uno sprite per modificare questo dialogo\tВыберете спрайт или объект, чтобы редактировать прикреплённый диалог.\tvälj en sprite eller föremål för att ändra dess dialog\tWähle einen Sprite oder ein Objekt aus, um den Dialog zu editieren.\tأختار رسمة تفاعلية او أداة لتعديل الحوار المرتبط بها.\tVeldu sprota eða hlut til að breyta þessu samtali.\tVálassz ki egy sprite-ot vagy tárgyat.\telektu enludanon aŭ objekton por redakti ties parolon.\tVali kuju või ese, et muuta selle dialoogi.\t选择一个精灵或物品编辑它的对话。\tWybierz sprite lub przedmiot żeby edytować.\tスプライトもしくはアイテムを選択してそのダイアログを編集\tpik iktas pi tilikum kopa tsum tlaska wawa.\tVyberte objekt nebo předmět a editujte daný rozhovor.\tВиберіть спрайт або предмет, щоб редагувати його діалог.\tSelecione um ator ou item para editar o seu diálogo.\ndialog_show_code\tshow code\tafficher le code\tmostrar códiogo\tmostrar código\tmostra codice\tпоказать код\tvisa kod\tCode zeigen\tأظهر شفرة البرنامج\tsýna kóða\tforráskód mutatása\tvidu kodon\tnäita koodi\t显示代码\tpokaż kod\tコード表示\tnanich kod\tzobrazit kód\tпоказати код\tmostrar código\ndialog_start_preview\tpreview\taperçu\tpré-vizualizar\tprevisualizar\tanteprima\tпредпросмотр\tförhandsvisning\tVorschau\tمعاينة\tsýnishorn\telőnézet\tantaŭvidu\teelvaade\t预览\tpodgląd\tプレビュー\ttenas nanich\tnáhled rozhovoru\tперегляд\tprévia\ndialog_tool_name\tdialog\tdialogue\tdiálogo\tdiálogo\tdialogo\tдиалог\tdialog\tDialoge\tحوار\tsamtöl\tpárbeszéd\tskribejo\tdialoog\t对话\tdialogi\tダイアログ\twawa\trozhovor\tдіалог\tdiálogo\ndownload_data\tdownload data\ttélécharger les données\tdescarregar data\tdescargar data\tscarica dati\tскачать данные\tladda ned data\tDaten herunterladen\tتحميل المعلومات\thala niður gögnum\tadatok letöltése\tprenu dosieron\tlae alla andmed\t下载数据\tpobierz dane\tデータをダウンロード\tiskum hihi kod\tstáhnout kód\tзавантажити дані\tbaixar código\ndownload_font\tdownload font\ttélécharger la police\tdescarregar fonte\tdescargar fuente\tscarica font\tскачать шрифт\tladda ned typsnitt\tSchriftart herunterladen\tتحميل الخط\thala niður leturgerð\tbetűtípus letöltése\tprenu tiparon\tlae alla font\t下载字体\tpobierz czcionkę\tフォントをダウンロード\tiskum .bitsyfont\tstáhnout font\tзавантажити шрифт\tbaixar fonte\ndownload_game\tdownload game\ttélécharger le jeu\tdescarregar jogo\tdescargar juego como html\tscarica gioco\tскачать игру\tladda ned\tSpiel herunterladen\tتحميل اللعبة\thala niður leik\tjáték letöltése\tprenu ludon\tlae alla mäng\t下载游戏\tpobierz grę\tゲームをダウンロード\tiskum hihi\tstáhnout hru\tзавантажити гру\tbaixar o jogo\ndownload_html\tdownload game as html file:\ttélécharger en fichier html\tdescarregar jogo como ficheiro html:\tdescargar juego como html\tscarica il gioco come html\tскачать игру как html файл:\tladda ned spelet som html-fil:\tLade das Spiel als HTML Datei herunter:\tتحميل اللعبة كملف html\thala niður leik sem html skjal\tjáték letöltése html fájlként:\tprenu la ludon kiel HTML-dosieron:\tlae alla mäng html failina:\t下载游戏为 html 文件:\tpobierz grę jako plik html:\tゲームをhtmlファイルとしてダウンロード\tiskum hihi kakwa .html:\tstáhnout hru v .html\tзавантажити гру як файл HTML:\tbaixar o jogo como arquivo html:\ndownload_tool_name\tdownload\ttélécharger\tdescarregar\tdescargar\tscarica\tскачать\tladda ned\tHerunterladen\tتحميل\tniðurhal\tletöltés\tprenu (elŝutu)\tlae alla\t下载\tpobierz\tダウンロード\tmunk-iskum hihi\tstáhnout\tзавантажити\tbaixar\neditor_settings\teditor settings\tparamètres de l'éditeur\tdefinições do editor\tajustes del editor\timpostazioni dell'editor\tнастройки редактора\teditor-inställningar\tEditor-Einstellungen\tإعدادات المحرر\tstillingar á tóli\tszerkesztő beállításai\tkreila elektaro\tbitsy seaded\t编辑器设置\tustawienia edytora\tエディター設定\tedita setins\tnastavení editoru\tналаштування редактору\tdefinições de editor\nending_dialog\tending dialog\tfin de dialogue\tdiálogo de final\tdialógo de final\tdialogo del finale\tтекст концовки\tslut-dialog\tEnd-Nachricht\tحوار النهاية\tenda samtal\tbefejező párbeszéd\tfinluda parolo\t\t结局对话\tdialog zakończenia\t\tkopit-hihi taim wawa\t\tдіалог закінчення\tdiálogo de final\nending_label\tending\tfin\tfinal\tfinal\tfinale\tконцовка\tslut\tEnde\tنهاية\tendir\tbefejezés\tludofino\tlõpp\t结局\tzakończenie\tエンディング\tkopit-skwil\tkonec hry\tзакінчення\tfinal\nending_place\tplace ending\tajouter une fin\tcolocar final\tcolocar final\tpiazza finale\tрасположить концовку\tplacera slut\tPlatziere Ende\tمكان النهاية\tstaðsetja enda\tbefejezés elhelyezése\tmetu finon\tpaiguta lõpp\t安排结局\tumieść zakończenie\tエンディング配置\tmunk-mitlait kopit-skwil\tumístit konec hry\tрозмістити закінчення\tcolocar final\nending_place_help\tclick space in room to add ending\tcliquez une case dans la salle pour ajouter une fin\tclica num espaço da sala para adicionar um final\thaz click en un espacio de la sala para añadir un final\tfai click nella stanza per aggiungere un finale\tщёлкните на точку пространстве, чтобы добавить концовку\tklicka någonstans i rummet för att lägga till ett slut\tKlicke einen Bereich in einem Raum an, um ein Ende zu platzieren\tحدد فراغ في الغرفة لوضع النهاية\tsmelltu á reit í rýminu til að bæta við enda\tkattints valahova a szobában a befejezés elhelyezéséhez\tklaku enĉambre por aldoni ludofinon\tvali toa ruut kuhu paigutada lõpp\t在房间中选中格子增加结局\tkliknij na wolnym miejscu w pomieszczeniu, aby dodać zakończenie\tルームの空いている場所をクリックしてエンディングを追加\tkwutl speis spos maika tiki munk-mitlait kopit-skwil\tvložte konec hry kliknutím do prostředí místnosti\tклацни місце, де буде закінчення\tclique na cena para colocar um final\nending_remove\tremove selected ending\tretirer la fin sélectionnée\tremover final selecionado\teliminar el final seleccionado\tcancella il finale selezionato\tубрать выбранную концовку\tta bort det valda slutet\tEntferne ausgewähltes Ende\tامسح النهاية المختارة\tfjarlæga valinn enda\tkiválasztott befejezés törlése\tforigu ĉi ludofinon\tkustuta valitud lõpp\t移除已选择的结局\tusuń wybrane zakończenie\t選択中のエンディングを削除\tmunk-heilo okok kopit-skwil\todstranit vybraný konec hry\tприбрати обране закінчення\ttirar final selecionado\nendings_tool_name\tendings\tfins\tfinais\tfinales\tfinali\tконцовки\tslut\tEnden\tنهايات\tendar\tbefejezések\tludofinoj\tlõpud\t结局\tzakończenia\tエンディング\tkopit\tkonce hry\tзакінчення\tfinais\nexit_click_add\tclick space in room to add exit\tCliquer un espace dans la salle pour ajouter une sortie\tclica num espaço da sala para adicionar uma saída\thaz click en un espacio de la sala para añadir una salida\tclicca nella stanza per aggiungere l'uscita\tкликните в панели пространства, чтобы добавить точку выхода\tklicka i rummet för att lägga till en utgång\tDrücke Leertaste im Raum um einen Ausgang hinzuzufügen\tحدد فراغ في الغرفة لوضع النهاية\tsmelltu á reit í rýminu til að bæta við útgönguleið\tkattints valahova a szobában a kijárat elhelyezéséhez\tklaku spacon enĉambre por aldoni pordon\t\t在房间中选中格子增加出口\tkliknij na wolnym miejscu w pomieszczeniu, aby dodać przejście\t\tspos maika tiki mash lapot, kwutl skwil (lapot-ilahi) kopa loom.\tvložte průchod kliknutím do prostředí místnosti\tклацни місце, де буде вихід\tclique na cena para colocar uma saída\nexit_delete\tdelete selected exit\tsupprimer la sortie sélectionnée\tapagar saída selecionada\tborrar salida seleccionada\tcancella uscita selezionata\tудалить выбранную точку выхода\tta bort den valda utgången\tAusgewählten Ausgang löschen\tإزالة المخرج المختار\teyða valdri útgönguleið\tkiválasztott kijárat törlése\tforigu ĉi pordon\tkustuta valitud väljapääs\t删除已选中的出口\tusuń wybrane przejście\t選択中の出口の削除\tmunk-heilo okok lapot\tsmazat vybraný průchod\tприбрати обраний вихід\ttirar saída selecionada\nexit_destination\tthis exit goes to\tcette sortie va à\testa saída leva-te a\testa salida te lleva a\tquesta uscita ti porta a\tэта точка выхода ведёт к\tdenna utgång leder till\tDieser Ausgang führt zu\tهذا المخرج يؤدي إلى\tþessi útgönguleið fer til\tkijárat ide\tĉi pordo iras al\tsee väljapääs viib\t这个出口去向\tto przejście prowadzi do\tこの出口はここに向かいます\tokok lapot tlatawa\ttento průchod vede do\tцей вихід прямує до\tessa saída sai em\nexit_destination_move\tmove destination\tdéplacer la destination\t\t\tmuovi destinazione\tпереместить точку прибытия\t\tZiel verschieben\tتحريك المقصد\tfæra ákvörðunarstað\térkezés mozgatása\t\t\t\tzmień pozycję docelową\t\t\t\tперемістити призначення\tmover destino\nexit_dialog\texit dialog\tfermer le dialogue\t\t\tinterrompi il dialogo\tвыход из диалога\t\tDialog\tحوار يحدث عند الخروج\tljúka samtali\tkilépés a párbeszédből\t\t\t\tdialog przejścia\t\t\t\tдіалог виходу\tsair de diálogo\nexit_dialog_lock_add\tadd lock\tajouter un verrou\t\t\tAggiungi blocco\tдобавить блокировку\t\tSperre\tاضافه قفل\tbæta við lás\tzár hozzáadása\t\t\t\tdodaj blokadę\t\t\t\tдодати замок\ttrancar\nexit_dialog_lock_default_text1\tThe key opens the door!\tLa clé ouvre la porte!\t\t\tLa chiave apre la porta!\tЭтот ключ открывает дверь!\t\tDer Schlüssel öffnet die Tür!\tالمفتاح يستعمل لفتح الباب!\tLykillinn opnar hurðina!\tA kulcs nyitja az ajtót.\t\t\t\tKlucz otwiera drzwi!\t\t\t\tКлюч відчиняє двері!\tA chave abre a porta!\nexit_dialog_lock_default_text2\tThe door is locked...\tLa porte est verrouillée...\t\t\tLa porta è chiusa a chiave...\tДверь заперта...\t\tDie Tür ist verschlossen...\tالبال مقفل بإحكام ...\tHurðin er læst...\tAz ajtó zárva...\t\t\t\tDrzwi są zamknięte na klucz…\t\t\t\tДвері зачинені…\tA porta está trancada...\nexit_dialog_lock_name\tlocked exit\tsortie verrouillée\t\t\tUscita bloccata\tЗаблокированный выход\t\tGesperrter Übergang\tمخرج مقفل\tlæst útgönguleið\tzárt kijárat\t\t\t\tzablokowane przejście\t\t\t\tзамкнений вихід\tsaída trancada\nexit_dialog_narration_add\tadd narration\t\t\t\taggiungi narrazione\tдобавить рассказ\t\tErzählung\tإضافة سرد\tbæta við frásögn\tnarráció hozzáadása\t\t\t\tdodaj narrację\t\t\t\tдодати розповідь\tcolocar narração\nexit_dialog_narration_default_text\tYou walk through the doorway\t\t\t\tAttraversi la porta\tТы проходишь через дверной проём\t\tDu gehst durch die Tür\tأنت تمشي عبر المدخل\tÞú gengur í gegnum dyragáttina\tÁtmész az ajtón\t\t\t\tPrzechodzisz przez drzwi\t\t\t\tВи проходите через дверний отвір\tVocê entrou pela porta\nexit_dialog_narration_name\texit narration\t\t\t\tinterrompi narrazione\tзавершающий рассказ\t\tÜbergangs-Erzählung\tسرد يحدث عند الخروج\tútgangs frásögn\tkilépés a narrációból\t\t\t\tnarracja przejścia\t\t\t\tрозповідь при виході\tsair da narração\nexit_label\texit\tsortie\tsaída\tsalida\tuscita\tточка выхода\tutgång\tEingang\tمخرج\tútgönguleið\tkijárat\tpordo\t\t退出\tprzejście\t\tlapot\tprůchod\tвихід\tsair\nexit_new\tplace new exit\tajouter une nouvelle sortie\tcolocar uma nova saída\tcoloca una nueva salida\tpiazza una nuova uscita\tрасположить новую точку выхода\tplacera ny utgång\tPlatziere neuen Ausgang\tوضع مخرج جديد\tsetja nýja útgönguleið\túj kijárat elhelyezése\tmetu novan pordon\taseta uus väljapääs\t放置新出口\tumieść nowe przejście\t新規出口の配置\tmunk-mitlait chi lapot\tumístit nový průchod\tдодати новий вихід\tcolocar uma nova saída\nexit_one_way_label\tone-way exit\t\t\t\tuscita a senso unico\tвыход в одну сторону\t\tEinseitiger Übergang\tمخرج بإتجاة واحد\teinstefnu útgönguleið\tegyirányú kijárat\t\t\t\twyjście w jedną stronę\t\t\t\tодносторонній вихід\tsaída de um lado\nexit_options\texit options\toptions de sortie\topções de saída\topciones de salida\topzioni dell'uscita\tопции точки выхода\tutgångs-inställningar\tÜbergangs-Optionen\tخيارات المخرج\tútgöngu valmöguleikar\tkijárat beállítások\tpordaj elektaroj\t\t对出选项\topcje przejścia\t\tkata lapot?\t\tопції виходу\topções de saída\nexit_return_label\treturn exit\t\tsaída de retorno\tregresar salida\tuscita di ritorno\tточка возвращения\tretur\tAusgang\tإعادة المخرج\tbaka til útgangs\tvisszaút\trevenila pordo\t\t返回退出\tprzejście powrotne\t\tkilapi lapot\t\tзворотній вихід\tsaída de retorno\nexit_selected\tselected exit\tsortie sélectionnée\tsaída selecionada\tsalida seleccionada\tuscita selezionata\tвыбрать точку выхода\tvald utgång\tAusgewählter Ausgang\tالمخرج المختار\tvalin útgönguleið\tkiválasztott kijárat\tĉi pordo\thetkel valitud väljapääs\t已选中的出口\twybrane przejście\t出口選択中\tokok lapot\tvybraný průchod\tобраний вихід\tsaída selecionada\nexit_selected_none\tno exit selected\tpas de sortie sélectionnée\tnão há uma saída selecionada\tno has seleccionado una salida\tnon hai selezionato nessuna uscita\tни одна точка выхода не выбрана\tingen vald utgång\tKein Ausgang ausgewählt\tلم يتم إختيار مخرج\tengin útgönguleið valin\tnincs kijárat kiválasztva\telektu pordon\thetkel pole ühtki väljapääsu valitud\t没有出口被选中\tnie wybrano żadnego przejścia\t出口未選択\tikta lapot?\tprůchod není vybrán\tвихід не обраний\tnenhuma saída selecionada\nexit_transition_label\ttransition effect\teffet de transition\tefeito de transição\tefectos de transición\teffetto di transizione\tэффект перехода\tövergångseffekt\tÜbergangs-Effekt\tالمؤثرات الإنتقالية\tumskipti\táttűnések\tporda-al-porda stilo\t\t转场效果\tefekt przejścia\t\tkata tlatawa chi loom\t\tефект переходу\tefeito de transição\nexits_tool_name\texits\tsorties\tsaídas\tsalidas\tuscite\tточки выхода\tutgång\tÜbergänge\tمخارج\tútgangar\tkijáratok\tpordoj\tväljapääsud\t出口\tprzejścia\t出口\tlapot\tprůchody\tвиходи\tsaídas\nexplorer_tool_name\tfind drawing\ttrouver un dessin\tprocurar desenho\tbuscar dibujo\tcerca disegno\tнайти рисунок\thitta bilder\tAlle Zeichnungen\tأبحث عن رسم\tteikningar\trajz keresése\ttrovu desegnon\totsi joonistusi\t素材库\tznajdź rysunek\t絵を探す\ttlap picha\tvyhledat objekt\tзнайти малюнок\tencontrar desenho\nexpression_builder_all_clear\tAC\t\t\t\tC\tочистить всё\t\tDEL\tAC\tAC\tösszes törlése\t\t\t\tAC\t\t\t\tAC\tAC\nfilter_placeholder\tfilter drawings\tFiltrer les dessins\tfiltrar desenho\tfiltrar dibujos\tFiltra i disegni\tвведите название рисунка\tsök bilder\tSuche Zeichnungen..\tصفي الرسومات\tfinna teikningar\trajz keresése\tforelektu desegnojn\t\t过滤图画\tfiltruj rysunki\t\ttlap picha\t\tфільтрувати малюнки\tfiltrar desenhos\nfont_arabic\tArabic\tArabe\tÁrabe\tarábico\tArabo\tарабский шрифт\tArabiska\tArabische Schrift\tالعربية\tArabískt\tarab\tla araba\t\tArabic\tArabska\t\talab wawa\t\tArabic\tArábico\nfont_arabic_description\tPixel font with Arabic characters\tPolice d'écriture avec l'alphabet arabe\tfonte pixelizada com caracteres árabes\tFuente pixel con carácteres arábicos.\tFont pixel con caratteri arabi\tописание арабского шрифта\tPixel-typsnitt med Arabiska tecken\tPixel-Schrift mit arabischen Buchstaben\tخط بكسل يحتوي على الحروف العربية\tSmátt letur sem inniheldur stafi fyrir arabísku.\tpixelbetűtípus arab karakterekkel\ttiparo por la araba lingvo\t\t使用 Arabic 字母的像素字体\tCzcionka pikselowa ze znakami Arabskimi\t\tpiksel pipa-tsum kopa alab wawa\t\tПіксельний шрифт з арабськими літерами\tFonte pixel com caractéres arábicos\nfont_ascii_small\tASCII Small\tASCII - petit\tASCII - pequeno\tASCII - pequeño\tASCII piccolo\tASCII Small\tASCII Small\tASCII Small\tASCII صغير\tASCII smátt\tASCII kicsi\tASCII Eta\tASCII Väike\tASCII Small\tASCII Mała\tASCII Small\ttenas ASCII\tASCII malé\tASCII Small\tASCII Small\nfont_ascii_small_description\tSmall font limited to ASCII, which includes English characters and some symbols.\tPetite police limitée à l'ASCII, incluant les caractères anglais et quelques symboles.\tFonte pequena limitada a ASCII que inclui caracteres ingleses e pequenos símbolos\tLa fuente pequeña sólo sirve para carácteres ASCII, que incluye los carácteres del inglés y algunos símbolos.\tFont piccolo, limitato all'ASCII, include i caratteri dell'inglese e alcuni simboli.\tмелкий шрифт только для ASCII, включая английские буквы и некоторые символы\tLitet ASCII-typsnitt, innehåller engelska bokstäver och vissa tecken\tKleine Schriftart, auf ASCII limitiert (englische Zeichen).\tخط صغير مقتصر على الـASCII، الذي يحتوي على الحروف الأنجليزية و بعض الرموز.\tSmátt letur sem styður ASCII stafi. ASCII staðallinn inniheldur enska stafi og algeng tákn.\tKisméretű betűtípus az angol ábécé betűivel és néhány speciális karakterrel\tMalgranda dosiero, nur anglaj literoj de ASCII. Ne ekzistas esperantaj (ĉapelitaj) literoj.\tVäike font ASCII piires, mis sisaldab Inglise keel tähti ja sümboleid.\t受 ASCII 限制的小字体,包含英文字母和一些符号。\tMała czcionka ograniczona do ASCII, angielskie litery i niektóre symbole.\t英語といくつかの記号を含む、ASCIIに限られる小さいフォント\twik hayu pipa-tsum. kopit tlosh spos maika tiki kopit ASCII tsum (pastin wawa pi wik-hayu huloima tsum).\t\tМаленький шрифт, обмежений ASCII, тобто англійські літери та ще деякі.\tPequena fonte limitada a ASCII, incluindo alguns carácteres e símbolos em inglês.\nfont_custom_description\t\"Upload your own custom font in the \"\".bitsyfont\"\" format!\"\t\"Importe ta propre police customisée dans le format \"\".bitsy\"\"!\"\t\"Envia a tua própria fonte costumizada no formato \"\".bitsyfont\"\"\"\tSube tú fuente personalizada en el formato “.bitsyfont”!\t\"Carica il tuo font personale nel formato \"\".bitsyfont\"\"\"\t\"загрузить собственный шрифт в формате \"\".bitsyfont\"\".\"\t\"Ladda upp ett eget typsnitt med filformatet \"\".bitsyfont\"\"\"\t\"Lade deine eigene Schriftart im \"\".bitsyfont\"\" Format hoch!\"\t\"أرفع خطك المعدل من ملف \"\"bitsyfont.\"\"!\"\t\"Notaðu þína eigin leturgerð með því að hlaða inn \"\".bitsyfont\"\" skrá!\"\t\"Töltsd fel a saját betűtípusodat \"\".bitsyfont\"\" formátumban!\"\tUzu vian propran .bitsyfont-dosieron kiel tiparo!\t\"Lae üles oma font \"\".bitsyfont\"\" formaadis!\"\t上传你的定制字体,请用“.bitsyfont”格式!\t\"Wyślij swoją własną czcionkę w formacie \"\".bitsyfont\"\"!\"\t\"自分のカスタムフォントを \"\".bitsyfont\"\"形式でアップロード!\"\t\"mash yakwa maika huloima (\"\"custom\"\") pipa-tsum (\"\"font\"\") kakwa \"\".bitsyfont\"\"!\"\tДодайте власний шрифт в форматі .bitsyfont!\t\t\"Envie a sua própria fonte no formato \"\".bitsyfont\"\"!\"\nfont_data_toggle_visible\tfont data\tDonnée de police d'écriture\tdata da fonte\tdatos de la fuente\tdati del font\tданные шрифта\ttypsnittsdata\tSchrift Daten sichtbar\tبيانات الخط\tsýna letur upplýsingar\tbetűtípus adatok\ttipara dosiero\t\t字体数据\tdane czcionki\t\tpipa-tsum kod\tfont (kód)\tдані шрифту\tdados de fonte\nfont_label\tfont\tpolice\tfonte\tfuente\tfont\tшрифт\ttypsnitt\tSchriftart\tالخط\tleturgerð\tbetűtípus\ttiparo\tfont\t字体\tczcionka\tフォント\tpipa setins\tfont\tшрифт\tfonte\nfont_missing_character\tThe current font is missing some of the characters used in your dialog. Not all text in your game will display correctly. Consider picking a different font in the settings.\tCertains caractères utilisés dans tes dialogues ne sont pas présents dans la police d'écriture sélectionnée. Certains textes ne s'afficheront pas correctement. Peut-être devrais-tu changer de police d'écriture dans les paramètres.\tA fonte actual não possui alguns dos caracteres usados no teu diálogo. Partes do texto no teu jogo podem não aparecer correctamente. Considera escolher uma fonte diferente nas definições.\tLa fuente actual no incluye algunos de los carácteres usados en tú dialógo. No todo el texto de tú juego se va a mostrar correctamente. Considera elegir una fuente distinta en las opciones.\tAl font attuale mancano alcuni caratteri usati nel tuo dialogo. Il testo del tuo gioco non apparità correttamente. Prova a scegliere un font diverso nelle impostazioni.\tвыбранный шрифт не поддерживает некоторые символы, использованные в диалогах. не весь текст в игре будет отображён правильным образом. вы можете выбрать другой шрифт в настройках.\tDet valda typsnittet saknar vissa tecken du har använt. En del av texten kommer inte att se rätt ut. Ett annat typsnitt kan väljas i inställningarna.\tDie aktuelle Schriftart unterstützt nicht alle Zeichen aus deinem Dialog. Das kann merkwürdig aussehen. Wähle lieber eine andere Schriftart in den Einstellungen.\tهذا الخط ينقصة بعض الرموز التي تستخدمها في حوارك. بعض الجمل قد تظهر بشكل خاطئ. يفضل أن تختار خط أخر من الإعدادات\tValið letur inniheldur ekki alla stafi sem koma fyrir í textanum þínum. Stafir sem eru ekki til staðir verða ekki sýnilegir í leiknum. Þú getur prófað að velja annað letur í leikjastillingunum.\tA kiválasztott betűtípus nem tartalmazza az összes betűt, amit használsz. Válassz egy másik betűtípust a beálllításoknál!\tLa nuna tiparo mankas kelkajn literojn de viaj skriboj, do kiam la ludo ludiĝas oni ne vidos ĉion el via skribo. Pensu pri elekti alian tiparon je la elektaro.\tValitud fondil puudub mõned tähed, mida oled kasutanud oma dialoogis. Osa sinu mängu tekstist ei ilmu õigesti. Ehk tahad valida teist fonti seadetes.\t使用在你对话中的当前字体中有一些字母丢失。你的游戏中的文字不能完全显示正确。请考虑在设置中选一个不同的字体。\tBieżąca czcionka nie posiada niektórych znaków, użytych w twoim dialogu. Nie wszystkie teksty w twojej grze zostaną wyświetlone prawidłowo. Rozważ wybranie innej czcionki, w panelu ustawienia.\t\talta maika tsum pipa kopa maika hihi, pi olo iht tsum (character) kopa maika pipa-tsum (font). spos tilikum nanich maika hihi, wik kata nanich kanawei maika pipa. tlosh spos maika pik huloima pipa-tsum.\t\tУ поточному шрифті відсутні деякі символи з ваших діалогів. Не весь текст гри буде відображений коректно. Радимо обрати інший шрифт.\tA fonte atual está sem alguns dos caractéres usados em seu diálogo. Nem todo o texto no seu jogo será mostrado corretamente. Tente escolher outra fonte nas definições.\nfont_unicode_asian\tUnicode Asian\tUnicode asiatique\tUnicode Asiático\tUnicode asiático\tUnicode Asiatico\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode أسيوي\tUnicode asískt\tUnicode ázsiai\tUnikodo Azia\tUnicode Aasia\tUnicode Asian\tUnicode Azjatycka\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode Asiático\nfont_unicode_asian_description\tLarge font which includes characters for Asian languages such as Chinese, Japanese, and Korean.\tLarge police incluant les caractères de langues asiatiques, comme le chinois, le japonais ou le coréen.\tFonte grande que inclui caracteres para linguagens asiáticas como o Chinês, o Japonês e o Coreano.\tFuente grande que incluye carácteres para lenguajes asiáticos como chino, japonés y coreano.\tFont grande, include i caratteri per lingue asiatiche quali cinese, giapponese, coreano\tкрупный шрифт, включающий символы из китайского, японского, корейского и других языков Азии.\tStort typsnitt med tecken för Asiatiska språk inklusive Kinesiska, Japanska, och Koreanska\tGroße Schriftart, unterstützt Asiatische Zeichen für z.B. Japan, China, Korea.\tخط كبير يحتوي على حروف من اللغات الأسيوية مثل الصينية، اليابانية، و الكورية.\tStórt letur sem inniheldur stafi fyrir asísk tungumál svo sem kínversku, japönsku, og kóresku.\tNagyméretű betűtípus, tartalmazza a legtöbb ázsiai nyelv karaktereit, mint a kínai, japán és a koreai.\tGranda dosiero, ekzistas literoj por aziaj lingvoj kiel la ĉina, japana kaj korea, ankaŭ eŭropecaj lingvoj kiel la esperanta.\tSuur font, mis sisaldab Aasia keelte tähti nagu näiteks Hiina, Jaapani ja Korea.\t为亚洲语言准备的大字体,比如中文,日文和韩文字母。\tDuża czcionka, która zawiera znaki dla języków azjatyckich, takich jak chiński, japoński i koreański.\t中国、韓国、日本などアジア圏の言語を含む大きいフォント\thayu pipa-tsum, tlosh kanawei wik-saya chaina-man ilahi le-lang (chapan ilahi, kolia ilahi, tailan ilahi...).\t\tВеликий шрифт з символами для азійських мов, таких як китайська, японська та корейська.\tFonte grande que inclui caractéres para línguas asiáticas como o Chinês, Japonês e Coreano.\nfont_unicode_european_large\tUnicode European Large\tUnicode européen - large\tUnicode Europeu - grande\tUnicode Europeo - grande\tUnicode Europeo Grande\tUnicode European Large\tUnicode European Large\tUnicode European Large\tUnicode أوربي كبير\tUnicode evrópskt stórt\tUnicode európai nagy\tUnikodo Eŭropa Granda\tUnicode Euroopa Suur\tUnicode European Large\tUnicode Europejska Duża\tUnicode European Large\thayas Unicode European\tUnicode European Large\tUnicode European Large\tUnicode Europeu Grande\nfont_unicode_european_large_description\tLarge font with more unicode support. Includes characters for all European languages.\tLarge police supportant plus d'unicode, incluant les caractères de la plupart des langages européens.\tFonte grande com mais suporte para unicode. Inclui caracteres para todas as linguas Europeias.\tFuente grande con soporte unicode. Incluye los carácteres usados en todos los lenguajes europeos.\tFont grande, con più supporto unicode. Include i caratteri per tutte le lingue europee\tкрупный шрифт с поддержкой символов из юникода. включает буквы их всех европейских языков.\tStort typsnitt med mer unicode-support. Innehåller tecken för alla Europeiska språk.\tGroße Schriftart, unterstützt Unicode (alle Europäischen Zeichen).\tخط كبير ذو دعم أكبر للـUnicode، يحتوي على حروف من كل اللغات الأروبية.\tStórt letur sem styður unicode stafi. Unicode staðallinn inniheldur stafi fyrir flest evrópsk tungumál.\tNagyméretű betűtípus unicode támogatással. Tartalmazza az európai nyelvek legtöbb karakterét.\tGranda dosiero, ekzistas literoj por ĉiuj eŭropaj lingvoj kaj esperanto.\tSuur font, mis toetab rohkem unicode-i. Sisaldab kõike Euroopa keelte tähti.\t有着更多Unicode支持的大字体,包括所有欧洲语言字母。\tDuża czcionka z większym wsparciem Unicode. Zawiera znaki dla wszystkich europejskich języków.\tヨーロッパ圏の言語の全てをカバーした、より多くのunicodeをサポートした大きいフォント\thayu pipa-tsum, tlosh kanawei dachman-ilahi le-lang.\t\tВеликий шрифт з підтримкою Unicode. Містить символи для всіх європейських мов.\tFonte grande com maior apoio ao unicode. Inclui caractéres de todas as línguas europeias.\nfont_unicode_european_small\tUnicode European Small\tUnicode européen - petit\tUnicode Europeu - pequeno\tUnicode Europeo - pequeño\tUnicode Europeo Piccolo\tUnicode European Small\tUnicode European Small\tUnicode European Small\tUnicode أوربي صغير\tUnicode evrópskt smátt\tUnicode európai kicsi\tUnikodo Eŭropa Eta\tUnicode Euroopa Väike\tUnicode European Small\tUnicode Europejska Mała\tUnicode European Small\ttenas Unicode European\tUnicode European Small\tUnicode European Small\tUnicode Europeu Pequeno\nfont_unicode_european_small_description\tSmall font with some unicode support. Includes characters for most European languages.\tPetite police supportant l'unicode, incluant les caractères de la plupart des langages européens.\tFonte pequena com algum suporte para unicode. Inclui caracteres de maior parte das linguagens Europeias.\tFuente pequeña con un poco de soporte unicode. Incluye los carácteres usados en la mayoría de los lenguajes europeos.\tFont piccolo, con un po' di supporto unicode. Include i caratteri per buona parte delle lingue europee.\tмелкий шрифт с поддержкой многих символов из юникода. включает буквы для большинства европейских языков.\tLitet typsnitt med viss unicode-support. Innehåller tecken för de flesta Europeiska språk.\tKleine Schriftart, unterstützt teilweise Unicode (fast alle Europäischen Zeichen).\tخط صغير يدعم بعض الـUnicode، يحتوي على حروف من معظم اللغات الأروبية.\tSmátt letur sem styður unicode stafi. Unicode staðallinn inniheldur stafi fyrir flest evrópsk tungumál.\tKisméretű betűtípus unicode támogatással. Tartalmazza az európai nyelvek legtöbb karakterét.\tMalgranda dosiero por la plejmulto da eŭropaj lingvoj, kaj povas skribi esperanton.\tVäike font, mis mõneti toetab unicode-i. Sisaldab enamus Euroopa keelte tähti.\t有一些Unicode支持的小字体,包含大部分欧洲语言字母。\tMała czcionka z częściowym wsparciem Unicode. Zawiera znaki dla większości języków europejskich.\tヨーロッパ圏の言語のほとんどをカバーした、 unicodeのサポートも含む小さいフォント\twik hayu pipa-tsum, tlosh wik-saya kanawei dachman-ilahi le-lang.\t\tМаленький шрифт з обмеженою підтримкою Unicode. Містить символи для більшості європейських мов.\tFonte pequena com maior apoio ao unicode. Inclui caractéres de todas as línguas europeias.\nfunction_end_description\tstop the game\tarreter le jeu\t\t\tferma il gioco\tостановить игру\t\tStoppe das Spiel\tإيقاف اللعبة\tenda leikinn\tjáték leálltása\t\t\t\tzatrzymaj grę\t\t\t\tзупинити гру\tparar o jogo\nfunction_end_help\tthe game stops immediately, but if there is dialog after this action, it will still play\tle jeu s'arrete immédiatement, mais si il y a encore des dialogues après cette action, il continura de jouer\t\t\til gioco si ferma immediatamente, ma se c'è un dialogo dopo questa azione, apparirà\tигра немедленно прекращается, но если после этого действия появится диалоговое окно, она всё равно будет воспроизводиться\t\tDas Spiel endet sofort, aber Blöcke nach diesem hier werden noch angezeigt\tإيقاف اللعبة حالاً، ولكن إذا كان يوجد حوار بعد ذلك فسوف يعرض\tendar leikinn samstundis, ef það er samtal eftir þessari aðgerð mun það samt keyra\ta játék azonnal leáll, de ha még van párbeszéd, az még lemegy\t\t\t\tgra zatrzymuje się natychmiast, ale jeżeli jest jakiś dialog po tej akcji, to wciąż zostanie on odegrany\t\t\t\tгра зупиняється негайно, але якщо за цією дією є діалог, він буде відображений до кінця\to jogo para na hora, mas se houver diálogo após essa ação, ele ainda será mostrado\nfunction_end_name\tend\tfin\t\t\tfine\tконец\t\tEnde\tإنهاء\tenda\tvége\t\t\t\tzakończenie\t\t\t\tкінець\tfinal\nfunction_exit_description\tmove player to _ at (_,_)[ with effect _]\tdéplacer\t\t\tsposta giocatore a _ a (_,_)[con effetto _]\tпереместить игрока в _ (_,_)[с эффектом _]\t\tBewege Avatar zu _ bei (_,_)[ mit Effekt _]\tتحريك اللاعب إلى ـــ في (ــ،ــ) [بإستخدام مؤثر الـ_]\tfærðu leikpeðið til _ á (_,_)[ með áhrifum _]\tjátékos mozgatása ide: _ innen (_,_)[ ezzel az effekttel _]\t\t\t\tprzenieś gracza do _ pozycja (_,_)[ z efektem _]\t\t\t\tперемістити гравця до _ в (_,_)[ з ефектом _]\tmover jogador para _ em (_,_)[ com efeito _]\nfunction_exit_name\texit\tsortie\t\t\tuscita\tвыход\t\tÜbergang\tخروج\tútgönguleið\tkijárat\t\t\t\tprzejście\t\t\t\tвихід\tsaída\nfunction_item_description\t_ in inventory[ = _]\t_ dans l'inventaire [ = _]\t\t\t_ nell'inventario [ = _]\t_ в инвентаре[ = _]\t\t_ im Inventar [ =_]\t_ في المخزن [ = _]\t_ í tösku [ = _]\t_ a leltárban[ = _]\t\t\t\t_ w ekwipunku[ = _]\t\t\t\t_ в інвентарі[ = _]\t_ no inventário[ = _]\nfunction_item_name\titem\tobjet\t\t\toggetto\tпредмет\t\tObjekt\tأداة\thlutur\ttárgy\t\t\t\tprzedmiot\t\t\t\tпредмет\titem\nfunction_pg_description\tstart a new page of dialog\tdémarrer une nouvelle page de dialogue\t\t\tcomincia una nuova pagina di dialogo\tначать новую страницу диалога\t\tWarte auf Tastendruck\tبدء صفحة جديدة من الحوار\tbyrja á nýrri síðu af samtali\tkezdj egy új párbeszédoldalt\t\t\t\trozpocznij nową stronę dialogu\t\t\t\tпочати нову сторінку діалогу\tcomeçar uma nova caixa de diálogo\nfunction_pg_help\tif there are actions after this one, they will start after the player presses continue\t\t\t\t\"se ci sono altre azioni dopo questa, si attiveranno dopo che il giocatore sceglie \"\"continua\"\"\"\tесли после этого будут какие-либо действия, они начнутся после того, как игрок нажмёт продолжить\t\tNachfolgende Handlungen werden erst nach einem Tastendruck in einem neuen Dialogs-Fenster angezeigt.\tإذا كان يوجد آي تصرفات بعد هذا التصرف، فسوف يبدئون بعد الضغط على إكمال\tef það eru aðrar aðgerðir eftir þessa, þá munu þær byrja eftir að spilarinn heldur áfram\tha vannak akciók még ez után, azok akkor játszódnak le, ha a játléos a folytatásra kattint\t\t\t\tjeśli po tej stronie są jeszcze jakieś akcje, to rozpoczną się one po tym, jak gracz naciśnie przycisk aby kontynuować\t\t\t\tдії після розриву почнуться тоді, як гравець натисне “продовжити”\tse houverem ações após essa, elas começam após o jogador apertar em continuar\nfunction_pg_name\tpagebreak\tsaut de page\t\t\tinterruzione di pagina\tразрыв\t\tPause\tفاصل صفحة\tsíðu skil\toldaltörés\t\t\t\tnowa strona\t\t\t\tрозрив сторінки\tdividir diálogo\nfunction_print_description\tprint _ in the dialog box\t\t\t\tscrive _ nel box di dialogo\tотобразить _ в окне диалога\t\tschreibe _ in die Dialog Box\tطباعة ـــ في صندوق الحوار\tskrifa _ í samtals glugga\tírd ki, hogy _ a párbeszédablakba\t\t\t\tpisze _ w oknie dialogu\t\t\t\tвивести _ в діалоговому вікні\tprint _ na caixa de diálogo\nfunction_print_name\tprint\t\t\t\tscrive\tотобразить\t\tschreibe\tطباعة\tskrifa\tkiírás\t\t\t\tnapisz\t\t\t\tвивід\tprint\nfunction_property_description\tproperty _[ = _]\tpropriété _[ = _]\t\t\tproprietà _[ = _]\tсвойство _[ = _]\t\tEigenschaft _[ = _]\tخاصية _[ = _]\teiginleiki _[= _]\ttulajdonság _[ = _]\t\t\t\twłaściwość _[ = _]\t\t\t\tвластивість _[ = _]\tpropriedade _[ = _]\nfunction_property_locked_example_help\tchange the value of a property: for example, set the locked property to true to stop an exit from changing rooms, or to prevent an ending from stopping the game\tchange the value of a property: for example, set the locked property to true to stop an exit from changing rooms, or to prevent an ending from stopping the game\t\t\tcambia il valore di una proprietà: ad esempio, imposta la proprietà bloccata come vero per impedire ad un'uscita di far cambiare stanza, o impedisci ad un finale di far finire il gioco\tизмените значение свойства: например, установите для свойства locked значение true, чтобы остановить выход из раздевалок или предотвратить остановку игры в конце\t\tÄndere den Wert einer Eigenschaft: z.B. setze gesperrt auf wahr, um einen Übergang zu sperren oder zu verhindern, dass ein Ende das Spiel beendet.\tتغيير قيمة خاصية: على سبيل المثال، تعين خاصية القفل لمنع مخرج من تغيير الغرف أو منع النهاية من إيقاف اللعبة\tstilltu eiginleika: til dæmis, að stilla læsingar eiginleikan í sannan til að stöðva útgönguleið frá því að skipta um rými, eða til að koma í veg fyrir að endir stöðvi leikinn\ta tulajdonság értékének megváltoztatása: például, állítsd a zárt tulajdonságot igazra, hogy megakadályozz egy kijáratot, hogy szobát változtasson, vagy egy befejezést, hogy megállítsa a játékot\t\t\t\tzmień wartość właściwości, np.: ustaw właściwość locked na true, żeby zatrzymać zmienianie pomieszczeń dla Przejścia, lub zapobiec zatrzymywaniu gry dla Zakończenia\t\t\t\tзмінити значення властивості; наприклад, встановте властивсть “замкнений” щоб не дозволити виходу змінити кімнату, або закінченню збутися\tmudar o valor de uma propriedade: por exemplo, trancar uma cena para impedir que uma saída mude de sala, ou para prevenir que um final pare o jogo\nfunction_property_name\tproperty\tpropriété\t\t\tproprietà\tсвойство\t\tEigenschaft\tخاصية\teiginleiki\ttulajdonság\t\t\t\twłaściwość\t\t\t\tвластивість\tpropriedade\nfunction_say_name\tsay\t\t\t\tscrive\tсказать\t\tsage\tتقول\tsegja\tmondd\t\t\t\tpowiedz\t\t\t\tсказати\tfalar\ngame_settings\tgame settings\tparamètres du jeu\tdefinições do jogo\tajustes del juego\timpostazioni di gioco\tнастройки игры\tspel-inställningar\tSpiel-Einstellungen\tإعدادات اللعبة\tleikjastillingar\tjátékbeállítások\tluda elektaro\tmängu seaded\t游戏设置\tustawienia gry\tゲーム設定\thihi setins\tnastavení hry\tналаштування гри\tdefinições de jogo\ngif_download\tdownload gif\ttélécharger le gif\tdescarregar gif\tdescargar gif\tscarica gif\tскачать гифку\tladda ned gif\tGIF herrunterladen\tتحميل GIF\thala niður gif\tgif letöltése\tprenu (elŝutu) GIF-bildon\tlae alla gif\t下载动图\tpobierz gif\tGIFダウンロード\tiskum GIF\tstáhnout gif\tскачати gif\tbaixar gif\ngif_encoding\tencoding...\tencodage en cours...\ta codificar...\tcodificando...\telaborando...\tобрабатываю...\tkodar...\tVerarbeite...\tتشفير...\tumrita...\ttömörítés\tkodiĝante...\tkodeerib...\t编码中……\tkodowanie…\tエンコード中・・・\talta GIF chako kod...\tzpracování...\tкодування…\tprocessando...\ngif_recording\trecording...\tenregistrement en cours...\ta gravar...\tgrabando...\tregistrando...\tзаписываю...\tspelar in...\tZeichne auf...\tتسجيل...\tupptaka í gangi...\tfelvétel...\tkreadante...\tlindistab...\t录制中……\tnagrywanie…\t録画中・・・\talta hayu-mamook GIF...\tnahrávání...\tзапис…\tgravando...\ngif_snapshot\tsnapshot\tcapture d'écran\tcaptura de ecrã\tcaptura\tistantanea\tсделать снимок\tta en bild\tSchnappschuss\tلمحة\tskjáskot\tpillanatkép\tantaŭvida bildo\tpildista\t截图\tzrzut ekranu\tスナップショット\ttenas picha\tscreenshot\tзнімок\ttirar foto\ngif_start\tstart recording\tcommencer l'enregistrement\tcomeçar gravação\tempezar grabación\tinizia la registrazione\tначать запись\tstarta inspelning\tAufnahme starten\tأبدأ التسجيل\thefja upptöku\tfelvétel indítása\tkomencu krei\talusta lindistamist\t开始录制\tnagrywaj\t録画開始\tmamook chi GIF\tspustit nahrávání\tпочати запис\tiniciar gravação\ngif_stop\tstop recording\tarrêter l'enregistrement\tparar gravação\tparar grabación\tferma la registrazione\tостановить запись\tstoppa inspelning\tAufnahme stoppen\tأوقف التسجيل\tstöðva upptöku\tfelvétel leállítása\tmalkomencu krei\tlõpeta lindistamine\t停止录制\tzatrzymaj nagrywanie\t録画停止\tkopit mamook GIF\tzastavit nahrávání\tзупинити запис\tparar gravação\ngif_tool_name\trecord gif\tenregistrer un gif\tgravar gif\tgrabar gif\tregistra gif\tзаписать гифку\tspela in gif\tGIF aufnehmen\tتسجيل GIF\tgif upptaka\tgif készítése\tfari GIF-bildon\tlindista gif\t录制动图\tnagraj GIF\tGIF録画\tmunk-GIF\tnahrát gif\tзаписати gif\tgravar um gif\ngrid_toggle_visible\tgrid\tgrille\tgrelha\tgrid\tgriglia\tсетка\trutnät\tRaster\tشبكة\trúðustrik\tnégyzetháló\tkrado\truudustik\t格子\tsiatka\tグリッド\tskwil-skwil\tmřížka\tсітка\tgrade\nimport_html\timport game from html file:\timporter à partir d'un fichier html :\timportar um jogo a partir de um ficheiro html:\timportar juego desde archivo html\timporta il gioco da un file html\tзагрузить игру из html файла:\timportera spel från html-fil\tSpiel aus HTML-Datei importieren:\tاستخراج بيانات اللعبة من ملف html:\tflytja inn leik úr html skjali\tjáték importálása html fájlból\tlegu iun ludon el HTML-dosiero:\timpordi mäng html failist:\t导入游戏从 html 文件:\timportuj grę z pliku html:\thtmlファイルからゲームを読み込む\tmash .html hihi kopa edita:\timportovat hru z .html\tімпортувати гру з файлу HTML:\timportar jogo de um arquivo html:\ninstructions_title\tinstructions\tinstructions\tinstruções\tinstrucciones\tistruzioni\tинструкции\tinstruktioner\tAnleitung\tالتعليمات\tleiðbeiningar\thasználati utasítás\tkiel krei?\tõpetused\t说明\tinstrukcja\t説明\tkata mamook\t\tінструкції\tinstruções\ninstructions1\tyou can draw things in the paint panel, then place them in your world in the room panel. you can write dialog for your characters (aka sprites) too. you can also animate your drawings if you want to!\tTu peux dessiner des choses dans la fenêtre de dessin, puis les placer dans tes mondes dans la fenêtre de salle. Tu peux aussi écrire des dialogues pour tes personnages (tes sprites) . Enfin, tu peux animer tes dessins si tu le souhaites!\tpodes desenhar coisas no painel de desenho para as poderes colocar no teu mundo usando o painel da sala e escrever diálogos para as tuas personagens (os teus sprites). podes também animar os teus desenhos se quiseres!\tpuedes dibujar cosas en la caja de pintura, después colócalo en tu mundo en la caja de room. Puedes escribir también diálogos para tus personajes (aka sprites). ¡Ah! Y también puedes animar tus dibujos si quieres!\tPuoi disegnare cose nel pannello di disegno, e poi piazzarle nel tuo mondo dal pannello della stanza. Puoi anche aggiungere dialoghi per i tuoi personaggi (gli sprite). Puoi anche animare i tuoi disegni, se vuoi!\tвы можете создавать изображения в панели рисования, и затем размещать их в игровом мире в панели пространства. ещё вы можете писать диалоги для персонажей (спрайтов). вы также можете анимировать рисунки, если захотите!\tdu kan rita saker i rita-fönstret och sedan sätta in dem i din värld i rum-fönstret. du kan också skriva dialog till dina figurer(sprites), och göra animerade bilder!\tMit dem Zeichnen-Panel, kannst du Dinge zeichnen und anschließend im Räume-Panel in der Welt platzieren. Du kannst auch Dialoge für deine Figuren (Sprites) schreiben. Wenn du magst, kannst du deine Zeichnungen sogar animieren!\tيمكنك أن ترسم الأشياء في لوحة الرسم، ثم يمكنك وضعها في عالمك عن طريق لوحة الغرفة. يمكنك أيضاً أن تكتب الحوار لشخصياتك (الرسومات التفاعلية). كما يمكنك أن تحرك روسماتك إذا أردت.\tþú getur teiknað hluti í teikningasvæðinu og sett þá svo í heiminn þinn í rýmissvæðinu. þú getur líka skrifað samtöl fyrir persónurnar þínar (þ.e. sprotana). það er líka hægt að láta teikningarnar hreyfa sig ef þú vilt!\ta rajz panelben bármit megrajzolhatsz, amit aztán elhelyezhetsz a világodban a szoba panel segítségével. itt írhatsz párbeszédeket is a karaktereidnek (más néven sprite-oknak). sőt animálhatod is őket!\tvi povas desegni bildon je la desegnejo, tiam meti la bildon en la ĉambron de via ludomondo. ankaŭ povas skribi parolojn kaj kodojn por la enludanoj (ludroluloj). eĉ povas krei movantajn bildojn!\tjoonista asju joonista paneelis, siis aseta need oma maailmasse tuba paneelis. Kui sa tahad võid kirjutada teksti oma tegelastele ja animeerida oma joonistusi ka!\t你可以在“画图”面板中画东西,然后可以将它们在“房间”面板中放到你的世界中。你也可以为你的角色(也叫精灵)写对话。你还可以为你的图画制作动画!\tmożesz rysować rzeczy w panelu rysowania, następnie umieść je w twoim świecie, w panelu pomieszczenia. możesz też napisać dialog dla twojej postaci(inaczej: sprite'a). możesz również animować swoje rysunki, jeśli chcesz!\tペイントパネルで何か絵を描いたら、それをルームパネルからあなたの世界に配置することができます。作ったキャラクター(スプライト)にダイアログを追加することもできます。描いた絵をアニメーションにして動かす事もできます!\telip maika tsum kopa pint skwil, alta maika munk-mitlait okok tsum kopa loom skwil. spos maika tiki, maika munk-pipa wawa kopa maika mitlait-hihi-tilikum (sprites), pi maika munk-hulel (animate) maika tsum.\t\tМалюй предмети в панелі малюнки, а потім будуй з них свій світ на панелі “кімнати. Також можна написати діалог для твоїх персонажів (спрайтів). Якщо хочеш, малюнки можна анімувати!\tvocê pode criar coisas no painel de desenho, podendo até animá-las, e colocá-las no seu mundo no painel de cena. você pode escrever diálogos para os seus personagens (atores) também!\ninstructions10\tyou can make multiple rooms, but make sure you add some exits to connect them to each other!\tTu peux faire plusieurs salles, mais fais bien attention d'ajouter des sorties pour les connecter entre elles!\tpodes criar múltiplas salas, mas não te esqueças de as conectar adicionando algumas saídas\tpuedes crear múltiples salas, pero no te olvides de conectarlas añadiendo algunas salidas\tpuoi creare più di una stanza, ma non dimenticarti di aggiungere delle uscite per collegarle tra di loro!\tвы можете создать несколько пространств, главное не забудьте добавить точки выхода, чтобы соединить их между собой!\tdu kan göra flera rum, men kom ihåg att koppla ihop dem med utgångar!\tDu kannst mehrere Räume anlegen - aber füge Ausgänge hinzu, um sie miteinander verbinden!\tيمكنك عمل مجموعة من الغرف، و لكن لا تنسى أن تضيف المخارج لوصلهم ببعض.\tþú getur búið til mörg rými en gættu þess að bæta við útgöngum til að tengja þau saman!\takár több szobát is létrehozhatsz, csak ne felejts el kijáratokat hozzáadni, hogy összekösd őket!\tvi povas krei pliajn ĉambrojn, sed aldonu pordojn (elirejojn) por iri kaj reveni!\tvõid luua mitu erinevat tuba peaasi, et lisad mõned väljapääsud, et neid omavahel ühendada!\t你可以制作多个房间,但记得连接他们的时候加入出口!\tmożesz zrobić wiele pomeszczeń, ale upewnij się, że dodasz kilka przejść, żeby połączyć je ze sobą\tルームは複数作ることができますが、出口を用意して繋げるようにしましょう。\tklosh spos maika tiki mamook hayu loom. pi klosh maika mash lapot-skwil, kakwa maika tlatawa kopa iht loom chi loom, wik maika tlap stuk!\t\tМожна зробити багато кімнат, тільки не забудь додати виходи, що зʼєднають їх між собою!\tvocê pode fazer várias cenas, mas não se esqueça de adicionar algumas saídas para conectá-las!\ninstructions11\tuse download game to download the game as an html file. you can email the file to a friend, or host it online!\tClique sur partager mon jeu pour télécharger ton jeu en fichier html. Tu peux l'envoyer par mail à tes amis ou le mettre en ligne!\tusa a opção de partilhar jogo para descarregares o teu jogo como ficheiro html. podes partilhá-lo com um amigo enviando um e-mail ou podes hospedá-lo online!\tusa compartir juego para descargarlo como un archivo html. Puedes enviarlo en un mail a un amigo o compartirlo online!\tusa scarica gioco per scaricarlo come file html. Puoi inviarlo come allegato ad una email, oppure condividerlo online!\tиспользуйте кнопку загрузить игру, чтобы загрузить вашу игру в формате html-файла. вы можете переслать её друзьям по электронной почте или опубликовать на сайте!\ti fönstret ladda ned kan du ladda ned ditt spel som en html-fil. sen kan du maila filen till en vän, eller ladda upp det på internet!\tKlick auf Herunterladen um dein Spiel als html Datei herunterzuladen. Verschicke die Datei via eMail oder stelle sie woanders online!\tأستخدم رابط تحميل اللعبة لتحميل اللعبة كملف html. يمكنك أن ترسلها إلى صديق أو تستضيفها أونلاين!\tnotaðu 'hala niður leik' til að hala leiknum niður sem html skjali. þú getur sent leikinn til vinar með tölvupósti eða setta hann á netið!\ta játék letöltése gombbal letöltheted a játékodat html fájlként. utána egyszerűen átküldheted emailben egy barátodnak vagy feltöltheted valahova!\tklaku 'prenu ludon' por elŝuti vian ludon kiel .HTML-dosiero. vi povas sendi la dosieron al amikoj per retletero, aŭ afiŝi ĝin rete!\tkasuta lae alla mäng, et laadi alla mäng html failina. võid saata mängu sõbrale emaili kaudu või hostida ise!\t使用“下载游戏“将游戏下载为 html 文件。你可以把文件发送给朋友,或者将其发布上线!\tużyj pobierz grę aby pobrać grę jako plik html. możesz go wysłać e-mailem do znajomych, lub umieścić w internecie!\tゲームをダウンロードを使って、ゲームをhtmlファイルとしてダウンロードする事ができます。友達にそのファイルをemailで送ったり、自分のホームページに乗せたりする事ができます。\tspos maika tiki iskum hihi pi mash hihi maika siks kakwa .html, klosh maika kwutl iskum hihi. alta maika mash okok .html maika siks, pi mash hihi kopa inanet\t\tВикористай “завантажити гру” щоб завантажити гру у вигляді файлу HTML. Її можна надслати другові, або розмістити онлайн!\tclique em baixar jogo para baixá-lo como um arquivo html. você pode enviar o arquivo para amigos por email ou até colocá-lo online!\ninstructions12\tif you want to talk about bitsy, report a bug, or share a game you made, i'm on twitter\tSi tu veux parler de Bitsy, rapporter un bug ou partager un jeu que tu as fait, je suis sur twitter\tse queres falar sobre o bitsy, relatar um erro ou partilhar um jogo que criaste, podes-me encontrar no twitter\tsi quieres hablar sobre bitsy, reportar un bug, o compartir el juego que has creado, estoy en twitter\tse vuoi parlare di Bitsy, segnalare un bug, o condividere il gioco che hai fatto, sono su twitter come\tесли вы хотите обсудить битси, сообщить об ошибке или поделиться игрой, которую вы сделали, вы можете найти меня в X (бывшем твиттере)\tom du vill prata om bitsy, rapportera en bugg, eller visa ditt spel, finns jag på twitter:\tWenn du über Bitsy reden möchtest, einen Fehler entdeckt hast oder ein Spiel von dir teilen magst - Ich bin auf Twitter\tإذا أردت أن تتكلم عن بيتسي، تبلغ عن مشكلة، أو تشارك لعبتك، أنا موجود على التويتر.\tef þú vilt tala um bitsy, tilkynna um villu eða deila leik sem þú bjóst til þá er ég á twitter\tha szeretnél a bitsyről beszélgetni, bugot jelenteni, vagy csak meg akarod osztani velem a játékodat, twitteren megtalálsz itt:\tse (en la angla) vi volas paroli pri Bitsy, informi min pri miskodo en la programo aŭ diskonigi vian ludon, mi uzas tviteron:\tkui tahad rääkida bitsy-st, anda teada rikkest, või jagada oma tehtud mängu, olen twitter-is\t如果你想和我聊聊 Bitsy,反馈问题,或者分享你制作的游戏,请在 Twitter 上找我\tjeśli chcesz porozmawiać o bitsy, zgłosić błąd, lub podzielić się swoją grą, jestem dostępny na twitterze\tもしbitsyについて、質問やバグのレポート、作ったゲームのシェアなどしたければ、ツイッターで連絡してください。\tspos maika tiki kultus-wawa kopa bitsy, pi maika tlap tsipi (bug), pi maika tiki munk-nanich maika bitsy hihi, maika tlap naika kopa twitter kopa pastin wawa\t\tЯкщо хочеш поговорити про Бітсі, відзвітувати про баг, або поділитися грою, я є у Twitter.\tse você quiser conversar sobre o bitsy, denunciar um bug ou compartilhar algo que você fez, estou no twitter\ninstructions13_link_twitter\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\tadamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t\t@adamledoux\t@adamledoux\ninstructions14\tor you can post in the\tou tu peux poster dans le\tou podes postar no\to puedes escribir en\to puoi scrivere nel\tили написать\teller så kan du skriva på\toder du schreibst in das\tأو يمكنك النشر في\teða þú getur sent á\t, vagy kiposztolhatod\taŭ vi povas afiŝi ĝin je la\tvõi võid postitada\t或者你可以发布在\tlub możesz ją umieścić na\tもしくはこちらに投稿する事ができます\twuht maika tsum kopa pastin wawa kopa\t\tабо можеш запостити в\tou você pode conversar no\ninstructions15_link_forum\tcommunity forum\tforum de la communauté\tforum da comunidade\tel foro de la comunidad\tforum della comunità\tна форуме нашего сообщества\tvårt forum\tCommunity Forum\tالمنتدى\tspjallsvæðið\ta közösségi fórumba\tBitsy-babilejo\tbitsy foorumis\t社区论坛\tforum społeczności\tコミュニティフォーラム\t\"kanawei tilikum tlaska wawa-ilahi (\"\"forum\"\")\"\t\tфорум спільноти\tfórum da comunidade\ninstructions16\tspecial thanks to:\tRemerciements spéciaux à :\tagradecimentos especiais a:\tAgradecimientos especiales a:\tRingraziamenti speciali a:\tотдельная благодарность:\ttack till:\tBesonderer Dank an:\tشكر خاص إلى:\tsérstakar þakkir til:\tkülön köszönet:\tkromajn dankojn al:\ttänud:\t特别感谢:\tspecjalne podziękowania dla:\tスペシャルサンクス:\thayu masi kopa:\t\tособлива подяка:\tagradecimentos a:\ninstructions17\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tмэри-маргарет\tmary-margaret\tmary-margaret\tماري-مارجرت\tmary-margaret\tmary-margaretnek\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\t\tmary-margaret\tmary-margaret\ninstructions18\tseattle game meetup folks\tles gens du seattle game meetup\tseattle game meetup folks\tseattle game meetup folks\tla gente dei seattle game meetup\tлюдям с митапа в сиэтле\tseattle game meetup folks\tseattle game meetup folks\tمجتمع سياتل للألعاب\tfólksins í seattle game meetup\tseattle-i game meetupos arcoknak\tla anojn de la ludrenkontiĝo de Seatlo\tseattle-i mängu gruppi inimesed\tseattle game meetup 朋友们\tludzi z seattle game meetup\tseattle game meetup folksの友達\t\"siatl tawn hihi-hiyu tilikum (\"\"seattle game meetup folks\"\")\"\t\tлюдям з seattle game meetup\tpessoas do seattle game meetup\ninstructions19\teveryone in the bitsy community!\ttoutes les personnes de la communauté Bitsy!\ttoda a gente na comunidade do bitsy!\t¡todas las personas que forman la comunidad de Bitsy!\ttutte le persone che formano la communità di Bitsy!\tвсем, всем, всем из сообщества битси!\talla i bitsy-communityn!\talle in der Bitsy Community!\tو كل من هو جزء من مجتمع بيتسي!\tallra í bitsy-samfélaginu!\tmindenkinek a bitsy közösségben!\tĉiujn Bitsy-uzantojn!\tkõik bitsy kogukonna inimesed!\t所有 Bitsy 社区的朋友们!\twszystkich ze społeczności bitsy!\tBitsyコミュニティーのみんな!\tkanawei mesaika bitsy tilikum!\t\tвсім у спільноті Бітсі!\ttodo mundo da comunidade bitsy!\ninstructions2\tsome words:\tvocabulaire :\talgumas palavras-chave:\talgunas palabras clave:\talcune parole chiave:\tключевые термины:\tviktiga ord:\tEinge Begriffe:\tبعض الكلمات:\tnokkur orð:\tnéhány kifejezés:\tĉefvortoj:\tmõned terminid:\t名词解释:\tniektóre słowa:\t用語解説\tiht-iht tenas-wawa:\t\tдекілька слів:\to que é, o que é:\ninstructions3\troom - a place in your game world\tsalle - un environnement dans ton jeu\tsala - um sítio no teu jogo\troom - una sala de tu juego\tstanza - una scena del tuo gioco\tпространство - фрагмент игрового мира\trum - en plats i din spel-värld\tRaum - Ein Bereich deiner Spielwelt\tغرفة - مكان في عالم لعبتك\trými - staður í heiminum þínum\tszoba - egy hely a játékod világában\tĉambro - eta loko en via ludomondo\ttuba - koht sinu mängu maailmas\t房间 - 你创造的游戏世界中的某处\tpomieszczenie - miejsce w twoim świecie\tルーム - あなたのゲーム世界の中のあるひとつの場所\tloom - maika hihi pelik iht ilahi\t\tкімната - простір у світі твоєї гри\tcena - um lugar no seu mundo\ninstructions4\tavatar - the player's character\tavatar - le personnage du joueur\tavatar - a personagem do jogador\tavatar: el personaje del jugador\tavatar - il personaggio del giocatore\tаватар - персонаж, управляемый игроком\tavatar - figuren som spelaren styr\tAvatar - Die Spielfigur\tالشخص الممثل - شخصية اللاعب\tleikpeð - persónan sem þú stjórnar\tavatár - a játékos karaktere\tmovulo - la ulo kiu movas kiam oni ludas la ludon\tavatar - mängija tegelane\t人物 - 玩家角色\tawatar - postać gracza\tアバター - プレイヤーのキャラクター\ttayi picha - spos tilikum munk-hihi, okok picha tlaska picha\t\tаватар - персонаж гравця\tavatar - o personagem do jogador\ninstructions5\ttile - a piece of the scenery\ttile - un élément du décor\ttile - um elemento do cenário\ttile: un elemento del escenario\ttile - un elemento dello scenario\tтайл - элемент окружения\truta- en del av bakgrunden\tTile - Ein Teil des Hintergrundes\tرقعة - قطعة من المشهد\tflís - hluti af umhverfinu\ttile - a környezet egy eleme\tplanko - bildo sub aŭ malantaŭ la movulo. Planko, muro, pejzaĝo ktp\tkild - üks tükk mängu maailma maastikku\t地块 - 你的场景中的一块\tkafelek - element scenerii\tスプライト - インタラクティブなキャラクターやもの\tlaplash - okok tsum kakwa tipso pi chuk pi kalahan\t\tплитка - шматочок декорацій\tdetalhe - um pedaço do cenário\ninstructions6\tsprite - interactive characters or things\tsprite - un élément ou personnage interactif\tsprite - personagens ou coisas com que podes interagir\tsprite: un elemento o personaje interactivo\tsprite - un elemento o personaggio interattivo\tспрайт - нечто интерактивное, персонаж или предмет\tsprite - interaktiva personer eller saker\tSprite - Interaktive Figuren oder Dinge\tرسمة تفاعلية - أشخاص أو أشياء تفاعلية\tsproti - gagnvirk persóna eða hlutur\tsprite - interaktív karakterek vagy dolgok\tenludano - bildo (ulo aŭ objekto) kiu povas paroli, agi ktp per kodo\tkuju - interaktiivsed tegelased või asjad\t精灵 - 可交互的角色或者事物\tsprite - interaktywna postać lub rzecz\tタイル - 風景の一部\ttilikum - okok tsum komtuks wawa\t\tспрайт - інтерактивний персонаж або предмет\tator - personagens ou coisas interativas\ninstructions7\tto try your game, switch the room to play mode:\tPour essayer ton jeu, clique sur le bouton jouer pour passer ta salle en mode jeu :\tpara experimentares o teu jogo, muda a sala para o modo de jogo:\tpara probar tu juego cambia a modo de juego:\tper provare il tuo gioco, cambia la modalità cliccando gioca nel pannello della stanza\tчтобы протестировать игру, переведите панель пространства в игровой режим\tför att prova ditt rum kan du starta rum-fönstrets spelläge:\tUm dein Spiel zu testen, schalte das Räume-Panel in den Spielmodus:\tلتجرب لعبتك، انقل الغرفة إلى وضع اللعب:\ttil að prófa leikinn þinn settu þá rýmið á leikstillinguna:\ta játékod kipróbálásához, kapcsold a szobát lejátszás módba:\tpor provi vian ludon, klaku 'provu la ludon'\tet proovida oma mängu, vaheta tuba paneel mängu reziimi:\t如果要试玩游戏,调节房间到游玩模式:\tżeby wypróbować swoją grę, przełącz pomieszczenie w tryb gry:\tゲームを動かしてみるには、ルームをプレイモードに変更\tspos maika tiki tlai maika hihi, kwutl nanich hihi chilchil:\t\tщоб випробувати гру, перемикни її в режим гри:\tpara testar o seu jogo, mude a cena para o modo jogar:\ninstructions8\twalk around with the arrow keys\tdéplace-toi avec les flèches directionnelles\tmove-te usando as teclas direccionais\tpara probar tu juego cambia al modo de juego:\tesplora il mondo usando i tasti freccia\tперемещайтесь в пространстве, используя клавиши со стрелками\tgå omkring med piltangenterna\tLaufe mit den Pfeiltasten umher\tتحرك بحرية عن طريق إستخدام الأسهم\tnotaðu örvatakkana til að ganga um\tmozogni a nyilakkal tudsz\tmovadu la movulon per la sagoklavoj\tjaluta ringi noolte nuppudega\t用方向键四处走走\tporuszaj się przy pomocy klawiszy strzałek\t十字キーで歩き回る\t\"spos maika kwutl kalitan-chilchil (\"\"arrow keys\"\"), maika kultus-tlatawa kopa hihi\"\t\tходи стрілочками\tande com as teclas de setas\ninstructions9\ttalk to sprites by walking up to them\tparle aux sprites en marchant vers eux\tfala com sprites movendo-te até eles\thabla con los sprites acercándote a ellos\tparla con gli sprite avvicinandoti a loro\tговорите со спрайтами, подойдя к ним вплотную\tgå in i en sprite för att prata med den\tRede mit Sprites indem du an sie trittst\tلتتكلم مع الرسومات التفاعلية، تحرك في إتجاههم\ttalaðu við sprota með því að ganga upp að þeim\tha sprite-okkal szeretnél beszélni, csak sétálj oda hozzájuk\tproksimiĝu la enludanojn por paroli\träägi kujudega neile otsa jalutades\t走向精灵来和他们对话\trozmawiaj ze sprite'ami nachodząc na nie\tスプライトに立ち寄って話しかける\tspos maika tiki wawa mitlait-hihi-tilikum, maika tlatawa wik-saya okok tilikum\t\tщоб поговорити зі спрайтом, підійди до нього\tconverse com atores ao chegar perto deles\ninventory_tool_name\tinventory\tinventaire\tinventário\tinventario\tinventario\tинвентарь\tinventarie\tInventar\tمخزن\ttaska\ttárgyak\tobjektaro\tvarustus\t清单\tekwipunek\tインベントリ\thuyhuy-nampa haws\tinventář\tінвентар\tinventário\nitem_label\titem\titem\titem\titem\toggetto\tобъект\tföremål\tObjekt\tأداة\thlutur\ttárgy\tobjekto\tese\t物品\tprzedmiot\tアイテム\tiktas\tpředmět\tпредмет\titem\nitems_label\titems\titems\titems\titems\toggetti\tобъекты\tföremål\tObjekte\tأدوات\thlutir\ttárgyak\tobjektoj\tesemed\t物品\tprzedmioty\tアイテム\tiktas\tpředměty\tпредмети\titens\nlanguage_label\tlanguage\tlangue\tlinguagem\tidioma\tlingua\tязык\tspråk\tSprache\tاللغة\ttungumál\tnyelv\tlingvo\tkeel\t语言\tjęzyk\t言語\tle-lang\tjazyk\tмова\tlíngua\nlanguage_name\tEnglish\tFrançais\tPortuguês\tEspañol\tItaliano\tРусский\tsvenska\tDeutsch\tالعربية\tÍslenska\tmagyar\tEsperanto\tEesti\t简体中文\tpolski\t日本語\tchinuk wawa\tČeština\tУкраїнська\tPortuguês Brasileiro\nlanguage_translator_credit\tEnglish text by Adam Le Doux\tVersion française par Peter Februar & Dorian Beaugendre\tVersão portuguesa por Bruno Silva\tVersión española por Marina Díez (@Ninfa_dp) y Florencia Rumpel Rodriguez (@__rumpel)\tVersione italiana a cura di enui (@enricapr)\tперевели: онион (@le_american), элки (@aloelazoe), павел бид (www.paul.bid)\tSvensk översättning av Em (@Embotronic)\tDeutsche Übersetzung von Kai Werder und Matthias Löwe\tالنصوص العربية بواسطة أحمد خليفة\tÍslensk þýðing eftir Game Makers Iceland\tmagyar változat: Török Ádám (@gepember)\tEsperanta eldono de Ariel J Moody/Bonkorpa kaj Sequoia Edwards\tEesti keele tõlge: @scumslug\t简体中文文本由 Ray Song 翻译\tWersja polska Mateusz Teklak (plyr0)\t日本語訳 野老快南\tsequoia edwards yaka tsum okok chinuk bitsy.\tZZ\tПереклав Леонід Шевцов (leonid.codes)\tTexto em Português Brasileiro por Marco\nlist_type_description_cycle\tcycle (say each line, then repeat)\tboucle (lit chaque phrase, puis recommence)\tciclo (diz cada fala, depois repete)\tciclo (dice cada línea, entonces repite)\tciclo (dice ogni frase, poi ripete)\tцикл (показать каждую линию, потом повторить)\tcykel (säg repliker i ordning och upprepa)\tSchleife (sagt nach letzter Zeile wieder erste)\tحلقي (يقول كل سطر، ثم يقوم بتكريرهم)\thringhrás (segir hverja setningu og byrjar svo aftur frá byrjun)\tismétlés (minden sor kimondása, aztán megismétlése)\trondo (diru kiel linio, tiam rekomencu)\tkorduv (ütle igat rida üks kord, siis korda)\t循环(说每一行,并重复)\tpętla (powiedz każdą linijkę, a potem od powtarzaj)\tサイクル(各行を話し、繰り返す)\tkakwa lolo (wawa kakwa layn, kwanisom kilapi okok layn, kakwa: 1-2-3 1-2-3 1-2-3)\tsmyčka (každý řádek jednou a pak celé znovu)\tцикл (сказати кожний рядок, потім повторити)\tciclo (de fala em fala, depois repete)\nlist_type_description_sequence\tsequence (say each line once)\tséquence (lit chaque phrase une fois)\tsequência (diz uma fala de cada vez)\tsecuencia (dice cada línea una vez)\tsequenza (dice la frase ogni volta)\tпоследовательность (показать каждую линию один раз)\tsekvens (säg repliker i ordning)\tReihe (sagt jede Zeile einmal)\tمتتالي (يقول كل سطر مرة واحدة)\truna (segir hverja setningu einu sinni)\tsorozat (sorok kimondása egymás után)\tlinio (diru ĉiun skribon nur unufoje)\tjärjend (ütle igat rida üks kord)\t顺序(每一行说一次)\tsekwencja (powiedz każdą linijkę tylko raz)\tシーケンス(各行を一度だけ話す)\tkakwa iht (wawa kakwa layn iht taim: 1-2-3)\tsekvence (každý řádek jednou)\tпослідовність (сказати кожний рядок один раз)\tsequência (de fala em fala, depois acaba)\nlist_type_description_shuffle\tshuffle (say lines in random order)\tmélange (lit les phrases dans un ordre aléatoire)\tbaralho (diz falas numa ordem aleatória\tshuffle (dice cada línea en un orden cualquiera)\tshuffle (dice ogni frase, ma in ordine casuale)\tперемешать (показать случайную линию)\tblanda (säg repliker i slumpad ordning)\tZufall (sagt eine zufällige Zeile)\tعشوائي (يقول السطور بترتيب عشوائي)\tstokkun (segir setningar að handahófi)\tvéletlen (sorok kimondása véletlen sorrendben)\thazardo (diru ne laŭorde)\tsegamini (ütle igat rida suvalises järjekorras)\t随机(随机说一行)\tlosowo (mów linijki w losowej kolejności)\tシャッフル(ランダムな行を話す)\tkakwa pelton (heilo layn. wawa kakwa: 1-3-1 2-2-3 2-3-1...)\tnáhodně (náhodné pořadí řádků)\tмішанина (казати рядки в довільному порядку)\tmistura (falas em ordem aleatória)\nmarker_move\tmove\tbouger\tmover\tmover\tmuovi\tпереместить\tflytta\tVerschieben\tتحريك\threyfa\tmozgatás\tmovu\t\t移动\tzmień pozycję\t\ttlatawa\t\tперемістити\tmover\nmarker_move_click\tclick in room\tcliquer dans la salle\tclicar numa sala\tclick en la sala\tclicca nella stanza\tкликните в панели пространства\tclicka i rummet\tKlicke in gewünschten Raum\tإضغط في الغرفة\tsmelltu innan svæðis\tkattints a szobába\tklaku enĉambre\t\t点击房间中的一格\tkliknij wewnątrz pomieszczenia\t\tkwutl kopa loom\t\tклацні в кімнаті\tclique na cena\nmarker_moving\tmoving\ten train de bouger\tmovendo\tmoviendo\tmuovendo\tперемещаю\tflyttar\tverschiebe...\tيتحرك\threyfing\tezt mozgatod\tmovanta\t\t移动\tprzesuwanie...\t\thayu-tlatawa\t\tпереміщення\tmovendo\nmarker_tool_empty\tThere are no exits or endings in this room yet! You can add one with this tool. :)\tIl n'y a pas encore de sorties ou de fins dans cette salle! Tu peux en ajouter une avec cet outil :)\tNão existem saídas ou finais nesta sala! Podes adicioná-los com esta ferramenta :)\tTodavía no hay salidas ni finales en esta sala! Las puedes agregar con esta herramienta. =)\tIn questa stanza non ci sono ancora uscite o finali! Puoi aggiungerli da qui :)\tв этом пространстве ещё нет точек выхода или концовок! вы можете добавить их в этой панели :)\tDet finns inga utgångar eller slut i det här rummet! Du kan lägga till dem med det här verktyget\tBisher gibt es in diesem Raum keine Übergänge oder Enden. Füge sie hier hinzu!\tلا يوجد اي مخارج أو نهايات! يمكنك إضافة إحداها عن طريق هذة الأداة. :)\tÞað er engar útgönguleiðir né endalok\tMég nincs kijárat vagy befejezés ebben a szobában. Ezzel az eszközzel adhatod hozzá :)\tNe ekzistas pordoj kaj ludofinoj en ĉi ludo! Vi povas aldoni iom per ĉi ilo. :)\t\t这个房间中还没有出口或者结局!你可以用这个工具添加。 :)\tW tym pomieszczeniu nie ma jeszcze wyjść ani zakończeń! Możesz je dodać tym narzędziem. :)\t\twik mitlait lapot pi kopit-skwil kopa okok loom! spos maika tiki, mamook iht yakwa. :)\t\tВ цій кімнаті ще немає виходів та закінчень! Можеш додати їх цим інструментом :)\tAinda não existem saídas ou finais nessa cena! Você consegue adicioná-los aqui. :)\nmarker_tool_name\texits & endings\tsorties & fins\tsaídas e finais\tsalidas y finales\tfinali e uscite\tточки выхода и концовок\tutgångar och slut\tÜbergänge & Enden\tمخارج و نهايات\tútgönguleiðir og endalok\tkijáratok és befejezések\tpordoj k ludofinoj\t\t退出&结局\tprzejścia i zakończenia\t\tlapot pi kopit-skwil\tprůchody a konec hry\tвиходи та закінчення\tsaídas & finais\nname_label\tname\tnom\tnome\tnombre\tnome\tимя\tnamn\tName\tالأسم\tnafn\tnév\tnomo\tnimi\t名字\tnazwa\t名前\tnim\tnázev\tімʼя\tnome\nopen_settings\topen settings\touvrir les paramètres\tabrir definições\tabrir opciones\tapri le impostazioni\tоткрыть настройки\töppna inställningar\tÖffne Einstellungen\tأفتح الإعدادات\topna stillingar\tbeállítások megnyitása\tvidu la elektaron\tava seaded\t开启设置\totwórz ustawienia\t\tnanich setins\totevřít nastavení\tвідкрити налаштування\tabrir definições\noption_fixed_size\tfixed size\trésolution fixe\tresolução fixa\tresolución fija\tdimensione fissa\tфиксированный размер\tfast storlek\tFeste Größe\tحجم ثابت\tföst stærð\tfix méret\tneplena paĝo\tfikseeritud suurus\t固定尺寸\tstały rozmiar\t固定サイズ\ttenas winto\tfixní velikost\tфіксований розмір\ttamanho fixo\noption_full_size\tfull page\tplein écran\tpágina completa\tpágina completa\tpagina intera\tна всю страницу\thela sidan\tVollbild\tالصفحة بالكامل\tfylla út í skjá\tteljes oldal\tplena paĝo\ttäida terve leht\t全页面\tcała strona\t全画面\tpatl winto\t\tповний екран\ttela cheia\noption_page_color\thtml page color:\tcouleur de la page html :\tcôr da página html:\tcolor de la página html\tcolore della pagina html\tцвет html страницы:\tfärg på html-sidan:\tHTML-Hintergrund-Farbe:\tلون صفحة الـhtml:\tvefsíðulitur\thtml oldal háttérszíne:\t.HTML paĝa koloro:\thtml lehe värv:\thtml 页面颜色:\tkolor strony html:\thtmlページの色\t.html pipa tsum\tbarva html stránky:\tколір сторінки HTML:\tcor da página html:\noption_window_size\tgame window size:\ttaille de l'écran de jeu :\ttamanho da janela do jogo:\ttamaño de la pantalla de juego\tmisura della finestra di gioco\tразмер окна игры:\tspelfönstrets storlek:\tSpiel-Fenstergröße:\tحجم نافذة اللعبة:\tstærð leikjaglugga\tjátékablak mérete:\tgrandeco kiam oni ludas:\tmängu akna suurus:\t游戏窗口尺寸:\trozmiar okna gry:\tゲームウィンドウの大きさ\tmaika nanich hihi, spos tenas spos hayas winto:\tvelikost okna hry:\tрозмір вікна гри:\ttamanho da janela do jogo:\noptions_label\toptions\toptions\topções\topciones\topzioni\tопции\tinställningar\tOptionen\tخيارات\tvalmöguleikar\tbeállítások\telektaro\tvalikud\t选项\topcje\t設定\tpik iktas\tmožnosti\tналаштування\topções\npaint_tool_name\tpaint\tdessin\tdesenhar\tdibujar\tdisegna\tрисовать\trita\tZeichnen\tأرسم\tteikna\trajz\tdesegnejo\tjoonista\t画图\trysowanie\tペイント\tpint\t\tмалювання\tdesenhar\npalette_background\tbackground color\tcouleur du fond\tcôr de fundo\tcolor de fondo\tcolore dello sfondo\tцвет фона\tbakgrundsfärg\tHintergrundfarbe\tلون الخلفية\tbakgrunnslitur\tháttér színe\tsuba koloro\ttagaplaani värv\t背景颜色\tkolor tła\t背景色\tkimta tsum\tbarva pozadí\tколір фону\tcor de fundo\npalette_label\tpalette\tpalette\tpalete de cores\tpaleta\tpalette\tпалитра\tpalett\tPalette\tلوحة الألوان\tlitaval\tszínpaletta\tkoloraro\t\t调色板\tpaleta\t\tloom tsum-hayu\tbarevná paleta\tпалітра\tpaleta\npalette_sprite\tsprite color\tcouleur du sprite\tcôr das sprites\tcolor del sprite\tcolore dello sprite\tцвет спрайта\tsprite-färg\tSprite Farbe\tلون الرسمة التفاعلية\tsprotalitur\tsprite színe\tenludana koloro\tkujude värv\t精灵颜色\tkolor sprite'a\tスプライト色\ttilikum tsum\tbarva objektů\tколір спрайтів\tcor de atores\npalette_tile\ttile color\tcouleur des tiles\tcôr das tiles\tcolor del tile\tcolore del tile\tцвет тайла\trut-färg\tTile Farbe\tلون الرقعة\tflísalitur\ttile színe\tplanka koloro\tkildude värv\t地块颜色\tkolor kafelka\tタイル色\tlaplash tsum\tbarva prostředí\tколір плиток\tcor de detalhes\npalette_tool_advanced\tpalette select\tsélectionner une palette\tescolher uma palete\tselecciona una paleta\tselezionare una palette\tвыбрать палитру\tvälj palett\tAusgewählte Farbpalette\tأختيار لوحة الألوان\tlitaval\tszínpaletta kiválasztása\telektu koloraron\tvali palett\t调色板选择\twybór palety\tパレット選択\tpik loom tsum-hayu\tvýběr barevné palety\tвибір палітри\tescolher paleta\npalette_tool_name\tcolors\tcouleurs\tcores\tcolores\tcolori\tцвета\tfärger\tFarben\tألوان\tlitaval\tszínek\tkoloroj\tvärvid\t颜色\tkolory\t色\tloom tsum-hayu\tbarvy\tкольори\tcores\nplay_game\tplay\tjouer\tjogar\tjugar\tgioca\tиграть\tspela\tSpielen\tألعب\tspila\tlejátszás\tprovu la ludon\tmängi\t玩\tgraj\tプレイ\tnanich hihi\thrát\tграти\ttestar\nreset_data\treset game data\tréinitialiser les données du jeu\trepor dados do jogo\treset datos de juego\tresetta i dati del gioco\tсбросить данные\tnollställ speldata\tSetze Spiel Daten zurück\tامسح بيانات اللعبة\tendursetja leikjagögn\tjátékadatok törlése\trelegu la ludodosieron\ttaasta esialgsed mängu andmed\t重置游戏数据\tusuń dane gry\tゲームデータをリセット\tmunk-heilo hihi kod\tresetovat kód hry\tскинути дані гри\tapagar dados de jogo\nreset_game\tnew game\tnouveau jeu\tnovo jogo\tnuevo juego\tnuovo gioco\tновая игра\tnytt spel\tNeues Spiel\tلعبة جديدة\tbyrja á nýjum leik\túj játék\tnovan ludon\tuus mäng\t新游戏\tnowa\t新規ゲーム\tchi hihi\tnová hra\tнова гра\tnovo jogo\nreset_game_message\tStarting a new game will erase your old data. Consider exporting your work first! Are you sure you want to start over?\tCommencer un nouveau jeu écrasera vos anciennes données. Pensez à exporter votre travail d'abord ! Êtes vous sûr de vouloir repartir de zero ?\tComeçar um jogo novo irá eliminar toda a data do antigo. Considera a exportação do teu trabalho primeiro. Tens a certeza que queres começar de novo?\tCrear un nuevo juego va a borrar tu data vieja. Considera exportar tu trabajo primero! Empezar de cero?\tIniziare un nuovo gioco cancellerà i dati esistenti. Hai pensato di esportarne una copia, prima? Sei sicuro di voler ripartire da zero?\tначав работу над новой игрой, вы сбросите все нынешние данные. вы можете сперва скачать свою игру. вы уверены, что хотите начать заново?\tNär du startar ett nytt spel raderas gammal data. Du kan exportera data först! Är du säker att du vill börja om?\tNeustarten des Spiels wird deinen alten Daten löschen. Exportiere deine Arbeit vielleicht vorher! Möchtest du wirklich von vorn beginnen?\tبداية لعبة جديدة ستؤدي إلى مسح البيانات القديمة. يفضل أن تستخرج بيانات اللعبة أولاً! هل أنت متأكد من أنك تريد البدأ من جديد؟\tAð byrja á nýjum leik getur eytt núverandi leikjagögnum. Íhugaði að niðurhala leikjagögnum áður! Ertu viss um að þú viljir byrja á nýjum leik?\tAz új játék létrehozása törli a meglévő adatokat. Lehet, hogy exportálni kéne előbb a munkádat. Biztos, hogy elölről akarod kezdeni?\tSe vi kreas novan ludon, via antaŭa ludodosiero foriĝos. Bonas konservi la dosieron antaŭ ol krei novan ludon. Ĉu vere vi volas krei novan ludon?\t\t开始一个新游戏会覆盖你的旧数据。请先考虑导出你的作品!你确定要重新来吗?\tRozpoczęcie nowej gry usunie twoje stare dane. Rozważ najpierw wyeksportowanie swojej pracy! Czy na pewno chcesz zacząć od nowa?\t\tspos maika mamook chi hihi, chako heilo maika ol hihi. klosh kakwa?\t\tПочаток нової гри видалить старі дані. Радимо експортувати свою роботу! Дійсно хочеш почати наново?\tAo começar um novo jogo, o seu antigo será apagado. Lembre-se de exportar o seu trabalho! Deseja recomeçar mesmo assim?\nroom_add_endings\tadd endings\tajouter une fin\tadicionar finais\tañadir finales\taggiungi finali\tдобавить концовку\tlägg till slut\tEnde hinzufügen\tإضافة النهايات\tbæta við endalokum\tbefejezés hozzáadása\taldonu ludofinojn\tlisa lõpud\t增加结局\tdodaj zakończenia\tエンディング追加\tmash kopit-skwil\tvložit konec hry\tдодати закінчення\tadicionar finais\nroom_add_exits\tadd exits\tajouter une sortie\tadicionar saídas\tañadir salidas\taggiungi uscite\tдобавить точку выхода\tlägg till utgångar\tAusgang hinzufügen\tإضافة المخارج\tbæta við útgönguleiðum\tkijárat hozzáadása\taldonu pordojn\tlisa väljapääsud\t增加出口\tdodaj przejścia\t出口追加\tmash lapot\tvložit průchod\tдодати виходи\tadicionar saídas\nroom_add_markers\tadd exits & endings\tajouter des sorties & des fins\tadicionar saídas e finais\tañadir salidas y finales\taggiungi finali e uscite\tдобавить точки выхода и концовки\tlägg till utgångar och slut\tFüge Übergänge & Enden hinzu\tإضافة مخارج و نهايات\tbæta við útgönguleiðum og endalokum\tkijárat vagy befejezés hozzáadása\taldonu pordojn k ludofinojn\t\t加入退出&结局\tdodaj przejścia i zakończenia\t\tmash lapot pi kopit-skwil\tpřidat průchody a konec hry\tдодати виходи та закінчення\tadicionar saídas & finais\nroom_label\troom\tsalle\tsala\tsala\tstanza\tпространство\trum\tRäume\tغرفة\trými\tszoba\tĉambro\ttuba\t房间\tpomieszczenie\t\tloom\tmístnost\tкімната\tcena\nroom_tool_advanced\troom tools\toutils d'édition de salle\toutras ferramentas para a edição de sala\therramientas de juego\tstrumenti della stanza\tнастройки пространства\trum-verktyg\tRaum-Werkzeuge\tأدوات الغرفة\trýmistól\tszoba eszközök\tĉambraĵoj\ttoa tööriistad\t房间工具\tnarzędzia pomieszczenia\tルームツール\tloom iktas\tnástroje místnosti\troom tools\topções de cena\nroom_tool_name\troom\tsalle\tsala\troom\tstanza\tпространство\trum\tRäume\tغرفة\trými\tszoba\tĉambro\ttuba\t房间\tpomieszczenie\tルーム\tloom\tmístnost\troom\tcena\nsequence_list_description\tgo through each item once in _:\t\t\t\togni frase una volta sola in _:\tпройтись по каждому объекту один раз в _:\t\tAlle Blöcke werden in _ eingeblendet.\tزيارة كل بند في الـ_ مرة واحدة:\tfarðu í gegnum hvern hlut einu sinni í _:\tmenj végig a tárgyakon ebben: _\t\t\t\tprzejdź przez każdą pozycję tylko raz w _:\t\t\t\tперебрати по одному разу всі пункти з _:\tpassar por cada item uma só vez em _:\nsequence_list_name\tsequence list\tliste de séquences\t\t\tlista sequesnza\tсписок последовательностей\t\tReihen-Liste\tقائمة التسلسل\traðar listi\tsorozat lista\t\t\t\tlista sekwencji\t\t\t\tсписок послідовності\tlista de sequência\nsequence_name\tsequence\tséquence\t\t\tsequenza\tпоследовательность\t\tReihe\tتسلسل\tröð\tsorozat\t\t\t\tsekwencja\t\t\t\tпослідовність\tsequência\nsettings_tool_name\tsettings\tparamètres\tdefinições\tajustes\timpostazioni\tнастройки\tinställningar\tEinstellungen\tالإعدادات\tstillingar\tbeállítások\telektaro\tseaded\t设置\tustawienia\t設定\tsetins\tnastavení\tналаштування\tconfigurações\nshuffle_list_description\t_ items in a random order:\t_\t\t\tfrasi in ordine _:\t_ объекты в случайном порядке:\t\tAlle Blöcke werden in _ eingeblendet.\t_ الأدوات بترتيب عشوائي\t_ hlutir í handhófskenndri röð:\t_ tárgy véletlen sorrendben:\t\t\t\t_ pozycje w przypadkowej kolejności\t\t\t\t_ пунктів в довільному порядку:\titens em _ por ordem aleatória:\nshuffle_list_name\tshuffle list\tmélanger liste\t\t\tlista casuale\tперемешать список\t\tZufalls-Liste\tقائمة مخلوطة\tuppstokkunarlisti\tvéletlen lista\t\t\t\tlista losowa\t\t\t\tсписок мішаніни\tlista de mistura\nshuffle_name\tshuffle\tmélanger\t\t\tcasuale\tперемешать\t\tZufall\tخلط\tstokka upp\tvéletlen\t\t\t\tlosowe\t\t\t\tмішаніна\tmistura\nsprite_label\tsprite\tsprite\tsprite\tsprite\tsprite\tспрайт\tsprite\tSprite\tرسمة تفاعلية\tsproti\tsprite\tenludano\tkuju\t精灵\tsprite\tスプライト\ttilikum\tobjekt\tспрайт\tator\nstop_game\tstop\tStop\tparar\tpausar\tstop\tостановить\tstopp\tSpiel stoppen\tأقف\tstop\tstop\tmalkomencu\t\t停止\tzatrzymaj\t\tkopit nanich\tzastavit\tзупинити\tparar\ntext_direction_description\tOption for languages that read right to left\tOptions pour les langues qui se lisent de la droite vers la gauche\tOpção para linguagens que se lêem da direita para a esquerda\tOpciones para idiomas que se leen de derecha a izquierda\tOpzione per le lingue che si leggono da destra a sinistra\tОпция для языков, которые читаются справа налево\tinställning för språk som skrivs från höger till vänster\tOptionen für Sprachen, die von Rechts nach Links gelesen werden\tخيار للغات التي تقرأ من اليمين إلى اليسار\tValmöguleiki fyrir tungumál sem er lesið frá hægri til vinstri.\tOpció jobbról balra író nyelvek használatához\tElektu por lingvoj kiel la araba, hebrea, judgermana...\t\t语言从右向左选项\tOpcje dla języków czytanych od prawej strony do lewej\t\tspos maika pipa kuli kakwa pastin pi lushan pi pasiuks man tlaska pipa. spos maika pipa kuli kakwa alab pi shyu tlaska pipa.\t\tОпція для мов з напрямком справа наліво\tOpção para línguas lidas da direita para a esquerda.\ntext_direction_label\ttext direction\tdirection du texte\tdirecção do texto\tdirección del texto\tdirezione del testo\tнаправление текста\ttextriktning\tText-Richtung\tإتجاة الخط\ttextastefna\tszöveg iránya\ttekstokielo\t\t文字方向\tkierunek tekstu\t\tpipa kuli\t\tнапрямок тексту\tdireção de texto\ntext_direction_ltr\tLeft to Right\tDe la Gauche vers la Droite\tEsquerda para a direita\tIzquierda a Derecha\tDa sinistra a destra\tслева направо\tvänster till höger\tLinks nach Rechts\tمن اليسار إلى اليمين\tVinstri til hægri\tbalról jobbra\tMaldekstre Dekstren\t\t从左向右\tOd lewej do prawej\t\tkakwa pastin\t\tзліва направо\tEsquerda para Direita\ntext_direction_rtl\tRight to Left\tDe la Droite vers la Gauche\tDireita para a esquerda\tDerecha a Izquierda\tDa destra a sinistra\tсправа налево\thöger till vänster\tRechts nach Links\tمن اليمين إلى اليسار\tHægri til vinstri\tjobbról balra\tMaldekstren Dekstre\t\t从右向左\tOd prawej do lewej\t\tkakwa shyu\t\tсправа наліво\tDireita para Esquerda\ntile_label\ttile\ttile\ttile\ttile\ttile\tтайл\truta\tTile\tرقعة\tflís\ttile\tplanko\tkild\t地块\tkafelek\tタイル\tlaplash\tprostředí\tплитка\tdetalhe\ntitle_placeholder\tTitle\tTitre\tTítulo\tTitúlo\tTitolo\tНазвание\ttitel\tTitel\tعنوان\tTitill\tCím\tLudonomo\t\t\b标题\tTytuł\t\thihi nim\t\tНазва\tTítulo\ntools_label\ttools\toutils\tferramentas\tHerramientas\tStrumenti\tпанель инструментов\tverktyg\tWerkzeuge\tأدوات\ttól\teszközök\tiloj\ttööriistad\t工具\tnarzędzia\tツール\tmamook-iktas\tnástroje\tінструменти\tferramentas\ntransition_fade_b\tfade (black)\tfondu (noir)\t\t\tdissolvenza (nero)\tзатухание (в чёрное)\t\tVerblassen (schwarz)\tتلاشي إلى أسود\thverfa (í svart)\táttűnés (fekete)\t\t\t\twyciemnienie\t\t\t\tзатухання (чорний)\tdesmaecer (preto)\ntransition_fade_w\tfade (white)\tfondu (blanc)\t\t\tdissolvenza (bianco)\tзатухание (в белое)\t\tVerblassen (weiß)\tتلاشي إلى أبيض\thverfa (í hvítt)\táttűnés (fehér)\t\t\t\trozjaśnienie\t\t\t\tзатухання (білий)\tdesmaecer (branco)\ntransition_slide_d\tslide down\tglisser vers le bas\t\t\tscorri verso il basso\tсдвиг вниз\t\tRunterschieben\tالزحف للأسفل\trenna niður\tlecsúszás\t\t\t\tprzesuń w dół\t\t\t\tковзання вниз\tdeslizar (baixo)\ntransition_slide_l\tslide left\tglisser vers la gauche\t\t\tscorri a sinistra\tсдвиг влево\t\tNach Links schieben\tالزحف لليسار\trenna til vinstri\tbalra csúszás\t\t\t\tprzesuń w lewo\t\t\t\tковзання ліворуч\tdeslizar (esquerda)\ntransition_slide_r\tslide right\tglisser vers la droite\t\t\tscorri a destra\tсдвиг вправо\t\tNach Rechts schieben\tالزحف لليمين\trenna til hægri\tjobbra csúszás\t\t\t\tprzesuń w prawo\t\t\t\tковзання праворуч\tdeslizar (direita)\ntransition_slide_u\tslide up\tglisser vers le haut\t\t\tscorri verso l'alto\tвдвиг вверх\t\tHochschieben\tالزحف للأعلى\trenna upp\tfelcsúszás\t\t\t\tprzesuń w górę\t\t\t\tковзання вгору\tdeslizar (cima)\ntransition_tunnel\ttunnel\ttunnel\t\t\ttunnel\tтоннель\t\tTunnel\tنفق\tgöng\talagút\t\t\t\ttunel\t\t\t\tтунель\ttúnel\ntransition_wave\twave\tvague\t\t\tonda\tволна\t\tWelle\tموجي\talda\thullám\t\t\t\tfalowanie\t\t\t\tволна\tondular\nunsupported_features\tuh oh ~ your browser doesn't support all of bitsy's features, so some things might not work right. maybe try another browser, such as:\toh oh ~ ton navigateur internet ne supporte pas toutes les fonctionnalités de Bitsy, certaines choses pourraient ne pas fonctionner correctement, essaie un autre navigateur, comme :\tuh oh - o teu explorador não suporta todas as funcionalidades do Bitsy, por isso algumas coisas poderão não funcionar correctamente. talvez queiras tentar outro explorador como:\toh oh! tu navegador no es compatible con todas las funcionalidades de Bitsy y es posible que algunas no funcionen correctamente. Puedes intentar utilizar otros como:\toh no! il tuo browser non è compatibile con tutte le funzionalità di Bitsy, ed è possibile che alcune non funzionino bene. Puoi usarne altri come:\tупс ~ ваш браузер не поддерживает все функции битси, поэтому что-то работать не будет. попробуйте другой браузер, например:\toj då ~ bitsy har vissa funktioner som inte stöds av din webbläsare, så allt kanske inte fungerar som det ska. du kan prova med en annan webbläsare, till exempel:\tOh oh - Dein Browser unterstützt nicht alle Funktionen von Bitsy. Manche Dinge könnten nicht funktionieren, probiere doch einen der anderen Browser aus:\tمتصفحك لا يدعم جميع ميزات بيتسي، و لهذا، بعض الوظائف قد لا تعمل. من الأفضل أن تجرب أحد المتصفحات التالية:\tæji nei ~ vafrinn sem þú notar styður ekki alla eiginleika bitsy, þannig að nokkrir hlutir munu ef til vill ekki virka. Ef þú getur prófaðu að nota aðra vafra eins og:\tajjajj ~ a böngésződ nem támogatja az összes bitsy funkciót, próbálj ki egy másik böngészőt, mondjuk ezeket:\tho ve ~ via retvidilo ne povas ĉiujn aferojn de Bitsy, do eble iuj aferoj ne funkcios ĝuste. vi povas provi alian retvidilon, ekzemple:\tups ~ sinu brauser ei toeta kõiki bitsy funktsioone, nii et mõned asjad ei pruugi õigesti töötada. Võibolla proovi teist brauserit, nagu näiteks:\t啊噢~你的浏览器不完全支持 Bitsy ,有些功能无法正确运行。试试其他的浏览器吧,比如:\to nie ~ twoja przeglądarka nie wspiera wszystkich funkcjonalności bitsy, więc niektóre rzeczy mogą nie działać prawidłowo. możesz wypróbować inną przeglądarkę, taką jak:\tあらら、残念ながら使用中のブラウザでは一部のBitsyの機能が使用できないようです。次のブラウザを使ってみるといいかもしれません:\taaana ~ wik maika nanich-inanet ikta klosh kopa nanich kanawei bitsy iktas. klosh maika tlai huloima nanich-inanet ikta, kakwa:\t\tйой ~ ваш браузер не підтримує всі функції Бітсі, щось може не працювати. Пропоную спробувати інший браузер, такий як:\tvish ~ o seu navegador não suporta todas as funções do bitsy, então algumas coisas não funcionam. tente um desses navegadores:\nunsupported_features_dismiss\tI'll be ok, hide this message\tj'ai compris, cacher ce message\teu compreendo, esconde esta mensagem\tEntiendo. Oculta este mensaje.\tCapito. Nascondi questo messaggio.\tсо мной всё будет в порядке, спрятать это сообщение\tdet går bra, göm det här meddelandet\tIst in Ordnung. Verberge diese Nachricht\tسأكون بخير، أخفي هذه الرسالة\tekkert mál, feldu þessi skilaboð\tköszi, de megoldom, üzenet elrejtése\tmi ne zorgas. malvidu ĉi informon\tkõik on korras, peida see sõnum\t没关系,隐藏这条消息\tWszystko w porządku, ukryj tę wiadomość\tなんとかなるので、このメッセージを隠す\tklosh naika. ipui okok sisim.\t\tнай буде, приховати повідомлення\tTudo certo, pode esconder a mensagem\nupload_font\tupload font\tcharger une police d'écriture\tcarregar uma fonte para utilização\tsubir fuente\tCarica font\tзагрузить шрифт\tladda upp typsnitt\tSchrift hochladen\tأرفع الخط\thala upp letri\tbetűtípus feltöltése\tlegu tiparon\t\t上传字体\twyślij czcionkę\t\tmash pipa-tsum\tnahrát font\tвідвантажити шрифт\tcarregar fonte\nupload_game\tupload game\tcharger un jeu\tcarregar um jogo para edição\tsubir juego\tCarica gioco\tзагрузить игру\tladda upp spel\tSpiel hochladen\tأرفع اللعبة\thala upp leik\tjáték feltöltése\tlegu (alŝutu) ludon\t\t上传游戏\twyślij grę\t\tmash hihi\tnahrát hru\tвідвантажити гру\tcarregar jogo\nvalue_type_text\ttext\ttexte\t\t\ttesto\tтекст\t\tText\tنص\ttexti\tszöveg\t\t\t\ttekst\t\t\t\tтекст\ttexto\nvariable_add\tadd variable\tajouter une variable\tadicionar variável\tañadir variable\taggiungi variabile\tдобавить переменную\tlägg till variabel\tVariable hinzufügen\tإضافة متغير\tný breyta\tváltozó hozzáadása\taldonu ŝangeblan numeron\tlisa muutuja\t加入变量\tdodaj zmienną\t変数の追加\tmash huyhuy-nampa\tpřidat proměnnou\tдодати змінну\tcolocar variável\nvariable_label\tvariable\tvariable\tvariável\tvariables\tvariabili\tпеременная\tvariabel\tVariablen\tمتغير\tbreyta\tváltozó\tŝangebla numero\tmuutuja\t变量\tzmienna\t変数\thuyhuy-nampa\tproměnná\tзмінна\tvariável\nvariables_label\tvariables\tvariables\tvariáveis\tvariables\tvariabili\tпеременные\tvariabler\tVariablen\tمتغيرات\tbreytur\tváltozók\tŝanĝeblaj numeroj\tmuutujad\t变量\tzmienne\t変数\thuyhuy-nampa\tproměnné\tзмінні\tvariáveis\nversion_notes_title\twhat's new in\tquoi de neuf\to que há de novo na\tqué hay de nuevo\tcosa c'è di nuovo\tчто нового в\tnyheter i\tWas gibt's Neues in\tما هو جديد في\tnýtt í\tújdonságok:\tkio novas en\tmida on uut:\t新东西:\tco nowego w\t新機能:\tchi ikta kopa\t\tщо нового у\to que tem de novo no\nwall_toggle\twall\tmur\tparede\tpared\tmuro\tстена\tvägg\tWand\tحائط\tveggur\tfal\tmuro\tsein\t墙\tściana\t壁\tkalahan\tzeď\tстіна\tparede\nwalls_toggle_visible\twalls\tmurs\tparedes\tmuros\tmuri\tстены\tväggar\tWände\tحوائط\tveggir\tfalak\tmuroj\tseinad\t墙\tściany\t壁\tkalahan\tzdi\tстіни\tparedes\ngeneral_edit\tedit\t\t\t\t\tредактировать\t\t\t\t\t\t\t\t\t\t\t\t\t\teditar\ngeneral_length\tlength\t\t\t\t\tдлина\t\t\t\t\t\t\t\t\t\t\t\t\t\tduração\ngeneral_speed\tspeed\t\t\t\t\tскорость\t\t\t\t\t\t\t\t\t\t\t\t\t\tvelocidade\nroom_eyedropper_pick\tpick\t\t\t\t\tвыбор\t\t\t\t\t\t\t\t\t\t\t\t\t\textrair\nblip_tool\tblip-o-matic\t\t\t\t\tзвуки\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodi-za-dô\nblip_sfx\tblip\t\t\t\t\tsfx импульса\t\t\t\t\t\t\t\t\t\t\t\t\t\tplim\nblip_pitch\tpitch\t\t\t\t\tвысота импульса\t\t\t\t\t\t\t\t\t\t\t\t\t\taltura\nblip_generator\tgenerator\t\t\t\t\tгенератор\t\t\t\t\t\t\t\t\t\t\t\t\t\tsom de base\ntune_tool\ttune\t\t\t\t\tмузыка\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia\ntune_bar\tbar\t\t\t\t\tпанель\t\t\t\t\t\t\t\t\t\t\t\t\t\tbarra\ntune_compose\tcompose\t\t\t\t\tсочинить\t\t\t\t\t\t\t\t\t\t\t\t\t\tcompor\ntune_instrument\tinstrument\t\t\t\t\tинструмент\t\t\t\t\t\t\t\t\t\t\t\t\t\tinstrumento\ntune_style\tstyle\t\t\t\t\tстиль\t\t\t\t\t\t\t\t\t\t\t\t\t\testilo\ntune_note\tnote\t\t\t\t\tзаметка\t\t\t\t\t\t\t\t\t\t\t\t\t\tnota\ntune_arp_off\tstrum off\t\t\t\t\tбренчание выкл.\t\t\t\t\t\t\t\t\t\t\t\t\t\tsem dedilhado\ntune_arp_up\tstrum chord (up)\t\t\t\t\tаккорд бренчания (вверх)\t\t\t\t\t\t\t\t\t\t\t\t\t\tdedilhar acorde (cima)\ntune_arp_down\tstrum chord (down)\t\t\t\t\tаккорд бренчания (вниз)\t\t\t\t\t\t\t\t\t\t\t\t\t\tdedilhar acorde (baixo)\ntune_arp_int5\tstrum interval (small)\t\t\t\t\tинтервал бренчания (небольшой)\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalo (pequeno)\ntune_arp_int8\tstrum interval (big)\t\t\t\t\tинтервал бренчания (большой)\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalo (grande)\ntune_wave_pulse2\ttone P2\t\t\t\t\tтон P2\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P2\ntune_wave_pulse4\ttone P4\t\t\t\t\tтон P4\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P4\ntune_wave_pulse8\ttone P8\t\t\t\t\tтон P8\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P8\ntune_tempo_slow\tslow\t\t\t\t\tмедленно\t\t\t\t\t\t\t\t\t\t\t\t\t\tlenta\ntune_tempo_med\tmedium\t\t\t\t\tсредне\t\t\t\t\t\t\t\t\t\t\t\t\t\tmédia\ntune_tempo_fast\tfast\t\t\t\t\tбыстро\t\t\t\t\t\t\t\t\t\t\t\t\t\trápida\ntune_tempo_xfast\tturbo\t\t\t\t\tтурбо\t\t\t\t\t\t\t\t\t\t\t\t\t\tturbo\ntune_key\tkey\t\t\t\t\tлад\t\t\t\t\t\t\t\t\t\t\t\t\t\ttom\ntune_key_basic\tmood\t\t\t\t\tнастрой\t\t\t\t\t\t\t\t\t\t\t\t\t\tritmo\ntune_key_major\tcheery\t\t\t\t\tвеселый\t\t\t\t\t\t\t\t\t\t\t\t\t\tfeliz\ntune_key_minor\tgloomy\t\t\t\t\tмрачный\t\t\t\t\t\t\t\t\t\t\t\t\t\ttriste\nsetting_text_mode\ttext mode\t\t\t\t\tтекстовый режим\t\t\t\t\t\t\t\t\t\t\t\t\t\testilo de texto\nsetting_text_hirez\tdefault\t\t\t\t\tсглаженный\t\t\t\t\t\t\t\t\t\t\t\t\t\tpadrão\nsetting_text_lorez\tchunky\t\t\t\t\tрубленный\t\t\t\t\t\t\t\t\t\t\t\t\t\tlargo\nfind_tool\tfind\t\t\t\t\tпоиск\t\t\t\t\t\t\t\t\t\t\t\t\t\tbuscar\nfind_all\tall\t\t\t\t\tнайти все\t\t\t\t\t\t\t\t\t\t\t\t\t\ttudo\n",
+ "localization.tsv": "id\ten\tfr\tpt\tes\tit\tru\tsv\tde\tar\tis\thu\teo\tet\tzh\tpl\tja\tchn\tcze\tuk\tbr\nabout_tool_name\tabout\tà propos\tsobre\tsobre\tA proposito di\tо битси\tinfo\tÜber\tعن\tupplýsingar\ta bitsyről\tpri\tbitsy kohta\t相关\tinfo\t説明\tikta bitsy?\tO bitsy\tпро Бітсі\tsobre\nabout_tool_title\tabout bitsy\tà propos de Bitsy\tsobre o Bitsy\tSobre Bitsy\tA proposito di Bitsy\tо битси\tom bitsy\tÜber Bitsy\tعن بيتسي\tum bitsy\ta bitsyről\tpri Bitsy\tbitsy kohta\t关于 Bitsy\to bitsy\tBitsyについて\tikta bitsy?\tO Bisty\tпро Бітсі\tsobre o bitsy\nabout1\thi! bitsy is a little editor for little games or worlds. the goal is to make it easy to make games where you can walk around and talk to people and be somewhere.\tSalut ! Bitsy est un petit moteur de jeu vidéo pour des petits jeux ou des petits mondes. Le but est de rendre accessible la création de jeux dans lesquels tu peux te promener, parler à des personnes et découvrir de nouveaux environnements.\tolá! o bitsy é um pequeno editor para pequenos jogos ou mundos. o objectivo é tornar fácil a criação de jogos onde podes mover-te, falar com pessoas e estar em algum lado.\t¡Hola! Bitsy es un motor para crear pequeños juegos y mundos. El objetivo es crear juegos fácilmente en cualquier lugar.\tCiao! Bitsy è un piccolo motore, concepito per creare piccoli giochi e mondi. Lo scopo è creare giochi dove puoi gironzolare, parlare con qualcuno, ed essere da qualche parte.\tпривет! битси это небольшой редактор для маленьких игр или игровых миров. главная цель этой программы - сделать простым и доступным процесс создания игры, в которой ты можешь ходить, разговаривать и просто быть где-то.\thej! bitsy är ett litet verktyg för små spel eller världar. syftet är att det ska vara lätt att göra spel där du kan gå omkring, prata med folk och vara någonstans.\tHi! Bitsy ist ein kleiner Editor für kleine Spiele oder Welten. Ziel ist, möglichst einfach Spiele machen zu können, in denen man umherlaufen, mit Figuren reden und sich irgendwo wiederfinden kann.\tأهلاً! بيتسي عبارة عن محرر لتصميم الألعاب الصغيرة و العوالم. الهدف تسهيل عملية تصميم الألعاب التي يمكن أن تتحرك وتتحدث مع شخصيات مختلفة في عالمها.\thæ! bitsy er tól til að búa til litla leiki eða heima. tilgangur þess er að auðvelda gerð leikja þar sem þú getur gengið um og talað við fólk og verið til staðar.\tszia! a bitsy egy kicsi fejlesztői eszköz pici játékokhoz és apró világokhoz. a cél, hogy segítségével könnyen létrehozhass olyan játékokat, ahol barangolhatsz, beszélgethetsz emberekkel és felfedezhetsz új világokat.\tsaluton! Bitsy estas eta kreilo por ludetoj kaj mondetoj. Bitsy celas faciligi la kreadon de ludoj pri marŝadi tien-ĉi-tien, paroli ludulojn kaj ĝui la ludomondon.\ttere! bitsy on väike programm millega saab luua väikseid mänge või maailmaid. bitsy eesmärk on teha lihtsaks mängude tegemise, kus saab ringi jalutada, inimestega rääkida ja olla kuskil.\t你好!Bitsy 是一个为小游戏和小世界制作的小编辑器,目的是为了使得制作游戏更容易,你可以在这些游戏中随处走走,随意聊聊,或者去到某个地方。\tCześć! Bitsy to mały edytor do małych gier lub światów. Powstał by ułatwić tworzenie gier, w których możesz chodzić, rozmawiać z ludźmi, i sobie przebywać.\tこんにちは!bitsyはちょっとしたゲームや世界のための編集ツール。歩き回って人と話たり、いろんな場所に行ったりするようなゲームを簡単に作れるようにする事を目指しています。\ttlahowya! maika mamook hihi kopa okok bitsy. bitsy mamook tenas hihi, okok hihi klosh spos maika tiki tlatawa pi wawa tilikum, spos maika tiki wawa yahim.\t\tпривіт! бітсі це невеличкий редактор для маленьких ігор або світів. його мета - спростити створення ігр, де ти можеш ходити, розмовляти з людьми, та просто бути.\tolá! o bitsy é um pequeno editor para pequenos mundos ou jogos. a ideia é facilitar a criação de um jogo onde você possa andar, explorar e conversar em um lugarzinho só seu.\nabout10_link_twitter\tadam\tadam\tadam\tadam\tadam\tадам\tadam\tadam\tآدم\tadam\tadam\tAdam\tadam\tadam\tadam\tadam\tadam\t\tадам\tadam\nabout2\tnot sure where to start? try this helpful\tTu ne sais pas par où commencer ? Essaie ce\tsem certeza por onde começar? tenta este\t¿No sabes por dónde empezar? ¡Prueba este\tNon sai da dove cominciare? Da un occhiata a questo utile\tне знаете с чего начать? прочитайте эту прекрасную обучающую\tVet du inte var du ska börja? här finns en hjälpsam\tNicht sicher wie du loslegen kannst? Probiere diese hilfreiche\tلا تدري من أين تبدأ؟ يمكنك تجربة\tekki viss um hvar best er að byrja? prófaðu þessa hjálplegu\tnem tudod hogyan kezdj neki? akkor kövesd ezt a hasznos\tĉu ne scias vi kion fari je la komenco? se scipovas la anglan, legu ĉi utilan\tpole kindel kust alustada? proovi seda suurepärast\t不知道从哪里开始?这个或许会帮到你。\tnie wiesz gdzie zacząć? wypróbuj ten pomocny\tどこから始めればいいかわからないんですか?じゃあこれをみてください!\twik maika komtuks kata maika chi? klosh maika nanich okok klosh\t\tне знаєш з чого почати? спробуй цей корисний\tsem ideia de por onde começar? tente o nosso\nabout3_link_tutorial\tbitsy tutorial!\ttutoriel Bitsy !\ttutorial do bitsy!\ttutorial de Bitsy!\ttutorial di Bitsy!\tстатью про битси!\tbitsy-guide!\tBitsy Anleitung!\tدورة بيتسي التعليمية\tbitsy kennslu!\tbitsy segédletet!\tBitsy-helposkribo!\tbitsy õpetust!\tBitsy 教程!\tsamouczek bitsy!\tBitsyチュートリアル(英語)。\tbitsy hilp-pipa!\t\tпідручнік по Бітсі!\ttutorial do bitsy!\nabout4\tif you need inspiration, you can\tSi tu as besoin d'inspiration, tu peux\tse precisares de inspiração, podes\tSi necesitas inspiración, puedes\tSe hai bisogno d'ispirazione puoi\tесли вы ищите вдохновения,\tför att få mer inspiration kan du\tFalls du Inspiration suchst, kannst du\tتبحث عن الإلهام؟ لما لا\tef þig vantar innblástur geturðu\tha inspirációra van szükséged,\tse vi volas ideojn por kian ludon krei, provu\tkui vajad inspiratsiooni, võid\t如果你需要灵感,你可以\tjeśli szukasz inspiracji, możesz\tもしインスピレーションが欲しいならこれをみてください→\tspos wik maika komtuks ikta hihi maika mamook, klosh maika\t\tбракує натхнення? можеш\tse precisar se inspirar, você pode\nabout5_link_games\tplay other bitsy games.\tjouer à ces jeux Bitsy.\tjogar outros jogos do bitsy.\tjugar a otros juegos de Bitsy\tgiocare altri giochi creati con Bitsy\tсыграйте в другие битси-игры.\tspela andra bitsy-spel.\tandere Bitsy Spiele spielen\tتلعب بعض ألعاب بيتسي الآخرى\tspilað aðra bitsy leiki.\tjátssz más bitsy játékokkal,\tludi aliajn Bitsy-ludojn.\tmängida teisi bitsy mänge.\t试试其它用 Bitsy 制作的游戏\tzagrać w inne gry bitsy.\t他のBitsyで作られたゲーム。\tkultus hihi kopa huloima tilikum tlaska bitsy hihi.\t\tпограти в інші ігри з Бітсі.\tjogar outros jogos feitos com bitsy.\nabout6\tyou can find many on\tTu peux aussi en trouver plein d'autres sur\tpodes encontrar muitos no\t¡Puedes encontrar otros títulos en\tPuoi trovarne altri su\tмногие из них можно найти на сайте\tdu kan hitta många på\tDu kannst viele von ihnen auf\tيمكنك أن تجد الكثير على\tþú finnur marga slíka á\tvan egy csomó itt:\tekzistas multaj ludoj ĉe\tmitmeid võib leida\t你可以在这里找到很多\tznajdziesz je na\t多くのゲームはここで見つけられます→\thayu bitsy hihi mitlayt kopa\t\tБагато з них можна знайти на\tvocê pode encontrar vários em\nabout7_link_itchio\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\titch.io\nabout8\t, which is a great place to share games!\t, qui est un endroit fantastique pour partager des jeux!\t, um sítio fantástico para partilhar jogos!\tque es un gran lugar para compartir tus creaciones!\tche è un bellissimo luogo per condividere le tue creazioni!\t, это отличное место для того, чтобы делиться своими играми!\t, ett bra ställe att dela sina spel!\tfinden, was auch ein toller Ort ist um deine Spiele zu teilen!\tفأنه المكان الأمثل لمشاركة الألعاب!\t, sem er frábær staður til að deila leikjum!\t- ez egy tök jó oldal ahol megoszthatod a játékaidat!\t, kiu estas bonega retejo por disdoni ludojn!\tlehelt - üks imepärane koht, kus jagada mänge!\t,这是个很棒的分享游戏的地方!\t, które jest świetnym miejscem do dzielenia się grami!\t、このサイトは作ったゲームをシェアするのにおすすめです!\t, okok itch.io klosh twa-pipa spos maika tiki munk-nanich huloima tilikum maika bitsy hihi!\t\t, це чудове місце для поширення ігор!\t, um ótimo lugar para compartilhar jogos!\nabout9\thave fun making things with bitsy!\tAmuse-toi bien avec Bitsy!\tdiverte-te a criar coisas com o bitsy!\t¡Diviértete creando en Bitsy!\tDivertiti creando con Bitsy!\tи пусть творческий процесс будет весёлым и захватывающим!\thoppas du har roligt med bitsy!\tViel Spaß beim Basteln mit Bitsy!\tو لا تنسى أن تستمتع و أنت تستخدم بيتسي!\tskemmtu þér vel við að skapa með bitsy!\tjó szórakozást a bitsyhez!\tĝuu kreadi per Bitsy!\tlõbutse bitsyga luues!\t希望你用 Bitsy 玩的开心!\tbaw się dobrze, tworząc z bitsy!\tBitsyでのゲームづくりを楽しんでください!\tnaika tiki spos maika hayu klosh-tumtum kopa mamook hihi kopa bitsy!\t\tНасолоджуйся творінням з Бітсі!\tdivirta-se criando com bitsy!\naction_add_new\tadd\tajouter\t\t\taggiungi\tдобавить\t\tHinzufügen\tإضافة\tbæta við\thozzáadás\t\t\t\tdodaj\t\t\t\tдодати\tcolocar\naction_back\tback\tretour\t\t\tindietro\tназад\t\tZurück\tرجوع\tbakka\tvissza\t\t\t\twstecz\t\t\tzpět\tназад\tvoltar\naction_cancel\tcancel\tannuler\t\t\tcancella\tотмена\t\tAbbrechen\tإلغاء\thætta við\tmégsem\t\t\t\tanuluj\t\t\t\tскасувати\tcancelar\naction_save\tsave\tsauvegarder\t\t\tsalva\tсохранить\t\tSpeichern\tحفظ\tvista\tmentés\t\t\t\tzapisz\t\t\tuložit\tзберегти\tsalvar\nanimation_frame1\tframe 1\tcalque 1\tframe 1\tframe 1\tframe 1\tкадр 1\tbild 1\tFrame 1\tإطار ١\trammi 1\t1. képkocka\tmovo 1\tkaader 1\t第1帧\tklatka 1\tフレーム1\thulel 1\tframe 1\tкадр 1\tquadro 1\nanimation_frame2\tframe 2\tcalque 2\tframe 2\tframe 2\tframe 2\tкадр 2\tbild 2\tFrame 2\tإطار ٢\trammi 2\t2. képkocka\tmovo 2\tkaader 2\t第2帧\tklatka 2\tフレーム2\thulel 2\tframe 2\tкадр 2\tquadro 2\nanimation_preview\tpreview\taperçu\tpré-vizualizar\tprevisualizar\tanteprima\tпредпросмотр\tförhandsvisning\tVorschau\tمعاينة\tsýnishorn\telőnézet\tantaŭvido\teelvaade\t预览\tpodgląd\tプレビュー\ttenas nanich\tnáhled\tперегляд\tprévia\nanimation_title\tanimation\tanimation\tanimação\tanimación\tanimazione\tанимация\tanimation\tAnimation\tالرسوم المتحركة\threyfimynd\tanimáció\tmovanta bildo\tanimatsioon\t动画\tanimacja\tアニメーション\thulel-picha\tanimace\tанімація\tanimação\nauthor_by\tby\tpar\tde\tpor\tdi\tот\tav\tvon\tبواسطة\teftir\tkészítette:\tde\tautor:\t来自\tautor\tby\tkopa\tautor\tвід\tpor\nauthor_twitter\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\navatar_label\tavatar\tavatar\tavatar\tavatar\tavatar\tаватар\tavatar\tAvatar\tالشخصية الممثل\tleikpeð\tavatár\tmovulo\tavatar\t人物\tawatar\tアバター\ttayi picha\tavatar\tаватар\tavatar\naxis_x_label\tx\tx\t\t\tx\tx\t\tx\tx\tx\tx\t\t\t\tx\t\t\tx\tx\tx\naxis_y_label\ty\ty\t\t\ty\ty\t\ty\ty\ty\ty\t\t\t\ty\t\t\ty\ty\ty\nbitsy_title\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tБитси\tBitsy\tBitsy\tبيتسي\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tBitsy\tБітсі\tBitsy\nbranch_add\tadd branch\tajouter un choix\t\t\taggiungi ramificazione\tдобавить ветвь\t\tVerzweigung hinzufügen\tإضافة فرع\tbæta við grein\telágazás hozzáadása\t\t\t\tdodaj gałąź\t\t\t\tдодати гілку\tcolocar opção\nbranch_type_default\tdefault branch\tchoix par défaut\t\t\tramificazione di default\tветвь по умолчанию\t\tdann\tفرع\tsjálfgefin grein\talapértelmezett elágazás\t\t\t\tgałąź domyślna\t\t\t\tгілка за замовчуванням\topção padrão\nbranch_type_item\titem branch\tchoix avec un objet\t\t\tramificazione con oggetti\tтип ветви объекта\t\tWenn Objekt\tفرع معتمد على أداة\thluta grein\ttárgy elágazás\t\t\t\tgałąź przedmiotu\t\t\t\tгілка з предметом\topção de item\nbranch_type_variable\tvariable branch\tchoix avec une variable\t\t\tramificazione con variabili\tпеременная ветви\t\tWenn Variable\tفرع معتمد على متغير\tbreytu grein\tváltozó elágazás\t\t\t\tgałąź zmiennej\t\t\t\tгілка зі змінною\topção de variável\nbranching_list_description\tgo to the first branch whose condition is true:\taller au premier choix dont la condition est vraie\t\t\tsegui la prima ramificazione la cui condizione è vera:\tперейдите к первой ветви, условие которой истинно:\t\tZeige den ersten Block in dem eine Bedingung erfüllt ist:\tالإتجاة إلى الفرع الأول إذا كان الشرط صحيح:\tfarðu að fyrstu greininni þar sem aðstæður eru sannar:\tmenj az első elágazáshoz, ami igaz:\t\t\t\tprzejdź do pierwszej gałęzi, której warunek jest spełniony:\t\t\t\tпіти на першу гілку, де умова справджується:\tir à primeira opção de condição verdadeira:\nbranching_list_name\tbranching list\tliste à choix\t\t\tlista ramificata\tветвящийся список\t\tVerzweigte Liste\tقائمة متفرعة\tgreinar listi\tszétágazó lista\t\t\t\tlista rozgałęzień\t\t\t\tсписок-розгалуження\tlista de opções\nbrowser_chrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tChrome\tكروم\tchrome\tchrome\tChrome\tChrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\tchrome\nbrowser_firefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tFirefox\tفيرفوكس\tfirefox\tfirefox\tFirefox\tFirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\tfirefox\nclose_label\tclose\tfermer\tfechar\tcerrar\tchiudi\tзакрыть\tstäng\tSchließen\tأغلق\tloka\tbezárás\tmalvidu\tsulge\t关闭\tzamknij\t\tipui\tzavřít\tзакрити\tfechar\ncondition_else_if_label\telse if\tsinon si\t\t\taltrimenti se\tиначе, если\t\tsonst - Wenn\telse if\tannars ef\tkülönben, ha\t\t\t\tw przeciwnym wypadku, jeżeli\t\t\t\tінакше, якщо\tou se\ncondition_else_label\telse\tsinon\t\t\taltrimenti\tиначе\t\tsonst\telse\tannars\tkülönben\t\t\t\tw przeciwnym wypadku\t\t\t\tінакше\tou\ncondition_if_label\tif\tsi\t\t\tse\tесли\t\tWenn\tif\tef\tha\t\t\t\tjeżeli\t\t\t\tякщо\tse\ncondition_then_label\tthen\tpuis\t\t\tallora\tзатем\t\tdann\tthen\tþá\takkor\t\t\t\twtedy\t\t\t\tто\tentão\ncondition_type_custom\tcustom\tcustomisée\tcostumizado\tpersonalizado\tpersonalizzato\tспециальное условие\tanpassa\tBenutzerdefiniert\tمعدل\tsérval\tegyedi\tlaŭmia\tmäära oma tingimus\t自定义\tużytkownika\tカスタム\thuloima\tvlastní\tвласний\tcustom\ncondition_type_default\tdefault\tpar défault\tpadrão\tpor defecto\tdefailt\tпо умолчанию\tstandard\tStandard\tافتراضي\tsjálfgefið\talapértelmezett\tnormala\tvaikevalik\t默认\tdomyślna\tデフォルト\tkwan\tvýchozí\tза замовчуванням\tpadrão\ncycle_list_description\trepeat items in a _:\t\t\t\tfrasi in _ che si ripetono:\tповторить объекты в _:\t\tAlle Blöcke werden in _ eingeblendet.\tتكرار البنود في _:\tendurtaktu hluti í _:\tismételd a tárgyakat ebben: _\t\t\t\tpowtarzaj pozycje zapętlone w _:\t\t\t\tповторювати пункти з _:\trepetir itens em _:\ncycle_list_name\tcycle list\tnom de la boucle\t\t\tlista serie\tсписок циклов\t\tSchleifen-Liste\tقائمة دائرية\tlotu listi\tismétlés lista\t\t\t\tlista cyklu\t\t\t\tсписок-цикл\tlista de ciclo\ncycle_name\tcycle\tboucle\t\t\tserie\tцикл\t\tSchleife\tدائري\tlota\tismétlés\t\t\t\tcykl\t\t\t\tцикл\tciclo\ndata_tool_name\tdata\tdonnées du jeu\tdados do jogo\tdatos del juego\tdati del gioco\tигровые данные\tspeldata\tSpieldaten\tبيانات اللعبة\tleikjagögn\tjátékadatok\tludodosiero\tmängu andmed\t游戏数据\tdane\tゲームデータ\thihi kod\tkód hry\tдані гри\tcódigo de jogo\ndefault_end_dlg\tThe end\tFin\tFim\tFin\tFine\tконец\tslut\tEnde\tالنهاية\tEndir\tItt a vége, fuss el véle!\tLudofino\t\t结束\tKoniec\t\talta kopit hihi\t\tКінець\tFim\ndefault_item_dlg\tYou found a nice warm cup of tea\tVous avez trouvé une bonne tasse de thé chaud\tEncontraste um copo com chá quentinho\tEncontraste una buena taza de té\tHai trovato una buona tazza di tè caldo\tвы нашли чашку тёплого чая\tdu hittade en varm kopp te\tDu hast einen schönen warmen Tee gefunden\tلقد وجدت كوب دافئ من الشاي\tÞú fannst heitann bolla af te\tTaláltál egy bögre forró teát\tVi trovis belan, varman tason da teo\t\t你找到了一杯暖心的茶\tZnajdujesz kubek ciepłej herbaty\t\tnaika tlap 1 uskan wam tipso-chuk!\t\tВи знайшли чашку запашного гарячого чаю\tVocê encontrou um chá quentinho\ndefault_sprite_dlg\tI'm a cat\tJe suis un chat\tSou um gato\t\tSono un gatto\tя котейка\tjag är en katt\tIch bin eine Katze\tأنا قطة\tÉg er köttur\tÉn egy cica vagyok\tMi estas kato\t\t我是一只猫\tJestem kotem\t\tpuspus naika\tJá jsem kočka.\tЯ киця\tMeow. Que foi?\ndefault_title\tWrite your game's title here\tEcris le nom de ton jeu ici\tEscreve o título do teu jogo aqui\tEscribe el título de tú juego aqui\tScrivi qui il titolo del tuo gioco\tНапишите название вашей игры здесь\tSkriv spelets titel här\tHier solltest du deinen Spieltitel hinschreiben\tأكتب عنوان لعبتك هنا\tSkrifaðu heiti leiksins þíns hér\tÍrd ide a játékod címét\tSkribu vian ludonomon\tKirjuta oma mängu pealkiri siia\t在这里写下你的游戏标题\tWpisz tutaj tytuł swojej gry\tゲームのタイトルを入力\thihi nim\tZde napište název vaší hry.\tНапиши назву гри тут\tDigite o título do seu jogo aqui\ndestination_label\tdestination\tdestination\tdestino\tdestino\tdestinazione\tточка прибытия\tdestination\tAusgang\tالمقصد\tákvörðunarstaður\térkezés\tcelo\t\t目的地\tcel przejścia\t\tko\t\tпризначення\tdestino\ndialog_action_category_dialog\tdialog\tdialogue\t\t\tdialogo\tдиалог\t\tDialog\tحوار\tsamtal\tpárbeszéd\t\t\t\tdialog\t\t\trozhovor\tдіалог\tdiálogo\ndialog_action_category_exit\texit and ending actions\t\t\t\tazioni di uscita e finale\tвыход и завершение действий\t\tAktionen: Übergänge & Enden\tتصرفات الخروج و النهاية\taðgerðir fyrir útgöngleiðir og endalok\tkijárat és befejezés akciók\t\t\t\takcje przejścia i zakończenia\t\t\t\tдії виходу та закінчень\tações de saída e final\ndialog_action_category_item\titem and variable actions\t\t\t\tazioni di variabili e oggetti\tдействия с объектами и переменными\t\tAktionen: Objekte & Variablen\tتصرفات الأدوات و المتغيرات\taðgerðir fyrir hluti og breytur\ttárgy és változó akciók\t\t\t\takcje przedmiotów i zmiennych\t\t\t\tдії предметів та змінних\tações de item e variável\ndialog_action_category_list\tlists\tlistes\t\t\tliste\tсписки\t\tListen\tقائمة\tlistar\tlisták\t\t\t\tkategorie list\t\t\tseznam\tсписки\tlistas\ndialog_action_item_decrease\tdecrease item count\t\t\t\tdiminuisci conteggio oggetti\tуменьшить счётчик объекта\t\tObjekt-Anzahl verringern\tتقليص عدد الأدوات\tfækka tölu hluta\ttárgyak számának csökkentése\t\t\t\tzmniejsz liczbę przedmiotów\t\t\t\tзменшити кількість предмету\tdiminuir itens\ndialog_action_item_increase\tincrease item count\t\t\t\taumenta conteggio oggetti\tувеличить счётчик объекта\t\tObjekt-Anzahl erhöhen\tزيادة عدد الأداوات\tfjölga tölu hluta\ttárgyak számának növelése\t\t\t\tzwiększ liczbę przedmiotów\t\t\t\tзбільшити кількість предмету\taumentar itens\ndialog_action_item_set\tset item count\t\t\t\timposta conteggio oggetti\tзадать значение счётчика\t\tObjekt-Anzahl festlegen\tتعيين عدد الأدوات\tsetja tölu hluta\ttárgyak számának beálltása\t\t\t\tustaw liczbę przedmiotów\t\t\t\tвстановити кількість предмету\tdefinir itens\ndialog_action_locked_set\tlock / unlock\tverrouiller/déverrouiller\t\t\tblocca/sblocca\tзаблокировать / разблокировать\t\tsperren / entsperren\tقفل / فتح\tlæsa / aflæsa\tnyitás / zárás\t\t\t\tzablokuj / odblokuj\t\t\t\tзамкнути / відімкнути\ttrancar / destrancar\ndialog_action_variable_change\tchange variable value\t\t\t\tcambia valore variabile\tизменить значение переменной\t\tVariablen-Wert ändern\tتغيير قيمة المتغير\tbreyta gildi í breytu\tváltozó értékének módoítása\t\t\t\tzmień wartość zmiennej\t\t\t\tзмінити значення змінної\tmudar valor de variável\ndialog_action_variable_set\tset variable value\t\t\t\timposta valore variabile\tзадать значение переменной\t\tVariablen-Wert festlegen\tتعيين قيمة المتغير\tsetja gildi í breytu\tváltozó értékének beálltása\t\t\t\tustaw wartość zmiennej\t\t\t\tвстановити значення змінної\tdefinir valor de variável\ndialog_block_basic\tdialog\tdialogue\tdiálogo\tdiálogo\tdialogo\tфаза\tdialog\tDialog\tحوار\tsamtal\tpárbeszéd\tparolo\tdialoog\t对话\tdialog\tダイアログ\twawa\trozhovor\tдіалог\tdiálogo\ndialog_block_conditional\tconditional\tconditions\tcondicional\tcondicional\tcondizionale\tусловие\tvillkor\tBedingungen\tشرطي\tskilyrt\tfeltételes\tse...do kodo\ttingimusega\t条件\twarunek\t条件\tspos\tpodmínka\tумова\tcondicional\ndialog_block_list\tlist\tliste\tlista\tlista\tlista\tсписок\tlista\tListe\tقائمة\tlisti\tlista\tlisto\tlist\t列表\tlista\tリスト\tlist\tseznam\tсписок\tlista\ndialog_block_new\tnew section\tnouvelle section\tnova secção\tnueva sección\tnuova sezione\tновая секция\tny replik\tNeuer Absatz\tقسم جديد\tnýr hluti\túj szakasz\tnovan parton\tlisa dialoogi kast\t新部分\tnowa sekcja\t新規セクション\tchi wawa sitkum\tnová část\tновий розділ\tnova seção\ndialog_conditional_add\tadd option\tajouter une option\tadicionar opção\tañadir opción\taggiungi opzione\tдобавить опцию\tlägg till alternativ\tOption hinzufügen\tإضافة أختيار\tnýr liður\topció hozzáadása\taldonu elekton\tlisa võimalus\t加入选项\tdodaj opcję\tオプション追加\tchi spos\t\tдодати опцію\tcolocar opção\ndialog_conditional_when\twhen\tquand\tquando\tcuando\tquando\tкогда\tom\tWenn\tمتى\tþegar\tha\tse\tkui\t当\tjeżeli\tいつ\tspos\t\tколи\tquando\ndialog_effect_color1\tcolor 1\tcouleur 1\tcôr 1\tcolor 1\tcolore 1\tцвет 1\tfärg 1\tFarbe 1\tلون ١\tlitur 1\tszín 1\tkoloro 1\tvärv 1\t颜色1\tkolor 1\t色1\ttsum 1\tbarva 1\tколір 1\tcor 1\ndialog_effect_color2\tcolor 2\tcouleur 2\tcôr 2\tcolor 2\tcolore 2\tцвет 2\tfärg 2\tFarbe 2\tلون ٢\tlitur 2\tszín 2\tkoloro 2\tvärv 2\t颜色2\tkolor 2\t色2\ttsum 2\tbarva 2\tколір 2\tcor 2\ndialog_effect_color3\tcolor 3\tcouleur 3\tcôr 3\tcolor 3\tcolore 3\tцвет 3\tfärg 3\tFarbe 3\tلون ٣\tlitur 3\tszín 3\tkoloro 3\tvärv 3\t颜色3\tkolor 3\t色3\ttsum 3\tbarva 3\tколір 3\tcor 3\ndialog_effect_drawing\tinsert drawing\tinsérer un dessin\t\t\tinserisci disegno\tвставить рисунок\t\tZeichnung einfügen\tإضافة رسم\tsetja inn teikningu\trajz beillesztése\t\t\t\twstaw rysunek\t\t\t\tвставити малюнок\tinserir desenho\ndialog_effect_new\ttext effects\teffets de texte\tefeitos do texto\tefectos de texto\teffetti di testo\tэффекты текста\ttexteffekter\tText Effekte\tمؤثرات كتابية\ttextaáhrif\tszövegeffektek\tskribostiloj\tteksti efektid\t文字效果\tefekt tekstu\tテキストエフェクト\tala pipa\ttextové efekty\tефекти тексту\tefeitos de texto\ndialog_effect_rainbow\trainbow\tarc-en-ciel\tarco-íris\tarcoiris\tarcobaleno\tрадужный\tregnbåge\tRegenbogen\tقوس قزح\tregnbogi\tszivárványos\tmultkolora\tvikerkaar\t彩虹\ttęcza\tレインボー\tolawtet\tduha\tвеселка\tarco-íris\ndialog_effect_shaky\tshaky\ttremblement\ttremido\ttembloroso\ttremante\tдрожащий\tskakig\tzitternd\tمهزوز\thristist\tremegő\tmovada\tvärisev\t晃动\tdrżenie\t振動\tshuk-shuk\ttřas\tтремтіння\ttremer\ndialog_effect_wavy\twavy\tvague\tondulado\tondulado\tondulato\tволнистый\tvågor\twellig\tمموج\tbylgjað\thullámos\tondeca\tlainetav\t波纹\tfala\t波紋\thantli\tvlnění\tволна\tondular\ndialog_hide_code\thide code\tcacher le code\tesconder código\tesconder código\tnascondi codice\tспрятать код\tgöm kod\tCode verbergen\tأخفي شفرة البرنامج\tfela kóða\tforráskód elrejtése\tmalvidu kodon\tpeida kood\t隐藏代码\tukryj kod\tコード格納\tipui kod\tskrýt kód\tприховати код\tesconder código\ndialog_list_add\tadd line\tajouter une ligne\tadicionar fala\tañadir línea\taggiungere frase\tдобавить линию\tlägg till replik\tZeile hinzufügen\tإضافة سطر\tný setning\tsor hozzáadása\taldonu skribon\tlisa rida\t加入行\tdodaj linijkę\t行の追加\tchi layn\tpřidat řádek\tдодати рядок\tcolocar linha\ndialog_list_order\torder:\tordre :\tordem:\torden:\tordine\tпорядок:\tordning:\tReihenfolge\tالترتيب:\tröð:\tsorrend:\tordo:\tjärjekord:\t次序:\tkolejność:\t順序:\tiht-iht:\tpořadí:\tпорядок:\tordem:\ndialog_no_selection\tSelect a sprite or item to edit its dialog.\tsélectionner un sprite ou un item pour éditer ses lignes de dialogue\tseleciona um sprite ou item para editares o seu diálogo\tSelecciona un sprite u objeto para editar este diálogo\tSeleziona uno sprite per modificare questo dialogo\tВыберете спрайт или объект, чтобы редактировать прикреплённый диалог.\tvälj en sprite eller föremål för att ändra dess dialog\tWähle einen Sprite oder ein Objekt aus, um den Dialog zu editieren.\tأختار رسمة تفاعلية او أداة لتعديل الحوار المرتبط بها.\tVeldu sprota eða hlut til að breyta þessu samtali.\tVálassz ki egy sprite-ot vagy tárgyat.\telektu enludanon aŭ objekton por redakti ties parolon.\tVali kuju või ese, et muuta selle dialoogi.\t选择一个精灵或物品编辑它的对话。\tWybierz sprite lub przedmiot żeby edytować.\tスプライトもしくはアイテムを選択してそのダイアログを編集\tpik iktas pi tilikum kopa tsum tlaska wawa.\tVyberte objekt nebo předmět a editujte daný rozhovor.\tВиберіть спрайт або предмет, щоб редагувати його діалог.\tSelecione um ator ou item para editar o seu diálogo.\ndialog_show_code\tshow code\tafficher le code\tmostrar códiogo\tmostrar código\tmostra codice\tпоказать код\tvisa kod\tCode zeigen\tأظهر شفرة البرنامج\tsýna kóða\tforráskód mutatása\tvidu kodon\tnäita koodi\t显示代码\tpokaż kod\tコード表示\tnanich kod\tzobrazit kód\tпоказати код\tmostrar código\ndialog_start_preview\tpreview\taperçu\tpré-vizualizar\tprevisualizar\tanteprima\tпредпросмотр\tförhandsvisning\tVorschau\tمعاينة\tsýnishorn\telőnézet\tantaŭvidu\teelvaade\t预览\tpodgląd\tプレビュー\ttenas nanich\tnáhled rozhovoru\tперегляд\tprévia\ndialog_tool_name\tdialog\tdialogue\tdiálogo\tdiálogo\tdialogo\tдиалог\tdialog\tDialoge\tحوار\tsamtöl\tpárbeszéd\tskribejo\tdialoog\t对话\tdialogi\tダイアログ\twawa\trozhovor\tдіалог\tdiálogo\ndownload_data\tsave data\ttélécharger les données\tdescarregar data\tdescargar data\tscarica dati\tскачать данные\tladda ned data\tDaten herunterladen\tتحميل المعلومات\thala niður gögnum\tadatok letöltése\tprenu dosieron\tlae alla andmed\t下载数据\tpobierz dane\tデータをダウンロード\tiskum hihi kod\tstáhnout kód\tзавантажити дані\tbaixar código\ndownload_font\tsave font\ttélécharger la police\tdescarregar fonte\tdescargar fuente\tscarica font\tскачать шрифт\tladda ned typsnitt\tSchriftart herunterladen\tتحميل الخط\thala niður leturgerð\tbetűtípus letöltése\tprenu tiparon\tlae alla font\t下载字体\tpobierz czcionkę\tフォントをダウンロード\tiskum .bitsyfont\tstáhnout font\tзавантажити шрифт\tbaixar fonte\ndownload_game\tsave game\ttélécharger le jeu\tdescarregar jogo\tdescargar juego como html\tscarica gioco\tскачать игру\tladda ned\tSpiel herunterladen\tتحميل اللعبة\thala niður leik\tjáték letöltése\tprenu ludon\tlae alla mäng\t下载游戏\tpobierz grę\tゲームをダウンロード\tiskum hihi\tstáhnout hru\tзавантажити гру\tbaixar o jogo\ndownload_html\tdownload game as html file:\ttélécharger en fichier html\tdescarregar jogo como ficheiro html:\tdescargar juego como html\tscarica il gioco come html\tскачать игру как html файл:\tladda ned spelet som html-fil:\tLade das Spiel als HTML Datei herunter:\tتحميل اللعبة كملف html\thala niður leik sem html skjal\tjáték letöltése html fájlként:\tprenu la ludon kiel HTML-dosieron:\tlae alla mäng html failina:\t下载游戏为 html 文件:\tpobierz grę jako plik html:\tゲームをhtmlファイルとしてダウンロード\tiskum hihi kakwa .html:\tstáhnout hru v .html\tзавантажити гру як файл HTML:\tbaixar o jogo como arquivo html:\ndownload_tool_name\tsave\ttélécharger\tdescarregar\tdescargar\tscarica\tскачать\tladda ned\tHerunterladen\tتحميل\tniðurhal\tletöltés\tprenu (elŝutu)\tlae alla\t下载\tpobierz\tダウンロード\tmunk-iskum hihi\tstáhnout\tзавантажити\tbaixar\neditor_settings\ttool settings\tparamètres de l'éditeur\tdefinições do editor\tajustes del editor\timpostazioni dell'editor\tнастройки редактора\teditor-inställningar\tEditor-Einstellungen\tإعدادات المحرر\tstillingar á tóli\tszerkesztő beállításai\tkreila elektaro\tbitsy seaded\t编辑器设置\tustawienia edytora\tエディター設定\tedita setins\tnastavení editoru\tналаштування редактору\tdefinições de editor\nending_dialog\tending dialog\tfin de dialogue\tdiálogo de final\tdialógo de final\tdialogo del finale\tтекст концовки\tslut-dialog\tEnd-Nachricht\tحوار النهاية\tenda samtal\tbefejező párbeszéd\tfinluda parolo\t\t结局对话\tdialog zakończenia\t\tkopit-hihi taim wawa\t\tдіалог закінчення\tdiálogo de final\nending_label\tending\tfin\tfinal\tfinal\tfinale\tконцовка\tslut\tEnde\tنهاية\tendir\tbefejezés\tludofino\tlõpp\t结局\tzakończenie\tエンディング\tkopit-skwil\tkonec hry\tзакінчення\tfinal\nending_place\tplace ending\tajouter une fin\tcolocar final\tcolocar final\tpiazza finale\tрасположить концовку\tplacera slut\tPlatziere Ende\tمكان النهاية\tstaðsetja enda\tbefejezés elhelyezése\tmetu finon\tpaiguta lõpp\t安排结局\tumieść zakończenie\tエンディング配置\tmunk-mitlait kopit-skwil\tumístit konec hry\tрозмістити закінчення\tcolocar final\nending_place_help\tclick space in room to add ending\tcliquez une case dans la salle pour ajouter une fin\tclica num espaço da sala para adicionar um final\thaz click en un espacio de la sala para añadir un final\tfai click nella stanza per aggiungere un finale\tщёлкните на точку пространстве, чтобы добавить концовку\tklicka någonstans i rummet för att lägga till ett slut\tKlicke einen Bereich in einem Raum an, um ein Ende zu platzieren\tحدد فراغ في الغرفة لوضع النهاية\tsmelltu á reit í rýminu til að bæta við enda\tkattints valahova a szobában a befejezés elhelyezéséhez\tklaku enĉambre por aldoni ludofinon\tvali toa ruut kuhu paigutada lõpp\t在房间中选中格子增加结局\tkliknij na wolnym miejscu w pomieszczeniu, aby dodać zakończenie\tルームの空いている場所をクリックしてエンディングを追加\tkwutl speis spos maika tiki munk-mitlait kopit-skwil\tvložte konec hry kliknutím do prostředí místnosti\tклацни місце, де буде закінчення\tclique na cena para colocar um final\nending_remove\tremove selected ending\tretirer la fin sélectionnée\tremover final selecionado\teliminar el final seleccionado\tcancella il finale selezionato\tубрать выбранную концовку\tta bort det valda slutet\tEntferne ausgewähltes Ende\tامسح النهاية المختارة\tfjarlæga valinn enda\tkiválasztott befejezés törlése\tforigu ĉi ludofinon\tkustuta valitud lõpp\t移除已选择的结局\tusuń wybrane zakończenie\t選択中のエンディングを削除\tmunk-heilo okok kopit-skwil\todstranit vybraný konec hry\tприбрати обране закінчення\ttirar final selecionado\nendings_tool_name\tendings\tfins\tfinais\tfinales\tfinali\tконцовки\tslut\tEnden\tنهايات\tendar\tbefejezések\tludofinoj\tlõpud\t结局\tzakończenia\tエンディング\tkopit\tkonce hry\tзакінчення\tfinais\nexit_click_add\tclick space in room to add exit\tCliquer un espace dans la salle pour ajouter une sortie\tclica num espaço da sala para adicionar uma saída\thaz click en un espacio de la sala para añadir una salida\tclicca nella stanza per aggiungere l'uscita\tкликните в панели пространства, чтобы добавить точку выхода\tklicka i rummet för att lägga till en utgång\tDrücke Leertaste im Raum um einen Ausgang hinzuzufügen\tحدد فراغ في الغرفة لوضع النهاية\tsmelltu á reit í rýminu til að bæta við útgönguleið\tkattints valahova a szobában a kijárat elhelyezéséhez\tklaku spacon enĉambre por aldoni pordon\t\t在房间中选中格子增加出口\tkliknij na wolnym miejscu w pomieszczeniu, aby dodać przejście\t\tspos maika tiki mash lapot, kwutl skwil (lapot-ilahi) kopa loom.\tvložte průchod kliknutím do prostředí místnosti\tклацни місце, де буде вихід\tclique na cena para colocar uma saída\nexit_delete\tdelete selected exit\tsupprimer la sortie sélectionnée\tapagar saída selecionada\tborrar salida seleccionada\tcancella uscita selezionata\tудалить выбранную точку выхода\tta bort den valda utgången\tAusgewählten Ausgang löschen\tإزالة المخرج المختار\teyða valdri útgönguleið\tkiválasztott kijárat törlése\tforigu ĉi pordon\tkustuta valitud väljapääs\t删除已选中的出口\tusuń wybrane przejście\t選択中の出口の削除\tmunk-heilo okok lapot\tsmazat vybraný průchod\tприбрати обраний вихід\ttirar saída selecionada\nexit_destination\tthis exit goes to\tcette sortie va à\testa saída leva-te a\testa salida te lleva a\tquesta uscita ti porta a\tэта точка выхода ведёт к\tdenna utgång leder till\tDieser Ausgang führt zu\tهذا المخرج يؤدي إلى\tþessi útgönguleið fer til\tkijárat ide\tĉi pordo iras al\tsee väljapääs viib\t这个出口去向\tto przejście prowadzi do\tこの出口はここに向かいます\tokok lapot tlatawa\ttento průchod vede do\tцей вихід прямує до\tessa saída sai em\nexit_destination_move\tmove destination\tdéplacer la destination\t\t\tmuovi destinazione\tпереместить точку прибытия\t\tZiel verschieben\tتحريك المقصد\tfæra ákvörðunarstað\térkezés mozgatása\t\t\t\tzmień pozycję docelową\t\t\t\tперемістити призначення\tmover destino\nexit_dialog\texit dialog\tfermer le dialogue\t\t\tinterrompi il dialogo\tвыход из диалога\t\tDialog\tحوار يحدث عند الخروج\tljúka samtali\tkilépés a párbeszédből\t\t\t\tdialog przejścia\t\t\t\tдіалог виходу\tsair de diálogo\nexit_dialog_lock_add\tadd lock\tajouter un verrou\t\t\tAggiungi blocco\tдобавить блокировку\t\tSperre\tاضافه قفل\tbæta við lás\tzár hozzáadása\t\t\t\tdodaj blokadę\t\t\t\tдодати замок\ttrancar\nexit_dialog_lock_default_text1\tThe key opens the door!\tLa clé ouvre la porte!\t\t\tLa chiave apre la porta!\tЭтот ключ открывает дверь!\t\tDer Schlüssel öffnet die Tür!\tالمفتاح يستعمل لفتح الباب!\tLykillinn opnar hurðina!\tA kulcs nyitja az ajtót.\t\t\t\tKlucz otwiera drzwi!\t\t\t\tКлюч відчиняє двері!\tA chave abre a porta!\nexit_dialog_lock_default_text2\tThe door is locked...\tLa porte est verrouillée...\t\t\tLa porta è chiusa a chiave...\tДверь заперта...\t\tDie Tür ist verschlossen...\tالبال مقفل بإحكام ...\tHurðin er læst...\tAz ajtó zárva...\t\t\t\tDrzwi są zamknięte na klucz…\t\t\t\tДвері зачинені…\tA porta está trancada...\nexit_dialog_lock_name\tlocked exit\tsortie verrouillée\t\t\tUscita bloccata\tЗаблокированный выход\t\tGesperrter Übergang\tمخرج مقفل\tlæst útgönguleið\tzárt kijárat\t\t\t\tzablokowane przejście\t\t\t\tзамкнений вихід\tsaída trancada\nexit_dialog_narration_add\tadd narration\t\t\t\taggiungi narrazione\tдобавить рассказ\t\tErzählung\tإضافة سرد\tbæta við frásögn\tnarráció hozzáadása\t\t\t\tdodaj narrację\t\t\t\tдодати розповідь\tcolocar narração\nexit_dialog_narration_default_text\tYou walk through the doorway\t\t\t\tAttraversi la porta\tТы проходишь через дверной проём\t\tDu gehst durch die Tür\tأنت تمشي عبر المدخل\tÞú gengur í gegnum dyragáttina\tÁtmész az ajtón\t\t\t\tPrzechodzisz przez drzwi\t\t\t\tВи проходите через дверний отвір\tVocê entrou pela porta\nexit_dialog_narration_name\texit narration\t\t\t\tinterrompi narrazione\tзавершающий рассказ\t\tÜbergangs-Erzählung\tسرد يحدث عند الخروج\tútgangs frásögn\tkilépés a narrációból\t\t\t\tnarracja przejścia\t\t\t\tрозповідь при виході\tsair da narração\nexit_label\texit\tsortie\tsaída\tsalida\tuscita\tточка выхода\tutgång\tEingang\tمخرج\tútgönguleið\tkijárat\tpordo\t\t退出\tprzejście\t\tlapot\tprůchod\tвихід\tsair\nexit_new\tplace new exit\tajouter une nouvelle sortie\tcolocar uma nova saída\tcoloca una nueva salida\tpiazza una nuova uscita\tрасположить новую точку выхода\tplacera ny utgång\tPlatziere neuen Ausgang\tوضع مخرج جديد\tsetja nýja útgönguleið\túj kijárat elhelyezése\tmetu novan pordon\taseta uus väljapääs\t放置新出口\tumieść nowe przejście\t新規出口の配置\tmunk-mitlait chi lapot\tumístit nový průchod\tдодати новий вихід\tcolocar uma nova saída\nexit_one_way_label\tone-way exit\t\t\t\tuscita a senso unico\tвыход в одну сторону\t\tEinseitiger Übergang\tمخرج بإتجاة واحد\teinstefnu útgönguleið\tegyirányú kijárat\t\t\t\twyjście w jedną stronę\t\t\t\tодносторонній вихід\tsaída de um lado\nexit_options\texit options\toptions de sortie\topções de saída\topciones de salida\topzioni dell'uscita\tопции точки выхода\tutgångs-inställningar\tÜbergangs-Optionen\tخيارات المخرج\tútgöngu valmöguleikar\tkijárat beállítások\tpordaj elektaroj\t\t对出选项\topcje przejścia\t\tkata lapot?\t\tопції виходу\topções de saída\nexit_return_label\treturn exit\t\tsaída de retorno\tregresar salida\tuscita di ritorno\tточка возвращения\tretur\tAusgang\tإعادة المخرج\tbaka til útgangs\tvisszaút\trevenila pordo\t\t返回退出\tprzejście powrotne\t\tkilapi lapot\t\tзворотній вихід\tsaída de retorno\nexit_selected\tselected exit\tsortie sélectionnée\tsaída selecionada\tsalida seleccionada\tuscita selezionata\tвыбрать точку выхода\tvald utgång\tAusgewählter Ausgang\tالمخرج المختار\tvalin útgönguleið\tkiválasztott kijárat\tĉi pordo\thetkel valitud väljapääs\t已选中的出口\twybrane przejście\t出口選択中\tokok lapot\tvybraný průchod\tобраний вихід\tsaída selecionada\nexit_selected_none\tno exit selected\tpas de sortie sélectionnée\tnão há uma saída selecionada\tno has seleccionado una salida\tnon hai selezionato nessuna uscita\tни одна точка выхода не выбрана\tingen vald utgång\tKein Ausgang ausgewählt\tلم يتم إختيار مخرج\tengin útgönguleið valin\tnincs kijárat kiválasztva\telektu pordon\thetkel pole ühtki väljapääsu valitud\t没有出口被选中\tnie wybrano żadnego przejścia\t出口未選択\tikta lapot?\tprůchod není vybrán\tвихід не обраний\tnenhuma saída selecionada\nexit_transition_label\ttransition effect\teffet de transition\tefeito de transição\tefectos de transición\teffetto di transizione\tэффект перехода\tövergångseffekt\tÜbergangs-Effekt\tالمؤثرات الإنتقالية\tumskipti\táttűnések\tporda-al-porda stilo\t\t转场效果\tefekt przejścia\t\tkata tlatawa chi loom\t\tефект переходу\tefeito de transição\nexits_tool_name\texits\tsorties\tsaídas\tsalidas\tuscite\tточки выхода\tutgång\tÜbergänge\tمخارج\tútgangar\tkijáratok\tpordoj\tväljapääsud\t出口\tprzejścia\t出口\tlapot\tprůchody\tвиходи\tsaídas\nexplorer_tool_name\tfind drawing\ttrouver un dessin\tprocurar desenho\tbuscar dibujo\tcerca disegno\tнайти рисунок\thitta bilder\tAlle Zeichnungen\tأبحث عن رسم\tteikningar\trajz keresése\ttrovu desegnon\totsi joonistusi\t素材库\tznajdź rysunek\t絵を探す\ttlap picha\tvyhledat objekt\tзнайти малюнок\tencontrar desenho\nexpression_builder_all_clear\tAC\t\t\t\tC\tочистить всё\t\tDEL\tAC\tAC\tösszes törlése\t\t\t\tAC\t\t\t\tAC\tAC\nfilter_placeholder\tfilter drawings\tFiltrer les dessins\tfiltrar desenho\tfiltrar dibujos\tFiltra i disegni\tвведите название рисунка\tsök bilder\tSuche Zeichnungen..\tصفي الرسومات\tfinna teikningar\trajz keresése\tforelektu desegnojn\t\t过滤图画\tfiltruj rysunki\t\ttlap picha\t\tфільтрувати малюнки\tfiltrar desenhos\nfont_arabic\tArabic\tArabe\tÁrabe\tarábico\tArabo\tарабский шрифт\tArabiska\tArabische Schrift\tالعربية\tArabískt\tarab\tla araba\t\tArabic\tArabska\t\talab wawa\t\tArabic\tArábico\nfont_arabic_description\tPixel font with Arabic characters\tPolice d'écriture avec l'alphabet arabe\tfonte pixelizada com caracteres árabes\tFuente pixel con carácteres arábicos.\tFont pixel con caratteri arabi\tописание арабского шрифта\tPixel-typsnitt med Arabiska tecken\tPixel-Schrift mit arabischen Buchstaben\tخط بكسل يحتوي على الحروف العربية\tSmátt letur sem inniheldur stafi fyrir arabísku.\tpixelbetűtípus arab karakterekkel\ttiparo por la araba lingvo\t\t使用 Arabic 字母的像素字体\tCzcionka pikselowa ze znakami Arabskimi\t\tpiksel pipa-tsum kopa alab wawa\t\tПіксельний шрифт з арабськими літерами\tFonte pixel com caractéres arábicos\nfont_ascii_small\tASCII Small\tASCII - petit\tASCII - pequeno\tASCII - pequeño\tASCII piccolo\tASCII Small\tASCII Small\tASCII Small\tASCII صغير\tASCII smátt\tASCII kicsi\tASCII Eta\tASCII Väike\tASCII Small\tASCII Mała\tASCII Small\ttenas ASCII\tASCII malé\tASCII Small\tASCII Small\nfont_ascii_small_description\tSmall font limited to ASCII, which includes English characters and some symbols.\tPetite police limitée à l'ASCII, incluant les caractères anglais et quelques symboles.\tFonte pequena limitada a ASCII que inclui caracteres ingleses e pequenos símbolos\tLa fuente pequeña sólo sirve para carácteres ASCII, que incluye los carácteres del inglés y algunos símbolos.\tFont piccolo, limitato all'ASCII, include i caratteri dell'inglese e alcuni simboli.\tмелкий шрифт только для ASCII, включая английские буквы и некоторые символы\tLitet ASCII-typsnitt, innehåller engelska bokstäver och vissa tecken\tKleine Schriftart, auf ASCII limitiert (englische Zeichen).\tخط صغير مقتصر على الـASCII، الذي يحتوي على الحروف الأنجليزية و بعض الرموز.\tSmátt letur sem styður ASCII stafi. ASCII staðallinn inniheldur enska stafi og algeng tákn.\tKisméretű betűtípus az angol ábécé betűivel és néhány speciális karakterrel\tMalgranda dosiero, nur anglaj literoj de ASCII. Ne ekzistas esperantaj (ĉapelitaj) literoj.\tVäike font ASCII piires, mis sisaldab Inglise keel tähti ja sümboleid.\t受 ASCII 限制的小字体,包含英文字母和一些符号。\tMała czcionka ograniczona do ASCII, angielskie litery i niektóre symbole.\t英語といくつかの記号を含む、ASCIIに限られる小さいフォント\twik hayu pipa-tsum. kopit tlosh spos maika tiki kopit ASCII tsum (pastin wawa pi wik-hayu huloima tsum).\t\tМаленький шрифт, обмежений ASCII, тобто англійські літери та ще деякі.\tPequena fonte limitada a ASCII, incluindo alguns carácteres e símbolos em inglês.\nfont_custom_description\t\"Upload your own custom font in the \"\".bitsyfont\"\" format!\"\t\"Importe ta propre police customisée dans le format \"\".bitsy\"\"!\"\t\"Envia a tua própria fonte costumizada no formato \"\".bitsyfont\"\"\"\tSube tú fuente personalizada en el formato “.bitsyfont”!\t\"Carica il tuo font personale nel formato \"\".bitsyfont\"\"\"\t\"загрузить собственный шрифт в формате \"\".bitsyfont\"\".\"\t\"Ladda upp ett eget typsnitt med filformatet \"\".bitsyfont\"\"\"\t\"Lade deine eigene Schriftart im \"\".bitsyfont\"\" Format hoch!\"\t\"أرفع خطك المعدل من ملف \"\"bitsyfont.\"\"!\"\t\"Notaðu þína eigin leturgerð með því að hlaða inn \"\".bitsyfont\"\" skrá!\"\t\"Töltsd fel a saját betűtípusodat \"\".bitsyfont\"\" formátumban!\"\tUzu vian propran .bitsyfont-dosieron kiel tiparo!\t\"Lae üles oma font \"\".bitsyfont\"\" formaadis!\"\t上传你的定制字体,请用“.bitsyfont”格式!\t\"Wyślij swoją własną czcionkę w formacie \"\".bitsyfont\"\"!\"\t\"自分のカスタムフォントを \"\".bitsyfont\"\"形式でアップロード!\"\t\"mash yakwa maika huloima (\"\"custom\"\") pipa-tsum (\"\"font\"\") kakwa \"\".bitsyfont\"\"!\"\tДодайте власний шрифт в форматі .bitsyfont!\t\t\"Envie a sua própria fonte no formato \"\".bitsyfont\"\"!\"\nfont_data_toggle_visible\tfont data\tDonnée de police d'écriture\tdata da fonte\tdatos de la fuente\tdati del font\tданные шрифта\ttypsnittsdata\tSchrift Daten sichtbar\tبيانات الخط\tsýna letur upplýsingar\tbetűtípus adatok\ttipara dosiero\t\t字体数据\tdane czcionki\t\tpipa-tsum kod\tfont (kód)\tдані шрифту\tdados de fonte\nfont_label\tfont\tpolice\tfonte\tfuente\tfont\tшрифт\ttypsnitt\tSchriftart\tالخط\tleturgerð\tbetűtípus\ttiparo\tfont\t字体\tczcionka\tフォント\tpipa setins\tfont\tшрифт\tfonte\nfont_missing_character\tThe current font is missing some of the characters used in your dialog. Not all text in your game will display correctly. Consider picking a different font in the settings.\tCertains caractères utilisés dans tes dialogues ne sont pas présents dans la police d'écriture sélectionnée. Certains textes ne s'afficheront pas correctement. Peut-être devrais-tu changer de police d'écriture dans les paramètres.\tA fonte actual não possui alguns dos caracteres usados no teu diálogo. Partes do texto no teu jogo podem não aparecer correctamente. Considera escolher uma fonte diferente nas definições.\tLa fuente actual no incluye algunos de los carácteres usados en tú dialógo. No todo el texto de tú juego se va a mostrar correctamente. Considera elegir una fuente distinta en las opciones.\tAl font attuale mancano alcuni caratteri usati nel tuo dialogo. Il testo del tuo gioco non apparità correttamente. Prova a scegliere un font diverso nelle impostazioni.\tвыбранный шрифт не поддерживает некоторые символы, использованные в диалогах. не весь текст в игре будет отображён правильным образом. вы можете выбрать другой шрифт в настройках.\tDet valda typsnittet saknar vissa tecken du har använt. En del av texten kommer inte att se rätt ut. Ett annat typsnitt kan väljas i inställningarna.\tDie aktuelle Schriftart unterstützt nicht alle Zeichen aus deinem Dialog. Das kann merkwürdig aussehen. Wähle lieber eine andere Schriftart in den Einstellungen.\tهذا الخط ينقصة بعض الرموز التي تستخدمها في حوارك. بعض الجمل قد تظهر بشكل خاطئ. يفضل أن تختار خط أخر من الإعدادات\tValið letur inniheldur ekki alla stafi sem koma fyrir í textanum þínum. Stafir sem eru ekki til staðir verða ekki sýnilegir í leiknum. Þú getur prófað að velja annað letur í leikjastillingunum.\tA kiválasztott betűtípus nem tartalmazza az összes betűt, amit használsz. Válassz egy másik betűtípust a beálllításoknál!\tLa nuna tiparo mankas kelkajn literojn de viaj skriboj, do kiam la ludo ludiĝas oni ne vidos ĉion el via skribo. Pensu pri elekti alian tiparon je la elektaro.\tValitud fondil puudub mõned tähed, mida oled kasutanud oma dialoogis. Osa sinu mängu tekstist ei ilmu õigesti. Ehk tahad valida teist fonti seadetes.\t使用在你对话中的当前字体中有一些字母丢失。你的游戏中的文字不能完全显示正确。请考虑在设置中选一个不同的字体。\tBieżąca czcionka nie posiada niektórych znaków, użytych w twoim dialogu. Nie wszystkie teksty w twojej grze zostaną wyświetlone prawidłowo. Rozważ wybranie innej czcionki, w panelu ustawienia.\t\talta maika tsum pipa kopa maika hihi, pi olo iht tsum (character) kopa maika pipa-tsum (font). spos tilikum nanich maika hihi, wik kata nanich kanawei maika pipa. tlosh spos maika pik huloima pipa-tsum.\t\tУ поточному шрифті відсутні деякі символи з ваших діалогів. Не весь текст гри буде відображений коректно. Радимо обрати інший шрифт.\tA fonte atual está sem alguns dos caractéres usados em seu diálogo. Nem todo o texto no seu jogo será mostrado corretamente. Tente escolher outra fonte nas definições.\nfont_unicode_asian\tUnicode Asian\tUnicode asiatique\tUnicode Asiático\tUnicode asiático\tUnicode Asiatico\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode أسيوي\tUnicode asískt\tUnicode ázsiai\tUnikodo Azia\tUnicode Aasia\tUnicode Asian\tUnicode Azjatycka\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode Asian\tUnicode Asiático\nfont_unicode_asian_description\tLarge font which includes characters for Asian languages such as Chinese, Japanese, and Korean.\tLarge police incluant les caractères de langues asiatiques, comme le chinois, le japonais ou le coréen.\tFonte grande que inclui caracteres para linguagens asiáticas como o Chinês, o Japonês e o Coreano.\tFuente grande que incluye carácteres para lenguajes asiáticos como chino, japonés y coreano.\tFont grande, include i caratteri per lingue asiatiche quali cinese, giapponese, coreano\tкрупный шрифт, включающий символы из китайского, японского, корейского и других языков Азии.\tStort typsnitt med tecken för Asiatiska språk inklusive Kinesiska, Japanska, och Koreanska\tGroße Schriftart, unterstützt Asiatische Zeichen für z.B. Japan, China, Korea.\tخط كبير يحتوي على حروف من اللغات الأسيوية مثل الصينية، اليابانية، و الكورية.\tStórt letur sem inniheldur stafi fyrir asísk tungumál svo sem kínversku, japönsku, og kóresku.\tNagyméretű betűtípus, tartalmazza a legtöbb ázsiai nyelv karaktereit, mint a kínai, japán és a koreai.\tGranda dosiero, ekzistas literoj por aziaj lingvoj kiel la ĉina, japana kaj korea, ankaŭ eŭropecaj lingvoj kiel la esperanta.\tSuur font, mis sisaldab Aasia keelte tähti nagu näiteks Hiina, Jaapani ja Korea.\t为亚洲语言准备的大字体,比如中文,日文和韩文字母。\tDuża czcionka, która zawiera znaki dla języków azjatyckich, takich jak chiński, japoński i koreański.\t中国、韓国、日本などアジア圏の言語を含む大きいフォント\thayu pipa-tsum, tlosh kanawei wik-saya chaina-man ilahi le-lang (chapan ilahi, kolia ilahi, tailan ilahi...).\t\tВеликий шрифт з символами для азійських мов, таких як китайська, японська та корейська.\tFonte grande que inclui caractéres para línguas asiáticas como o Chinês, Japonês e Coreano.\nfont_unicode_european_large\tUnicode European Large\tUnicode européen - large\tUnicode Europeu - grande\tUnicode Europeo - grande\tUnicode Europeo Grande\tUnicode European Large\tUnicode European Large\tUnicode European Large\tUnicode أوربي كبير\tUnicode evrópskt stórt\tUnicode európai nagy\tUnikodo Eŭropa Granda\tUnicode Euroopa Suur\tUnicode European Large\tUnicode Europejska Duża\tUnicode European Large\thayas Unicode European\tUnicode European Large\tUnicode European Large\tUnicode Europeu Grande\nfont_unicode_european_large_description\tLarge font with more unicode support. Includes characters for all European languages.\tLarge police supportant plus d'unicode, incluant les caractères de la plupart des langages européens.\tFonte grande com mais suporte para unicode. Inclui caracteres para todas as linguas Europeias.\tFuente grande con soporte unicode. Incluye los carácteres usados en todos los lenguajes europeos.\tFont grande, con più supporto unicode. Include i caratteri per tutte le lingue europee\tкрупный шрифт с поддержкой символов из юникода. включает буквы их всех европейских языков.\tStort typsnitt med mer unicode-support. Innehåller tecken för alla Europeiska språk.\tGroße Schriftart, unterstützt Unicode (alle Europäischen Zeichen).\tخط كبير ذو دعم أكبر للـUnicode، يحتوي على حروف من كل اللغات الأروبية.\tStórt letur sem styður unicode stafi. Unicode staðallinn inniheldur stafi fyrir flest evrópsk tungumál.\tNagyméretű betűtípus unicode támogatással. Tartalmazza az európai nyelvek legtöbb karakterét.\tGranda dosiero, ekzistas literoj por ĉiuj eŭropaj lingvoj kaj esperanto.\tSuur font, mis toetab rohkem unicode-i. Sisaldab kõike Euroopa keelte tähti.\t有着更多Unicode支持的大字体,包括所有欧洲语言字母。\tDuża czcionka z większym wsparciem Unicode. Zawiera znaki dla wszystkich europejskich języków.\tヨーロッパ圏の言語の全てをカバーした、より多くのunicodeをサポートした大きいフォント\thayu pipa-tsum, tlosh kanawei dachman-ilahi le-lang.\t\tВеликий шрифт з підтримкою Unicode. Містить символи для всіх європейських мов.\tFonte grande com maior apoio ao unicode. Inclui caractéres de todas as línguas europeias.\nfont_unicode_european_small\tUnicode European Small\tUnicode européen - petit\tUnicode Europeu - pequeno\tUnicode Europeo - pequeño\tUnicode Europeo Piccolo\tUnicode European Small\tUnicode European Small\tUnicode European Small\tUnicode أوربي صغير\tUnicode evrópskt smátt\tUnicode európai kicsi\tUnikodo Eŭropa Eta\tUnicode Euroopa Väike\tUnicode European Small\tUnicode Europejska Mała\tUnicode European Small\ttenas Unicode European\tUnicode European Small\tUnicode European Small\tUnicode Europeu Pequeno\nfont_unicode_european_small_description\tSmall font with some unicode support. Includes characters for most European languages.\tPetite police supportant l'unicode, incluant les caractères de la plupart des langages européens.\tFonte pequena com algum suporte para unicode. Inclui caracteres de maior parte das linguagens Europeias.\tFuente pequeña con un poco de soporte unicode. Incluye los carácteres usados en la mayoría de los lenguajes europeos.\tFont piccolo, con un po' di supporto unicode. Include i caratteri per buona parte delle lingue europee.\tмелкий шрифт с поддержкой многих символов из юникода. включает буквы для большинства европейских языков.\tLitet typsnitt med viss unicode-support. Innehåller tecken för de flesta Europeiska språk.\tKleine Schriftart, unterstützt teilweise Unicode (fast alle Europäischen Zeichen).\tخط صغير يدعم بعض الـUnicode، يحتوي على حروف من معظم اللغات الأروبية.\tSmátt letur sem styður unicode stafi. Unicode staðallinn inniheldur stafi fyrir flest evrópsk tungumál.\tKisméretű betűtípus unicode támogatással. Tartalmazza az európai nyelvek legtöbb karakterét.\tMalgranda dosiero por la plejmulto da eŭropaj lingvoj, kaj povas skribi esperanton.\tVäike font, mis mõneti toetab unicode-i. Sisaldab enamus Euroopa keelte tähti.\t有一些Unicode支持的小字体,包含大部分欧洲语言字母。\tMała czcionka z częściowym wsparciem Unicode. Zawiera znaki dla większości języków europejskich.\tヨーロッパ圏の言語のほとんどをカバーした、 unicodeのサポートも含む小さいフォント\twik hayu pipa-tsum, tlosh wik-saya kanawei dachman-ilahi le-lang.\t\tМаленький шрифт з обмеженою підтримкою Unicode. Містить символи для більшості європейських мов.\tFonte pequena com maior apoio ao unicode. Inclui caractéres de todas as línguas europeias.\nfunction_end_description\tstop the game\tarreter le jeu\t\t\tferma il gioco\tостановить игру\t\tStoppe das Spiel\tإيقاف اللعبة\tenda leikinn\tjáték leálltása\t\t\t\tzatrzymaj grę\t\t\t\tзупинити гру\tparar o jogo\nfunction_end_help\tthe game stops immediately, but if there is dialog after this action, it will still play\tle jeu s'arrete immédiatement, mais si il y a encore des dialogues après cette action, il continura de jouer\t\t\til gioco si ferma immediatamente, ma se c'è un dialogo dopo questa azione, apparirà\tигра немедленно прекращается, но если после этого действия появится диалоговое окно, она всё равно будет воспроизводиться\t\tDas Spiel endet sofort, aber Blöcke nach diesem hier werden noch angezeigt\tإيقاف اللعبة حالاً، ولكن إذا كان يوجد حوار بعد ذلك فسوف يعرض\tendar leikinn samstundis, ef það er samtal eftir þessari aðgerð mun það samt keyra\ta játék azonnal leáll, de ha még van párbeszéd, az még lemegy\t\t\t\tgra zatrzymuje się natychmiast, ale jeżeli jest jakiś dialog po tej akcji, to wciąż zostanie on odegrany\t\t\t\tгра зупиняється негайно, але якщо за цією дією є діалог, він буде відображений до кінця\to jogo para na hora, mas se houver diálogo após essa ação, ele ainda será mostrado\nfunction_end_name\tend\tfin\t\t\tfine\tконец\t\tEnde\tإنهاء\tenda\tvége\t\t\t\tzakończenie\t\t\t\tкінець\tfinal\nfunction_exit_description\tmove player to _ at (_,_)[ with effect _]\tdéplacer\t\t\tsposta giocatore a _ a (_,_)[con effetto _]\tпереместить игрока в _ (_,_)[с эффектом _]\t\tBewege Avatar zu _ bei (_,_)[ mit Effekt _]\tتحريك اللاعب إلى ـــ في (ــ،ــ) [بإستخدام مؤثر الـ_]\tfærðu leikpeðið til _ á (_,_)[ með áhrifum _]\tjátékos mozgatása ide: _ innen (_,_)[ ezzel az effekttel _]\t\t\t\tprzenieś gracza do _ pozycja (_,_)[ z efektem _]\t\t\t\tперемістити гравця до _ в (_,_)[ з ефектом _]\tmover jogador para _ em (_,_)[ com efeito _]\nfunction_exit_name\texit\tsortie\t\t\tuscita\tвыход\t\tÜbergang\tخروج\tútgönguleið\tkijárat\t\t\t\tprzejście\t\t\t\tвихід\tsaída\nfunction_item_description\t_ in inventory[ = _]\t_ dans l'inventaire [ = _]\t\t\t_ nell'inventario [ = _]\t_ в инвентаре[ = _]\t\t_ im Inventar [ =_]\t_ في المخزن [ = _]\t_ í tösku [ = _]\t_ a leltárban[ = _]\t\t\t\t_ w ekwipunku[ = _]\t\t\t\t_ в інвентарі[ = _]\t_ no inventário[ = _]\nfunction_item_name\titem\tobjet\t\t\toggetto\tпредмет\t\tObjekt\tأداة\thlutur\ttárgy\t\t\t\tprzedmiot\t\t\t\tпредмет\titem\nfunction_pg_description\tstart a new page of dialog\tdémarrer une nouvelle page de dialogue\t\t\tcomincia una nuova pagina di dialogo\tначать новую страницу диалога\t\tWarte auf Tastendruck\tبدء صفحة جديدة من الحوار\tbyrja á nýrri síðu af samtali\tkezdj egy új párbeszédoldalt\t\t\t\trozpocznij nową stronę dialogu\t\t\t\tпочати нову сторінку діалогу\tcomeçar uma nova caixa de diálogo\nfunction_pg_help\tif there are actions after this one, they will start after the player presses continue\t\t\t\t\"se ci sono altre azioni dopo questa, si attiveranno dopo che il giocatore sceglie \"\"continua\"\"\"\tесли после этого будут какие-либо действия, они начнутся после того, как игрок нажмёт продолжить\t\tNachfolgende Handlungen werden erst nach einem Tastendruck in einem neuen Dialogs-Fenster angezeigt.\tإذا كان يوجد آي تصرفات بعد هذا التصرف، فسوف يبدئون بعد الضغط على إكمال\tef það eru aðrar aðgerðir eftir þessa, þá munu þær byrja eftir að spilarinn heldur áfram\tha vannak akciók még ez után, azok akkor játszódnak le, ha a játléos a folytatásra kattint\t\t\t\tjeśli po tej stronie są jeszcze jakieś akcje, to rozpoczną się one po tym, jak gracz naciśnie przycisk aby kontynuować\t\t\t\tдії після розриву почнуться тоді, як гравець натисне “продовжити”\tse houverem ações após essa, elas começam após o jogador apertar em continuar\nfunction_pg_name\tpagebreak\tsaut de page\t\t\tinterruzione di pagina\tразрыв\t\tPause\tفاصل صفحة\tsíðu skil\toldaltörés\t\t\t\tnowa strona\t\t\t\tрозрив сторінки\tdividir diálogo\nfunction_print_description\tprint _ in the dialog box\t\t\t\tscrive _ nel box di dialogo\tотобразить _ в окне диалога\t\tschreibe _ in die Dialog Box\tطباعة ـــ في صندوق الحوار\tskrifa _ í samtals glugga\tírd ki, hogy _ a párbeszédablakba\t\t\t\tpisze _ w oknie dialogu\t\t\t\tвивести _ в діалоговому вікні\tprint _ na caixa de diálogo\nfunction_print_name\tprint\t\t\t\tscrive\tотобразить\t\tschreibe\tطباعة\tskrifa\tkiírás\t\t\t\tnapisz\t\t\t\tвивід\tprint\nfunction_property_description\tproperty _[ = _]\tpropriété _[ = _]\t\t\tproprietà _[ = _]\tсвойство _[ = _]\t\tEigenschaft _[ = _]\tخاصية _[ = _]\teiginleiki _[= _]\ttulajdonság _[ = _]\t\t\t\twłaściwość _[ = _]\t\t\t\tвластивість _[ = _]\tpropriedade _[ = _]\nfunction_property_locked_example_help\tchange the value of a property: for example, set the locked property to true to stop an exit from changing rooms, or to prevent an ending from stopping the game\tchange the value of a property: for example, set the locked property to true to stop an exit from changing rooms, or to prevent an ending from stopping the game\t\t\tcambia il valore di una proprietà: ad esempio, imposta la proprietà bloccata come vero per impedire ad un'uscita di far cambiare stanza, o impedisci ad un finale di far finire il gioco\tизмените значение свойства: например, установите для свойства locked значение true, чтобы остановить выход из раздевалок или предотвратить остановку игры в конце\t\tÄndere den Wert einer Eigenschaft: z.B. setze gesperrt auf wahr, um einen Übergang zu sperren oder zu verhindern, dass ein Ende das Spiel beendet.\tتغيير قيمة خاصية: على سبيل المثال، تعين خاصية القفل لمنع مخرج من تغيير الغرف أو منع النهاية من إيقاف اللعبة\tstilltu eiginleika: til dæmis, að stilla læsingar eiginleikan í sannan til að stöðva útgönguleið frá því að skipta um rými, eða til að koma í veg fyrir að endir stöðvi leikinn\ta tulajdonság értékének megváltoztatása: például, állítsd a zárt tulajdonságot igazra, hogy megakadályozz egy kijáratot, hogy szobát változtasson, vagy egy befejezést, hogy megállítsa a játékot\t\t\t\tzmień wartość właściwości, np.: ustaw właściwość locked na true, żeby zatrzymać zmienianie pomieszczeń dla Przejścia, lub zapobiec zatrzymywaniu gry dla Zakończenia\t\t\t\tзмінити значення властивості; наприклад, встановте властивсть “замкнений” щоб не дозволити виходу змінити кімнату, або закінченню збутися\tmudar o valor de uma propriedade: por exemplo, trancar uma cena para impedir que uma saída mude de sala, ou para prevenir que um final pare o jogo\nfunction_property_name\tproperty\tpropriété\t\t\tproprietà\tсвойство\t\tEigenschaft\tخاصية\teiginleiki\ttulajdonság\t\t\t\twłaściwość\t\t\t\tвластивість\tpropriedade\nfunction_say_name\tsay\t\t\t\tscrive\tсказать\t\tsage\tتقول\tsegja\tmondd\t\t\t\tpowiedz\t\t\t\tсказати\tfalar\ngame_settings\tgame settings\tparamètres du jeu\tdefinições do jogo\tajustes del juego\timpostazioni di gioco\tнастройки игры\tspel-inställningar\tSpiel-Einstellungen\tإعدادات اللعبة\tleikjastillingar\tjátékbeállítások\tluda elektaro\tmängu seaded\t游戏设置\tustawienia gry\tゲーム設定\thihi setins\tnastavení hry\tналаштування гри\tdefinições de jogo\ngif_download\tdownload gif\ttélécharger le gif\tdescarregar gif\tdescargar gif\tscarica gif\tскачать гифку\tladda ned gif\tGIF herrunterladen\tتحميل GIF\thala niður gif\tgif letöltése\tprenu (elŝutu) GIF-bildon\tlae alla gif\t下载动图\tpobierz gif\tGIFダウンロード\tiskum GIF\tstáhnout gif\tскачати gif\tbaixar gif\ngif_encoding\tencoding...\tencodage en cours...\ta codificar...\tcodificando...\telaborando...\tобрабатываю...\tkodar...\tVerarbeite...\tتشفير...\tumrita...\ttömörítés\tkodiĝante...\tkodeerib...\t编码中……\tkodowanie…\tエンコード中・・・\talta GIF chako kod...\tzpracování...\tкодування…\tprocessando...\ngif_recording\trecording...\tenregistrement en cours...\ta gravar...\tgrabando...\tregistrando...\tзаписываю...\tspelar in...\tZeichne auf...\tتسجيل...\tupptaka í gangi...\tfelvétel...\tkreadante...\tlindistab...\t录制中……\tnagrywanie…\t録画中・・・\talta hayu-mamook GIF...\tnahrávání...\tзапис…\tgravando...\ngif_snapshot\tsnapshot\tcapture d'écran\tcaptura de ecrã\tcaptura\tistantanea\tсделать снимок\tta en bild\tSchnappschuss\tلمحة\tskjáskot\tpillanatkép\tantaŭvida bildo\tpildista\t截图\tzrzut ekranu\tスナップショット\ttenas picha\tscreenshot\tзнімок\ttirar foto\ngif_start\tstart recording\tcommencer l'enregistrement\tcomeçar gravação\tempezar grabación\tinizia la registrazione\tначать запись\tstarta inspelning\tAufnahme starten\tأبدأ التسجيل\thefja upptöku\tfelvétel indítása\tkomencu krei\talusta lindistamist\t开始录制\tnagrywaj\t録画開始\tmamook chi GIF\tspustit nahrávání\tпочати запис\tiniciar gravação\ngif_stop\tstop recording\tarrêter l'enregistrement\tparar gravação\tparar grabación\tferma la registrazione\tостановить запись\tstoppa inspelning\tAufnahme stoppen\tأوقف التسجيل\tstöðva upptöku\tfelvétel leállítása\tmalkomencu krei\tlõpeta lindistamine\t停止录制\tzatrzymaj nagrywanie\t録画停止\tkopit mamook GIF\tzastavit nahrávání\tзупинити запис\tparar gravação\ngif_tool_name\trecord gif\tenregistrer un gif\tgravar gif\tgrabar gif\tregistra gif\tзаписать гифку\tspela in gif\tGIF aufnehmen\tتسجيل GIF\tgif upptaka\tgif készítése\tfari GIF-bildon\tlindista gif\t录制动图\tnagraj GIF\tGIF録画\tmunk-GIF\tnahrát gif\tзаписати gif\tgravar um gif\ngrid_toggle_visible\tgrid\tgrille\tgrelha\tgrid\tgriglia\tсетка\trutnät\tRaster\tشبكة\trúðustrik\tnégyzetháló\tkrado\truudustik\t格子\tsiatka\tグリッド\tskwil-skwil\tmřížka\tсітка\tgrade\nimport_html\timport game from html file:\timporter à partir d'un fichier html :\timportar um jogo a partir de um ficheiro html:\timportar juego desde archivo html\timporta il gioco da un file html\tзагрузить игру из html файла:\timportera spel från html-fil\tSpiel aus HTML-Datei importieren:\tاستخراج بيانات اللعبة من ملف html:\tflytja inn leik úr html skjali\tjáték importálása html fájlból\tlegu iun ludon el HTML-dosiero:\timpordi mäng html failist:\t导入游戏从 html 文件:\timportuj grę z pliku html:\thtmlファイルからゲームを読み込む\tmash .html hihi kopa edita:\timportovat hru z .html\tімпортувати гру з файлу HTML:\timportar jogo de um arquivo html:\ninstructions_title\tinstructions\tinstructions\tinstruções\tinstrucciones\tistruzioni\tинструкции\tinstruktioner\tAnleitung\tالتعليمات\tleiðbeiningar\thasználati utasítás\tkiel krei?\tõpetused\t说明\tinstrukcja\t説明\tkata mamook\t\tінструкції\tinstruções\ninstructions1\tyou can draw things in the paint panel, then place them in your world in the room panel. you can write dialog for your characters (aka sprites) too. you can also animate your drawings if you want to!\tTu peux dessiner des choses dans la fenêtre de dessin, puis les placer dans tes mondes dans la fenêtre de salle. Tu peux aussi écrire des dialogues pour tes personnages (tes sprites) . Enfin, tu peux animer tes dessins si tu le souhaites!\tpodes desenhar coisas no painel de desenho para as poderes colocar no teu mundo usando o painel da sala e escrever diálogos para as tuas personagens (os teus sprites). podes também animar os teus desenhos se quiseres!\tpuedes dibujar cosas en la caja de pintura, después colócalo en tu mundo en la caja de room. Puedes escribir también diálogos para tus personajes (aka sprites). ¡Ah! Y también puedes animar tus dibujos si quieres!\tPuoi disegnare cose nel pannello di disegno, e poi piazzarle nel tuo mondo dal pannello della stanza. Puoi anche aggiungere dialoghi per i tuoi personaggi (gli sprite). Puoi anche animare i tuoi disegni, se vuoi!\tвы можете создавать изображения в панели рисования, и затем размещать их в игровом мире в панели пространства. ещё вы можете писать диалоги для персонажей (спрайтов). вы также можете анимировать рисунки, если захотите!\tdu kan rita saker i rita-fönstret och sedan sätta in dem i din värld i rum-fönstret. du kan också skriva dialog till dina figurer(sprites), och göra animerade bilder!\tMit dem Zeichnen-Panel, kannst du Dinge zeichnen und anschließend im Räume-Panel in der Welt platzieren. Du kannst auch Dialoge für deine Figuren (Sprites) schreiben. Wenn du magst, kannst du deine Zeichnungen sogar animieren!\tيمكنك أن ترسم الأشياء في لوحة الرسم، ثم يمكنك وضعها في عالمك عن طريق لوحة الغرفة. يمكنك أيضاً أن تكتب الحوار لشخصياتك (الرسومات التفاعلية). كما يمكنك أن تحرك روسماتك إذا أردت.\tþú getur teiknað hluti í teikningasvæðinu og sett þá svo í heiminn þinn í rýmissvæðinu. þú getur líka skrifað samtöl fyrir persónurnar þínar (þ.e. sprotana). það er líka hægt að láta teikningarnar hreyfa sig ef þú vilt!\ta rajz panelben bármit megrajzolhatsz, amit aztán elhelyezhetsz a világodban a szoba panel segítségével. itt írhatsz párbeszédeket is a karaktereidnek (más néven sprite-oknak). sőt animálhatod is őket!\tvi povas desegni bildon je la desegnejo, tiam meti la bildon en la ĉambron de via ludomondo. ankaŭ povas skribi parolojn kaj kodojn por la enludanoj (ludroluloj). eĉ povas krei movantajn bildojn!\tjoonista asju joonista paneelis, siis aseta need oma maailmasse tuba paneelis. Kui sa tahad võid kirjutada teksti oma tegelastele ja animeerida oma joonistusi ka!\t你可以在“画图”面板中画东西,然后可以将它们在“房间”面板中放到你的世界中。你也可以为你的角色(也叫精灵)写对话。你还可以为你的图画制作动画!\tmożesz rysować rzeczy w panelu rysowania, następnie umieść je w twoim świecie, w panelu pomieszczenia. możesz też napisać dialog dla twojej postaci(inaczej: sprite'a). możesz również animować swoje rysunki, jeśli chcesz!\tペイントパネルで何か絵を描いたら、それをルームパネルからあなたの世界に配置することができます。作ったキャラクター(スプライト)にダイアログを追加することもできます。描いた絵をアニメーションにして動かす事もできます!\telip maika tsum kopa pint skwil, alta maika munk-mitlait okok tsum kopa loom skwil. spos maika tiki, maika munk-pipa wawa kopa maika mitlait-hihi-tilikum (sprites), pi maika munk-hulel (animate) maika tsum.\t\tМалюй предмети в панелі малюнки, а потім будуй з них свій світ на панелі “кімнати. Також можна написати діалог для твоїх персонажів (спрайтів). Якщо хочеш, малюнки можна анімувати!\tvocê pode criar coisas no painel de desenho, podendo até animá-las, e colocá-las no seu mundo no painel de cena. você pode escrever diálogos para os seus personagens (atores) também!\ninstructions10\tyou can make multiple rooms, but make sure you add some exits to connect them to each other!\tTu peux faire plusieurs salles, mais fais bien attention d'ajouter des sorties pour les connecter entre elles!\tpodes criar múltiplas salas, mas não te esqueças de as conectar adicionando algumas saídas\tpuedes crear múltiples salas, pero no te olvides de conectarlas añadiendo algunas salidas\tpuoi creare più di una stanza, ma non dimenticarti di aggiungere delle uscite per collegarle tra di loro!\tвы можете создать несколько пространств, главное не забудьте добавить точки выхода, чтобы соединить их между собой!\tdu kan göra flera rum, men kom ihåg att koppla ihop dem med utgångar!\tDu kannst mehrere Räume anlegen - aber füge Ausgänge hinzu, um sie miteinander verbinden!\tيمكنك عمل مجموعة من الغرف، و لكن لا تنسى أن تضيف المخارج لوصلهم ببعض.\tþú getur búið til mörg rými en gættu þess að bæta við útgöngum til að tengja þau saman!\takár több szobát is létrehozhatsz, csak ne felejts el kijáratokat hozzáadni, hogy összekösd őket!\tvi povas krei pliajn ĉambrojn, sed aldonu pordojn (elirejojn) por iri kaj reveni!\tvõid luua mitu erinevat tuba peaasi, et lisad mõned väljapääsud, et neid omavahel ühendada!\t你可以制作多个房间,但记得连接他们的时候加入出口!\tmożesz zrobić wiele pomeszczeń, ale upewnij się, że dodasz kilka przejść, żeby połączyć je ze sobą\tルームは複数作ることができますが、出口を用意して繋げるようにしましょう。\tklosh spos maika tiki mamook hayu loom. pi klosh maika mash lapot-skwil, kakwa maika tlatawa kopa iht loom chi loom, wik maika tlap stuk!\t\tМожна зробити багато кімнат, тільки не забудь додати виходи, що зʼєднають їх між собою!\tvocê pode fazer várias cenas, mas não se esqueça de adicionar algumas saídas para conectá-las!\ninstructions11\tuse download game to download the game as an html file. you can email the file to a friend, or host it online!\tClique sur partager mon jeu pour télécharger ton jeu en fichier html. Tu peux l'envoyer par mail à tes amis ou le mettre en ligne!\tusa a opção de partilhar jogo para descarregares o teu jogo como ficheiro html. podes partilhá-lo com um amigo enviando um e-mail ou podes hospedá-lo online!\tusa compartir juego para descargarlo como un archivo html. Puedes enviarlo en un mail a un amigo o compartirlo online!\tusa scarica gioco per scaricarlo come file html. Puoi inviarlo come allegato ad una email, oppure condividerlo online!\tиспользуйте кнопку загрузить игру, чтобы загрузить вашу игру в формате html-файла. вы можете переслать её друзьям по электронной почте или опубликовать на сайте!\ti fönstret ladda ned kan du ladda ned ditt spel som en html-fil. sen kan du maila filen till en vän, eller ladda upp det på internet!\tKlick auf Herunterladen um dein Spiel als html Datei herunterzuladen. Verschicke die Datei via eMail oder stelle sie woanders online!\tأستخدم رابط تحميل اللعبة لتحميل اللعبة كملف html. يمكنك أن ترسلها إلى صديق أو تستضيفها أونلاين!\tnotaðu 'hala niður leik' til að hala leiknum niður sem html skjali. þú getur sent leikinn til vinar með tölvupósti eða setta hann á netið!\ta játék letöltése gombbal letöltheted a játékodat html fájlként. utána egyszerűen átküldheted emailben egy barátodnak vagy feltöltheted valahova!\tklaku 'prenu ludon' por elŝuti vian ludon kiel .HTML-dosiero. vi povas sendi la dosieron al amikoj per retletero, aŭ afiŝi ĝin rete!\tkasuta lae alla mäng, et laadi alla mäng html failina. võid saata mängu sõbrale emaili kaudu või hostida ise!\t使用“下载游戏“将游戏下载为 html 文件。你可以把文件发送给朋友,或者将其发布上线!\tużyj pobierz grę aby pobrać grę jako plik html. możesz go wysłać e-mailem do znajomych, lub umieścić w internecie!\tゲームをダウンロードを使って、ゲームをhtmlファイルとしてダウンロードする事ができます。友達にそのファイルをemailで送ったり、自分のホームページに乗せたりする事ができます。\tspos maika tiki iskum hihi pi mash hihi maika siks kakwa .html, klosh maika kwutl iskum hihi. alta maika mash okok .html maika siks, pi mash hihi kopa inanet\t\tВикористай “завантажити гру” щоб завантажити гру у вигляді файлу HTML. Її можна надслати другові, або розмістити онлайн!\tclique em baixar jogo para baixá-lo como um arquivo html. você pode enviar o arquivo para amigos por email ou até colocá-lo online!\ninstructions12\tif you want to talk about bitsy, report a bug, or share a game you made, i'm on twitter\tSi tu veux parler de Bitsy, rapporter un bug ou partager un jeu que tu as fait, je suis sur twitter\tse queres falar sobre o bitsy, relatar um erro ou partilhar um jogo que criaste, podes-me encontrar no twitter\tsi quieres hablar sobre bitsy, reportar un bug, o compartir el juego que has creado, estoy en twitter\tse vuoi parlare di Bitsy, segnalare un bug, o condividere il gioco che hai fatto, sono su twitter come\tесли вы хотите обсудить битси, сообщить об ошибке или поделиться игрой, которую вы сделали, вы можете найти меня в X (бывшем твиттере)\tom du vill prata om bitsy, rapportera en bugg, eller visa ditt spel, finns jag på twitter:\tWenn du über Bitsy reden möchtest, einen Fehler entdeckt hast oder ein Spiel von dir teilen magst - Ich bin auf Twitter\tإذا أردت أن تتكلم عن بيتسي، تبلغ عن مشكلة، أو تشارك لعبتك، أنا موجود على التويتر.\tef þú vilt tala um bitsy, tilkynna um villu eða deila leik sem þú bjóst til þá er ég á twitter\tha szeretnél a bitsyről beszélgetni, bugot jelenteni, vagy csak meg akarod osztani velem a játékodat, twitteren megtalálsz itt:\tse (en la angla) vi volas paroli pri Bitsy, informi min pri miskodo en la programo aŭ diskonigi vian ludon, mi uzas tviteron:\tkui tahad rääkida bitsy-st, anda teada rikkest, või jagada oma tehtud mängu, olen twitter-is\t如果你想和我聊聊 Bitsy,反馈问题,或者分享你制作的游戏,请在 Twitter 上找我\tjeśli chcesz porozmawiać o bitsy, zgłosić błąd, lub podzielić się swoją grą, jestem dostępny na twitterze\tもしbitsyについて、質問やバグのレポート、作ったゲームのシェアなどしたければ、ツイッターで連絡してください。\tspos maika tiki kultus-wawa kopa bitsy, pi maika tlap tsipi (bug), pi maika tiki munk-nanich maika bitsy hihi, maika tlap naika kopa twitter kopa pastin wawa\t\tЯкщо хочеш поговорити про Бітсі, відзвітувати про баг, або поділитися грою, я є у Twitter.\tse você quiser conversar sobre o bitsy, denunciar um bug ou compartilhar algo que você fez, estou no twitter\ninstructions13_link_twitter\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\tadamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t@adamledoux\t\t@adamledoux\t@adamledoux\ninstructions14\tor you can post in the\tou tu peux poster dans le\tou podes postar no\to puedes escribir en\to puoi scrivere nel\tили написать\teller så kan du skriva på\toder du schreibst in das\tأو يمكنك النشر في\teða þú getur sent á\t, vagy kiposztolhatod\taŭ vi povas afiŝi ĝin je la\tvõi võid postitada\t或者你可以发布在\tlub możesz ją umieścić na\tもしくはこちらに投稿する事ができます\twuht maika tsum kopa pastin wawa kopa\t\tабо можеш запостити в\tou você pode conversar no\ninstructions15_link_forum\tcommunity forum\tforum de la communauté\tforum da comunidade\tel foro de la comunidad\tforum della comunità\tна форуме нашего сообщества\tvårt forum\tCommunity Forum\tالمنتدى\tspjallsvæðið\ta közösségi fórumba\tBitsy-babilejo\tbitsy foorumis\t社区论坛\tforum społeczności\tコミュニティフォーラム\t\"kanawei tilikum tlaska wawa-ilahi (\"\"forum\"\")\"\t\tфорум спільноти\tfórum da comunidade\ninstructions16\tspecial thanks to:\tRemerciements spéciaux à :\tagradecimentos especiais a:\tAgradecimientos especiales a:\tRingraziamenti speciali a:\tотдельная благодарность:\ttack till:\tBesonderer Dank an:\tشكر خاص إلى:\tsérstakar þakkir til:\tkülön köszönet:\tkromajn dankojn al:\ttänud:\t特别感谢:\tspecjalne podziękowania dla:\tスペシャルサンクス:\thayu masi kopa:\t\tособлива подяка:\tagradecimentos a:\ninstructions17\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tмэри-маргарет\tmary-margaret\tmary-margaret\tماري-مارجرت\tmary-margaret\tmary-margaretnek\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\tmary-margaret\t\tmary-margaret\tmary-margaret\ninstructions18\tseattle game meetup folks\tles gens du seattle game meetup\tseattle game meetup folks\tseattle game meetup folks\tla gente dei seattle game meetup\tлюдям с митапа в сиэтле\tseattle game meetup folks\tseattle game meetup folks\tمجتمع سياتل للألعاب\tfólksins í seattle game meetup\tseattle-i game meetupos arcoknak\tla anojn de la ludrenkontiĝo de Seatlo\tseattle-i mängu gruppi inimesed\tseattle game meetup 朋友们\tludzi z seattle game meetup\tseattle game meetup folksの友達\t\"siatl tawn hihi-hiyu tilikum (\"\"seattle game meetup folks\"\")\"\t\tлюдям з seattle game meetup\tpessoas do seattle game meetup\ninstructions19\teveryone in the bitsy community!\ttoutes les personnes de la communauté Bitsy!\ttoda a gente na comunidade do bitsy!\t¡todas las personas que forman la comunidad de Bitsy!\ttutte le persone che formano la communità di Bitsy!\tвсем, всем, всем из сообщества битси!\talla i bitsy-communityn!\talle in der Bitsy Community!\tو كل من هو جزء من مجتمع بيتسي!\tallra í bitsy-samfélaginu!\tmindenkinek a bitsy közösségben!\tĉiujn Bitsy-uzantojn!\tkõik bitsy kogukonna inimesed!\t所有 Bitsy 社区的朋友们!\twszystkich ze społeczności bitsy!\tBitsyコミュニティーのみんな!\tkanawei mesaika bitsy tilikum!\t\tвсім у спільноті Бітсі!\ttodo mundo da comunidade bitsy!\ninstructions2\tsome words:\tvocabulaire :\talgumas palavras-chave:\talgunas palabras clave:\talcune parole chiave:\tключевые термины:\tviktiga ord:\tEinge Begriffe:\tبعض الكلمات:\tnokkur orð:\tnéhány kifejezés:\tĉefvortoj:\tmõned terminid:\t名词解释:\tniektóre słowa:\t用語解説\tiht-iht tenas-wawa:\t\tдекілька слів:\to que é, o que é:\ninstructions3\troom - a place in your game world\tsalle - un environnement dans ton jeu\tsala - um sítio no teu jogo\troom - una sala de tu juego\tstanza - una scena del tuo gioco\tпространство - фрагмент игрового мира\trum - en plats i din spel-värld\tRaum - Ein Bereich deiner Spielwelt\tغرفة - مكان في عالم لعبتك\trými - staður í heiminum þínum\tszoba - egy hely a játékod világában\tĉambro - eta loko en via ludomondo\ttuba - koht sinu mängu maailmas\t房间 - 你创造的游戏世界中的某处\tpomieszczenie - miejsce w twoim świecie\tルーム - あなたのゲーム世界の中のあるひとつの場所\tloom - maika hihi pelik iht ilahi\t\tкімната - простір у світі твоєї гри\tcena - um lugar no seu mundo\ninstructions4\tavatar - the player's character\tavatar - le personnage du joueur\tavatar - a personagem do jogador\tavatar: el personaje del jugador\tavatar - il personaggio del giocatore\tаватар - персонаж, управляемый игроком\tavatar - figuren som spelaren styr\tAvatar - Die Spielfigur\tالشخص الممثل - شخصية اللاعب\tleikpeð - persónan sem þú stjórnar\tavatár - a játékos karaktere\tmovulo - la ulo kiu movas kiam oni ludas la ludon\tavatar - mängija tegelane\t人物 - 玩家角色\tawatar - postać gracza\tアバター - プレイヤーのキャラクター\ttayi picha - spos tilikum munk-hihi, okok picha tlaska picha\t\tаватар - персонаж гравця\tavatar - o personagem do jogador\ninstructions5\ttile - a piece of the scenery\ttile - un élément du décor\ttile - um elemento do cenário\ttile: un elemento del escenario\ttile - un elemento dello scenario\tтайл - элемент окружения\truta- en del av bakgrunden\tTile - Ein Teil des Hintergrundes\tرقعة - قطعة من المشهد\tflís - hluti af umhverfinu\ttile - a környezet egy eleme\tplanko - bildo sub aŭ malantaŭ la movulo. Planko, muro, pejzaĝo ktp\tkild - üks tükk mängu maailma maastikku\t地块 - 你的场景中的一块\tkafelek - element scenerii\tスプライト - インタラクティブなキャラクターやもの\tlaplash - okok tsum kakwa tipso pi chuk pi kalahan\t\tплитка - шматочок декорацій\tdetalhe - um pedaço do cenário\ninstructions6\tsprite - interactive characters or things\tsprite - un élément ou personnage interactif\tsprite - personagens ou coisas com que podes interagir\tsprite: un elemento o personaje interactivo\tsprite - un elemento o personaggio interattivo\tспрайт - нечто интерактивное, персонаж или предмет\tsprite - interaktiva personer eller saker\tSprite - Interaktive Figuren oder Dinge\tرسمة تفاعلية - أشخاص أو أشياء تفاعلية\tsproti - gagnvirk persóna eða hlutur\tsprite - interaktív karakterek vagy dolgok\tenludano - bildo (ulo aŭ objekto) kiu povas paroli, agi ktp per kodo\tkuju - interaktiivsed tegelased või asjad\t精灵 - 可交互的角色或者事物\tsprite - interaktywna postać lub rzecz\tタイル - 風景の一部\ttilikum - okok tsum komtuks wawa\t\tспрайт - інтерактивний персонаж або предмет\tator - personagens ou coisas interativas\ninstructions7\tto try your game, switch the room to play mode:\tPour essayer ton jeu, clique sur le bouton jouer pour passer ta salle en mode jeu :\tpara experimentares o teu jogo, muda a sala para o modo de jogo:\tpara probar tu juego cambia a modo de juego:\tper provare il tuo gioco, cambia la modalità cliccando gioca nel pannello della stanza\tчтобы протестировать игру, переведите панель пространства в игровой режим\tför att prova ditt rum kan du starta rum-fönstrets spelläge:\tUm dein Spiel zu testen, schalte das Räume-Panel in den Spielmodus:\tلتجرب لعبتك، انقل الغرفة إلى وضع اللعب:\ttil að prófa leikinn þinn settu þá rýmið á leikstillinguna:\ta játékod kipróbálásához, kapcsold a szobát lejátszás módba:\tpor provi vian ludon, klaku 'provu la ludon'\tet proovida oma mängu, vaheta tuba paneel mängu reziimi:\t如果要试玩游戏,调节房间到游玩模式:\tżeby wypróbować swoją grę, przełącz pomieszczenie w tryb gry:\tゲームを動かしてみるには、ルームをプレイモードに変更\tspos maika tiki tlai maika hihi, kwutl nanich hihi chilchil:\t\tщоб випробувати гру, перемикни її в режим гри:\tpara testar o seu jogo, mude a cena para o modo jogar:\ninstructions8\twalk around with the arrow keys\tdéplace-toi avec les flèches directionnelles\tmove-te usando as teclas direccionais\tpara probar tu juego cambia al modo de juego:\tesplora il mondo usando i tasti freccia\tперемещайтесь в пространстве, используя клавиши со стрелками\tgå omkring med piltangenterna\tLaufe mit den Pfeiltasten umher\tتحرك بحرية عن طريق إستخدام الأسهم\tnotaðu örvatakkana til að ganga um\tmozogni a nyilakkal tudsz\tmovadu la movulon per la sagoklavoj\tjaluta ringi noolte nuppudega\t用方向键四处走走\tporuszaj się przy pomocy klawiszy strzałek\t十字キーで歩き回る\t\"spos maika kwutl kalitan-chilchil (\"\"arrow keys\"\"), maika kultus-tlatawa kopa hihi\"\t\tходи стрілочками\tande com as teclas de setas\ninstructions9\ttalk to sprites by walking up to them\tparle aux sprites en marchant vers eux\tfala com sprites movendo-te até eles\thabla con los sprites acercándote a ellos\tparla con gli sprite avvicinandoti a loro\tговорите со спрайтами, подойдя к ним вплотную\tgå in i en sprite för att prata med den\tRede mit Sprites indem du an sie trittst\tلتتكلم مع الرسومات التفاعلية، تحرك في إتجاههم\ttalaðu við sprota með því að ganga upp að þeim\tha sprite-okkal szeretnél beszélni, csak sétálj oda hozzájuk\tproksimiĝu la enludanojn por paroli\träägi kujudega neile otsa jalutades\t走向精灵来和他们对话\trozmawiaj ze sprite'ami nachodząc na nie\tスプライトに立ち寄って話しかける\tspos maika tiki wawa mitlait-hihi-tilikum, maika tlatawa wik-saya okok tilikum\t\tщоб поговорити зі спрайтом, підійди до нього\tconverse com atores ao chegar perto deles\ninventory_tool_name\tinventory\tinventaire\tinventário\tinventario\tinventario\tинвентарь\tinventarie\tInventar\tمخزن\ttaska\ttárgyak\tobjektaro\tvarustus\t清单\tekwipunek\tインベントリ\thuyhuy-nampa haws\tinventář\tінвентар\tinventário\nitem_label\titem\titem\titem\titem\toggetto\tобъект\tföremål\tObjekt\tأداة\thlutur\ttárgy\tobjekto\tese\t物品\tprzedmiot\tアイテム\tiktas\tpředmět\tпредмет\titem\nitems_label\titems\titems\titems\titems\toggetti\tобъекты\tföremål\tObjekte\tأدوات\thlutir\ttárgyak\tobjektoj\tesemed\t物品\tprzedmioty\tアイテム\tiktas\tpředměty\tпредмети\titens\nlanguage_label\tlanguage\tlangue\tlinguagem\tidioma\tlingua\tязык\tspråk\tSprache\tاللغة\ttungumál\tnyelv\tlingvo\tkeel\t语言\tjęzyk\t言語\tle-lang\tjazyk\tмова\tlíngua\nlanguage_name\tEnglish\tFrançais\tPortuguês\tEspañol\tItaliano\tРусский\tsvenska\tDeutsch\tالعربية\tÍslenska\tmagyar\tEsperanto\tEesti\t简体中文\tpolski\t日本語\tchinuk wawa\tČeština\tУкраїнська\tPortuguês Brasileiro\nlanguage_translator_credit\tEnglish text by Adam Le Doux\tVersion française par Peter Februar & Dorian Beaugendre\tVersão portuguesa por Bruno Silva\tVersión española por Marina Díez (@Ninfa_dp) y Florencia Rumpel Rodriguez (@__rumpel)\tVersione italiana a cura di enui (@enricapr)\tперевели: онион (@le_american), элки (@aloelazoe), павел бид (www.paul.bid)\tSvensk översättning av Em (@Embotronic)\tDeutsche Übersetzung von Kai Werder und Matthias Löwe\tالنصوص العربية بواسطة أحمد خليفة\tÍslensk þýðing eftir Game Makers Iceland\tmagyar változat: Török Ádám (@gepember)\tEsperanta eldono de Ariel J Moody/Bonkorpa kaj Sequoia Edwards\tEesti keele tõlge: @scumslug\t简体中文文本由 Ray Song 翻译\tWersja polska Mateusz Teklak (plyr0)\t日本語訳 野老快南\tsequoia edwards yaka tsum okok chinuk bitsy.\tZZ\tПереклав Леонід Шевцов (leonid.codes)\tTexto em Português Brasileiro por Marco\nlist_type_description_cycle\tcycle (say each line, then repeat)\tboucle (lit chaque phrase, puis recommence)\tciclo (diz cada fala, depois repete)\tciclo (dice cada línea, entonces repite)\tciclo (dice ogni frase, poi ripete)\tцикл (показать каждую линию, потом повторить)\tcykel (säg repliker i ordning och upprepa)\tSchleife (sagt nach letzter Zeile wieder erste)\tحلقي (يقول كل سطر، ثم يقوم بتكريرهم)\thringhrás (segir hverja setningu og byrjar svo aftur frá byrjun)\tismétlés (minden sor kimondása, aztán megismétlése)\trondo (diru kiel linio, tiam rekomencu)\tkorduv (ütle igat rida üks kord, siis korda)\t循环(说每一行,并重复)\tpętla (powiedz każdą linijkę, a potem od powtarzaj)\tサイクル(各行を話し、繰り返す)\tkakwa lolo (wawa kakwa layn, kwanisom kilapi okok layn, kakwa: 1-2-3 1-2-3 1-2-3)\tsmyčka (každý řádek jednou a pak celé znovu)\tцикл (сказати кожний рядок, потім повторити)\tciclo (de fala em fala, depois repete)\nlist_type_description_sequence\tsequence (say each line once)\tséquence (lit chaque phrase une fois)\tsequência (diz uma fala de cada vez)\tsecuencia (dice cada línea una vez)\tsequenza (dice la frase ogni volta)\tпоследовательность (показать каждую линию один раз)\tsekvens (säg repliker i ordning)\tReihe (sagt jede Zeile einmal)\tمتتالي (يقول كل سطر مرة واحدة)\truna (segir hverja setningu einu sinni)\tsorozat (sorok kimondása egymás után)\tlinio (diru ĉiun skribon nur unufoje)\tjärjend (ütle igat rida üks kord)\t顺序(每一行说一次)\tsekwencja (powiedz każdą linijkę tylko raz)\tシーケンス(各行を一度だけ話す)\tkakwa iht (wawa kakwa layn iht taim: 1-2-3)\tsekvence (každý řádek jednou)\tпослідовність (сказати кожний рядок один раз)\tsequência (de fala em fala, depois acaba)\nlist_type_description_shuffle\tshuffle (say lines in random order)\tmélange (lit les phrases dans un ordre aléatoire)\tbaralho (diz falas numa ordem aleatória\tshuffle (dice cada línea en un orden cualquiera)\tshuffle (dice ogni frase, ma in ordine casuale)\tперемешать (показать случайную линию)\tblanda (säg repliker i slumpad ordning)\tZufall (sagt eine zufällige Zeile)\tعشوائي (يقول السطور بترتيب عشوائي)\tstokkun (segir setningar að handahófi)\tvéletlen (sorok kimondása véletlen sorrendben)\thazardo (diru ne laŭorde)\tsegamini (ütle igat rida suvalises järjekorras)\t随机(随机说一行)\tlosowo (mów linijki w losowej kolejności)\tシャッフル(ランダムな行を話す)\tkakwa pelton (heilo layn. wawa kakwa: 1-3-1 2-2-3 2-3-1...)\tnáhodně (náhodné pořadí řádků)\tмішанина (казати рядки в довільному порядку)\tmistura (falas em ordem aleatória)\nmarker_move\tmove\tbouger\tmover\tmover\tmuovi\tпереместить\tflytta\tVerschieben\tتحريك\threyfa\tmozgatás\tmovu\t\t移动\tzmień pozycję\t\ttlatawa\t\tперемістити\tmover\nmarker_move_click\tclick in room\tcliquer dans la salle\tclicar numa sala\tclick en la sala\tclicca nella stanza\tкликните в панели пространства\tclicka i rummet\tKlicke in gewünschten Raum\tإضغط في الغرفة\tsmelltu innan svæðis\tkattints a szobába\tklaku enĉambre\t\t点击房间中的一格\tkliknij wewnątrz pomieszczenia\t\tkwutl kopa loom\t\tклацні в кімнаті\tclique na cena\nmarker_moving\tmoving\ten train de bouger\tmovendo\tmoviendo\tmuovendo\tперемещаю\tflyttar\tverschiebe...\tيتحرك\threyfing\tezt mozgatod\tmovanta\t\t移动\tprzesuwanie...\t\thayu-tlatawa\t\tпереміщення\tmovendo\nmarker_tool_empty\tThere are no exits or endings in this room yet! You can add one with this tool. :)\tIl n'y a pas encore de sorties ou de fins dans cette salle! Tu peux en ajouter une avec cet outil :)\tNão existem saídas ou finais nesta sala! Podes adicioná-los com esta ferramenta :)\tTodavía no hay salidas ni finales en esta sala! Las puedes agregar con esta herramienta. =)\tIn questa stanza non ci sono ancora uscite o finali! Puoi aggiungerli da qui :)\tв этом пространстве ещё нет точек выхода или концовок! вы можете добавить их в этой панели :)\tDet finns inga utgångar eller slut i det här rummet! Du kan lägga till dem med det här verktyget\tBisher gibt es in diesem Raum keine Übergänge oder Enden. Füge sie hier hinzu!\tلا يوجد اي مخارج أو نهايات! يمكنك إضافة إحداها عن طريق هذة الأداة. :)\tÞað er engar útgönguleiðir né endalok\tMég nincs kijárat vagy befejezés ebben a szobában. Ezzel az eszközzel adhatod hozzá :)\tNe ekzistas pordoj kaj ludofinoj en ĉi ludo! Vi povas aldoni iom per ĉi ilo. :)\t\t这个房间中还没有出口或者结局!你可以用这个工具添加。 :)\tW tym pomieszczeniu nie ma jeszcze wyjść ani zakończeń! Możesz je dodać tym narzędziem. :)\t\twik mitlait lapot pi kopit-skwil kopa okok loom! spos maika tiki, mamook iht yakwa. :)\t\tВ цій кімнаті ще немає виходів та закінчень! Можеш додати їх цим інструментом :)\tAinda não existem saídas ou finais nessa cena! Você consegue adicioná-los aqui. :)\nmarker_tool_name\texits & endings\tsorties & fins\tsaídas e finais\tsalidas y finales\tfinali e uscite\tточки выхода и концовок\tutgångar och slut\tÜbergänge & Enden\tمخارج و نهايات\tútgönguleiðir og endalok\tkijáratok és befejezések\tpordoj k ludofinoj\t\t退出&结局\tprzejścia i zakończenia\t\tlapot pi kopit-skwil\tprůchody a konec hry\tвиходи та закінчення\tsaídas & finais\nname_label\tname\tnom\tnome\tnombre\tnome\tимя\tnamn\tName\tالأسم\tnafn\tnév\tnomo\tnimi\t名字\tnazwa\t名前\tnim\tnázev\tімʼя\tnome\nopen_settings\topen settings\touvrir les paramètres\tabrir definições\tabrir opciones\tapri le impostazioni\tоткрыть настройки\töppna inställningar\tÖffne Einstellungen\tأفتح الإعدادات\topna stillingar\tbeállítások megnyitása\tvidu la elektaron\tava seaded\t开启设置\totwórz ustawienia\t\tnanich setins\totevřít nastavení\tвідкрити налаштування\tabrir definições\noption_fixed_size\tfixed size\trésolution fixe\tresolução fixa\tresolución fija\tdimensione fissa\tфиксированный размер\tfast storlek\tFeste Größe\tحجم ثابت\tföst stærð\tfix méret\tneplena paĝo\tfikseeritud suurus\t固定尺寸\tstały rozmiar\t固定サイズ\ttenas winto\tfixní velikost\tфіксований розмір\ttamanho fixo\noption_full_size\tfull page\tplein écran\tpágina completa\tpágina completa\tpagina intera\tна всю страницу\thela sidan\tVollbild\tالصفحة بالكامل\tfylla út í skjá\tteljes oldal\tplena paĝo\ttäida terve leht\t全页面\tcała strona\t全画面\tpatl winto\t\tповний екран\ttela cheia\noption_page_color\thtml page color:\tcouleur de la page html :\tcôr da página html:\tcolor de la página html\tcolore della pagina html\tцвет html страницы:\tfärg på html-sidan:\tHTML-Hintergrund-Farbe:\tلون صفحة الـhtml:\tvefsíðulitur\thtml oldal háttérszíne:\t.HTML paĝa koloro:\thtml lehe värv:\thtml 页面颜色:\tkolor strony html:\thtmlページの色\t.html pipa tsum\tbarva html stránky:\tколір сторінки HTML:\tcor da página html:\noption_window_size\tgame window size:\ttaille de l'écran de jeu :\ttamanho da janela do jogo:\ttamaño de la pantalla de juego\tmisura della finestra di gioco\tразмер окна игры:\tspelfönstrets storlek:\tSpiel-Fenstergröße:\tحجم نافذة اللعبة:\tstærð leikjaglugga\tjátékablak mérete:\tgrandeco kiam oni ludas:\tmängu akna suurus:\t游戏窗口尺寸:\trozmiar okna gry:\tゲームウィンドウの大きさ\tmaika nanich hihi, spos tenas spos hayas winto:\tvelikost okna hry:\tрозмір вікна гри:\ttamanho da janela do jogo:\noptions_label\toptions\toptions\topções\topciones\topzioni\tопции\tinställningar\tOptionen\tخيارات\tvalmöguleikar\tbeállítások\telektaro\tvalikud\t选项\topcje\t設定\tpik iktas\tmožnosti\tналаштування\topções\npaint_tool_name\tpaint\tdessin\tdesenhar\tdibujar\tdisegna\tрисовать\trita\tZeichnen\tأرسم\tteikna\trajz\tdesegnejo\tjoonista\t画图\trysowanie\tペイント\tpint\t\tмалювання\tdesenhar\npalette_background\tbackground color\tcouleur du fond\tcôr de fundo\tcolor de fondo\tcolore dello sfondo\tцвет фона\tbakgrundsfärg\tHintergrundfarbe\tلون الخلفية\tbakgrunnslitur\tháttér színe\tsuba koloro\ttagaplaani värv\t背景颜色\tkolor tła\t背景色\tkimta tsum\tbarva pozadí\tколір фону\tcor de fundo\npalette_label\tpalette\tpalette\tpalete de cores\tpaleta\tpalette\tпалитра\tpalett\tPalette\tلوحة الألوان\tlitaval\tszínpaletta\tkoloraro\t\t调色板\tpaleta\t\tloom tsum-hayu\tbarevná paleta\tпалітра\tpaleta\npalette_sprite\tsprite color\tcouleur du sprite\tcôr das sprites\tcolor del sprite\tcolore dello sprite\tцвет спрайта\tsprite-färg\tSprite Farbe\tلون الرسمة التفاعلية\tsprotalitur\tsprite színe\tenludana koloro\tkujude värv\t精灵颜色\tkolor sprite'a\tスプライト色\ttilikum tsum\tbarva objektů\tколір спрайтів\tcor de atores\npalette_tile\ttile color\tcouleur des tiles\tcôr das tiles\tcolor del tile\tcolore del tile\tцвет тайла\trut-färg\tTile Farbe\tلون الرقعة\tflísalitur\ttile színe\tplanka koloro\tkildude värv\t地块颜色\tkolor kafelka\tタイル色\tlaplash tsum\tbarva prostředí\tколір плиток\tcor de detalhes\npalette_tool_advanced\tpalette select\tsélectionner une palette\tescolher uma palete\tselecciona una paleta\tselezionare una palette\tвыбрать палитру\tvälj palett\tAusgewählte Farbpalette\tأختيار لوحة الألوان\tlitaval\tszínpaletta kiválasztása\telektu koloraron\tvali palett\t调色板选择\twybór palety\tパレット選択\tpik loom tsum-hayu\tvýběr barevné palety\tвибір палітри\tescolher paleta\npalette_tool_name\tcolors\tcouleurs\tcores\tcolores\tcolori\tцвета\tfärger\tFarben\tألوان\tlitaval\tszínek\tkoloroj\tvärvid\t颜色\tkolory\t色\tloom tsum-hayu\tbarvy\tкольори\tcores\nplay_game\tplay\tjouer\tjogar\tjugar\tgioca\tиграть\tspela\tSpielen\tألعب\tspila\tlejátszás\tprovu la ludon\tmängi\t玩\tgraj\tプレイ\tnanich hihi\thrát\tграти\ttestar\nreset_data\treset game data\tréinitialiser les données du jeu\trepor dados do jogo\treset datos de juego\tresetta i dati del gioco\tсбросить данные\tnollställ speldata\tSetze Spiel Daten zurück\tامسح بيانات اللعبة\tendursetja leikjagögn\tjátékadatok törlése\trelegu la ludodosieron\ttaasta esialgsed mängu andmed\t重置游戏数据\tusuń dane gry\tゲームデータをリセット\tmunk-heilo hihi kod\tresetovat kód hry\tскинути дані гри\tapagar dados de jogo\nreset_game\tnew game\tnouveau jeu\tnovo jogo\tnuevo juego\tnuovo gioco\tновая игра\tnytt spel\tNeues Spiel\tلعبة جديدة\tbyrja á nýjum leik\túj játék\tnovan ludon\tuus mäng\t新游戏\tnowa\t新規ゲーム\tchi hihi\tnová hra\tнова гра\tnovo jogo\nreset_game_message\tStarting a new game will erase your old data. Consider exporting your work first! Are you sure you want to start over?\tCommencer un nouveau jeu écrasera vos anciennes données. Pensez à exporter votre travail d'abord ! Êtes vous sûr de vouloir repartir de zero ?\tComeçar um jogo novo irá eliminar toda a data do antigo. Considera a exportação do teu trabalho primeiro. Tens a certeza que queres começar de novo?\tCrear un nuevo juego va a borrar tu data vieja. Considera exportar tu trabajo primero! Empezar de cero?\tIniziare un nuovo gioco cancellerà i dati esistenti. Hai pensato di esportarne una copia, prima? Sei sicuro di voler ripartire da zero?\tначав работу над новой игрой, вы сбросите все нынешние данные. вы можете сперва скачать свою игру. вы уверены, что хотите начать заново?\tNär du startar ett nytt spel raderas gammal data. Du kan exportera data först! Är du säker att du vill börja om?\tNeustarten des Spiels wird deinen alten Daten löschen. Exportiere deine Arbeit vielleicht vorher! Möchtest du wirklich von vorn beginnen?\tبداية لعبة جديدة ستؤدي إلى مسح البيانات القديمة. يفضل أن تستخرج بيانات اللعبة أولاً! هل أنت متأكد من أنك تريد البدأ من جديد؟\tAð byrja á nýjum leik getur eytt núverandi leikjagögnum. Íhugaði að niðurhala leikjagögnum áður! Ertu viss um að þú viljir byrja á nýjum leik?\tAz új játék létrehozása törli a meglévő adatokat. Lehet, hogy exportálni kéne előbb a munkádat. Biztos, hogy elölről akarod kezdeni?\tSe vi kreas novan ludon, via antaŭa ludodosiero foriĝos. Bonas konservi la dosieron antaŭ ol krei novan ludon. Ĉu vere vi volas krei novan ludon?\t\t开始一个新游戏会覆盖你的旧数据。请先考虑导出你的作品!你确定要重新来吗?\tRozpoczęcie nowej gry usunie twoje stare dane. Rozważ najpierw wyeksportowanie swojej pracy! Czy na pewno chcesz zacząć od nowa?\t\tspos maika mamook chi hihi, chako heilo maika ol hihi. klosh kakwa?\t\tПочаток нової гри видалить старі дані. Радимо експортувати свою роботу! Дійсно хочеш почати наново?\tAo começar um novo jogo, o seu antigo será apagado. Lembre-se de exportar o seu trabalho! Deseja recomeçar mesmo assim?\nroom_add_endings\tadd endings\tajouter une fin\tadicionar finais\tañadir finales\taggiungi finali\tдобавить концовку\tlägg till slut\tEnde hinzufügen\tإضافة النهايات\tbæta við endalokum\tbefejezés hozzáadása\taldonu ludofinojn\tlisa lõpud\t增加结局\tdodaj zakończenia\tエンディング追加\tmash kopit-skwil\tvložit konec hry\tдодати закінчення\tadicionar finais\nroom_add_exits\tadd exits\tajouter une sortie\tadicionar saídas\tañadir salidas\taggiungi uscite\tдобавить точку выхода\tlägg till utgångar\tAusgang hinzufügen\tإضافة المخارج\tbæta við útgönguleiðum\tkijárat hozzáadása\taldonu pordojn\tlisa väljapääsud\t增加出口\tdodaj przejścia\t出口追加\tmash lapot\tvložit průchod\tдодати виходи\tadicionar saídas\nroom_add_markers\tadd exits & endings\tajouter des sorties & des fins\tadicionar saídas e finais\tañadir salidas y finales\taggiungi finali e uscite\tдобавить точки выхода и концовки\tlägg till utgångar och slut\tFüge Übergänge & Enden hinzu\tإضافة مخارج و نهايات\tbæta við útgönguleiðum og endalokum\tkijárat vagy befejezés hozzáadása\taldonu pordojn k ludofinojn\t\t加入退出&结局\tdodaj przejścia i zakończenia\t\tmash lapot pi kopit-skwil\tpřidat průchody a konec hry\tдодати виходи та закінчення\tadicionar saídas & finais\nroom_label\troom\tsalle\tsala\tsala\tstanza\tпространство\trum\tRäume\tغرفة\trými\tszoba\tĉambro\ttuba\t房间\tpomieszczenie\t\tloom\tmístnost\tкімната\tcena\nroom_tool_advanced\troom tools\toutils d'édition de salle\toutras ferramentas para a edição de sala\therramientas de juego\tstrumenti della stanza\tнастройки пространства\trum-verktyg\tRaum-Werkzeuge\tأدوات الغرفة\trýmistól\tszoba eszközök\tĉambraĵoj\ttoa tööriistad\t房间工具\tnarzędzia pomieszczenia\tルームツール\tloom iktas\tnástroje místnosti\troom tools\topções de cena\nroom_tool_name\troom\tsalle\tsala\troom\tstanza\tпространство\trum\tRäume\tغرفة\trými\tszoba\tĉambro\ttuba\t房间\tpomieszczenie\tルーム\tloom\tmístnost\troom\tcena\nsequence_list_description\tgo through each item once in _:\t\t\t\togni frase una volta sola in _:\tпройтись по каждому объекту один раз в _:\t\tAlle Blöcke werden in _ eingeblendet.\tزيارة كل بند في الـ_ مرة واحدة:\tfarðu í gegnum hvern hlut einu sinni í _:\tmenj végig a tárgyakon ebben: _\t\t\t\tprzejdź przez każdą pozycję tylko raz w _:\t\t\t\tперебрати по одному разу всі пункти з _:\tpassar por cada item uma só vez em _:\nsequence_list_name\tsequence list\tliste de séquences\t\t\tlista sequesnza\tсписок последовательностей\t\tReihen-Liste\tقائمة التسلسل\traðar listi\tsorozat lista\t\t\t\tlista sekwencji\t\t\t\tсписок послідовності\tlista de sequência\nsequence_name\tsequence\tséquence\t\t\tsequenza\tпоследовательность\t\tReihe\tتسلسل\tröð\tsorozat\t\t\t\tsekwencja\t\t\t\tпослідовність\tsequência\nsettings_tool_name\tsettings\tparamètres\tdefinições\tajustes\timpostazioni\tнастройки\tinställningar\tEinstellungen\tالإعدادات\tstillingar\tbeállítások\telektaro\tseaded\t设置\tustawienia\t設定\tsetins\tnastavení\tналаштування\tconfigurações\nshuffle_list_description\t_ items in a random order:\t_\t\t\tfrasi in ordine _:\t_ объекты в случайном порядке:\t\tAlle Blöcke werden in _ eingeblendet.\t_ الأدوات بترتيب عشوائي\t_ hlutir í handhófskenndri röð:\t_ tárgy véletlen sorrendben:\t\t\t\t_ pozycje w przypadkowej kolejności\t\t\t\t_ пунктів в довільному порядку:\titens em _ por ordem aleatória:\nshuffle_list_name\tshuffle list\tmélanger liste\t\t\tlista casuale\tперемешать список\t\tZufalls-Liste\tقائمة مخلوطة\tuppstokkunarlisti\tvéletlen lista\t\t\t\tlista losowa\t\t\t\tсписок мішаніни\tlista de mistura\nshuffle_name\tshuffle\tmélanger\t\t\tcasuale\tперемешать\t\tZufall\tخلط\tstokka upp\tvéletlen\t\t\t\tlosowe\t\t\t\tмішаніна\tmistura\nsprite_label\tsprite\tsprite\tsprite\tsprite\tsprite\tспрайт\tsprite\tSprite\tرسمة تفاعلية\tsproti\tsprite\tenludano\tkuju\t精灵\tsprite\tスプライト\ttilikum\tobjekt\tспрайт\tator\nstop_game\tstop\tStop\tparar\tpausar\tstop\tостановить\tstopp\tSpiel stoppen\tأقف\tstop\tstop\tmalkomencu\t\t停止\tzatrzymaj\t\tkopit nanich\tzastavit\tзупинити\tparar\ntext_direction_description\tOption for languages that read right to left\tOptions pour les langues qui se lisent de la droite vers la gauche\tOpção para linguagens que se lêem da direita para a esquerda\tOpciones para idiomas que se leen de derecha a izquierda\tOpzione per le lingue che si leggono da destra a sinistra\tОпция для языков, которые читаются справа налево\tinställning för språk som skrivs från höger till vänster\tOptionen für Sprachen, die von Rechts nach Links gelesen werden\tخيار للغات التي تقرأ من اليمين إلى اليسار\tValmöguleiki fyrir tungumál sem er lesið frá hægri til vinstri.\tOpció jobbról balra író nyelvek használatához\tElektu por lingvoj kiel la araba, hebrea, judgermana...\t\t语言从右向左选项\tOpcje dla języków czytanych od prawej strony do lewej\t\tspos maika pipa kuli kakwa pastin pi lushan pi pasiuks man tlaska pipa. spos maika pipa kuli kakwa alab pi shyu tlaska pipa.\t\tОпція для мов з напрямком справа наліво\tOpção para línguas lidas da direita para a esquerda.\ntext_direction_label\ttext direction\tdirection du texte\tdirecção do texto\tdirección del texto\tdirezione del testo\tнаправление текста\ttextriktning\tText-Richtung\tإتجاة الخط\ttextastefna\tszöveg iránya\ttekstokielo\t\t文字方向\tkierunek tekstu\t\tpipa kuli\t\tнапрямок тексту\tdireção de texto\ntext_direction_ltr\tLeft to Right\tDe la Gauche vers la Droite\tEsquerda para a direita\tIzquierda a Derecha\tDa sinistra a destra\tслева направо\tvänster till höger\tLinks nach Rechts\tمن اليسار إلى اليمين\tVinstri til hægri\tbalról jobbra\tMaldekstre Dekstren\t\t从左向右\tOd lewej do prawej\t\tkakwa pastin\t\tзліва направо\tEsquerda para Direita\ntext_direction_rtl\tRight to Left\tDe la Droite vers la Gauche\tDireita para a esquerda\tDerecha a Izquierda\tDa destra a sinistra\tсправа налево\thöger till vänster\tRechts nach Links\tمن اليمين إلى اليسار\tHægri til vinstri\tjobbról balra\tMaldekstren Dekstre\t\t从右向左\tOd prawej do lewej\t\tkakwa shyu\t\tсправа наліво\tDireita para Esquerda\ntile_label\ttile\ttile\ttile\ttile\ttile\tтайл\truta\tTile\tرقعة\tflís\ttile\tplanko\tkild\t地块\tkafelek\tタイル\tlaplash\tprostředí\tплитка\tdetalhe\ntitle_placeholder\tTitle\tTitre\tTítulo\tTitúlo\tTitolo\tНазвание\ttitel\tTitel\tعنوان\tTitill\tCím\tLudonomo\t\t\b标题\tTytuł\t\thihi nim\t\tНазва\tTítulo\ntools_label\ttools\toutils\tferramentas\tHerramientas\tStrumenti\tпанель инструментов\tverktyg\tWerkzeuge\tأدوات\ttól\teszközök\tiloj\ttööriistad\t工具\tnarzędzia\tツール\tmamook-iktas\tnástroje\tінструменти\tferramentas\ntransition_fade_b\tfade (black)\tfondu (noir)\t\t\tdissolvenza (nero)\tзатухание (в чёрное)\t\tVerblassen (schwarz)\tتلاشي إلى أسود\thverfa (í svart)\táttűnés (fekete)\t\t\t\twyciemnienie\t\t\t\tзатухання (чорний)\tdesmaecer (preto)\ntransition_fade_w\tfade (white)\tfondu (blanc)\t\t\tdissolvenza (bianco)\tзатухание (в белое)\t\tVerblassen (weiß)\tتلاشي إلى أبيض\thverfa (í hvítt)\táttűnés (fehér)\t\t\t\trozjaśnienie\t\t\t\tзатухання (білий)\tdesmaecer (branco)\ntransition_slide_d\tslide down\tglisser vers le bas\t\t\tscorri verso il basso\tсдвиг вниз\t\tRunterschieben\tالزحف للأسفل\trenna niður\tlecsúszás\t\t\t\tprzesuń w dół\t\t\t\tковзання вниз\tdeslizar (baixo)\ntransition_slide_l\tslide left\tglisser vers la gauche\t\t\tscorri a sinistra\tсдвиг влево\t\tNach Links schieben\tالزحف لليسار\trenna til vinstri\tbalra csúszás\t\t\t\tprzesuń w lewo\t\t\t\tковзання ліворуч\tdeslizar (esquerda)\ntransition_slide_r\tslide right\tglisser vers la droite\t\t\tscorri a destra\tсдвиг вправо\t\tNach Rechts schieben\tالزحف لليمين\trenna til hægri\tjobbra csúszás\t\t\t\tprzesuń w prawo\t\t\t\tковзання праворуч\tdeslizar (direita)\ntransition_slide_u\tslide up\tglisser vers le haut\t\t\tscorri verso l'alto\tвдвиг вверх\t\tHochschieben\tالزحف للأعلى\trenna upp\tfelcsúszás\t\t\t\tprzesuń w górę\t\t\t\tковзання вгору\tdeslizar (cima)\ntransition_tunnel\ttunnel\ttunnel\t\t\ttunnel\tтоннель\t\tTunnel\tنفق\tgöng\talagút\t\t\t\ttunel\t\t\t\tтунель\ttúnel\ntransition_wave\twave\tvague\t\t\tonda\tволна\t\tWelle\tموجي\talda\thullám\t\t\t\tfalowanie\t\t\t\tволна\tondular\nunsupported_features\tuh oh ~ your browser doesn't support all of bitsy's features, so some things might not work right. maybe try another browser, such as:\toh oh ~ ton navigateur internet ne supporte pas toutes les fonctionnalités de Bitsy, certaines choses pourraient ne pas fonctionner correctement, essaie un autre navigateur, comme :\tuh oh - o teu explorador não suporta todas as funcionalidades do Bitsy, por isso algumas coisas poderão não funcionar correctamente. talvez queiras tentar outro explorador como:\toh oh! tu navegador no es compatible con todas las funcionalidades de Bitsy y es posible que algunas no funcionen correctamente. Puedes intentar utilizar otros como:\toh no! il tuo browser non è compatibile con tutte le funzionalità di Bitsy, ed è possibile che alcune non funzionino bene. Puoi usarne altri come:\tупс ~ ваш браузер не поддерживает все функции битси, поэтому что-то работать не будет. попробуйте другой браузер, например:\toj då ~ bitsy har vissa funktioner som inte stöds av din webbläsare, så allt kanske inte fungerar som det ska. du kan prova med en annan webbläsare, till exempel:\tOh oh - Dein Browser unterstützt nicht alle Funktionen von Bitsy. Manche Dinge könnten nicht funktionieren, probiere doch einen der anderen Browser aus:\tمتصفحك لا يدعم جميع ميزات بيتسي، و لهذا، بعض الوظائف قد لا تعمل. من الأفضل أن تجرب أحد المتصفحات التالية:\tæji nei ~ vafrinn sem þú notar styður ekki alla eiginleika bitsy, þannig að nokkrir hlutir munu ef til vill ekki virka. Ef þú getur prófaðu að nota aðra vafra eins og:\tajjajj ~ a böngésződ nem támogatja az összes bitsy funkciót, próbálj ki egy másik böngészőt, mondjuk ezeket:\tho ve ~ via retvidilo ne povas ĉiujn aferojn de Bitsy, do eble iuj aferoj ne funkcios ĝuste. vi povas provi alian retvidilon, ekzemple:\tups ~ sinu brauser ei toeta kõiki bitsy funktsioone, nii et mõned asjad ei pruugi õigesti töötada. Võibolla proovi teist brauserit, nagu näiteks:\t啊噢~你的浏览器不完全支持 Bitsy ,有些功能无法正确运行。试试其他的浏览器吧,比如:\to nie ~ twoja przeglądarka nie wspiera wszystkich funkcjonalności bitsy, więc niektóre rzeczy mogą nie działać prawidłowo. możesz wypróbować inną przeglądarkę, taką jak:\tあらら、残念ながら使用中のブラウザでは一部のBitsyの機能が使用できないようです。次のブラウザを使ってみるといいかもしれません:\taaana ~ wik maika nanich-inanet ikta klosh kopa nanich kanawei bitsy iktas. klosh maika tlai huloima nanich-inanet ikta, kakwa:\t\tйой ~ ваш браузер не підтримує всі функції Бітсі, щось може не працювати. Пропоную спробувати інший браузер, такий як:\tvish ~ o seu navegador não suporta todas as funções do bitsy, então algumas coisas não funcionam. tente um desses navegadores:\nunsupported_features_dismiss\tI'll be ok, hide this message\tj'ai compris, cacher ce message\teu compreendo, esconde esta mensagem\tEntiendo. Oculta este mensaje.\tCapito. Nascondi questo messaggio.\tсо мной всё будет в порядке, спрятать это сообщение\tdet går bra, göm det här meddelandet\tIst in Ordnung. Verberge diese Nachricht\tسأكون بخير، أخفي هذه الرسالة\tekkert mál, feldu þessi skilaboð\tköszi, de megoldom, üzenet elrejtése\tmi ne zorgas. malvidu ĉi informon\tkõik on korras, peida see sõnum\t没关系,隐藏这条消息\tWszystko w porządku, ukryj tę wiadomość\tなんとかなるので、このメッセージを隠す\tklosh naika. ipui okok sisim.\t\tнай буде, приховати повідомлення\tTudo certo, pode esconder a mensagem\nupload_font\tload font\tcharger une police d'écriture\tcarregar uma fonte para utilização\tsubir fuente\tCarica font\tзагрузить шрифт\tladda upp typsnitt\tSchrift hochladen\tأرفع الخط\thala upp letri\tbetűtípus feltöltése\tlegu tiparon\t\t上传字体\twyślij czcionkę\t\tmash pipa-tsum\tnahrát font\tвідвантажити шрифт\tcarregar fonte\nupload_game\tload game\tcharger un jeu\tcarregar um jogo para edição\tsubir juego\tCarica gioco\tзагрузить игру\tladda upp spel\tSpiel hochladen\tأرفع اللعبة\thala upp leik\tjáték feltöltése\tlegu (alŝutu) ludon\t\t上传游戏\twyślij grę\t\tmash hihi\tnahrát hru\tвідвантажити гру\tcarregar jogo\nvalue_type_text\ttext\ttexte\t\t\ttesto\tтекст\t\tText\tنص\ttexti\tszöveg\t\t\t\ttekst\t\t\t\tтекст\ttexto\nvariable_add\tadd variable\tajouter une variable\tadicionar variável\tañadir variable\taggiungi variabile\tдобавить переменную\tlägg till variabel\tVariable hinzufügen\tإضافة متغير\tný breyta\tváltozó hozzáadása\taldonu ŝangeblan numeron\tlisa muutuja\t加入变量\tdodaj zmienną\t変数の追加\tmash huyhuy-nampa\tpřidat proměnnou\tдодати змінну\tcolocar variável\nvariable_label\tvariable\tvariable\tvariável\tvariables\tvariabili\tпеременная\tvariabel\tVariablen\tمتغير\tbreyta\tváltozó\tŝangebla numero\tmuutuja\t变量\tzmienna\t変数\thuyhuy-nampa\tproměnná\tзмінна\tvariável\nvariables_label\tvariables\tvariables\tvariáveis\tvariables\tvariabili\tпеременные\tvariabler\tVariablen\tمتغيرات\tbreytur\tváltozók\tŝanĝeblaj numeroj\tmuutujad\t变量\tzmienne\t変数\thuyhuy-nampa\tproměnné\tзмінні\tvariáveis\nversion_notes_title\twhat's new in\tquoi de neuf\to que há de novo na\tqué hay de nuevo\tcosa c'è di nuovo\tчто нового в\tnyheter i\tWas gibt's Neues in\tما هو جديد في\tnýtt í\tújdonságok:\tkio novas en\tmida on uut:\t新东西:\tco nowego w\t新機能:\tchi ikta kopa\t\tщо нового у\to que tem de novo no\nwall_toggle\twall\tmur\tparede\tpared\tmuro\tстена\tvägg\tWand\tحائط\tveggur\tfal\tmuro\tsein\t墙\tściana\t壁\tkalahan\tzeď\tстіна\tparede\nwalls_toggle_visible\twalls\tmurs\tparedes\tmuros\tmuri\tстены\tväggar\tWände\tحوائط\tveggir\tfalak\tmuroj\tseinad\t墙\tściany\t壁\tkalahan\tzdi\tстіни\tparedes\ngeneral_edit\tedit\t\t\t\t\tредактировать\t\t\t\t\t\t\t\t\t\t\t\t\t\teditar\ngeneral_length\tlength\t\t\t\t\tдлина\t\t\t\t\t\t\t\t\t\t\t\t\t\tduração\ngeneral_speed\tspeed\t\t\t\t\tскорость\t\t\t\t\t\t\t\t\t\t\t\t\t\tvelocidade\nroom_eyedropper_pick\tpick\t\t\t\t\tвыбор\t\t\t\t\t\t\t\t\t\t\t\t\t\textrair\nblip_tool\tblip-o-matic\t\t\t\t\tзвуки\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodi-za-dô\nblip_sfx\tblip\t\t\t\t\tsfx импульса\t\t\t\t\t\t\t\t\t\t\t\t\t\tplim\nblip_pitch\tpitch\t\t\t\t\tвысота импульса\t\t\t\t\t\t\t\t\t\t\t\t\t\taltura\nblip_generator\tgenerator\t\t\t\t\tгенератор\t\t\t\t\t\t\t\t\t\t\t\t\t\tsom de base\ntune_tool\ttune\t\t\t\t\tмузыка\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia\ntune_bar\tbar\t\t\t\t\tпанель\t\t\t\t\t\t\t\t\t\t\t\t\t\tbarra\ntune_compose\tcompose\t\t\t\t\tсочинить\t\t\t\t\t\t\t\t\t\t\t\t\t\tcompor\ntune_instrument\tinstrument\t\t\t\t\tинструмент\t\t\t\t\t\t\t\t\t\t\t\t\t\tinstrumento\ntune_style\tstyle\t\t\t\t\tстиль\t\t\t\t\t\t\t\t\t\t\t\t\t\testilo\ntune_note\tnote\t\t\t\t\tзаметка\t\t\t\t\t\t\t\t\t\t\t\t\t\tnota\ntune_arp_off\tstrum off\t\t\t\t\tбренчание выкл.\t\t\t\t\t\t\t\t\t\t\t\t\t\tsem dedilhado\ntune_arp_up\tstrum chord (up)\t\t\t\t\tаккорд бренчания (вверх)\t\t\t\t\t\t\t\t\t\t\t\t\t\tdedilhar acorde (cima)\ntune_arp_down\tstrum chord (down)\t\t\t\t\tаккорд бренчания (вниз)\t\t\t\t\t\t\t\t\t\t\t\t\t\tdedilhar acorde (baixo)\ntune_arp_int5\tstrum interval (small)\t\t\t\t\tинтервал бренчания (небольшой)\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalo (pequeno)\ntune_arp_int8\tstrum interval (big)\t\t\t\t\tинтервал бренчания (большой)\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalo (grande)\ntune_wave_pulse2\ttone P2\t\t\t\t\tтон P2\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P2\ntune_wave_pulse4\ttone P4\t\t\t\t\tтон P4\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P4\ntune_wave_pulse8\ttone P8\t\t\t\t\tтон P8\t\t\t\t\t\t\t\t\t\t\t\t\t\tmelodia P8\ntune_tempo_slow\tslow\t\t\t\t\tмедленно\t\t\t\t\t\t\t\t\t\t\t\t\t\tlenta\ntune_tempo_med\tmedium\t\t\t\t\tсредне\t\t\t\t\t\t\t\t\t\t\t\t\t\tmédia\ntune_tempo_fast\tfast\t\t\t\t\tбыстро\t\t\t\t\t\t\t\t\t\t\t\t\t\trápida\ntune_tempo_xfast\tturbo\t\t\t\t\tтурбо\t\t\t\t\t\t\t\t\t\t\t\t\t\tturbo\ntune_key\tkey\t\t\t\t\tлад\t\t\t\t\t\t\t\t\t\t\t\t\t\ttom\ntune_key_basic\tmood\t\t\t\t\tнастрой\t\t\t\t\t\t\t\t\t\t\t\t\t\tritmo\ntune_key_major\tcheery\t\t\t\t\tвеселый\t\t\t\t\t\t\t\t\t\t\t\t\t\tfeliz\ntune_key_minor\tgloomy\t\t\t\t\tмрачный\t\t\t\t\t\t\t\t\t\t\t\t\t\ttriste\nsetting_text_mode\ttext mode\t\t\t\t\tтекстовый режим\t\t\t\t\t\t\t\t\t\t\t\t\t\testilo de texto\nsetting_text_hirez\tdefault\t\t\t\t\tсглаженный\t\t\t\t\t\t\t\t\t\t\t\t\t\tpadrão\nsetting_text_lorez\tchunky\t\t\t\t\tрубленный\t\t\t\t\t\t\t\t\t\t\t\t\t\tlargo\nfind_tool\tfind\t\t\t\t\tпоиск\t\t\t\t\t\t\t\t\t\t\t\t\t\tbuscar\nfind_all\tall\t\t\t\t\tнайти все\t\t\t\t\t\t\t\t\t\t\t\t\t\ttudo\n",
"defaultGameData.bitsy": "Write your game's title here\n\n# BITSY VERSION 8.0\n\n! VER_MAJ 8\n! VER_MIN 0\n! ROOM_FORMAT 1\n! DLG_COMPAT 0\n! TXT_MODE 0\n\nPAL 0\n0,82,204\n128,159,255\n255,255,255\nNAME blueprint\n\nROOM 0\n0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0\n0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0\n0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\nNAME example room\nPAL 0\nTUNE 2\n\nTIL a\n11111111\n10000001\n10000001\n10011001\n10011001\n10000001\n10000001\n11111111\nNAME block\n\nSPR A\n00011000\n00011000\n00011000\n00111100\n01111110\n10111101\n00100100\n00100100\nPOS 0 4,4\n\nSPR a\n00000000\n00000000\n01010001\n01110001\n01110010\n01111100\n00111100\n00100100\nNAME cat\nDLG 0\nPOS 0 8,12\nBLIP 1\n\nITM 0\n00000000\n00000000\n00000000\n00111100\n01100100\n00100100\n00011000\n00000000\nNAME tea\nDLG 1\n\nITM 1\n00000000\n00111100\n00100100\n00111100\n00010000\n00011000\n00010000\n00011000\nNAME key\nDLG 2\nBLIP 2\n\nDLG 0\nI'm a cat\nNAME cat dialog\n\nDLG 1\nYou found a nice warm cup of tea\nNAME tea dialog\n\nDLG 2\nA key! {wvy}What does it open?{wvy}\nNAME key dialog\n\nVAR a\n42\n\nTUNE 1\n3d,0,0,0,3d5,0,0,0,3l,0,0,0,3s,0,0,0\n16d2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n>\n4l,0,0,0,s,0,3l,0,0,0,2s,0,2m,0,2r,0\n16m2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n>\n3d,0,0,0,3d5,0,0,0,3l,0,0,0,3s,0,0,0\n16l2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n>\n3l,0,0,0,s,0,4m,0,0,0,4r,0,0,0,0,0\n16s2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\nNAME finale fanfare\nKEY C,D,E,F,G,A,B d,r,m,s,l\nTMP XFST\nSQR P2 P8\nARP INT8\n\nTUNE 2\n0,0,2G,0,A,0,B,0,2C5,0,B,A,G,0,2G,0\nG3,0,D,0,G3,0,D,0,2A3,0,E,0,C,0,E,0\n>\n2F#,0,G,0,A,0,F#,0,2E,0,F#,E,4D,0,0,0\n2D,0,E,0,F#,0,D,0,2C,0,2G3,0,2F#3,0,D2,0\n>\n0,0,2G,0,A,0,B,0,2C5,0,B,A,G,0,G,0\n2G2,0,D,D5,G3,G,D,0,2C2,0,E,E5,C3,C,E5,0\n>\n2D,0,C5,B,A,0,A,0,4A,0,0,0,F#,0,0,0\nA2,0,E3,0,C3,0,E3,0,D3,0,A3,0,D,0,0,0\n>\n2E5,0,2G,0,2G5,0,2G,0,2F#5,0,2E5,0,2D5,0,2C5,0\n2C3,0,2E,0,2E5,0,2C,0,2A3,0,2C,0,2F#,0,2E,0\n>\n3B,0,0,0,2E5,0,D5,0,4A,0,0,0,G,0,0,0\n2G3,0,B3,0,2D,0,D3,0,2C3,0,G3,0,D#,0,0,0\n>\n0,0,2G,0,A,0,2B,0,C5,0,B,C5,A,0,G,0\nA2,0,A3,0,C,0,2D,0,D#,0,D,E,C,0,C3,0\n>\n8B,0,0,0,0,0,0,0,A,0,2F#,0,E,0,D,0\nD3,0,A3,0,F#,0,D,0,C,0,2D3,0,C3,0,F#3,0\nNAME tuneful town\nTMP FST\nSQR P4 P2\n\nTUNE 3\n3F5,0,0,A#,0,2C#5,0,A#,3F5,0,0,F#5,0,0,2F5,0\nA#3,C#,F,0,0,F,C#,F,A#3,C#,F,A#,0,A#,C#,F\n>\n3F5,0,0,A,0,2C#5,0,A,3F5,0,0,2A#5,0,A#,D#5,0\nA3,C#,F,A3,0,F,C#,F,A,C#,F,0,D#,0,C#5,0\n>\n4F5,0,0,0,G#,2C#5,0,G#,3F5,0,0,D#5,0,F5,2C#5,0\nG#3,C#,F,3F#,0,0,2F,0,G#3,C#,F,F#,B,A,F,D#\n>\n4D#5,0,0,0,0,0,2A#,0,4A#,0,0,0,0,0,A#,C5\nG3,D#,F,G,0,D#,F,G,G3,D#,G,F,0,F,D#,C#3\n>\n4C#5,0,0,0,0,0,2C#5,0,3C#5,0,0,D#5,0,0,2C#5,0\nF#2,C#,F#3,A#3,0,F#3,C#3,F#3,F#2,A,F#3,C#,0,F#3,A3,F#3\n>\n3C#5,0,0,F,3C5,0,0,C,3C5,0,0,D#,3A#,0,0,0\nF2,D#3,A3,0,0,F3,C#,0,F#2,A#3,D#3,0,0,F#3,C#,C\n>\n3A#,0,0,0,C5,0,C#5,0,A#,0,C#,D#,G#,G#3,0,C#3\nC3,A#3,C,E,A,0,A#,0,0,B2,B3,0,2F,0,0,0\n>\nA#,0,A#3,0,C#,0,F,0,A#,0,0,0,0,0,0,0\nA#2,0,C#3,0,F3,0,A#3,0,D,0,0,0,0,F#3,0,F3\nNAME rhythmic ruins\nTMP MED\nSQR P4 P4\n\nBLIP 1\nE5,B5,B5\nNAME meow\nENV 40 99 4 185 138\nBEAT 61 115\nSQR P2\n\nBLIP 2\nD5,E5,D5\nNAME pick up key\nENV 99 65 6 96 152\nBEAT 95 0\nSQR P4\n\n",
"ascii_small.bitsyfont": "FONT ascii_small\nSIZE 6 8\nCHAR 0\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 32\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 33\n000100\n001110\n001110\n000100\n000100\n000000\n000100\n000000\nCHAR 34\n011011\n011011\n010010\n000000\n000000\n000000\n000000\n000000\nCHAR 8220\n011011\n011011\n010010\n000000\n000000\n000000\n000000\n000000\nCHAR 8221\n011011\n011011\n010010\n000000\n000000\n000000\n000000\n000000\nCHAR 35\n000000\n001010\n011111\n001010\n001010\n011111\n001010\n000000\nCHAR 36\n001000\n001110\n010000\n001100\n000010\n011100\n000100\n000000\nCHAR 37\n011001\n011001\n000010\n000100\n001000\n010011\n010011\n000000\nCHAR 38\n001000\n010100\n010100\n001000\n010101\n010010\n001101\n000000\nCHAR 39\n001100\n001100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 8216\n001100\n001100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 8217\n001100\n001100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 40\n000100\n001000\n001000\n001000\n001000\n001000\n000100\n000000\nCHAR 41\n001000\n000100\n000100\n000100\n000100\n000100\n001000\n000000\nCHAR 42\n000000\n001010\n001110\n011111\n001110\n001010\n000000\n000000\nCHAR 43\n000000\n000100\n000100\n011111\n000100\n000100\n000000\n000000\nCHAR 44\n000000\n000000\n000000\n000000\n000000\n001100\n001100\n001000\nCHAR 45\n000000\n000000\n000000\n011111\n000000\n000000\n000000\n000000\nCHAR 46\n000000\n000000\n000000\n000000\n000000\n001100\n001100\n000000\nCHAR 47\n000000\n000001\n000010\n000100\n001000\n010000\n000000\n000000\nCHAR 48\n001110\n010001\n010011\n010101\n011001\n010001\n001110\n000000\nCHAR 49\n000100\n001100\n000100\n000100\n000100\n000100\n001110\n000000\nCHAR 50\n001110\n010001\n000001\n000110\n001000\n010000\n011111\n000000\nCHAR 51\n001110\n010001\n000001\n001110\n000001\n010001\n001110\n000000\nCHAR 52\n000010\n000110\n001010\n010010\n011111\n000010\n000010\n000000\nCHAR 53\n011111\n010000\n010000\n011110\n000001\n010001\n001110\n000000\nCHAR 54\n000110\n001000\n010000\n011110\n010001\n010001\n001110\n000000\nCHAR 55\n011111\n000001\n000010\n000100\n001000\n001000\n001000\n000000\nCHAR 56\n001110\n010001\n010001\n001110\n010001\n010001\n001110\n000000\nCHAR 57\n001110\n010001\n010001\n001111\n000001\n000010\n001100\n000000\nCHAR 58\n000000\n000000\n001100\n001100\n000000\n001100\n001100\n000000\nCHAR 59\n000000\n000000\n001100\n001100\n000000\n001100\n001100\n001000\nCHAR 60\n000010\n000100\n001000\n010000\n001000\n000100\n000010\n000000\nCHAR 61\n000000\n000000\n011111\n000000\n000000\n011111\n000000\n000000\nCHAR 62\n001000\n000100\n000010\n000001\n000010\n000100\n001000\n000000\nCHAR 63\n001110\n010001\n000001\n000110\n000100\n000000\n000100\n000000\nCHAR 64\n001110\n010001\n010111\n010101\n010111\n010000\n001110\n000000\nCHAR 65\n001110\n010001\n010001\n010001\n011111\n010001\n010001\n000000\nCHAR 66\n011110\n010001\n010001\n011110\n010001\n010001\n011110\n000000\nCHAR 67\n001110\n010001\n010000\n010000\n010000\n010001\n001110\n000000\nCHAR 68\n011110\n010001\n010001\n010001\n010001\n010001\n011110\n000000\nCHAR 69\n011111\n010000\n010000\n011110\n010000\n010000\n011111\n000000\nCHAR 70\n011111\n010000\n010000\n011110\n010000\n010000\n010000\n000000\nCHAR 71\n001110\n010001\n010000\n010111\n010001\n010001\n001111\n000000\nCHAR 72\n010001\n010001\n010001\n011111\n010001\n010001\n010001\n000000\nCHAR 73\n001110\n000100\n000100\n000100\n000100\n000100\n001110\n000000\nCHAR 74\n000001\n000001\n000001\n000001\n010001\n010001\n001110\n000000\nCHAR 75\n010001\n010010\n010100\n011000\n010100\n010010\n010001\n000000\nCHAR 76\n010000\n010000\n010000\n010000\n010000\n010000\n011111\n000000\nCHAR 77\n010001\n011011\n010101\n010001\n010001\n010001\n010001\n000000\nCHAR 78\n010001\n011001\n010101\n010011\n010001\n010001\n010001\n000000\nCHAR 79\n001110\n010001\n010001\n010001\n010001\n010001\n001110\n000000\nCHAR 80\n011110\n010001\n010001\n011110\n010000\n010000\n010000\n000000\nCHAR 81\n001110\n010001\n010001\n010001\n010101\n010010\n001101\n000000\nCHAR 82\n011110\n010001\n010001\n011110\n010010\n010001\n010001\n000000\nCHAR 83\n001110\n010001\n010000\n001110\n000001\n010001\n001110\n000000\nCHAR 84\n011111\n000100\n000100\n000100\n000100\n000100\n000100\n000000\nCHAR 85\n010001\n010001\n010001\n010001\n010001\n010001\n001110\n000000\nCHAR 86\n010001\n010001\n010001\n010001\n010001\n001010\n000100\n000000\nCHAR 87\n010001\n010001\n010101\n010101\n010101\n010101\n001010\n000000\nCHAR 88\n010001\n010001\n001010\n000100\n001010\n010001\n010001\n000000\nCHAR 89\n010001\n010001\n010001\n001010\n000100\n000100\n000100\n000000\nCHAR 90\n011110\n000010\n000100\n001000\n010000\n010000\n011110\n000000\nCHAR 91\n001110\n001000\n001000\n001000\n001000\n001000\n001110\n000000\nCHAR 92\n000000\n010000\n001000\n000100\n000010\n000001\n000000\n000000\nCHAR 93\n001110\n000010\n000010\n000010\n000010\n000010\n001110\n000000\nCHAR 94\n000100\n001010\n010001\n000000\n000000\n000000\n000000\n000000\nCHAR 95\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111111\nCHAR 96\n001100\n001100\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 97\n000000\n000000\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 98\n010000\n010000\n011110\n010001\n010001\n010001\n011110\n000000\nCHAR 99\n000000\n000000\n001110\n010001\n010000\n010001\n001110\n000000\nCHAR 100\n000001\n000001\n001111\n010001\n010001\n010001\n001111\n000000\nCHAR 101\n000000\n000000\n001110\n010001\n011110\n010000\n001110\n000000\nCHAR 102\n000110\n001000\n001000\n011110\n001000\n001000\n001000\n000000\nCHAR 103\n000000\n000000\n001111\n010001\n010001\n001111\n000001\n001110\nCHAR 104\n010000\n010000\n011100\n010010\n010010\n010010\n010010\n000000\nCHAR 105\n000100\n000000\n000100\n000100\n000100\n000100\n000110\n000000\nCHAR 106\n000010\n000000\n000110\n000010\n000010\n000010\n010010\n001100\nCHAR 107\n010000\n010000\n010010\n010100\n011000\n010100\n010010\n000000\nCHAR 108\n000100\n000100\n000100\n000100\n000100\n000100\n000110\n000000\nCHAR 109\n000000\n000000\n011010\n010101\n010101\n010001\n010001\n000000\nCHAR 110\n000000\n000000\n011100\n010010\n010010\n010010\n010010\n000000\nCHAR 111\n000000\n000000\n001110\n010001\n010001\n010001\n001110\n000000\nCHAR 112\n000000\n000000\n011110\n010001\n010001\n010001\n011110\n010000\nCHAR 113\n000000\n000000\n001111\n010001\n010001\n010001\n001111\n000001\nCHAR 114\n000000\n000000\n010110\n001001\n001000\n001000\n011100\n000000\nCHAR 115\n000000\n000000\n001110\n010000\n001110\n000001\n001110\n000000\nCHAR 116\n000000\n001000\n011110\n001000\n001000\n001010\n000100\n000000\nCHAR 117\n000000\n000000\n010010\n010010\n010010\n010110\n001010\n000000\nCHAR 118\n000000\n000000\n010001\n010001\n010001\n001010\n000100\n000000\nCHAR 119\n000000\n000000\n010001\n010001\n010101\n011111\n001010\n000000\nCHAR 120\n000000\n000000\n010010\n010010\n001100\n010010\n010010\n000000\nCHAR 121\n000000\n000000\n010010\n010010\n010010\n001110\n000100\n011000\nCHAR 122\n000000\n000000\n011110\n000010\n001100\n010000\n011110\n000000\nCHAR 123\n000110\n001000\n001000\n011000\n001000\n001000\n000110\n000000\nCHAR 124\n000100\n000100\n000100\n000100\n000100\n000100\n000100\n000100\nCHAR 125\n001100\n000010\n000010\n000011\n000010\n000010\n001100\n000000\nCHAR 126\n000000\n000000\n000000\n001010\n010100\n000000\n000000\n000000\nCHAR 160\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 161\n000100\n000000\n000100\n000100\n001110\n001110\n000100\n000000\nCHAR 162\n000000\n000100\n001110\n010000\n010000\n001110\n000100\n000000\nCHAR 163\n000110\n001001\n001000\n011110\n001000\n001001\n010111\n000000\nCHAR 165\n010001\n001010\n000100\n011111\n000100\n011111\n000100\n000000\nCHAR 167\n001110\n010001\n001100\n001010\n000110\n010001\n001110\n000000\nCHAR 171\n000000\n000000\n001001\n010010\n001001\n000000\n000000\n000000\nCHAR 172\n000000\n000000\n111111\n000001\n000001\n000000\n000000\n000000\nCHAR 177\n000000\n000100\n001110\n000100\n000000\n001110\n000000\n000000\nCHAR 178\n011000\n000100\n001000\n011100\n000000\n000000\n000000\n000000\nCHAR 181\n000000\n000000\n010010\n010010\n010010\n011100\n010000\n010000\nCHAR 182\n001111\n010101\n010101\n001101\n000101\n000101\n000101\n000000\nCHAR 187\n000000\n000000\n010010\n001001\n010010\n000000\n000000\n000000\nCHAR 188\n010000\n010010\n010100\n001011\n010101\n000111\n000001\n000000\nCHAR 189\n010000\n010010\n010100\n001110\n010001\n000010\n000111\n000000\nCHAR 191\n000100\n000000\n000100\n001100\n010000\n010001\n001110\n000000\nCHAR 196\n001010\n000000\n000100\n001010\n010001\n011111\n010001\n000000\nCHAR 197\n001110\n001010\n001110\n011011\n010001\n011111\n010001\n000000\nCHAR 198\n001111\n010100\n010100\n011111\n010100\n010100\n010111\n000000\nCHAR 199\n001110\n010001\n010000\n010000\n010001\n001110\n000100\n001100\nCHAR 201\n000011\n000000\n011111\n010000\n011110\n010000\n011111\n000000\nCHAR 209\n001010\n010100\n000000\n010010\n011010\n010110\n010010\n000000\nCHAR 214\n010010\n001100\n010010\n010010\n010010\n010010\n001100\n000000\nCHAR 220\n001010\n000000\n010010\n010010\n010010\n010010\n001100\n000000\nCHAR 223\n000000\n011100\n010010\n011100\n010010\n010010\n011100\n010000\nCHAR 224\n001100\n000000\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 225\n000110\n000000\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 226\n001110\n000000\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 228\n001010\n000000\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 229\n001110\n001010\n001110\n000001\n001111\n010001\n001111\n000000\nCHAR 230\n000000\n000000\n011110\n000101\n011111\n010100\n001111\n000000\nCHAR 231\n000000\n001110\n010001\n010000\n010001\n001110\n000100\n001100\nCHAR 232\n001100\n000000\n001110\n010001\n011110\n010000\n001110\n000000\nCHAR 233\n000011\n000000\n001110\n010001\n011110\n010000\n001110\n000000\nCHAR 234\n001110\n000000\n001110\n010001\n011110\n010000\n001110\n000000\nCHAR 235\n001010\n000000\n001110\n010001\n011110\n010000\n001110\n000000\nCHAR 236\n001000\n000000\n000100\n000100\n000100\n000100\n000110\n000000\nCHAR 237\n000110\n000000\n000100\n000100\n000100\n000100\n000110\n000000\nCHAR 238\n000100\n001010\n000000\n000100\n000100\n000100\n000110\n000000\nCHAR 239\n001010\n000000\n000100\n000100\n000100\n000100\n000110\n000000\nCHAR 241\n001010\n010100\n000000\n011100\n010010\n010010\n010010\n000000\nCHAR 242\n011000\n000000\n001100\n010010\n010010\n010010\n001100\n000000\nCHAR 243\n000110\n000000\n001100\n010010\n010010\n010010\n001100\n000000\nCHAR 244\n001110\n000000\n001100\n010010\n010010\n010010\n001100\n000000\nCHAR 246\n001010\n000000\n001100\n010010\n010010\n010010\n001100\n000000\nCHAR 247\n001010\n000000\n001110\n010001\n010001\n010001\n001110\n000000\nCHAR 249\n011000\n000000\n010010\n010010\n010010\n010110\n001010\n000000\nCHAR 250\n000110\n000000\n010010\n010010\n010010\n010110\n001010\n000000\nCHAR 251\n001110\n000000\n010010\n010010\n010010\n010110\n001010\n000000\nCHAR 252\n010010\n000000\n010010\n010010\n010010\n010110\n001010\n000000\nCHAR 255\n001010\n000000\n010010\n010010\n010010\n001110\n000100\n011000\nCHAR 402\n000010\n000101\n000100\n001110\n000100\n000100\n010100\n001000\nCHAR 915\n011110\n010010\n010000\n010000\n010000\n010000\n010000\n000000\nCHAR 920\n001100\n010010\n010010\n011110\n010010\n010010\n001100\n000000\nCHAR 931\n011111\n010000\n001000\n000100\n001000\n010000\n011111\n000000\nCHAR 934\n001110\n000100\n001110\n010001\n001110\n000100\n001110\n000000\nCHAR 937\n000000\n001110\n010001\n010001\n001010\n001010\n011011\n000000\nCHAR 948\n001100\n010000\n001000\n000100\n001110\n010010\n001100\n000000\nCHAR 949\n000000\n001110\n010000\n011110\n010000\n001110\n000000\n000000\nCHAR 960\n000000\n011111\n001010\n001010\n001010\n001010\n001010\n000000\nCHAR 963\n000000\n000000\n001111\n010010\n010010\n001100\n000000\n000000\nCHAR 964\n000000\n000000\n001010\n010100\n000100\n000100\n000100\n000000\nCHAR 966\n000000\n000100\n001110\n010101\n010101\n001110\n000100\n000000\nCHAR 8226\n000000\n000000\n000000\n001100\n001100\n000000\n000000\n000000\nCHAR 8252\n001010\n001010\n001010\n001010\n001010\n000000\n001010\n000000\nCHAR 8592\n000000\n000100\n001100\n011111\n001100\n000100\n000000\n000000\nCHAR 8593\n000100\n001110\n011111\n000100\n000100\n000100\n000100\n000000\nCHAR 8594\n000000\n000100\n000110\n011111\n000110\n000100\n000000\n000000\nCHAR 8595\n000100\n000100\n000100\n000100\n011111\n001110\n000100\n000000\nCHAR 8734\n000000\n000000\n001010\n010101\n010101\n001010\n000000\n000000\nCHAR 8735\n000000\n000000\n000000\n010000\n010000\n010000\n011111\n000000\nCHAR 8801\n000000\n011110\n000000\n011110\n000000\n011110\n000000\n000000\nCHAR 8804\n000010\n001100\n010000\n001100\n000010\n000000\n011110\n000000\nCHAR 8805\n010000\n001100\n000010\n001100\n010000\n000000\n011110\n000000\nCHAR 8962\n000100\n001110\n011011\n010001\n010001\n011111\n000000\n000000\nCHAR 8976\n000000\n000000\n011111\n010000\n010000\n010000\n000000\n000000\nCHAR 9472\n000000\n000000\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 9474\n000100\n000100\n000100\n000100\n000100\n000100\n000100\n000100\nCHAR 9488\n000000\n000000\n000000\n111100\n000100\n000100\n000100\n000100\nCHAR 9492\n000100\n000100\n000100\n000111\n000000\n000000\n000000\n000000\nCHAR 9500\n000100\n000100\n000100\n000111\n000100\n000100\n000100\n000100\nCHAR 9508\n000100\n000100\n000100\n111100\n000100\n000100\n000100\n000100\nCHAR 9516\n000000\n000000\n000000\n111111\n000100\n000100\n000100\n000100\nCHAR 9524\n000100\n000100\n000100\n111111\n000000\n000000\n000000\n000000\nCHAR 9532\n000100\n000100\n000100\n111111\n000100\n000100\n000100\n000100\nCHAR 9552\n000000\n111111\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 9553\n010100\n010100\n010100\n010100\n010100\n010100\n010100\n010100\nCHAR 9556\n000000\n011111\n010000\n010111\n010100\n010100\n010100\n010100\nCHAR 9557\n000000\n111100\n000100\n111100\n000100\n000100\n000100\n000100\nCHAR 9558\n000000\n000000\n000000\n111100\n010100\n010100\n010100\n010100\nCHAR 9559\n000000\n111100\n000100\n110100\n010100\n010100\n010100\n010100\nCHAR 9561\n010100\n010100\n010100\n011111\n000000\n000000\n000000\n000000\nCHAR 9562\n010100\n010111\n010000\n011111\n000000\n000000\n000000\n000000\nCHAR 9563\n000100\n111100\n000100\n111100\n000000\n000000\n000000\n000000\nCHAR 9564\n010100\n010100\n010100\n111100\n000000\n000000\n000000\n000000\nCHAR 9565\n010100\n110100\n000100\n111100\n000000\n000000\n000000\n000000\nCHAR 9566\n000100\n000111\n000100\n000111\n000100\n000100\n000100\n000100\nCHAR 9567\n010100\n010100\n010100\n010111\n010100\n010100\n010100\n010100\nCHAR 9568\n010100\n010111\n010000\n010111\n010100\n010100\n010100\n010100\nCHAR 9569\n000000\n000000\n010010\n010010\n010010\n011100\n010000\n010000\nCHAR 9570\n010100\n010100\n010100\n110100\n010100\n010100\n010100\n010100\nCHAR 9571\n010100\n110100\n000100\n110100\n010100\n010100\n010100\n010100\nCHAR 9572\n000000\n111111\n000000\n111111\n000100\n000100\n000100\n000100\nCHAR 9573\n000000\n000000\n000000\n111111\n010100\n010100\n010100\n010100\nCHAR 9574\n000000\n111111\n000000\n110111\n010100\n010100\n010100\n010100\nCHAR 9575\n000100\n111111\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 9576\n010100\n010100\n010100\n111111\n000000\n000000\n000000\n000000\nCHAR 9577\n010100\n110111\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 9580\n010100\n110111\n000000\n110111\n010100\n010100\n010100\n010100\nCHAR 9601\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111111\nCHAR 9602\n000000\n000000\n000000\n000000\n000000\n000000\n111111\n111111\nCHAR 9603\n000000\n000000\n000000\n000000\n000000\n111111\n111111\n111111\nCHAR 9604\n000000\n000000\n000000\n000000\n111111\n111111\n111111\n111111\nCHAR 9605\n000000\n000000\n000000\n111111\n111111\n111111\n111111\n111111\nCHAR 9606\n000000\n000000\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9607\n000000\n111111\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9608\n111111\n111111\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9609\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\nCHAR 9610\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\nCHAR 9611\n111000\n111000\n111000\n111000\n111000\n111000\n111000\n111000\nCHAR 9613\n110000\n110000\n110000\n110000\n110000\n110000\n110000\n110000\nCHAR 9615\n100000\n100000\n100000\n100000\n100000\n100000\n100000\n100000\nCHAR 9617\n010101\n000000\n101010\n000000\n010101\n000000\n101010\n000000\nCHAR 9618\n010101\n101010\n010101\n101010\n010101\n101010\n010101\n101010\nCHAR 9619\n101010\n111111\n010101\n111111\n101010\n111111\n010101\n111111\nCHAR 9644\n000000\n000000\n000000\n000000\n000000\n011110\n011110\n000000\nCHAR 9650\n000100\n000100\n001110\n001110\n011111\n011111\n000000\n000000\nCHAR 9658\n001000\n001100\n001110\n001111\n001110\n001100\n001000\n000000\nCHAR 9660\n011111\n011111\n001110\n001110\n000100\n000100\n000000\n000000\nCHAR 9668\n000010\n000110\n001110\n011110\n001110\n000110\n000010\n000000\nCHAR 9675\n000000\n000000\n011110\n010010\n010010\n011110\n000000\n000000\nCHAR 9688\n111111\n111111\n111111\n110011\n110011\n111111\n111111\n111111\nCHAR 9689\n111111\n111111\n100001\n101101\n101101\n100001\n111111\n111111\nCHAR 9786\n001110\n010001\n011011\n010001\n010101\n010001\n001110\n000000\nCHAR 9787\n001110\n011111\n010101\n011111\n010001\n011111\n001110\n000000\nCHAR 9788\n000000\n010101\n001110\n011011\n001110\n010101\n000000\n000000\nCHAR 9792\n001110\n010001\n010001\n001110\n000100\n001110\n000100\n000000\nCHAR 9794\n000000\n000111\n000011\n001101\n010010\n010010\n001100\n000000\nCHAR 9824\n000000\n000100\n001110\n011111\n011111\n000100\n001110\n000000\nCHAR 9827\n000100\n001110\n001110\n000100\n011111\n011111\n000100\n000000\nCHAR 9829\n000000\n001010\n011111\n011111\n011111\n001110\n000100\n000000\nCHAR 9830\n000000\n000100\n001110\n011111\n011111\n001110\n000100\n000000\nCHAR 9834\n000100\n000110\n000101\n000100\n001100\n011100\n011000\n000000\nCHAR 9835\n000011\n001101\n001011\n001101\n001011\n011011\n011000\n000000",
"unicode_european_small.bitsyfont": "FONT unicode_european_small\nSIZE 6 9\nCHAR 0\n000000\n001010\n010000\n000010\n010000\n000010\n010100\n000000\n000000\nCHAR 32\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 33\n000000\n001000\n001000\n001000\n001000\n000000\n001000\n000000\n000000\nCHAR 34\n000000\n010100\n010100\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 35\n000000\n010100\n010100\n111110\n010100\n111110\n010100\n010100\n000000\nCHAR 36\n001000\n011100\n101010\n101000\n011100\n001010\n101010\n011100\n001000\nCHAR 37\n010000\n101010\n010010\n000100\n001000\n010010\n010101\n000010\n000000\nCHAR 38\n000000\n011000\n100100\n100100\n011000\n100110\n100100\n011010\n000000\nCHAR 39\n000000\n001000\n001000\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 40\n000000\n000100\n001000\n001000\n001000\n001000\n001000\n000100\n000000\nCHAR 41\n000000\n001000\n000100\n000100\n000100\n000100\n000100\n001000\n000000\nCHAR 42\n000000\n000000\n100010\n010100\n111110\n010100\n100010\n000000\n000000\nCHAR 43\n000000\n000000\n001000\n001000\n111110\n001000\n001000\n000000\n000000\nCHAR 44\n000000\n000000\n000000\n000000\n000000\n001100\n000100\n000100\n001000\nCHAR 45\n000000\n000000\n000000\n000000\n111110\n000000\n000000\n000000\n000000\nCHAR 46\n000000\n000000\n000000\n000000\n000000\n001100\n001100\n000000\n000000\nCHAR 47\n000000\n000010\n000010\n000100\n001000\n010000\n010000\n000000\n000000\nCHAR 48\n000000\n001100\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 49\n000000\n001000\n011000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 50\n000000\n001100\n010010\n000010\n000100\n001000\n011110\n000000\n000000\nCHAR 51\n000000\n011110\n000100\n001100\n000010\n000010\n011100\n000000\n000000\nCHAR 52\n000000\n000100\n001100\n010100\n100100\n111110\n000100\n000000\n000000\nCHAR 53\n000000\n011110\n010000\n011100\n000010\n000010\n011100\n000000\n000000\nCHAR 54\n000000\n001100\n010000\n011100\n010010\n010010\n001100\n000000\n000000\nCHAR 55\n000000\n011110\n000010\n000010\n000100\n001000\n001000\n000000\n000000\nCHAR 56\n000000\n001100\n010010\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 57\n000000\n001100\n010010\n010010\n001110\n000010\n001100\n000000\n000000\nCHAR 58\n000000\n000000\n001100\n001100\n000000\n001100\n001100\n000000\n000000\nCHAR 59\n000000\n000000\n001100\n001100\n000000\n001100\n000100\n000100\n001000\nCHAR 60\n000000\n000000\n000110\n011000\n100000\n011000\n000110\n000000\n000000\nCHAR 61\n000000\n000000\n000000\n111110\n000000\n111110\n000000\n000000\n000000\nCHAR 62\n000000\n000000\n110000\n001100\n000010\n001100\n110000\n000000\n000000\nCHAR 63\n001100\n010010\n000010\n001100\n001000\n000000\n001000\n000000\n000000\nCHAR 64\n000000\n011100\n100100\n101010\n101100\n100000\n011100\n000000\n000000\nCHAR 65\n000000\n001000\n010100\n100010\n111110\n100010\n100010\n000000\n000000\nCHAR 66\n000000\n111100\n100010\n111100\n100010\n100010\n111100\n000000\n000000\nCHAR 67\n000000\n001100\n010010\n010000\n010000\n010010\n001100\n000000\n000000\nCHAR 68\n000000\n011100\n010010\n010010\n010010\n010010\n011100\n000000\n000000\nCHAR 69\n000000\n011110\n010000\n011100\n010000\n010000\n011110\n000000\n000000\nCHAR 70\n000000\n011110\n010000\n011100\n010000\n010000\n010000\n000000\n000000\nCHAR 71\n000000\n001100\n010010\n010000\n010110\n010010\n001100\n000000\n000000\nCHAR 72\n000000\n010010\n010010\n011110\n010010\n010010\n010010\n000000\n000000\nCHAR 73\n000000\n011100\n001000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 74\n000000\n001110\n000100\n000100\n000100\n100100\n011000\n000000\n000000\nCHAR 75\n000000\n010010\n010100\n011000\n010100\n010010\n010010\n000000\n000000\nCHAR 76\n000000\n010000\n010000\n010000\n010000\n010000\n011110\n000000\n000000\nCHAR 77\n000000\n100010\n110110\n101010\n101010\n100010\n100010\n000000\n000000\nCHAR 78\n000000\n010010\n011010\n010110\n010010\n010010\n010010\n000000\n000000\nCHAR 79\n000000\n011100\n100010\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 80\n000000\n011100\n010010\n010010\n011100\n010000\n010000\n000000\n000000\nCHAR 81\n000000\n001100\n010010\n010010\n011010\n010110\n001100\n000010\n000000\nCHAR 82\n000000\n011100\n010010\n010010\n011100\n010010\n010010\n000000\n000000\nCHAR 83\n000000\n001100\n010010\n001000\n000100\n010010\n001100\n000000\n000000\nCHAR 84\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 85\n000000\n010010\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 86\n000000\n010010\n010010\n010010\n011110\n001100\n001100\n000000\n000000\nCHAR 87\n000000\n100010\n100010\n101010\n101010\n110110\n100010\n000000\n000000\nCHAR 88\n000000\n100010\n010100\n001000\n001000\n010100\n100010\n000000\n000000\nCHAR 89\n000000\n100010\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 90\n000000\n011110\n000010\n000100\n001000\n010000\n011110\n000000\n000000\nCHAR 91\n000000\n011100\n010000\n010000\n010000\n010000\n011100\n000000\n000000\nCHAR 92\n000000\n010000\n010000\n001000\n000100\n000010\n000010\n000000\n000000\nCHAR 93\n000000\n011100\n000100\n000100\n000100\n000100\n011100\n000000\n000000\nCHAR 94\n000000\n001000\n010100\n100010\n000000\n000000\n000000\n000000\n000000\nCHAR 95\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111110\nCHAR 96\n000000\n001000\n000100\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 97\n000000\n000000\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 98\n000000\n010000\n010000\n011100\n010010\n010010\n011100\n000000\n000000\nCHAR 99\n000000\n000000\n000000\n001110\n010000\n010000\n001110\n000000\n000000\nCHAR 100\n000000\n000010\n000010\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 101\n000000\n000000\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 102\n000000\n000100\n001010\n001000\n011100\n001000\n001000\n000000\n000000\nCHAR 103\n000000\n000000\n000000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 104\n000000\n010000\n010000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 105\n000000\n001000\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 106\n000000\n000100\n000000\n001100\n000100\n000100\n000100\n010100\n001000\nCHAR 107\n000000\n010000\n010000\n010100\n011000\n010100\n010010\n000000\n000000\nCHAR 108\n000000\n011000\n001000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 109\n000000\n000000\n000000\n110100\n101010\n101010\n100010\n000000\n000000\nCHAR 110\n000000\n000000\n000000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 111\n000000\n000000\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 112\n000000\n000000\n000000\n011100\n010010\n010010\n011100\n010000\n010000\nCHAR 113\n000000\n000000\n000000\n001110\n010010\n010010\n001110\n000010\n000010\nCHAR 114\n000000\n000000\n000000\n010100\n011010\n010000\n010000\n000000\n000000\nCHAR 115\n000000\n000000\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 116\n000000\n001000\n001000\n011100\n001000\n001010\n000100\n000000\n000000\nCHAR 117\n000000\n000000\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 118\n000000\n000000\n000000\n010010\n010010\n001100\n001100\n000000\n000000\nCHAR 119\n000000\n000000\n000000\n100010\n101010\n101010\n010100\n000000\n000000\nCHAR 120\n000000\n000000\n000000\n010010\n001100\n001100\n010010\n000000\n000000\nCHAR 121\n000000\n000000\n000000\n010010\n010010\n010010\n001110\n010010\n001100\nCHAR 122\n000000\n000000\n000000\n011110\n000100\n001000\n011110\n000000\n000000\nCHAR 123\n000100\n001000\n001000\n010000\n001000\n001000\n000100\n000000\n000000\nCHAR 124\n000000\n001000\n001000\n001000\n001000\n001000\n001000\n001000\n000000\nCHAR 125\n010000\n001000\n001000\n000100\n001000\n001000\n010000\n000000\n000000\nCHAR 126\n000000\n000000\n001010\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 160\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 161\n000000\n001000\n000000\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 162\n000000\n000000\n000100\n001110\n010100\n010100\n001110\n000100\n000000\nCHAR 163\n000000\n001100\n010010\n010000\n111100\n010000\n010000\n111110\n000000\nCHAR 164\n000000\n000000\n101010\n010100\n100010\n010100\n101010\n000000\n000000\nCHAR 165\n000000\n100010\n010100\n111110\n001000\n111110\n001000\n000000\n000000\nCHAR 166\n000000\n001000\n001000\n001000\n000000\n001000\n001000\n001000\n000000\nCHAR 167\n000000\n001110\n010000\n001100\n010010\n001100\n000010\n011100\n000000\nCHAR 168\n000000\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 169\n011110\n100001\n100101\n101001\n100101\n100001\n011110\n000000\n000000\nCHAR 170\n000000\n001100\n010100\n001100\n000000\n011100\n000000\n000000\n000000\nCHAR 171\n000000\n000000\n001010\n010100\n101000\n010100\n001010\n000000\n000000\nCHAR 172\n000000\n000000\n000000\n000000\n011110\n000010\n000010\n000000\n000000\nCHAR 173\n000000\n000000\n000000\n000000\n011110\n000000\n000000\n000000\n000000\nCHAR 174\n011110\n100001\n101101\n101001\n101001\n100001\n011110\n000000\n000000\nCHAR 175\n000000\n011110\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 176\n000000\n000000\n001100\n010010\n001100\n000000\n000000\n000000\n000000\nCHAR 177\n000000\n001000\n001000\n111110\n001000\n001000\n000000\n111110\n000000\nCHAR 178\n000000\n001000\n010100\n000100\n001000\n011100\n000000\n000000\n000000\nCHAR 179\n000000\n011000\n000100\n001000\n000100\n011000\n000000\n000000\n000000\nCHAR 180\n000000\n000100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 181\n000000\n000000\n000000\n010010\n010010\n010110\n011010\n010000\n000000\nCHAR 182\n000000\n011110\n111010\n111010\n011010\n001010\n001010\n000000\n000000\nCHAR 183\n000000\n000000\n000000\n000000\n001000\n000000\n000000\n000000\n000000\nCHAR 184\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000100\n001000\nCHAR 185\n000000\n001000\n011000\n001000\n001000\n011100\n000000\n000000\n000000\nCHAR 186\n000000\n001000\n010100\n001000\n000000\n011100\n000000\n000000\n000000\nCHAR 187\n000000\n000000\n101000\n010100\n001010\n010100\n101000\n000000\n000000\nCHAR 188\n010000\n110000\n010000\n010100\n011100\n001100\n011110\n000100\n000000\nCHAR 189\n010000\n110000\n010000\n010100\n011010\n000010\n000100\n001110\n000000\nCHAR 190\n110000\n001000\n010000\n001100\n111100\n001100\n011110\n000100\n000000\nCHAR 191\n000100\n000000\n000100\n001100\n010000\n010010\n001100\n000000\n000000\nCHAR 192\n010000\n001000\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 193\n000100\n001000\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 194\n001000\n010100\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 195\n001010\n010100\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 196\n010100\n000000\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 197\n001000\n010100\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 198\n000000\n011110\n101000\n111100\n101000\n101000\n101110\n000000\n000000\nCHAR 199\n000000\n001100\n010010\n010000\n010000\n010010\n001100\n000100\n001000\nCHAR 200\n001000\n000100\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 201\n000100\n001000\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 202\n000100\n001010\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 203\n001010\n000000\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 204\n010000\n001000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 205\n000100\n001000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 206\n001000\n010100\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 207\n010100\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 208\n000000\n011100\n010010\n111010\n010010\n010010\n011100\n000000\n000000\nCHAR 209\n001010\n010100\n010010\n011010\n010110\n010010\n010010\n000000\n000000\nCHAR 210\n001000\n000100\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 211\n000100\n001000\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 212\n000100\n001010\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 213\n001010\n010100\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 214\n001010\n000000\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 215\n000000\n000000\n100010\n010100\n001000\n010100\n100010\n000000\n000000\nCHAR 216\n000010\n001110\n010110\n010110\n011010\n011010\n011100\n010000\n000000\nCHAR 217\n001000\n000100\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 218\n000100\n001000\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 219\n000100\n001010\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 220\n001010\n000000\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 221\n000100\n001000\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 222\n000000\n010000\n011100\n010010\n010010\n011100\n010000\n000000\n000000\nCHAR 223\n000000\n001100\n010010\n010100\n010100\n010010\n010100\n000000\n000000\nCHAR 224\n001000\n000100\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 225\n000100\n001000\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 226\n000100\n001010\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 227\n001010\n010100\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 228\n000000\n001010\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 229\n000100\n001010\n000100\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 230\n000000\n000000\n000000\n011100\n101010\n101100\n011110\n000000\n000000\nCHAR 231\n000000\n000000\n000000\n001110\n010000\n010000\n001110\n000100\n001000\nCHAR 232\n001000\n000100\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 233\n000100\n001000\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 234\n000100\n001010\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 235\n000000\n001010\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 236\n010000\n001000\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 237\n000100\n001000\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 238\n001000\n010100\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 239\n000000\n010100\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 240\n000110\n001100\n000010\n001110\n010010\n010010\n001100\n000000\n000000\nCHAR 241\n001010\n010100\n000000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 242\n001000\n000100\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 243\n000100\n001000\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 244\n000100\n001010\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 245\n001010\n010100\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 246\n000000\n001010\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 247\n000000\n000000\n001000\n000000\n111110\n000000\n001000\n000000\n000000\nCHAR 248\n000000\n000000\n000000\n001110\n010110\n011010\n011100\n000000\n000000\nCHAR 249\n001000\n000100\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 250\n000100\n001000\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 251\n000100\n001010\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 252\n000000\n001010\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 253\n000100\n001000\n000000\n010010\n010010\n010010\n001110\n010010\n001100\nCHAR 254\n000000\n010000\n010000\n011100\n010010\n010010\n011100\n010000\n010000\nCHAR 255\n000000\n001010\n000000\n010010\n010010\n010010\n001110\n010010\n001100\nCHAR 256\n011100\n000000\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 257\n000000\n001110\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 258\n100100\n011000\n001000\n010100\n011100\n100010\n100010\n000000\n000000\nCHAR 259\n010010\n001100\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 260\n000000\n001000\n010100\n100010\n111110\n100010\n100010\n000100\n000010\nCHAR 261\n000000\n000000\n000000\n001110\n010010\n010010\n001110\n000100\n000010\nCHAR 262\n000100\n001000\n001100\n010010\n010000\n010010\n001100\n000000\n000000\nCHAR 263\n000100\n001000\n000000\n001110\n010000\n010000\n001110\n000000\n000000\nCHAR 264\n000100\n001010\n001100\n010010\n010000\n010010\n001100\n000000\n000000\nCHAR 265\n000100\n001010\n000000\n001110\n010000\n010000\n001110\n000000\n000000\nCHAR 266\n000100\n000000\n001100\n010010\n010000\n010010\n001100\n000000\n000000\nCHAR 267\n000000\n000100\n000000\n001110\n010000\n010000\n001110\n000000\n000000\nCHAR 268\n001010\n000100\n001100\n010010\n010000\n010010\n001100\n000000\n000000\nCHAR 269\n001010\n000100\n000000\n001110\n010000\n010000\n001110\n000000\n000000\nCHAR 270\n001010\n000100\n011100\n010010\n010010\n010010\n011100\n000000\n000000\nCHAR 271\n010100\n001010\n000010\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 272\n000000\n011100\n010010\n111010\n010010\n010010\n011100\n000000\n000000\nCHAR 273\n000000\n000100\n001110\n011100\n100100\n100100\n011100\n000000\n000000\nCHAR 274\n011110\n000000\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 275\n000000\n011110\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 276\n010010\n001100\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 277\n010010\n001100\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 278\n001000\n000000\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 279\n000000\n001000\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 280\n000000\n011110\n010000\n011100\n010000\n010000\n011110\n001000\n000100\nCHAR 281\n000000\n000000\n000000\n001100\n010110\n011000\n001110\n001000\n000100\nCHAR 282\n001010\n000100\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 283\n001010\n000100\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 284\n000100\n001010\n001110\n010000\n010110\n010010\n001100\n000000\n000000\nCHAR 285\n000100\n001010\n000000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 286\n010010\n001100\n001110\n010000\n010110\n010010\n001100\n000000\n000000\nCHAR 287\n010010\n001100\n000000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 288\n000100\n000000\n001110\n010000\n010110\n010010\n001100\n000000\n000000\nCHAR 289\n000000\n000100\n000000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 290\n000000\n001100\n010010\n010000\n010110\n010010\n001100\n000100\n001000\nCHAR 291\n000000\n000100\n001000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 292\n000100\n001010\n000000\n010010\n011110\n010010\n010010\n000000\n000000\nCHAR 293\n000100\n101010\n100000\n111000\n100100\n100100\n100100\n000000\n000000\nCHAR 294\n000000\n010100\n111110\n010100\n011100\n010100\n010100\n000000\n000000\nCHAR 295\n000000\n010000\n111000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 296\n001010\n010100\n001110\n000100\n000100\n000100\n001110\n000000\n000000\nCHAR 297\n001010\n010100\n000000\n001100\n000100\n000100\n001110\n000000\n000000\nCHAR 298\n011100\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 299\n000000\n011100\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 300\n010010\n001100\n000000\n001110\n000100\n000100\n001110\n000000\n000000\nCHAR 301\n010010\n001100\n000000\n001100\n000100\n000100\n001110\n000000\n000000\nCHAR 302\n000000\n011100\n001000\n001000\n001000\n001000\n011100\n001000\n000100\nCHAR 303\n000000\n001000\n000000\n011000\n001000\n001000\n011100\n001000\n000100\nCHAR 304\n001000\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 305\n000000\n000000\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 306\n000000\n101110\n100010\n100010\n100010\n101010\n100100\n000000\n000000\nCHAR 307\n000000\n100010\n000000\n100110\n100010\n100010\n100010\n001010\n000100\nCHAR 308\n000100\n001010\n001110\n000100\n000100\n100100\n011000\n000000\n000000\nCHAR 309\n000100\n001010\n000000\n001100\n000100\n000100\n000100\n010100\n001000\nCHAR 310\n000000\n010010\n010100\n011000\n010100\n010010\n010010\n001000\n010000\nCHAR 311\n000000\n010000\n010000\n010100\n011000\n010100\n010010\n001000\n010000\nCHAR 312\n000000\n000000\n000000\n010110\n011000\n010100\n010010\n000000\n000000\nCHAR 313\n000010\n000100\n010000\n010000\n010000\n010000\n011110\n000000\n000000\nCHAR 314\n001000\n010000\n011000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 315\n000000\n010000\n010000\n010000\n010000\n010000\n011110\n000100\n001000\nCHAR 316\n000000\n011000\n001000\n001000\n001000\n001000\n011100\n001000\n010000\nCHAR 317\n001010\n000100\n010000\n010000\n010000\n010000\n011110\n000000\n000000\nCHAR 318\n010100\n001000\n011000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 319\n000000\n010000\n010000\n010100\n010000\n010000\n011110\n000000\n000000\nCHAR 320\n000000\n011000\n001000\n001010\n001000\n001000\n011100\n000000\n000000\nCHAR 321\n000000\n010000\n010000\n011000\n110000\n010000\n011110\n000000\n000000\nCHAR 322\n000000\n011000\n001000\n001100\n011000\n001000\n011100\n000000\n000000\nCHAR 323\n000100\n001000\n010010\n011010\n010110\n010010\n010010\n000000\n000000\nCHAR 324\n000100\n001000\n000000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 325\n000000\n010010\n011010\n010110\n010010\n010010\n010010\n001000\n010000\nCHAR 326\n000000\n000000\n000000\n011100\n010010\n010010\n010010\n001000\n010000\nCHAR 327\n001010\n000100\n010010\n011010\n010110\n010010\n010010\n000000\n000000\nCHAR 328\n010100\n001000\n000000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 329\n000000\n110000\n010000\n101100\n001010\n001010\n001010\n000000\n000000\nCHAR 330\n000000\n010010\n011010\n010110\n010010\n010010\n010010\n000010\n000100\nCHAR 331\n000000\n000000\n000000\n011100\n010010\n010010\n010010\n000010\n000100\nCHAR 332\n111110\n000000\n011100\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 333\n000000\n011110\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 334\n100010\n011100\n011100\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 335\n010010\n001100\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 336\n010010\n100100\n011100\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 337\n010010\n100100\n000000\n011000\n100100\n100100\n011000\n000000\n000000\nCHAR 338\n000000\n011110\n101000\n101100\n101000\n101000\n011110\n000000\n000000\nCHAR 339\n000000\n000000\n000000\n010100\n101010\n101100\n010110\n000000\n000000\nCHAR 340\n000100\n001000\n011100\n010010\n011100\n010100\n010010\n000000\n000000\nCHAR 341\n000100\n001000\n000000\n010100\n011010\n010000\n010000\n000000\n000000\nCHAR 342\n000000\n011100\n010010\n010010\n011100\n010010\n010010\n001000\n010000\nCHAR 343\n000000\n000000\n000000\n010100\n011010\n010000\n010000\n001000\n010000\nCHAR 344\n010100\n001000\n011100\n010010\n011100\n010100\n010010\n000000\n000000\nCHAR 345\n010100\n001000\n000000\n010100\n011010\n010000\n010000\n000000\n000000\nCHAR 346\n000100\n001000\n001110\n010000\n001100\n000010\n011100\n000000\n000000\nCHAR 347\n000100\n001000\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 348\n000100\n001010\n001110\n010000\n001100\n000010\n011100\n000000\n000000\nCHAR 349\n000100\n001010\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 350\n000000\n001100\n010010\n001000\n000100\n010010\n001100\n000100\n001000\nCHAR 351\n000000\n000000\n000000\n001110\n011000\n000110\n011100\n000100\n001000\nCHAR 352\n001010\n000100\n001110\n010000\n001100\n000010\n011100\n000000\n000000\nCHAR 353\n001010\n000100\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 354\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000100\n001000\nCHAR 355\n000000\n001000\n001000\n011100\n001000\n001010\n000100\n000100\n001000\nCHAR 356\n010100\n001000\n111110\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 357\n001010\n000100\n001000\n011100\n001000\n001010\n000100\n000000\n000000\nCHAR 358\n000000\n111110\n001000\n001000\n011100\n001000\n001000\n000000\n000000\nCHAR 359\n000000\n001000\n001000\n011100\n011100\n001010\n000100\n000000\n000000\nCHAR 360\n001010\n010100\n000000\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 361\n001010\n010100\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 362\n011110\n000000\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 363\n000000\n011110\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 364\n010010\n001100\n000000\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 365\n010010\n001100\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 366\n001100\n010010\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 367\n001100\n010010\n001100\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 368\n010010\n100100\n000000\n100100\n100100\n100100\n011000\n000000\n000000\nCHAR 369\n010010\n100100\n000000\n100100\n100100\n100100\n011100\n000000\n000000\nCHAR 370\n000000\n010010\n010010\n010010\n010010\n010010\n001100\n001000\n000100\nCHAR 371\n000000\n000000\n000000\n010010\n010010\n010010\n001110\n001000\n000100\nCHAR 372\n001000\n010100\n000000\n100010\n101010\n110110\n100010\n000000\n000000\nCHAR 373\n001000\n010100\n000000\n100010\n101010\n101010\n010100\n000000\n000000\nCHAR 374\n001000\n010100\n000000\n100010\n010100\n001000\n001000\n000000\n000000\nCHAR 375\n000100\n001010\n000000\n010010\n010010\n010010\n001110\n010010\n001100\nCHAR 376\n010100\n000000\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 377\n000100\n001000\n011110\n000100\n001000\n010000\n011110\n000000\n000000\nCHAR 378\n000100\n001000\n000000\n011110\n000100\n001000\n011110\n000000\n000000\nCHAR 379\n000100\n000000\n011110\n000100\n001000\n010000\n011110\n000000\n000000\nCHAR 380\n000000\n000100\n000000\n011110\n000100\n001000\n011110\n000000\n000000\nCHAR 381\n001010\n000100\n011110\n000100\n001000\n010000\n011110\n000000\n000000\nCHAR 382\n001010\n000100\n000000\n011110\n000100\n001000\n011110\n000000\n000000\nCHAR 383\n000000\n000100\n001010\n011000\n001000\n001000\n001000\n000000\n000000\nCHAR 399\n000000\n001100\n010010\n000010\n011110\n010010\n001100\n000000\n000000\nCHAR 402\n000000\n000100\n001010\n001000\n011100\n001000\n001000\n001000\n010000\nCHAR 416\n000000\n011001\n100101\n100110\n100100\n100100\n011000\n000000\n000000\nCHAR 417\n000000\n000000\n000000\n001101\n010010\n010010\n001100\n000000\n000000\nCHAR 431\n000000\n010101\n010101\n010110\n010100\n010100\n001000\n000000\n000000\nCHAR 432\n000000\n000000\n000000\n010101\n010110\n010100\n001100\n000000\n000000\nCHAR 437\n000000\n011110\n000010\n011110\n001000\n010000\n011110\n000000\n000000\nCHAR 438\n000000\n000000\n000000\n011110\n011110\n001000\n011110\n000000\n000000\nCHAR 465\n001010\n000100\n001100\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 466\n001010\n000100\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 486\n001010\n000100\n001110\n010000\n010110\n010010\n001100\n000000\n000000\nCHAR 487\n001010\n000100\n000000\n001100\n010010\n010010\n001110\n000010\n001100\nCHAR 506\n000100\n001000\n010100\n001000\n010100\n011100\n010100\n000000\n000000\nCHAR 507\n000010\n000100\n001010\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 508\n000100\n001000\n011110\n101000\n111100\n101000\n101110\n000000\n000000\nCHAR 509\n000100\n001000\n000000\n011100\n101010\n101100\n011110\n000000\n000000\nCHAR 510\n000010\n000100\n001110\n010110\n011010\n011010\n011100\n000000\n000000\nCHAR 511\n000100\n001000\n000000\n001110\n010110\n011010\n011100\n000000\n000000\nCHAR 536\n000000\n001100\n010010\n001000\n000100\n010010\n001100\n000100\n001000\nCHAR 537\n000000\n000000\n000000\n001110\n011000\n000110\n011100\n000100\n001000\nCHAR 538\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000100\n001000\nCHAR 539\n000000\n001000\n001000\n011100\n001000\n001010\n000100\n000100\n001000\nCHAR 600\n000000\n000000\n000000\n001100\n011010\n000110\n011100\n000000\n000000\nCHAR 601\n000000\n000000\n000000\n011100\n000110\n011010\n001100\n000000\n000000\nCHAR 699\n000000\n000100\n001000\n001100\n000000\n000000\n000000\n000000\n000000\nCHAR 700\n000000\n001100\n000100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 701\n000000\n001100\n001000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 710\n001000\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 711\n010100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 713\n000000\n011100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 728\n010010\n001100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 729\n000000\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 730\n001000\n010100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 731\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n001000\n000100\nCHAR 732\n001010\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 733\n010010\n100100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 768\n010000\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 769\n000100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 770\n001000\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 771\n001010\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 772\n011100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 773\n111110\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 774\n100010\n011100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 775\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 776\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 777\n011000\n000100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 778\n001000\n010100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 779\n010010\n100100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 780\n010100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 781\n001000\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 782\n010100\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 783\n100100\n010010\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 784\n101010\n011100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 785\n011100\n100010\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 786\n000100\n001000\n001100\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 787\n001100\n000100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 788\n001100\n001000\n000100\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 803\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n001000\nCHAR 804\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n010100\nCHAR 884\n000100\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 885\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000100\n001000\nCHAR 890\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n001000\n001100\nCHAR 894\n000000\n000000\n001100\n001100\n000000\n001100\n000100\n000100\n001000\nCHAR 900\n001000\n001000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 901\n001000\n101010\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 902\n100000\n101100\n010010\n010010\n011110\n010010\n010010\n000000\n000000\nCHAR 903\n000000\n000000\n000000\n000000\n001000\n000000\n000000\n000000\n000000\nCHAR 904\n100000\n101110\n001000\n001100\n001000\n001000\n001110\n000000\n000000\nCHAR 905\n100000\n101010\n001010\n001110\n001010\n001010\n001010\n000000\n000000\nCHAR 906\n100000\n101110\n000100\n000100\n000100\n000100\n001110\n000000\n000000\nCHAR 908\n100000\n101100\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 910\n100000\n101010\n001010\n001010\n000100\n000100\n000100\n000000\n000000\nCHAR 911\n100000\n101100\n010010\n010010\n010010\n001100\n011110\n000000\n000000\nCHAR 912\n001000\n101010\n000000\n001000\n001000\n001000\n000100\n000000\n000000\nCHAR 913\n000000\n001000\n010100\n100010\n111110\n100010\n100010\n000000\n000000\nCHAR 914\n000000\n111100\n100010\n111100\n100010\n100010\n111100\n000000\n000000\nCHAR 915\n000000\n011110\n010000\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 916\n000000\n001000\n001000\n010100\n010100\n100010\n111110\n000000\n000000\nCHAR 917\n000000\n011110\n010000\n011100\n010000\n010000\n011110\n000000\n000000\nCHAR 918\n000000\n011110\n000010\n000100\n001000\n010000\n011110\n000000\n000000\nCHAR 919\n000000\n010010\n010010\n011110\n010010\n010010\n010010\n000000\n000000\nCHAR 920\n000000\n011100\n100010\n111110\n100010\n100010\n011100\n000000\n000000\nCHAR 921\n000000\n011100\n001000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 922\n000000\n010010\n010100\n011000\n010100\n010010\n010010\n000000\n000000\nCHAR 923\n000000\n001000\n001000\n010100\n010100\n100010\n100010\n000000\n000000\nCHAR 924\n000000\n100010\n110110\n101010\n101010\n100010\n100010\n000000\n000000\nCHAR 925\n000000\n010010\n011010\n010110\n010010\n010010\n010010\n000000\n000000\nCHAR 926\n000000\n011110\n000000\n001100\n000000\n000000\n011110\n000000\n000000\nCHAR 927\n000000\n011100\n100010\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 928\n000000\n011110\n010010\n010010\n010010\n010010\n010010\n000000\n000000\nCHAR 929\n000000\n011100\n010010\n010010\n011100\n010000\n010000\n000000\n000000\nCHAR 931\n000000\n011110\n010000\n001000\n001000\n010000\n011110\n000000\n000000\nCHAR 932\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 933\n000000\n100010\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 934\n000000\n001000\n011100\n101010\n101010\n011100\n001000\n000000\n000000\nCHAR 935\n000000\n100010\n010100\n001000\n001000\n010100\n100010\n000000\n000000\nCHAR 936\n000000\n101010\n101010\n101010\n011100\n001000\n001000\n000000\n000000\nCHAR 937\n000000\n011100\n100010\n100010\n100010\n010100\n110110\n000000\n000000\nCHAR 938\n010100\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 939\n010100\n000000\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 940\n000010\n000100\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 941\n000010\n000100\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 942\n000100\n001000\n000000\n101100\n110010\n100010\n100010\n000010\n000010\nCHAR 943\n001000\n010000\n000000\n010000\n010000\n010100\n001000\n000000\n000000\nCHAR 944\n001000\n101010\n000000\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 945\n000000\n000000\n000000\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 946\n000000\n001100\n010010\n011100\n010010\n010010\n011100\n010000\n010000\nCHAR 947\n000000\n000000\n000000\n100010\n100010\n010100\n010100\n001000\n001000\nCHAR 948\n000000\n011100\n100000\n011100\n100010\n100010\n011100\n000000\n000000\nCHAR 949\n000000\n000000\n000000\n001100\n010110\n011000\n001110\n000000\n000000\nCHAR 950\n000000\n111110\n001000\n010000\n010000\n001100\n000010\n001100\n000000\nCHAR 951\n000000\n000000\n000000\n101100\n110010\n100010\n100010\n000010\n000010\nCHAR 952\n000000\n001100\n010010\n011110\n010010\n010010\n001100\n000000\n000000\nCHAR 953\n000000\n000000\n000000\n010000\n010000\n010100\n001000\n000000\n000000\nCHAR 954\n000000\n000000\n000000\n010010\n010100\n011000\n010110\n000000\n000000\nCHAR 955\n000000\n011100\n100010\n000010\n011010\n100110\n100010\n000000\n000000\nCHAR 956\n000000\n000000\n000000\n100010\n100010\n100110\n111010\n100000\n100000\nCHAR 957\n000000\n000000\n000000\n100010\n100010\n010100\n001000\n000000\n000000\nCHAR 958\n000000\n111110\n001000\n010000\n011100\n100000\n011100\n000010\n000100\nCHAR 959\n000000\n000000\n000000\n001100\n010010\n010010\n001100\n000000\n000000\nCHAR 960\n000000\n000000\n000000\n111110\n010100\n010100\n010100\n000000\n000000\nCHAR 961\n000000\n000000\n000000\n011100\n100010\n100010\n111100\n100000\n100000\nCHAR 962\n000000\n000000\n000000\n011100\n100010\n100000\n011100\n000010\n001100\nCHAR 963\n000000\n000000\n000000\n011110\n100100\n100010\n011100\n000000\n000000\nCHAR 964\n000000\n000000\n000000\n011110\n001000\n001010\n000100\n000000\n000000\nCHAR 965\n000000\n000000\n000000\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 966\n000000\n000000\n000000\n100100\n101010\n101010\n011100\n001000\n001000\nCHAR 967\n000000\n000000\n000000\n100010\n010100\n001000\n010100\n100010\n100010\nCHAR 968\n000000\n000000\n000000\n101010\n101010\n101010\n011100\n001000\n001000\nCHAR 969\n000000\n000000\n000000\n010100\n100010\n101010\n010100\n000000\n000000\nCHAR 970\n000000\n010100\n000000\n001000\n001000\n001010\n000100\n000000\n000000\nCHAR 971\n000000\n010100\n000000\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 972\n000100\n001000\n000000\n011100\n100010\n100010\n011100\n000000\n000000\nCHAR 973\n000100\n001000\n000000\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 974\n000100\n001000\n000000\n010100\n100010\n101010\n010100\n000000\n000000\nCHAR 976\n000000\n011000\n100100\n100100\n111100\n100010\n011100\n000000\n000000\nCHAR 977\n000000\n001000\n010100\n001110\n110100\n010100\n001000\n000000\n000000\nCHAR 978\n000000\n100100\n101010\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 979\n000000\n110100\n001010\n011000\n101000\n001000\n001000\n000000\n000000\nCHAR 980\n101000\n000000\n100100\n101010\n010000\n010000\n010000\n000000\n000000\nCHAR 981\n000000\n001000\n001000\n011100\n101010\n101010\n011100\n001000\n001000\nCHAR 982\n000000\n000000\n000000\n111110\n100010\n101010\n010100\n000000\n000000\nCHAR 983\n000100\n001000\n000000\n100010\n010010\n011100\n100010\n000010\n001100\nCHAR 986\n000000\n001100\n010010\n100000\n100000\n100000\n011100\n000010\n000100\nCHAR 987\n000000\n000000\n000010\n011100\n100000\n100000\n011100\n000010\n001100\nCHAR 988\n000000\n111110\n100000\n100000\n111000\n100000\n100000\n000000\n000000\nCHAR 989\n000000\n000000\n000000\n011110\n010000\n010000\n011100\n010000\n010000\nCHAR 990\n000000\n100000\n100010\n100110\n101010\n110010\n000010\n000000\n000000\nCHAR 991\n000000\n001000\n010000\n100000\n111110\n000010\n000100\n001000\n001000\nCHAR 992\n000000\n001000\n010100\n010100\n100110\n101010\n101010\n000000\n000000\nCHAR 993\n000000\n111000\n000100\n001100\n010010\n000110\n001010\n000010\n000010\nCHAR 994\n000000\n101010\n101010\n101010\n101010\n011110\n000010\n111100\n000000\nCHAR 995\n000000\n000000\n000000\n101010\n101010\n011110\n000010\n111100\n000000\nCHAR 996\n000000\n010010\n101010\n100010\n011110\n000010\n000010\n000000\n000000\nCHAR 997\n000000\n000000\n000000\n001010\n010010\n001110\n000010\n000000\n000000\nCHAR 998\n000000\n100000\n100000\n101100\n110010\n100010\n000010\n111100\n000000\nCHAR 999\n000000\n001000\n011100\n101010\n010010\n000010\n111100\n000000\n000000\nCHAR 1000\n000000\n011100\n100010\n000010\n011100\n100000\n011110\n000000\n000000\nCHAR 1001\n000000\n000000\n011100\n100010\n011100\n100000\n011110\n000000\n000000\nCHAR 1002\n000000\n010100\n101010\n001000\n010100\n100010\n111110\n000000\n000000\nCHAR 1003\n000000\n000000\n010100\n101010\n001000\n010100\n011100\n000000\n000000\nCHAR 1004\n000000\n000010\n011100\n100000\n111100\n100010\n011100\n000000\n000000\nCHAR 1005\n000000\n000000\n000010\n011100\n100000\n110110\n011100\n000000\n000000\nCHAR 1006\n000000\n011100\n001000\n111110\n101010\n001000\n011100\n000000\n000000\nCHAR 1007\n000000\n001000\n001100\n001000\n111110\n101000\n001000\n011000\n001000\nCHAR 1008\n000000\n000000\n000000\n100010\n010010\n011100\n100010\n000000\n000000\nCHAR 1009\n000000\n000000\n000000\n011100\n100010\n100010\n111100\n100000\n011110\nCHAR 1010\n000000\n000000\n000000\n011100\n100010\n100000\n011110\n000000\n000000\nCHAR 1011\n000000\n000010\n000000\n000110\n000010\n000010\n010010\n010010\n001100\nCHAR 1012\n000000\n011100\n100010\n111110\n100010\n100010\n011100\n000000\n000000\nCHAR 1013\n000000\n000000\n000000\n001110\n011100\n010000\n001110\n000000\n000000\nCHAR 1024\n001000\n000100\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 1025\n001010\n000000\n011110\n010000\n011100\n010000\n011110\n000000\n000000\nCHAR 1026\n000000\n111110\n001000\n001100\n001010\n001010\n001010\n000010\n000100\nCHAR 1027\n000100\n001000\n011110\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 1028\n000000\n011100\n100010\n111000\n100000\n100010\n011100\n000000\n000000\nCHAR 1029\n000000\n001100\n010010\n001000\n000100\n010010\n001100\n000000\n000000\nCHAR 1030\n000000\n011100\n001000\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 1031\n010100\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 1032\n000000\n001110\n000100\n000100\n000100\n100100\n011000\n000000\n000000\nCHAR 1033\n000000\n011000\n101000\n101100\n101010\n101010\n101100\n000000\n000000\nCHAR 1034\n000000\n101000\n101000\n111100\n101010\n101010\n101100\n000000\n000000\nCHAR 1035\n000000\n111110\n001000\n001100\n001010\n001010\n001010\n000000\n000000\nCHAR 1036\n000010\n000100\n010010\n010100\n011000\n010100\n010010\n000000\n000000\nCHAR 1037\n010000\n001000\n100010\n100110\n101010\n110010\n100010\n000000\n000000\nCHAR 1038\n010010\n001100\n100010\n100010\n011110\n000010\n011100\n000000\n000000\nCHAR 1039\n000000\n100010\n100010\n100010\n100010\n100010\n111110\n001000\n001000\nCHAR 1040\n000000\n001000\n010100\n100010\n111110\n100010\n100010\n000000\n000000\nCHAR 1041\n000000\n111100\n100000\n111100\n100010\n100010\n111100\n000000\n000000\nCHAR 1042\n000000\n111100\n100010\n111100\n100010\n100010\n111100\n000000\n000000\nCHAR 1043\n000000\n011110\n010000\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 1044\n000000\n001100\n010100\n010100\n010100\n010100\n111110\n100010\n000000\nCHAR 1045\n000000\n011110\n010000\n011100\n010000\n010000\n011110\n000000\n000000\nCHAR 1046\n000000\n101010\n101010\n011100\n101010\n101010\n101010\n000000\n000000\nCHAR 1047\n000000\n011100\n100010\n001100\n000010\n100010\n011100\n000000\n000000\nCHAR 1048\n000000\n010010\n010110\n011010\n010010\n010010\n010010\n000000\n000000\nCHAR 1049\n010010\n001100\n010010\n010110\n011010\n010010\n010010\n000000\n000000\nCHAR 1050\n000000\n010010\n010100\n011000\n010100\n010010\n010010\n000000\n000000\nCHAR 1051\n000000\n001110\n010010\n010010\n010010\n010010\n100010\n000000\n000000\nCHAR 1052\n000000\n100010\n110110\n101010\n101010\n100010\n100010\n000000\n000000\nCHAR 1053\n000000\n010010\n010010\n011110\n010010\n010010\n010010\n000000\n000000\nCHAR 1054\n000000\n011100\n100010\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 1055\n000000\n111110\n100010\n100010\n100010\n100010\n100010\n000000\n000000\nCHAR 1056\n000000\n011100\n010010\n010010\n011100\n010000\n010000\n000000\n000000\nCHAR 1057\n000000\n001100\n010010\n010000\n010000\n010010\n001100\n000000\n000000\nCHAR 1058\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 1059\n000000\n100010\n100010\n100010\n011110\n000010\n011100\n000000\n000000\nCHAR 1060\n000000\n001000\n011100\n101010\n101010\n011100\n001000\n000000\n000000\nCHAR 1061\n000000\n100010\n010100\n001000\n001000\n010100\n100010\n000000\n000000\nCHAR 1062\n000000\n100100\n100100\n100100\n100100\n100100\n111110\n000010\n000100\nCHAR 1063\n000000\n100010\n100010\n100010\n011110\n000010\n000010\n000000\n000000\nCHAR 1064\n000000\n101010\n101010\n101010\n101010\n101010\n111110\n000000\n000000\nCHAR 1065\n000000\n101010\n101010\n101010\n101010\n101010\n111110\n000010\n000100\nCHAR 1066\n000000\n110000\n010000\n011100\n010010\n010010\n011100\n000000\n000000\nCHAR 1067\n000000\n100010\n100010\n111010\n100110\n100110\n111010\n000000\n000000\nCHAR 1068\n000000\n010000\n010000\n011100\n010010\n010010\n011100\n000000\n000000\nCHAR 1069\n000000\n011100\n100010\n001110\n000010\n100010\n011100\n000000\n000000\nCHAR 1070\n000000\n100100\n101010\n111010\n111010\n101010\n100100\n000000\n000000\nCHAR 1071\n000000\n001110\n010010\n010010\n001110\n010010\n010010\n000000\n000000\nCHAR 1072\n000000\n000000\n000000\n011100\n100100\n100100\n011110\n000000\n000000\nCHAR 1073\n000010\n001100\n010000\n011100\n010010\n010010\n001100\n000000\n000000\nCHAR 1074\n000000\n000000\n000000\n011000\n010100\n011010\n011100\n000000\n000000\nCHAR 1075\n000000\n000000\n000000\n011110\n010000\n010000\n010000\n000000\n000000\nCHAR 1076\n000000\n000000\n000000\n001100\n010100\n010100\n111110\n100010\n000000\nCHAR 1077\n000000\n000000\n000000\n011000\n100100\n111000\n011100\n000000\n000000\nCHAR 1078\n000000\n000000\n000000\n101010\n011100\n011100\n101010\n000000\n000000\nCHAR 1079\n000000\n000000\n000000\n001100\n001010\n000110\n011100\n000000\n000000\nCHAR 1080\n000000\n000000\n000000\n100010\n100110\n101010\n110010\n000000\n000000\nCHAR 1081\n100010\n011100\n000000\n100010\n100110\n101010\n110010\n000000\n000000\nCHAR 1082\n000000\n000000\n000000\n010010\n010100\n011100\n010010\n000000\n000000\nCHAR 1083\n000000\n000000\n000000\n001110\n010010\n010010\n100010\n000000\n000000\nCHAR 1084\n000000\n000000\n000000\n100010\n110110\n101010\n100010\n000000\n000000\nCHAR 1085\n000000\n000000\n000000\n100010\n111110\n100010\n100010\n000000\n000000\nCHAR 1086\n000000\n000000\n000000\n011100\n100010\n100010\n011100\n000000\n000000\nCHAR 1087\n000000\n000000\n000000\n111110\n100010\n100010\n100010\n000000\n000000\nCHAR 1088\n000000\n000000\n000000\n111100\n100010\n100010\n111100\n100000\n100000\nCHAR 1089\n000000\n000000\n000000\n011100\n100010\n100000\n011110\n000000\n000000\nCHAR 1090\n000000\n000000\n000000\n111110\n001000\n001000\n001000\n000000\n000000\nCHAR 1091\n000000\n000000\n000000\n100010\n100010\n100010\n011110\n000010\n011100\nCHAR 1092\n000000\n001000\n001000\n011100\n101010\n101010\n011100\n001000\n001000\nCHAR 1093\n000000\n000000\n000000\n010010\n001100\n001100\n010010\n000000\n000000\nCHAR 1094\n000000\n000000\n000000\n100100\n100100\n100100\n111110\n000010\n000100\nCHAR 1095\n000000\n000000\n000000\n100010\n100010\n011110\n000010\n000000\n000000\nCHAR 1096\n000000\n000000\n000000\n101010\n101010\n101010\n111110\n000000\n000000\nCHAR 1097\n000000\n000000\n000000\n101010\n101010\n101010\n111110\n000010\n000100\nCHAR 1098\n000000\n000000\n000000\n110000\n011100\n010010\n011100\n000000\n000000\nCHAR 1099\n000000\n000000\n000000\n100010\n111010\n100110\n111010\n000000\n000000\nCHAR 1100\n000000\n000000\n000000\n010000\n011100\n010010\n011100\n000000\n000000\nCHAR 1101\n000000\n000000\n000000\n111000\n010100\n001100\n111000\n000000\n000000\nCHAR 1102\n000000\n000000\n000000\n100100\n111010\n111010\n100100\n000000\n000000\nCHAR 1103\n000000\n000000\n000000\n011100\n100100\n011100\n100100\n000000\n000000\nCHAR 1104\n001000\n000100\n000000\n001100\n010010\n011100\n001110\n000000\n000000\nCHAR 1105\n000000\n010100\n000000\n001100\n010010\n011100\n001110\n000000\n000000\nCHAR 1106\n000000\n010000\n111000\n010000\n011100\n010010\n010010\n000010\n000100\nCHAR 1107\n000100\n001000\n000000\n011110\n010000\n010000\n010000\n000000\n000000\nCHAR 1108\n000000\n000000\n000000\n001110\n010100\n011000\n001110\n000000\n000000\nCHAR 1109\n000000\n000000\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 1110\n000000\n001000\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 1111\n000000\n010100\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 1112\n000000\n000010\n000000\n000110\n000010\n000010\n000010\n010010\n001100\nCHAR 1113\n000000\n000000\n000000\n011000\n101100\n101010\n101100\n000000\n000000\nCHAR 1114\n000000\n000000\n000000\n101000\n111100\n101010\n101100\n000000\n000000\nCHAR 1115\n000000\n010000\n111000\n010000\n011100\n010010\n010010\n000000\n000000\nCHAR 1116\n000100\n001000\n000000\n010010\n010100\n011100\n010010\n000000\n000000\nCHAR 1117\n010000\n001000\n000000\n100010\n100110\n101010\n110010\n000000\n000000\nCHAR 1118\n000000\n010010\n001100\n000000\n100010\n100010\n011110\n000010\n011100\nCHAR 1119\n000000\n000000\n000000\n100010\n100010\n100010\n111110\n001000\n001000\nCHAR 1122\n010000\n111000\n010000\n011100\n010010\n010010\n011100\n000000\n000000\nCHAR 1123\n000000\n010000\n010000\n111000\n011100\n010010\n011100\n000000\n000000\nCHAR 1136\n000000\n101010\n101010\n101010\n011100\n001000\n001000\n000000\n000000\nCHAR 1137\n000000\n000000\n000000\n101010\n101010\n011100\n001000\n000000\n000000\nCHAR 1138\n000000\n011100\n100010\n111110\n100010\n100010\n011100\n000000\n000000\nCHAR 1139\n000000\n000000\n000000\n011100\n101010\n110110\n011100\n000000\n000000\nCHAR 1140\n000000\n010000\n010010\n010110\n011100\n001000\n001000\n000000\n000000\nCHAR 1141\n000000\n000000\n000000\n010000\n010110\n001100\n001000\n000000\n000000\nCHAR 1168\n000010\n011110\n010000\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 1169\n000000\n000000\n000010\n011110\n010000\n010000\n010000\n000000\n000000\nCHAR 1170\n000000\n011110\n010000\n111100\n010000\n010000\n010000\n000000\n000000\nCHAR 1171\n000000\n000000\n000000\n011110\n010000\n111000\n010000\n000000\n000000\nCHAR 1174\n000000\n101010\n101010\n011100\n101010\n101010\n101011\n000001\n000001\nCHAR 1175\n000000\n000000\n000000\n101010\n011100\n011100\n101010\n000001\n000001\nCHAR 1178\n000000\n010010\n010100\n011000\n010100\n010010\n010010\n000001\n000001\nCHAR 1179\n000000\n000000\n000000\n010010\n010100\n011100\n010010\n000001\n000001\nCHAR 1198\n000000\n100010\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 1199\n000000\n000000\n000000\n100010\n100010\n010100\n001000\n001000\n001000\nCHAR 1200\n000000\n100010\n100010\n010100\n111110\n001000\n001000\n000000\n000000\nCHAR 1201\n000000\n000000\n000000\n100010\n100010\n010100\n111110\n001000\n001000\nCHAR 1202\n000000\n100010\n010100\n001000\n001000\n010100\n100010\n000001\n000001\nCHAR 1203\n000000\n000000\n000000\n010010\n001100\n001100\n010010\n000001\n000001\nCHAR 1210\n000000\n100000\n100000\n111100\n100010\n100010\n100010\n000000\n000000\nCHAR 1211\n000000\n010000\n010000\n011100\n010010\n010010\n010010\n000000\n000000\nCHAR 1240\n000000\n001100\n010010\n000010\n011110\n010010\n001100\n000000\n000000\nCHAR 1241\n000000\n000000\n000000\n011100\n000110\n011010\n001100\n000000\n000000\nCHAR 1250\n011100\n000000\n011100\n001000\n001000\n001000\n011100\n000000\n000000\nCHAR 1251\n000000\n011100\n000000\n011000\n001000\n001000\n011100\n000000\n000000\nCHAR 1256\n000000\n011100\n100010\n111110\n100010\n100010\n011100\n000000\n000000\nCHAR 1257\n000000\n000000\n000000\n001100\n011110\n010010\n001100\n000000\n000000\nCHAR 1262\n011110\n000000\n010010\n010010\n010010\n010010\n001100\n000000\n000000\nCHAR 1263\n000000\n011110\n000000\n010010\n010010\n010010\n001110\n000000\n000000\nCHAR 1488\n000000\n000000\n000000\n100100\n010010\n101100\n100010\n000000\n000000\nCHAR 1489\n000000\n000000\n000000\n111100\n000100\n000100\n111110\n000000\n000000\nCHAR 1490\n000000\n000000\n000000\n010000\n001000\n001100\n010100\n000000\n000000\nCHAR 1491\n000000\n000000\n000000\n111110\n000100\n000100\n000100\n000000\n000000\nCHAR 1492\n000000\n000000\n000000\n111110\n000010\n100010\n100010\n000000\n000000\nCHAR 1493\n000000\n000000\n000000\n001100\n000100\n000100\n000100\n000000\n000000\nCHAR 1494\n000000\n000000\n000000\n111110\n001000\n000100\n000100\n000000\n000000\nCHAR 1495\n000000\n000000\n000000\n111110\n010010\n010010\n010010\n000000\n000000\nCHAR 1496\n000000\n000000\n000000\n100110\n101010\n100010\n111110\n000000\n000000\nCHAR 1497\n000000\n000000\n000000\n001100\n000100\n000000\n000000\n000000\n000000\nCHAR 1498\n000000\n000000\n000000\n111110\n000010\n000010\n000010\n000010\n000010\nCHAR 1499\n000000\n000000\n000000\n111110\n000010\n000010\n111100\n000000\n000000\nCHAR 1500\n000000\n100000\n100000\n111110\n000010\n000100\n011000\n000000\n000000\nCHAR 1501\n000000\n000000\n000000\n111110\n010010\n010010\n011110\n000000\n000000\nCHAR 1502\n000000\n000000\n000000\n100100\n101010\n010010\n100110\n000000\n000000\nCHAR 1503\n000000\n000000\n000000\n001100\n000100\n000100\n000100\n000100\n000100\nCHAR 1504\n000000\n000000\n000000\n000110\n000010\n000010\n011110\n000000\n000000\nCHAR 1505\n000000\n000000\n000000\n011110\n001010\n001010\n001100\n000000\n000000\nCHAR 1506\n000000\n000000\n000000\n110110\n010010\n010100\n111000\n000000\n000000\nCHAR 1507\n000000\n000000\n000000\n111110\n010010\n010010\n000010\n000010\n000010\nCHAR 1508\n000000\n000000\n000000\n111110\n010010\n000010\n111100\n000000\n000000\nCHAR 1509\n000000\n000000\n000000\n100100\n010010\n010100\n011000\n010000\n010000\nCHAR 1510\n000000\n000000\n000000\n100100\n010010\n001100\n111110\n000000\n000000\nCHAR 1511\n000000\n000000\n000000\n111110\n000010\n100010\n101100\n100000\n100000\nCHAR 1512\n000000\n000000\n000000\n111100\n000010\n000010\n000010\n000000\n000000\nCHAR 1513\n000000\n000000\n000000\n101010\n111010\n100010\n111100\n000000\n000000\nCHAR 1514\n000000\n000000\n000000\n111110\n010010\n010010\n110010\n000000\n000000\nCHAR 1520\n000000\n000000\n000000\n110110\n010010\n010010\n010010\n000000\n000000\nCHAR 1521\n000000\n000000\n000000\n110110\n010010\n000010\n000010\n000000\n000000\nCHAR 1522\n000000\n000000\n000000\n110110\n010010\n000000\n000000\n000000\n000000\nCHAR 1523\n000000\n000000\n000000\n000100\n001000\n000000\n000000\n000000\n000000\nCHAR 1524\n000000\n000000\n000000\n010010\n100100\n000000\n000000\n000000\n000000\nCHAR 7682\n001000\n000000\n111100\n100010\n111100\n100010\n111100\n000000\n000000\nCHAR 7683\n000100\n010000\n010000\n011100\n010010\n010010\n011100\n000000\n000000\nCHAR 7690\n001000\n000000\n011100\n010010\n010010\n010010\n011100\n000000\n000000\nCHAR 7691\n001000\n000010\n000010\n001110\n010010\n010010\n001110\n000000\n000000\nCHAR 7710\n001000\n000000\n011110\n010000\n011100\n010000\n010000\n000000\n000000\nCHAR 7711\n001000\n000000\n000100\n001010\n011100\n001000\n001000\n000000\n000000\nCHAR 7744\n001000\n100010\n110110\n101010\n101010\n100010\n100010\n000000\n000000\nCHAR 7745\n000000\n001000\n000000\n110100\n101010\n101010\n100010\n000000\n000000\nCHAR 7766\n001000\n000000\n011100\n010010\n011100\n010000\n010000\n000000\n000000\nCHAR 7767\n000000\n001000\n000000\n011100\n010010\n010010\n011100\n010000\n010000\nCHAR 7776\n000100\n000000\n001110\n010000\n001100\n000010\n011100\n000000\n000000\nCHAR 7777\n000000\n000100\n000000\n001110\n011000\n000110\n011100\n000000\n000000\nCHAR 7786\n001000\n000000\n111110\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 7787\n001000\n000000\n001000\n011100\n001000\n001010\n000100\n000000\n000000\nCHAR 7808\n010000\n001000\n100010\n100010\n101010\n110110\n100010\n000000\n000000\nCHAR 7809\n010000\n001000\n000000\n100010\n101010\n101010\n010100\n000000\n000000\nCHAR 7810\n000100\n001000\n100010\n100010\n101010\n110110\n100010\n000000\n000000\nCHAR 7811\n000100\n001000\n000000\n100010\n101010\n101010\n010100\n000000\n000000\nCHAR 7812\n010100\n000000\n100010\n100010\n101010\n110110\n100010\n000000\n000000\nCHAR 7813\n000000\n010100\n000000\n100010\n101010\n101010\n010100\n000000\n000000\nCHAR 7922\n010000\n001000\n100010\n010100\n001000\n001000\n001000\n000000\n000000\nCHAR 7923\n001000\n000100\n000000\n010010\n010010\n010010\n001110\n010010\n001100\nCHAR 8208\n000000\n000000\n000000\n000000\n011110\n000000\n000000\n000000\n000000\nCHAR 8209\n000000\n000000\n000000\n000000\n011110\n000000\n000000\n000000\n000000\nCHAR 8210\n000000\n000000\n000000\n000000\n111110\n000000\n000000\n000000\n000000\nCHAR 8211\n000000\n000000\n000000\n000000\n111110\n000000\n000000\n000000\n000000\nCHAR 8212\n000000\n000000\n000000\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 8213\n000000\n000000\n000000\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 8214\n000000\n010100\n010100\n010100\n010100\n010100\n010100\n000000\n000000\nCHAR 8215\n000000\n000000\n000000\n000000\n000000\n000000\n111111\n000000\n111111\nCHAR 8216\n000000\n000100\n001000\n001100\n000000\n000000\n000000\n000000\n000000\nCHAR 8217\n000000\n001100\n000100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 8218\n000000\n000000\n000000\n000000\n000000\n000000\n001100\n000100\n001000\nCHAR 8219\n000000\n001100\n001000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 8220\n000000\n010010\n100100\n110110\n000000\n000000\n000000\n000000\n000000\nCHAR 8221\n000000\n110110\n010010\n100100\n000000\n000000\n000000\n000000\n000000\nCHAR 8222\n000000\n000000\n000000\n000000\n000000\n000000\n110110\n010010\n100100\nCHAR 8223\n000000\n110110\n100100\n010010\n000000\n000000\n000000\n000000\n000000\nCHAR 8224\n000000\n001000\n001000\n111110\n001000\n001000\n001000\n001000\n000000\nCHAR 8225\n000000\n001000\n001000\n111110\n001000\n111110\n001000\n001000\n000000\nCHAR 8226\n000000\n000000\n001100\n011110\n011110\n001100\n000000\n000000\n000000\nCHAR 8227\n000000\n000000\n010000\n011000\n011100\n011000\n010000\n000000\n000000\nCHAR 8228\n000000\n000000\n000000\n000000\n000000\n000000\n001000\n000000\n000000\nCHAR 8229\n000000\n000000\n000000\n000000\n000000\n000000\n010100\n000000\n000000\nCHAR 8230\n000000\n000000\n000000\n000000\n000000\n000000\n101010\n000000\n000000\nCHAR 8231\n000000\n000000\n000000\n000000\n001100\n000000\n000000\n000000\n000000\nCHAR 8240\n010000\n101010\n010010\n000100\n001000\n011010\n110101\n001010\n000000\nCHAR 8242\n000000\n000100\n000100\n001000\n000000\n000000\n000000\n000000\n000000\nCHAR 8243\n000000\n010010\n010010\n100100\n000000\n000000\n000000\n000000\n000000\nCHAR 8244\n000000\n010101\n010101\n101010\n000000\n000000\n000000\n000000\n000000\nCHAR 8245\n000000\n001000\n001000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 8246\n000000\n100100\n100100\n010010\n000000\n000000\n000000\n000000\n000000\nCHAR 8247\n000000\n101010\n101010\n010101\n000000\n000000\n000000\n000000\n000000\nCHAR 8249\n000000\n000000\n000100\n001000\n010000\n001000\n000100\n000000\n000000\nCHAR 8250\n000000\n000000\n010000\n001000\n000100\n001000\n010000\n000000\n000000\nCHAR 8252\n000000\n010100\n010100\n010100\n010100\n000000\n010100\n000000\n000000\nCHAR 8254\n000000\n111111\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8260\n000000\n000010\n000010\n000100\n001000\n010000\n010000\n000000\n000000\nCHAR 8304\n000000\n001000\n010100\n010100\n010100\n001000\n000000\n000000\n000000\nCHAR 8305\n000000\n001000\n000000\n011000\n001000\n011100\n000000\n000000\n000000\nCHAR 8308\n000000\n010100\n010100\n011100\n000100\n000100\n000000\n000000\n000000\nCHAR 8309\n000000\n011100\n010000\n011000\n000100\n011000\n000000\n000000\n000000\nCHAR 8310\n000000\n001000\n010000\n011000\n010100\n001000\n000000\n000000\n000000\nCHAR 8311\n000000\n011100\n000100\n001000\n001000\n001000\n000000\n000000\n000000\nCHAR 8312\n000000\n001000\n010100\n001000\n010100\n001000\n000000\n000000\n000000\nCHAR 8313\n000000\n001000\n010100\n001100\n000100\n001000\n000000\n000000\n000000\nCHAR 8314\n000000\n000000\n000000\n001000\n011100\n001000\n000000\n000000\n000000\nCHAR 8315\n000000\n000000\n000000\n000000\n011100\n000000\n000000\n000000\n000000\nCHAR 8316\n000000\n000000\n000000\n011100\n000000\n011100\n000000\n000000\n000000\nCHAR 8317\n000000\n001000\n010000\n010000\n010000\n001000\n000000\n000000\n000000\nCHAR 8318\n000000\n001000\n000100\n000100\n000100\n001000\n000000\n000000\n000000\nCHAR 8319\n000000\n000000\n011000\n010100\n010100\n010100\n000000\n000000\n000000\nCHAR 8320\n000000\n000000\n000000\n000000\n001000\n010100\n010100\n010100\n001000\nCHAR 8321\n000000\n000000\n000000\n000000\n001000\n011000\n001000\n001000\n011100\nCHAR 8322\n000000\n000000\n000000\n000000\n001000\n010100\n000100\n001000\n011100\nCHAR 8323\n000000\n000000\n000000\n000000\n011000\n000100\n001000\n000100\n011000\nCHAR 8324\n000000\n000000\n000000\n000000\n010100\n010100\n011100\n000100\n000100\nCHAR 8325\n000000\n000000\n000000\n000000\n011100\n010000\n011000\n000100\n011000\nCHAR 8326\n000000\n000000\n000000\n000000\n001000\n010000\n011000\n010100\n001000\nCHAR 8327\n000000\n000000\n000000\n000000\n011100\n000100\n001000\n001000\n001000\nCHAR 8328\n000000\n000000\n000000\n000000\n001000\n010100\n001000\n010100\n001000\nCHAR 8329\n000000\n000000\n000000\n000000\n001000\n010100\n001100\n000100\n001000\nCHAR 8330\n000000\n000000\n000000\n000000\n000000\n000000\n001000\n011100\n001000\nCHAR 8331\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n011100\n000000\nCHAR 8332\n000000\n000000\n000000\n000000\n000000\n000000\n011100\n000000\n011100\nCHAR 8333\n000000\n000000\n000000\n000000\n001000\n010000\n010000\n010000\n001000\nCHAR 8334\n000000\n000000\n000000\n000000\n001000\n000100\n000100\n000100\n001000\nCHAR 8355\n000000\n111110\n100000\n111010\n101100\n101000\n101000\n000000\n000000\nCHAR 8356\n000000\n001100\n010010\n111000\n010000\n111000\n010000\n111110\n000000\nCHAR 8359\n000000\n011000\n010100\n111110\n010100\n011000\n010000\n000000\n000000\nCHAR 8363\n000000\n000010\n000111\n001110\n010010\n010010\n001110\n000000\n011110\nCHAR 8364\n000000\n001110\n010000\n111100\n111100\n010000\n001110\n000000\n000000\nCHAR 8367\n010000\n111000\n010100\n010010\n010010\n111010\n110100\n000000\n000000\nCHAR 8400\n010000\n111110\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8401\n000100\n111110\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8402\n001000\n001000\n001000\n001000\n001000\n001000\n001000\n001000\n001000\nCHAR 8403\n000000\n000000\n001000\n001000\n001000\n001000\n001000\n001000\n000000\nCHAR 8404\n101100\n110010\n111000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8405\n011010\n100110\n001110\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8406\n010000\n111110\n010000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8407\n000100\n111110\n000100\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 8450\n000000\n011100\n101010\n101000\n101000\n101010\n011100\n000000\n000000\nCHAR 8453\n000000\n010000\n100000\n010000\n000100\n001010\n000100\n000000\n000000\nCHAR 8467\n000000\n000100\n001010\n001010\n001100\n001000\n010110\n000000\n000000\nCHAR 8469\n000000\n110010\n111010\n111010\n110110\n110110\n110010\n000000\n000000\nCHAR 8470\n000000\n100100\n110100\n110100\n101110\n101101\n100110\n000000\n000000\nCHAR 8474\n000000\n011100\n101010\n101010\n101010\n101010\n011100\n000110\n000000\nCHAR 8477\n000000\n111100\n101010\n101010\n101100\n101010\n101010\n000000\n000000\nCHAR 8482\n000000\n111111\n010111\n010101\n000000\n000000\n000000\n000000\n000000\nCHAR 8484\n000000\n111110\n001010\n010100\n010100\n101000\n111110\n000000\n000000\nCHAR 8486\n000000\n011100\n100010\n100010\n100010\n010100\n110110\n000000\n000000\nCHAR 8494\n000000\n000000\n011100\n110110\n111110\n110000\n011100\n000000\n000000\nCHAR 8539\n010000\n110000\n010000\n010100\n011010\n000100\n001010\n000100\n000000\nCHAR 8540\n110000\n001000\n010000\n001100\n111010\n000100\n001010\n000100\n000000\nCHAR 8541\n111000\n100000\n110000\n001100\n111010\n000100\n001010\n000100\n000000\nCHAR 8542\n111000\n001000\n010000\n010100\n011010\n000100\n001010\n000100\n000000\nCHAR 8592\n000000\n000000\n001000\n010000\n111110\n010000\n001000\n000000\n000000\nCHAR 8593\n000000\n001000\n011100\n101010\n001000\n001000\n001000\n000000\n000000\nCHAR 8594\n000000\n000000\n001000\n000100\n111110\n000100\n001000\n000000\n000000\nCHAR 8595\n000000\n001000\n001000\n001000\n101010\n011100\n001000\n000000\n000000\nCHAR 8596\n000000\n000000\n000000\n010010\n111111\n010010\n000000\n000000\n000000\nCHAR 8597\n000000\n001000\n011100\n101010\n001000\n101010\n011100\n001000\n000000\nCHAR 8612\n000000\n000000\n001000\n010010\n111110\n010010\n001000\n000000\n000000\nCHAR 8613\n000000\n001000\n011100\n101010\n001000\n001000\n011100\n000000\n000000\nCHAR 8614\n000000\n000000\n001000\n100100\n111110\n100100\n001000\n000000\n000000\nCHAR 8615\n000000\n011100\n001000\n001000\n101010\n011100\n001000\n000000\n000000\nCHAR 8616\n001000\n011100\n101010\n001000\n101010\n011100\n001000\n111110\n000000\nCHAR 8656\n000000\n000000\n001000\n011110\n100000\n011110\n001000\n000000\n000000\nCHAR 8657\n000000\n001000\n010100\n110110\n010100\n010100\n010100\n000000\n000000\nCHAR 8658\n000000\n000000\n001000\n111100\n000010\n111100\n001000\n000000\n000000\nCHAR 8659\n000000\n010100\n010100\n010100\n110110\n011100\n001000\n000000\n000000\nCHAR 8660\n000000\n000000\n001100\n011110\n100001\n011110\n001100\n000000\n000000\nCHAR 8661\n000000\n001000\n011100\n110110\n010100\n110110\n011100\n001000\n000000\nCHAR 8704\n000000\n100010\n100010\n111110\n100010\n010100\n001000\n000000\n000000\nCHAR 8705\n000000\n001000\n010100\n010000\n010000\n010000\n010100\n001000\n000000\nCHAR 8706\n000000\n001100\n000010\n001110\n010010\n010010\n001100\n000000\n000000\nCHAR 8707\n000000\n011110\n000010\n001110\n000010\n000010\n011110\n000000\n000000\nCHAR 8708\n001000\n011110\n001010\n001110\n001010\n001010\n011110\n001000\n000000\nCHAR 8709\n000000\n011110\n100110\n101010\n101010\n110010\n111100\n000000\n000000\nCHAR 8710\n000000\n001000\n001000\n010100\n010100\n100010\n111110\n000000\n000000\nCHAR 8711\n000000\n111110\n100010\n010100\n010100\n001000\n001000\n000000\n000000\nCHAR 8712\n000000\n000000\n001110\n010000\n011110\n010000\n001110\n000000\n000000\nCHAR 8713\n000000\n000100\n001110\n010100\n011110\n010100\n001110\n000100\n000000\nCHAR 8715\n000000\n000000\n011100\n000010\n011110\n000010\n011100\n000000\n000000\nCHAR 8716\n000000\n001000\n011100\n001010\n011110\n001010\n011100\n001000\n000000\nCHAR 8719\n111110\n010100\n010100\n010100\n010100\n010100\n010100\n010100\n000000\nCHAR 8720\n010100\n010100\n010100\n010100\n010100\n010100\n010100\n111110\n000000\nCHAR 8721\n000000\n111110\n010000\n001000\n001000\n010000\n111110\n000000\n000000\nCHAR 8722\n000000\n000000\n000000\n000000\n111110\n000000\n000000\n000000\n000000\nCHAR 8723\n000000\n111110\n000000\n001000\n001000\n111110\n001000\n001000\n000000\nCHAR 8725\n000000\n000010\n000010\n000100\n001000\n010000\n010000\n000000\n000000\nCHAR 8728\n000000\n000000\n000000\n001000\n010100\n001000\n000000\n000000\n000000\nCHAR 8729\n000000\n000000\n000000\n001000\n011100\n001000\n000000\n000000\n000000\nCHAR 8730\n000011\n000010\n000010\n000010\n000100\n110100\n001100\n001100\n000100\nCHAR 8733\n000000\n000000\n001010\n010100\n010100\n001010\n000000\n000000\n000000\nCHAR 8734\n000000\n000000\n010100\n101010\n101010\n010100\n000000\n000000\n000000\nCHAR 8735\n000000\n000000\n100000\n100000\n100000\n100000\n111110\n000000\n000000\nCHAR 8737\n000000\n000000\n010100\n001000\n010100\n100100\n111110\n000100\n000000\nCHAR 8740\n000000\n001000\n001000\n001100\n011000\n001000\n001000\n000000\n000000\nCHAR 8741\n000000\n010100\n010100\n010100\n010100\n010100\n010100\n000000\n000000\nCHAR 8742\n000000\n010100\n010110\n011100\n110100\n010100\n010100\n000000\n000000\nCHAR 8743\n000000\n000000\n000000\n001000\n010100\n010100\n100010\n000000\n000000\nCHAR 8744\n000000\n000000\n000000\n100010\n010100\n010100\n001000\n000000\n000000\nCHAR 8745\n000000\n000000\n000000\n011100\n100010\n100010\n100010\n000000\n000000\nCHAR 8746\n000000\n000000\n000000\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 8747\n000100\n001010\n001000\n001000\n001000\n001000\n001000\n101000\n010000\nCHAR 8750\n000100\n001010\n001000\n011100\n101010\n011100\n001000\n101000\n010000\nCHAR 8756\n000000\n000000\n001000\n000000\n000000\n100010\n000000\n000000\n000000\nCHAR 8757\n000000\n000000\n100010\n000000\n000000\n001000\n000000\n000000\n000000\nCHAR 8758\n000000\n000000\n001000\n000000\n000000\n001000\n000000\n000000\n000000\nCHAR 8759\n000000\n000000\n010010\n000000\n000000\n010010\n000000\n000000\n000000\nCHAR 8760\n000000\n000000\n001000\n000000\n111110\n000000\n000000\n000000\n000000\nCHAR 8761\n000000\n000000\n000010\n000000\n111000\n000000\n000010\n000000\n000000\nCHAR 8762\n000000\n000000\n100010\n000000\n111110\n000000\n100010\n000000\n000000\nCHAR 8763\n000000\n000000\n000100\n000000\n001010\n010100\n000000\n001000\n000000\nCHAR 8764\n000000\n000000\n000000\n000000\n001010\n010100\n000000\n000000\n000000\nCHAR 8765\n000000\n000000\n000000\n000000\n010100\n001010\n000000\n000000\n000000\nCHAR 8771\n000000\n000000\n000000\n001010\n010100\n000000\n011110\n000000\n000000\nCHAR 8773\n000000\n000000\n001010\n010100\n000000\n011110\n000000\n011110\n000000\nCHAR 8776\n000000\n000000\n010000\n101010\n010100\n101010\n000100\n000000\n000000\nCHAR 8777\n000000\n000100\n010100\n101010\n011100\n101010\n010100\n010000\n000000\nCHAR 8793\n001000\n010100\n000000\n111110\n000000\n111110\n000000\n000000\n000000\nCHAR 8799\n011000\n000100\n001000\n000000\n001000\n000000\n111110\n000000\n111110\nCHAR 8800\n000000\n000000\n000100\n111110\n001000\n111110\n010000\n000000\n000000\nCHAR 8801\n000000\n000000\n111110\n000000\n111110\n000000\n111110\n000000\n000000\nCHAR 8802\n000000\n000100\n111110\n001000\n111110\n001000\n111110\n010000\n000000\nCHAR 8803\n000000\n111110\n000000\n111110\n000000\n111110\n000000\n111110\n000000\nCHAR 8804\n000000\n000110\n011000\n100000\n011000\n100110\n011000\n000110\n000000\nCHAR 8805\n000000\n110000\n001100\n000010\n001100\n110010\n001100\n110000\n000000\nCHAR 8810\n000000\n000000\n001010\n010100\n101000\n010100\n001010\n000000\n000000\nCHAR 8811\n000000\n000000\n101000\n010100\n001010\n010100\n101000\n000000\n000000\nCHAR 8834\n000000\n000000\n001110\n010000\n010000\n001110\n000000\n000000\n000000\nCHAR 8835\n000000\n000000\n011100\n000010\n000010\n011100\n000000\n000000\n000000\nCHAR 8836\n000000\n000100\n001110\n010100\n010100\n001110\n000100\n000000\n000000\nCHAR 8837\n000000\n001000\n011100\n001010\n001010\n011100\n001000\n000000\n000000\nCHAR 8838\n000000\n001110\n010000\n010000\n001110\n000000\n011110\n000000\n000000\nCHAR 8839\n000000\n011100\n000010\n000010\n011100\n000000\n011110\n000000\n000000\nCHAR 8840\n000100\n001110\n010100\n010100\n001110\n000100\n011110\n000100\n000000\nCHAR 8841\n001000\n011100\n001010\n001010\n011100\n001000\n011110\n001000\n000000\nCHAR 8842\n000000\n001110\n010000\n010000\n001110\n000100\n011110\n001000\n000000\nCHAR 8843\n000000\n011100\n000010\n000010\n011100\n000100\n011110\n001000\n000000\nCHAR 8853\n000000\n000000\n011100\n101010\n111110\n101010\n011100\n000000\n000000\nCHAR 8854\n000000\n000000\n011100\n100010\n111110\n100010\n011100\n000000\n000000\nCHAR 8855\n000000\n000000\n011100\n110110\n101010\n110110\n011100\n000000\n000000\nCHAR 8856\n000000\n000000\n011100\n100110\n101010\n110010\n011100\n000000\n000000\nCHAR 8857\n000000\n000000\n011100\n100010\n101010\n100010\n011100\n000000\n000000\nCHAR 8866\n000000\n000000\n100000\n100000\n111110\n100000\n100000\n000000\n000000\nCHAR 8867\n000000\n000000\n000010\n000010\n111110\n000010\n000010\n000000\n000000\nCHAR 8868\n000000\n111110\n001000\n001000\n001000\n001000\n001000\n000000\n000000\nCHAR 8869\n000000\n001000\n001000\n001000\n001000\n001000\n111110\n000000\n000000\nCHAR 8870\n000000\n000000\n010000\n010000\n011100\n010000\n010000\n000000\n000000\nCHAR 8871\n000000\n000000\n010000\n011100\n010000\n011100\n010000\n000000\n000000\nCHAR 8872\n000000\n000000\n100000\n111110\n100000\n111110\n100000\n000000\n000000\nCHAR 8896\n001000\n001000\n010100\n010100\n010100\n100010\n100010\n100010\n000000\nCHAR 8897\n100010\n100010\n100010\n010100\n010100\n010100\n001000\n001000\n000000\nCHAR 8898\n011100\n100010\n100010\n100010\n100010\n100010\n100010\n100010\n000000\nCHAR 8899\n100010\n100010\n100010\n100010\n100010\n100010\n100010\n011100\n000000\nCHAR 8901\n000000\n000000\n000000\n000000\n001000\n000000\n000000\n000000\n000000\nCHAR 8942\n000000\n001000\n000000\n000000\n001000\n000000\n000000\n001000\n000000\nCHAR 8943\n000000\n000000\n000000\n000000\n101010\n000000\n000000\n000000\n000000\nCHAR 8944\n000000\n000010\n000000\n000000\n001000\n000000\n000000\n100000\n000000\nCHAR 8945\n000000\n100000\n000000\n000000\n001000\n000000\n000000\n000010\n000000\nCHAR 8960\n000000\n000000\n011110\n100110\n101010\n110010\n111100\n000000\n000000\nCHAR 8962\n000000\n001000\n010100\n100010\n100010\n100010\n111110\n000000\n000000\nCHAR 8968\n000000\n011100\n010000\n010000\n010000\n010000\n010000\n000000\n000000\nCHAR 8969\n000000\n011100\n000100\n000100\n000100\n000100\n000100\n000000\n000000\nCHAR 8970\n000000\n010000\n010000\n010000\n010000\n010000\n011100\n000000\n000000\nCHAR 8971\n000000\n000100\n000100\n000100\n000100\n000100\n011100\n000000\n000000\nCHAR 8976\n000000\n000000\n000000\n000000\n011110\n010000\n010000\n000000\n000000\nCHAR 8992\n000000\n000010\n000101\n000100\n000100\n000100\n000100\n000100\n000100\nCHAR 8993\n000100\n000100\n000100\n000100\n000100\n000100\n010100\n001000\n000000\nCHAR 9146\n111111\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 9147\n000000\n000000\n111111\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 9148\n000000\n000000\n000000\n000000\n000000\n000000\n111111\n000000\n000000\nCHAR 9149\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111111\nCHAR 9225\n000000\n101000\n111000\n101000\n000000\n001110\n000100\n000100\n000000\nCHAR 9226\n000000\n100000\n100000\n100000\n110000\n001110\n001000\n001100\n001000\nCHAR 9227\n000000\n101000\n101000\n010000\n010000\n001110\n000100\n000100\n000100\nCHAR 9228\n000000\n111000\n100000\n110000\n100000\n001110\n001000\n001100\n001000\nCHAR 9229\n000000\n011000\n100000\n011000\n000000\n001100\n001010\n001100\n001010\nCHAR 9252\n000000\n100100\n110100\n101100\n100100\n001000\n001000\n001000\n001110\nCHAR 9472\n000000\n000000\n000000\n000000\n111111\n000000\n000000\n000000\n000000\nCHAR 9473\n000000\n000000\n000000\n111111\n111111\n000000\n000000\n000000\n000000\nCHAR 9474\n000100\n000100\n000100\n000100\n000100\n000100\n000100\n000100\n000100\nCHAR 9475\n001100\n001100\n001100\n001100\n001100\n001100\n001100\n001100\n001100\nCHAR 9484\n000000\n000000\n000000\n000000\n000111\n000100\n000100\n000100\n000100\nCHAR 9488\n000000\n000000\n000000\n000000\n111100\n000100\n000100\n000100\n000100\nCHAR 9492\n000100\n000100\n000100\n000100\n000111\n000000\n000000\n000000\n000000\nCHAR 9496\n000100\n000100\n000100\n000100\n111100\n000000\n000000\n000000\n000000\nCHAR 9500\n000100\n000100\n000100\n000100\n000111\n000100\n000100\n000100\n000100\nCHAR 9508\n000100\n000100\n000100\n000100\n111100\n000100\n000100\n000100\n000100\nCHAR 9516\n000000\n000000\n000000\n000000\n111111\n000100\n000100\n000100\n000100\nCHAR 9524\n000100\n000100\n000100\n000100\n111111\n000000\n000000\n000000\n000000\nCHAR 9532\n000100\n000100\n000100\n000100\n111111\n000100\n000100\n000100\n000100\nCHAR 9548\n000000\n000000\n000000\n000000\n110110\n000000\n000000\n000000\n000000\nCHAR 9549\n000000\n000000\n000000\n110110\n110110\n000000\n000000\n000000\n000000\nCHAR 9550\n000100\n000100\n000100\n000100\n000000\n000100\n000100\n000100\n000000\nCHAR 9551\n001100\n001100\n001100\n001100\n000000\n001100\n001100\n001100\n000000\nCHAR 9552\n000000\n000000\n000000\n111111\n000000\n111111\n000000\n000000\n000000\nCHAR 9553\n001010\n001010\n001010\n001010\n001010\n001010\n001010\n001010\n001010\nCHAR 9554\n000000\n000000\n000000\n000111\n000100\n000111\n000100\n000100\n000100\nCHAR 9555\n000000\n000000\n000000\n000000\n001111\n001010\n001010\n001010\n001010\nCHAR 9556\n000000\n000000\n000000\n001111\n001000\n001011\n001010\n001010\n001010\nCHAR 9557\n000000\n000000\n000000\n111100\n000100\n111100\n000100\n000100\n000100\nCHAR 9558\n000000\n000000\n000000\n000000\n111110\n001010\n001010\n001010\n001010\nCHAR 9559\n000000\n000000\n000000\n111110\n000010\n111010\n001010\n001010\n001010\nCHAR 9560\n000100\n000100\n000100\n000111\n000100\n000111\n000000\n000000\n000000\nCHAR 9561\n001010\n001010\n001010\n001010\n001111\n000000\n000000\n000000\n000000\nCHAR 9562\n001010\n001010\n001010\n001011\n001000\n001111\n000000\n000000\n000000\nCHAR 9563\n000100\n000100\n000100\n111100\n000100\n111100\n000000\n000000\n000000\nCHAR 9564\n001010\n001010\n001010\n001010\n111110\n000000\n000000\n000000\n000000\nCHAR 9565\n001010\n001010\n001010\n111010\n000010\n111110\n000000\n000000\n000000\nCHAR 9566\n000100\n000100\n000100\n000111\n000100\n000111\n000100\n000100\n000100\nCHAR 9567\n001010\n001010\n001010\n001010\n001011\n001010\n001010\n001010\n001010\nCHAR 9568\n001010\n001010\n001010\n001011\n001000\n001011\n001010\n001010\n001010\nCHAR 9569\n000100\n000100\n000100\n111100\n000100\n111100\n000100\n000100\n000100\nCHAR 9570\n001010\n001010\n001010\n001010\n111010\n001010\n001010\n001010\n001010\nCHAR 9571\n001010\n001010\n001010\n111010\n000010\n111010\n001010\n001010\n001010\nCHAR 9572\n000000\n000000\n000000\n111111\n000000\n111111\n000100\n000100\n000100\nCHAR 9573\n000000\n000000\n000000\n000000\n111111\n001010\n001010\n001010\n001010\nCHAR 9574\n000000\n000000\n000000\n111111\n000000\n111011\n001010\n001010\n001010\nCHAR 9575\n000100\n000100\n000100\n111111\n000000\n111111\n000000\n000000\n000000\nCHAR 9576\n001010\n001010\n001010\n001010\n111111\n000000\n000000\n000000\n000000\nCHAR 9577\n001010\n001010\n001010\n111011\n000000\n111111\n000000\n000000\n000000\nCHAR 9578\n000100\n000100\n000100\n111111\n000100\n111111\n000100\n000100\n000100\nCHAR 9579\n001010\n001010\n001010\n001010\n111111\n001010\n001010\n001010\n001010\nCHAR 9580\n001010\n001010\n001010\n111011\n000000\n111011\n001010\n001010\n001010\nCHAR 9581\n000000\n000000\n000000\n000000\n000001\n000010\n000100\n000100\n000100\nCHAR 9582\n000000\n000000\n000000\n000000\n110000\n001000\n000100\n000100\n000100\nCHAR 9583\n000100\n000100\n000100\n001000\n110000\n000000\n000000\n000000\n000000\nCHAR 9584\n000100\n000100\n000100\n000010\n000001\n000000\n000000\n000000\n000000\nCHAR 9585\n000001\n000001\n000010\n000100\n000100\n001000\n010000\n010000\n100000\nCHAR 9586\n100000\n100000\n010000\n001000\n001000\n000100\n000010\n000010\n000001\nCHAR 9587\n100001\n100001\n010010\n001100\n001100\n001100\n010010\n010010\n100001\nCHAR 9600\n111111\n111111\n111111\n111111\n000000\n000000\n000000\n000000\n000000\nCHAR 9601\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111111\nCHAR 9602\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n111111\n111111\nCHAR 9603\n000000\n000000\n000000\n000000\n000000\n111111\n111111\n111111\n111111\nCHAR 9604\n000000\n000000\n000000\n000000\n111111\n111111\n111111\n111111\n111111\nCHAR 9605\n000000\n000000\n000000\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9606\n000000\n000000\n111111\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9607\n000000\n111111\n111111\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9608\n111111\n111111\n111111\n111111\n111111\n111111\n111111\n111111\n111111\nCHAR 9609\n111110\n111110\n111110\n111110\n111110\n111110\n111110\n111110\n111110\nCHAR 9610\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\nCHAR 9611\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\n111100\nCHAR 9612\n111000\n111000\n111000\n111000\n111000\n111000\n111000\n111000\n111000\nCHAR 9613\n110000\n110000\n110000\n110000\n110000\n110000\n110000\n110000\n110000\nCHAR 9614\n110000\n110000\n110000\n110000\n110000\n110000\n110000\n110000\n110000\nCHAR 9615\n100000\n100000\n100000\n100000\n100000\n100000\n100000\n100000\n100000\nCHAR 9616\n000111\n000111\n000111\n000111\n000111\n000111\n000111\n000111\n000111\nCHAR 9617\n101010\n000000\n010101\n000000\n101010\n000000\n010101\n000000\n101010\nCHAR 9618\n010101\n101010\n010101\n101010\n010101\n101010\n010101\n101010\n010101\nCHAR 9619\n111111\n101010\n111111\n010101\n111111\n101010\n111111\n010101\n111111\nCHAR 9620\n111111\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 9621\n000001\n000001\n000001\n000001\n000001\n000001\n000001\n000001\n000001\nCHAR 9622\n000000\n000000\n000000\n000000\n111000\n111000\n111000\n111000\n111000\nCHAR 9623\n000000\n000000\n000000\n000000\n000111\n000111\n000111\n000111\n000111\nCHAR 9624\n111000\n111000\n111000\n111000\n000000\n000000\n000000\n000000\n000000\nCHAR 9625\n111000\n111000\n111000\n111000\n111111\n111111\n111111\n111111\n111111\nCHAR 9626\n111000\n111000\n111000\n111000\n000111\n000111\n000111\n000111\n000111\nCHAR 9627\n111111\n111111\n111111\n111111\n111000\n111000\n111000\n111000\n111000\nCHAR 9628\n111111\n111111\n111111\n111111\n000111\n000111\n000111\n000111\n000111\nCHAR 9629\n000111\n000111\n000111\n000111\n000000\n000000\n000000\n000000\n000000\nCHAR 9630\n000111\n000111\n000111\n000111\n111000\n111000\n111000\n111000\n111000\nCHAR 9631\n000111\n000111\n000111\n000111\n111111\n111111\n111111\n111111\n111111\nCHAR 9632\n000000\n000000\n111110\n111110\n111110\n111110\n111110\n000000\n000000\nCHAR 9633\n000000\n000000\n111110\n100010\n100010\n100010\n111110\n000000\n000000\nCHAR 9634\n000000\n000000\n011100\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 9635\n000000\n000000\n111110\n100010\n101010\n100010\n111110\n000000\n000000\nCHAR 9642\n000000\n000000\n000000\n011100\n011100\n011100\n000000\n000000\n000000\nCHAR 9643\n000000\n000000\n000000\n011100\n010100\n011100\n000000\n000000\n000000\nCHAR 9644\n000000\n000000\n000000\n111110\n111110\n111110\n000000\n000000\n000000\nCHAR 9645\n000000\n000000\n000000\n111110\n100010\n111110\n000000\n000000\n000000\nCHAR 9646\n000000\n011110\n011110\n011110\n011110\n011110\n011110\n000000\n000000\nCHAR 9647\n000000\n011110\n010010\n010010\n010010\n010010\n011110\n000000\n000000\nCHAR 9648\n000000\n000000\n000000\n011110\n111110\n111100\n000000\n000000\n000000\nCHAR 9649\n000000\n000000\n000000\n011110\n100010\n111100\n000000\n000000\n000000\nCHAR 9650\n000000\n001000\n001000\n011100\n011100\n111110\n111110\n000000\n000000\nCHAR 9651\n000000\n001000\n001000\n010100\n010100\n100010\n111110\n000000\n000000\nCHAR 9652\n000000\n000000\n001000\n001000\n011100\n011100\n000000\n000000\n000000\nCHAR 9653\n000000\n000000\n001000\n001000\n010100\n011100\n000000\n000000\n000000\nCHAR 9654\n000000\n000000\n110000\n111100\n111110\n111100\n110000\n000000\n000000\nCHAR 9655\n000000\n000000\n110000\n101100\n100010\n101100\n110000\n000000\n000000\nCHAR 9656\n000000\n000000\n000000\n011000\n011110\n011000\n000000\n000000\n000000\nCHAR 9657\n000000\n000000\n000000\n011000\n010110\n011000\n000000\n000000\n000000\nCHAR 9658\n000000\n000000\n000000\n111000\n111110\n111000\n000000\n000000\n000000\nCHAR 9659\n000000\n000000\n000000\n111000\n100110\n111000\n000000\n000000\n000000\nCHAR 9660\n000000\n111110\n111110\n011100\n011100\n001000\n001000\n000000\n000000\nCHAR 9661\n000000\n111110\n100010\n010100\n010100\n001000\n001000\n000000\n000000\nCHAR 9662\n000000\n000000\n011100\n011100\n001000\n001000\n000000\n000000\n000000\nCHAR 9663\n000000\n000000\n011100\n010100\n001000\n001000\n000000\n000000\n000000\nCHAR 9664\n000000\n000000\n000110\n011110\n111110\n011110\n000110\n000000\n000000\nCHAR 9665\n000000\n000000\n000110\n011010\n100010\n011010\n000110\n000000\n000000\nCHAR 9666\n000000\n000000\n000000\n000110\n011110\n000110\n000000\n000000\n000000\nCHAR 9667\n000000\n000000\n000000\n000110\n011010\n000110\n000000\n000000\n000000\nCHAR 9668\n000000\n000000\n000000\n001110\n111110\n001110\n000000\n000000\n000000\nCHAR 9669\n000000\n000000\n000000\n001110\n110010\n001110\n000000\n000000\n000000\nCHAR 9670\n000000\n000000\n001100\n011110\n111111\n011110\n001100\n000000\n000000\nCHAR 9674\n000000\n001000\n010100\n010100\n100010\n010100\n010100\n001000\n000000\nCHAR 9675\n000000\n000000\n011100\n100010\n100010\n100010\n011100\n000000\n000000\nCHAR 9679\n000000\n000000\n011100\n111110\n111110\n111110\n011100\n000000\n000000\nCHAR 9688\n111111\n111111\n110011\n100001\n100001\n110011\n111111\n111111\n111111\nCHAR 9689\n111111\n111111\n110011\n101101\n101101\n110011\n111111\n111111\n111111\nCHAR 9702\n000000\n000000\n001100\n010010\n010010\n001100\n000000\n000000\n000000\nCHAR 9728\n000000\n001000\n101010\n011100\n111110\n011100\n101010\n001000\n000000\nCHAR 9785\n011110\n100001\n110011\n100001\n101101\n110011\n100001\n011110\n000000\nCHAR 9786\n011110\n100001\n110011\n100001\n110011\n101101\n100001\n011110\n000000\nCHAR 9787\n011110\n111111\n101101\n111111\n101101\n110011\n111111\n011110\n000000\nCHAR 9788\n000000\n001000\n101010\n011100\n110110\n011100\n101010\n001000\n000000\nCHAR 9791\n100010\n011100\n100010\n100010\n011100\n001000\n111110\n001000\n000000\nCHAR 9792\n000000\n011100\n100010\n100010\n011100\n001000\n111110\n001000\n000000\nCHAR 9793\n000000\n001000\n111110\n001000\n011100\n100010\n100010\n011100\n000000\nCHAR 9794\n000000\n000111\n000011\n000101\n011100\n100010\n100010\n011100\n000000\nCHAR 9824\n000000\n001000\n011100\n111110\n111110\n001000\n011100\n000000\n000000\nCHAR 9825\n000000\n010100\n101010\n100010\n100010\n010100\n001000\n000000\n000000\nCHAR 9826\n000000\n000000\n001000\n010100\n100010\n010100\n001000\n000000\n000000\nCHAR 9827\n001000\n011100\n101010\n111110\n101010\n001000\n011100\n000000\n000000\nCHAR 9828\n000000\n001000\n010100\n100010\n111110\n001000\n011100\n000000\n000000\nCHAR 9829\n000000\n010100\n111110\n111110\n111110\n011100\n001000\n000000\n000000\nCHAR 9830\n000000\n000000\n001000\n011100\n111110\n011100\n001000\n000000\n000000\nCHAR 9833\n000000\n000100\n000100\n000100\n000100\n011100\n011000\n000000\n000000\nCHAR 9834\n000000\n001100\n001010\n001000\n001000\n111000\n110000\n000000\n000000\nCHAR 9835\n000000\n011000\n010110\n010010\n110010\n110110\n000110\n000000\n000000\nCHAR 10216\n000000\n000100\n000100\n001000\n001000\n001000\n000100\n000100\n000000\nCHAR 10217\n000000\n001000\n001000\n000100\n000100\n000100\n001000\n001000\n000000\nCHAR 10240\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 10241\n000000\n010000\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 10242\n000000\n000000\n000000\n010000\n000000\n000000\n000000\n000000\n000000\nCHAR 10243\n000000\n010000\n000000\n010000\n000000\n000000\n000000\n000000\n000000\nCHAR 10244\n000000\n000000\n000000\n000000\n000000\n010000\n000000\n000000\n000000\nCHAR 10245\n000000\n010000\n000000\n000000\n000000\n010000\n000000\n000000\n000000\nCHAR 10246\n000000\n000000\n000000\n010000\n000000\n010000\n000000\n000000\n000000\nCHAR 10247\n000000\n010000\n000000\n010000\n000000\n010000\n000000\n000000\n000000\nCHAR 10248\n000000\n000100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 10249\n000000\n010100\n000000\n000000\n000000\n000000\n000000\n000000\n000000\nCHAR 10250\n000000\n000100\n000000\n010000\n000000\n000000\n000000\n000000\n000000\nCHAR 10251\n000000\n010100\n000000\n010000\n000000\n000000\n000000\n000000\n000000\nCHAR 10252\n000000\n000100\n000000\n000000\n000000\n010000\n000000\n000000\n000000\nCHAR 10253\n000000\n010100\n000000\n000000\n000000\n010000\n000000\n000000\n000000\nCHAR 10254\n000000\n000100\n000000\n010000\n000000\n010000\n000000\n000000\n000000\nCHAR 10255\n000000\n010100\n000000\n010000\n000000\n010000\n000000\n000000\n000000\nCHAR 10256\n000000\n000000\n000000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 10257\n000000\n010000\n000000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 10258\n000000\n000000\n000000\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 10259\n000000\n010000\n000000\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 10260\n000000\n000000\n000000\n000100\n000000\n010000\n000000\n000000\n000000\nCHAR 10261\n000000\n010000\n000000\n000100\n000000\n010000\n000000\n000000\n000000\nCHAR 10262\n000000\n000000\n000000\n010100\n000000\n010000\n000000\n000000\n000000\nCHAR 10263\n000000\n010000\n000000\n010100\n000000\n010000\n000000\n000000\n000000\nCHAR 10264\n000000\n000100\n000000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 10265\n000000\n010100\n000000\n000100\n000000\n000000\n000000\n000000\n000000\nCHAR 10266\n000000\n000100\n000000\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 10267\n000000\n010100\n000000\n010100\n000000\n000000\n000000\n000000\n000000\nCHAR 10268\n000000\n000100\n000000\n000100\n000000\n010000\n000000\n000000\n000000\nCHAR 10269\n000000\n010100\n000000\n000100\n000000\n010000\n000000\n000000\n000000\nCHAR 10270\n000000\n000100\n000000\n010100\n000000\n010000\n000000\n000000\n000000\nCHAR 10271\n000000\n010100\n000000\n010100\n000000\n010000\n000000\n000000\n000000\nCHAR 10272\n000000\n000000\n000000\n000000\n000000\n000100\n000000\n000000\n000000\nCHAR 10273\n000000\n010000\n000000\n000000\n000000\n000100\n000000\n000000\n000000\nCHAR 10274\n000000\n000000\n000000\n010000\n000000\n000100\n000000\n000000\n000000\nCHAR 10275\n000000\n010000\n000000\n010000\n000000\n000100\n000000\n000000\n000000\nCHAR 10276\n000000\n000000\n000000\n000000\n000000\n010100\n000000\n000000\n000000\nCHAR 10277\n000000\n010000\n000000\n000000\n000000\n010100\n000000\n000000\n000000\nCHAR 10278\n000000\n000000\n000000\n010000\n000000\n010100\n000000\n000000\n000000\nCHAR 10279\n000000\n010000\n000000\n010000\n000000\n010100\n000000\n000000\n000000\nCHAR 10280\n000000\n000100\n000000\n000000\n000000\n000100\n000000\n000000\n000000\nCHAR 10281\n000000\n010100\n000000\n000000\n000000\n000100\n000000\n000000\n000000\nCHAR 10282\n000000\n000100\n000000\n010000\n000000\n000100\n000000\n000000\n000000\nCHAR 10283\n000000\n010100\n000000\n010000\n000000\n000100\n000000\n000000\n000000\nCHAR 10284\n000000\n000100\n000000\n000000\n000000\n010100\n000000\n000000\n000000\nCHAR 10285\n000000\n010100\n000000\n000000\n000000\n010100\n000000\n000000\n000000\nCHAR 10286\n000000\n000100\n000000\n010000\n000000\n010100\n000000\n000000\n000000\nCHAR 10287\n000000\n010100\n000000\n010000\n000000\n010100\n000000\n000000\n000000\nCHAR 10288\n000000\n000000\n000000\n000100\n000000\n000100\n000000\n000000\n000000\nCHAR 10289\n000000\n010000\n000000\n000100\n000000\n000100\n000000\n000000\n000000\nCHAR 10290\n000000\n000000\n000000\n010100\n000000\n000100\n000000\n000000\n000000\nCHAR 10291\n000000\n010000\n000000\n010100\n000000\n000100\n000000\n000000\n000000\nCHAR 10292\n000000\n000000\n000000\n000100\n000000\n010100\n000000\n000000\n000000\nCHAR 10293\n000000\n010000\n000000\n000100\n000000\n010100\n000000\n000000\n000000\nCHAR 10294\n000000\n000000\n000000\n010100\n000000\n010100\n000000\n000000\n000000\nCHAR 10295\n000000\n010000\n000000\n010100\n000000\n010100\n000000\n000000\n000000\nCHAR 10296\n000000\n000100\n000000\n000100\n000000\n000100\n000000\n000000\n000000\nCHAR 10297\n000000\n010100\n000000\n000100\n000000\n000100\n000000\n000000\n000000\nCHAR 10298\n000000\n000100\n000000\n010100\n000000\n000100\n000000\n000000\n000000\nCHAR 10299\n000000\n010100\n000000\n010100\n000000\n000100\n000000\n000000\n000000\nCHAR 10300\n000000\n000100\n000000\n000100\n000000\n010100\n000000\n000000\n000000\nCHAR 10301\n000000\n010100\n000000\n000100\n000000\n010100\n000000\n000000\n000000\nCHAR 10302\n000000\n000100\n000000\n010100\n000000\n010100\n000000\n000000\n000000\nCHAR 10303\n000000\n010100\n000000\n010100\n000000\n010100\n000000\n000000\n000000\nCHAR 10304\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n010000\n000000\nCHAR 10305\n000000\n010000\n000000\n000000\n000000\n000000\n000000\n010000\n000000\nCHAR 10306\n000000\n000000\n000000\n010000\n000000\n000000\n000000\n010000\n000000\nCHAR 10307\n000000\n010000\n000000\n010000\n000000\n000000\n000000\n010000\n000000\nCHAR 10308\n000000\n000000\n000000\n000000\n000000\n010000\n000000\n010000\n000000\nCHAR 10309\n000000\n010000\n000000\n000000\n000000\n010000\n000000\n010000\n000000\nCHAR 10310\n000000\n000000\n000000\n010000\n000000\n010000\n000000\n010000\n000000\nCHAR 10311\n000000\n010000\n000000\n010000\n000000\n010000\n000000\n010000\n000000\nCHAR 10312\n000000\n000100\n000000\n000000\n000000\n000000\n000000\n010000\n000000\nCHAR 10313\n000000\n010100\n000000\n000000\n000000\n000000\n000000\n010000\n000000\nCHAR 10314\n000000\n000100\n000000\n010000\n000000\n000000\n000000\n010000\n000000\nCHAR 10315\n000000\n010100\n000000\n010000\n000000\n000000\n000000\n010000\n000000\nCHAR 10316\n000000\n000100\n000000\n000000\n000000\n010000\n000000\n010000\n000000\nCHAR 10317\n000000\n010100\n000000\n000000\n000000\n010000\n000000\n010000\n000000\nCHAR 10318\n000000\n000100\n000000\n010000\n000000\n010000\n000000\n010000\n000000\nCHAR 10319\n000000\n010100\n000000\n010000\n000000\n010000\n000000\n010000\n000000\nCHAR 10320\n000000\n000000\n000000\n000100\n000000\n000000\n000000\n010000\n000000\nCHAR 10321\n000000\n010000\n000000\n000100\n000000\n000000\n000000\n010000\n000000\nCHAR 10322\n000000\n000000\n000000\n010100\n000000\n000000\n000000\n010000\n000000\nCHAR 10323\n000000\n010000\n000000\n010100\n000000\n000000\n000000\n010000\n000000\nCHAR 10324\n000000\n000000\n000000\n000100\n000000\n010000\n000000\n010000\n000000\nCHAR 10325\n000000\n010000\n000000\n000100\n000000\n010000\n000000\n010000\n000000\nCHAR 10326\n000000\n000000\n000000\n010100\n000000\n010000\n000000\n010000\n000000\nCHAR 10327\n000000\n010000\n000000\n010100\n000000\n010000\n000000\n010000\n000000\nCHAR 10328\n000000\n000100\n000000\n000100\n000000\n000000\n000000\n010000\n000000\nCHAR 10329\n000000\n010100\n000000\n000100\n000000\n000000\n000000\n010000\n000000\nCHAR 10330\n000000\n000100\n000000\n010100\n000000\n000000\n000000\n010000\n000000\nCHAR 10331\n000000\n010100\n000000\n010100\n000000\n000000\n000000\n010000\n000000\nCHAR 10332\n000000\n000100\n000000\n000100\n000000\n010000\n000000\n010000\n000000\nCHAR 10333\n000000\n010100\n000000\n000100\n000000\n010000\n000000\n010000\n000000\nCHAR 10334\n000000\n000100\n000000\n010100\n000000\n010000\n000000\n010000\n000000\nCHAR 10335\n000000\n010100\n000000\n010100\n000000\n010000\n000000\n010000\n000000\nCHAR 10336\n000000\n000000\n000000\n000000\n000000\n000100\n000000\n010000\n000000\nCHAR 10337\n000000\n010000\n000000\n000000\n000000\n000100\n000000\n010000\n000000\nCHAR 10338\n000000\n000000\n000000\n010000\n000000\n000100\n000000\n010000\n000000\nCHAR 10339\n000000\n010000\n000000\n010000\n000000\n000100\n000000\n010000\n000000\nCHAR 10340\n000000\n000000\n000000\n000000\n000000\n010100\n000000\n010000\n000000\nCHAR 10341\n000000\n010000\n000000\n000000\n000000\n010100\n000000\n010000\n000000\nCHAR 10342\n000000\n000000\n000000\n010000\n000000\n010100\n000000\n010000\n000000\nCHAR 10343\n000000\n010000\n000000\n010000\n000000\n010100\n000000\n010000\n000000\nCHAR 10344\n000000\n000100\n000000\n000000\n000000\n000100\n000000\n010000\n000000\nCHAR 10345\n000000\n010100\n000000\n000000\n000000\n000100\n000000\n010000\n000000\nCHAR 10346\n000000\n000100\n000000\n010000\n000000\n000100\n000000\n010000\n000000\nCHAR 10347\n000000\n010100\n000000\n010000\n000000\n000100\n000000\n010000\n000000\nCHAR 10348\n000000\n000100\n000000\n000000\n000000\n010100\n000000\n010000\n000000\nCHAR 10349\n000000\n010100\n000000\n000000\n000000\n010100\n000000\n010000\n000000\nCHAR 10350\n000000\n000100\n000000\n010000\n000000\n010100\n000000\n010000\n000000\nCHAR 10351\n000000\n010100\n000000\n010000\n000000\n010100\n000000\n010000\n000000\nCHAR 10352\n000000\n000000\n000000\n000100\n000000\n000100\n000000\n010000\n000000\nCHAR 10353\n000000\n010000\n000000\n000100\n000000\n000100\n000000\n010000\n000000\nCHAR 10354\n000000\n000000\n000000\n010100\n000000\n000100\n000000\n010000\n000000\nCHAR 10355\n000000\n010000\n000000\n010100\n000000\n000100\n000000\n010000\n000000\nCHAR 10356\n000000\n000000\n000000\n000100\n000000\n010100\n000000\n010000\n000000\nCHAR 10357\n000000\n010000\n000000\n000100\n000000\n010100\n000000\n010000\n000000\nCHAR 10358\n000000\n000000\n000000\n010100\n000000\n010100\n000000\n010000\n000000\nCHAR 10359\n000000\n010000\n000000\n010100\n000000\n010100\n000000\n010000\n000000\nCHAR 10360\n000000\n000100\n000000\n000100\n000000\n000100\n000000\n010000\n000000\nCHAR 10361\n000000\n010100\n000000\n000100\n000000\n000100\n000000\n010000\n000000\nCHAR 10362\n000000\n000100\n000000\n010100\n000000\n000100\n000000\n010000\n000000\nCHAR 10363\n000000\n010100\n000000\n010100\n000000\n000100\n000000\n010000\n000000\nCHAR 10364\n000000\n000100\n000000\n000100\n000000\n010100\n000000\n010000\n000000\nCHAR 10365\n000000\n010100\n000000\n000100\n000000\n010100\n000000\n010000\n000000\nCHAR 10366\n000000\n000100\n000000\n010100\n000000\n010100\n000000\n010000\n000000\nCHAR 10367\n000000\n010100\n000000\n010100\n000000\n010100\n000000\n010000\n000000\nCHAR 10368\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n000100\n000000\nCHAR 10369\n000000\n010000\n000000\n000000\n000000\n000000\n000000\n000100\n000000\nCHAR 10370\n000000\n000000\n000000\n010000\n000000\n000000\n000000\n000100\n000000\nCHAR 10371\n000000\n010000\n000000\n010000\n000000\n000000\n000000\n000100\n000000\nCHAR 10372\n000000\n000000\n000000\n000000\n000000\n010000\n000000\n000100\n000000\nCHAR 10373\n000000\n010000\n000000\n000000\n000000\n010000\n000000\n000100\n000000\nCHAR 10374\n000000\n000000\n000000\n010000\n000000\n010000\n000000\n000100\n000000\nCHAR 10375\n000000\n010000\n000000\n010000\n000000\n010000\n000000\n000100\n000000\nCHAR 10376\n000000\n000100\n000000\n000000\n000000\n000000\n000000\n000100\n000000\nCHAR 10377\n000000\n010100\n000000\n000000\n000000\n000000\n000000\n000100\n000000\nCHAR 10378\n000000\n000100\n000000\n010000\n000000\n000000\n000000\n000100\n000000\nCHAR 10379\n000000\n010100\n000000\n010000\n000000\n000000\n000000\n000100\n000000\nCHAR 10380\n000000\n000100\n000000\n000000\n000000\n010000\n000000\n000100\n000000\nCHAR 10381\n000000\n010100\n000000\n000000\n000000\n010000\n000000\n000100\n000000\nCHAR 10382\n000000\n000100\n000000\n010000\n000000\n010000\n000000\n000100\n000000\nCHAR 10383\n000000\n010100\n000000\n010000\n000000\n010000\n000000\n000100\n000000\nCHAR 10384\n000000\n000000\n000000\n000100\n000000\n000000\n000000\n000100\n000000\nCHAR 10385\n000000\n010000\n000000\n000100\n000000\n000000\n000000\n000100\n000000\nCHAR 10386\n000000\n000000\n000000\n010100\n000000\n000000\n000000\n000100\n000000\nCHAR 10387\n000000\n010000\n000000\n010100\n000000\n000000\n000000\n000100\n000000\nCHAR 10388\n000000\n000000\n000000\n000100\n000000\n010000\n000000\n000100\n000000\nCHAR 10389\n000000\n010000\n000000\n000100\n000000\n010000\n000000\n000100\n000000\nCHAR 10390\n000000\n000000\n000000\n010100\n000000\n010000\n000000\n000100\n000000\nCHAR 10391\n000000\n010000\n000000\n010100\n000000\n010000\n000000\n000100\n000000\nCHAR 10392\n000000\n000100\n000000\n000100\n000000\n000000\n000000\n000100\n000000\nCHAR 10393\n000000\n010100\n000000\n000100\n000000\n000000\n000000\n000100\n000000\nCHAR 10394\n000000\n000100\n000000\n010100\n000000\n000000\n000000\n000100\n000000\nCHAR 10395\n000000\n010100\n000000\n010100\n000000\n000000\n000000\n000100\n000000\nCHAR 10396\n000000\n000100\n000000\n000100\n000000\n010000\n000000\n000100\n000000\nCHAR 10397\n000000\n010100\n000000\n000100\n000000\n010000\n000000\n000100\n000000\nCHAR 10398\n000000\n000100\n000000\n010100\n000000\n010000\n000000\n000100\n000000\nCHAR 10399\n000000\n010100\n000000\n010100\n000000\n010000\n000000\n000100\n000000\nCHAR 10400\n000000\n000000\n000000\n000000\n000000\n000100\n000000\n000100\n000000\nCHAR 10401\n000000\n010000\n000000\n000000\n000000\n000100\n000000\n000100\n000000\nCHAR 10402\n000000\n000000\n000000\n010000\n000000\n000100\n000000\n000100\n000000\nCHAR 10403\n000000\n010000\n000000\n010000\n000000\n000100\n000000\n000100\n000000\nCHAR 10404\n000000\n000000\n000000\n000000\n000000\n010100\n000000\n000100\n000000\nCHAR 10405\n000000\n010000\n000000\n000000\n000000\n010100\n000000\n000100\n000000\nCHAR 10406\n000000\n000000\n000000\n010000\n000000\n010100\n000000\n000100\n000000\nCHAR 10407\n000000\n010000\n000000\n010000\n000000\n010100\n000000\n000100\n000000\nCHAR 10408\n000000\n000100\n000000\n000000\n000000\n000100\n000000\n000100\n000000\nCHAR 10409\n000000\n010100\n000000\n000000\n000000\n000100\n000000\n000100\n000000\nCHAR 10410\n000000\n000100\n000000\n010000\n000000\n000100\n000000\n000100\n000000\nCHAR 10411\n000000\n010100\n000000\n010000\n000000\n000100\n000000\n000100\n000000\nCHAR 10412\n000000\n000100\n000000\n000000\n000000\n010100\n000000\n000100\n000000\nCHAR 10413\n000000\n010100\n000000\n000000\n000000\n010100\n000000\n000100\n000000\nCHAR 10414\n000000\n000100\n000000\n010000\n000000\n010100\n000000\n000100\n000000\nCHAR 10415\n000000\n010100\n000000\n010000\n000000\n010100\n000000\n000100\n000000\nCHAR 10416\n000000\n000000\n000000\n000100\n000000\n000100\n000000\n000100\n000000\nCHAR 10417\n000000\n010000\n000000\n000100\n000000\n000100\n000000\n000100\n000000\nCHAR 10418\n000000\n000000\n000000\n010100\n000000\n000100\n000000\n000100\n000000\nCHAR 10419\n000000\n010000\n000000\n010100\n000000\n000100\n000000\n000100\n000000\nCHAR 10420\n000000\n000000\n000000\n000100\n000000\n010100\n000000\n000100\n000000\nCHAR 10421\n000000\n010000\n000000\n000100\n000000\n010100\n000000\n000100\n000000\nCHAR 10422\n000000\n000000\n000000\n010100\n000000\n010100\n000000\n000100\n000000\nCHAR 10423\n000000\n010000\n000000\n010100\n000000\n010100\n000000\n000100\n000000\nCHAR 10424\n000000\n000100\n000000\n000100\n000000\n000100\n000000\n000100\n000000\nCHAR 10425\n000000\n010100\n000000\n000100\n000000\n000100\n000000\n000100\n000000\nCHAR 10426\n000000\n000100\n000000\n010100\n000000\n000100\n000000\n000100\n000000\nCHAR 10427\n000000\n010100\n000000\n010100\n000000\n000100\n000000\n000100\n000000\nCHAR 10428\n000000\n000100\n000000\n000100\n000000\n010100\n000000\n000100\n000000\nCHAR 10429\n000000\n010100\n000000\n000100\n000000\n010100\n000000\n000100\n000000\nCHAR 10430\n000000\n000100\n000000\n010100\n000000\n010100\n000000\n000100\n000000\nCHAR 10431\n000000\n010100\n000000\n010100\n000000\n010100\n000000\n000100\n000000\nCHAR 10432\n000000\n000000\n000000\n000000\n000000\n000000\n000000\n010100\n000000\nCHAR 10433\n000000\n010000\n000000\n000000\n000000\n000000\n000000\n010100\n000000\nCHAR 10434\n000000\n000000\n000000\n010000\n000000\n000000\n000000\n010100\n000000\nCHAR 10435\n000000\n010000\n000000\n010000\n000000\n000000\n000000\n010100\n000000\nCHAR 10436\n000000\n000000\n000000\n000000\n000000\n010000\n000000\n010100\n000000\nCHAR 10437\n000000\n010000\n000000\n000000\n000000\n010000\n000000\n010100\n000000\nCHAR 10438\n000000\n000000\n000000\n010000\n000000\n010000\n000000\n010100\n000000\nCHAR 10439\n000000\n010000\n000000\n010000\n000000\n010000\n000000\n010100\n000000\nCHAR 10440\n000000\n000100\n000000\n000000\n000000\n000000\n000000\n010100\n000000\nCHAR 10441\n000000\n010100\n000000\n000000\n000000\n000000\n000000\n010100\n000000\nCHAR 10442\n000000\n000100\n000000\n010000\n000000\n000000\n000000\n010100\n000000\nCHAR 10443\n000000\n010100\n000000\n010000\n000000\n000000\n000000\n010100\n000000\nCHAR 10444\n000000\n000100\n000000\n000000\n000000\n010000\n000000\n010100\n000000\nCHAR 10445\n000000\n010100\n000000\n000000\n000000\n010000\n000000\n010100\n000000\nCHAR 10446\n000000\n000100\n000000\n010000\n000000\n010000\n000000\n010100\n000000\nCHAR 10447\n000000\n010100\n000000\n010000\n000000\n010000\n000000\n010100\n000000\nCHAR 10448\n000000\n000000\n000000\n000100\n000000\n000000\n000000\n010100\n000000\nCHAR 10449\n000000\n010000\n000000\n000100\n000000\n000000\n000000\n010100\n000000\nCHAR 10450\n000000\n000000\n000000\n010100\n000000\n000000\n000000\n010100\n000000\nCHAR 10451\n000000\n010000\n000000\n010100\n000000\n000000\n000000\n010100\n000000\nCHAR 10452\n000000\n000000\n000000\n000100\n000000\n010000\n000000\n010100\n000000\nCHAR 10453\n000000\n010000\n000000\n000100\n000000\n010000\n000000\n010100\n000000\nCHAR 10454\n000000\n000000\n000000\n010100\n000000\n010000\n000000\n010100\n000000\nCHAR 10455\n000000\n010000\n000000\n010100\n000000\n010000\n000000\n010100\n000000\nCHAR 10456\n000000\n000100\n000000\n000100\n000000\n000000\n000000\n010100\n000000\nCHAR 10457\n000000\n010100\n000000\n000100\n000000\n000000\n000000\n010100\n000000\nCHAR 10458\n000000\n000100\n000000\n010100\n000000\n000000\n000000\n010100\n000000\nCHAR 10459\n000000\n010100\n000000\n010100\n000000\n000000\n000000\n010100\n000000\nCHAR 10460\n000000\n000100\n000000\n000100\n000000\n010000\n000000\n010100\n000000\nCHAR 10461\n000000\n010100\n000000\n000100\n000000\n010000\n000000\n010100\n000000\nCHAR 10462\n000000\n000100\n000000\n010100\n000000\n010000\n000000\n010100\n000000\nCHAR 10463\n000000\n010100\n000000\n010100\n000000\n010000\n000000\n010100\n000000\nCHAR 10464\n000000\n000000\n000000\n000000\n000000\n000100\n000000\n010100\n000000\nCHAR 10465\n000000\n010000\n000000\n000000\n000000\n000100\n000000\n010100\n000000\nCHAR 10466\n000000\n000000\n000000\n010000\n000000\n000100\n000000\n010100\n000000\nCHAR 10467\n000000\n010000\n000000\n010000\n000000\n000100\n000000\n010100\n000000\nCHAR 10468\n000000\n000000\n000000\n000000\n000000\n010100\n000000\n010100\n000000\nCHAR 10469\n000000\n010000\n000000\n000000\n000000\n010100\n000000\n010100\n000000\nCHAR 10470\n000000\n000000\n000000\n010000\n000000\n010100\n000000\n010100\n000000\nCHAR 10471\n000000\n010000\n000000\n010000\n000000\n010100\n000000\n010100\n000000\nCHAR 10472\n000000\n000100\n000000\n000000\n000000\n000100\n000000\n010100\n000000\nCHAR 10473\n000000\n010100\n000000\n000000\n000000\n000100\n000000\n010100\n000000\nCHAR 10474\n000000\n000100\n000000\n010000\n000000\n000100\n000000\n010100\n000000\nCHAR 10475\n000000\n010100\n000000\n010000\n000000\n000100\n000000\n010100\n000000\nCHAR 10476\n000000\n000100\n000000\n000000\n000000\n010100\n000000\n010100\n000000\nCHAR 10477\n000000\n010100\n000000\n000000\n000000\n010100\n000000\n010100\n000000\nCHAR 10478\n000000\n000100\n000000\n010000\n000000\n010100\n000000\n010100\n000000\nCHAR 10479\n000000\n010100\n000000\n010000\n000000\n010100\n000000\n010100\n000000\nCHAR 10480\n000000\n000000\n000000\n000100\n000000\n000100\n000000\n010100\n000000\nCHAR 10481\n000000\n010000\n000000\n000100\n000000\n000100\n000000\n010100\n000000\nCHAR 10482\n000000\n000000\n000000\n010100\n000000\n000100\n000000\n010100\n000000\nCHAR 10483\n000000\n010000\n000000\n010100\n000000\n000100\n000000\n010100\n000000\nCHAR 10484\n000000\n000000\n000000\n000100\n000000\n010100\n000000\n010100\n000000\nCHAR 10485\n000000\n010000\n000000\n000100\n000000\n010100\n000000\n010100\n000000\nCHAR 10486\n000000\n000000\n000000\n010100\n000000\n010100\n000000\n010100\n000000\nCHAR 10487\n000000\n010000\n000000\n010100\n000000\n010100\n000000\n010100\n000000\nCHAR 10488\n000000\n000100\n000000\n000100\n000000\n000100\n000000\n010100\n000000\nCHAR 10489\n000000\n010100\n000000\n000100\n000000\n000100\n000000\n010100\n000000\nCHAR 10490\n000000\n000100\n000000\n010100\n000000\n000100\n000000\n010100\n000000\nCHAR 10491\n000000\n010100\n000000\n010100\n000000\n000100\n000000\n010100\n000000\nCHAR 10492\n000000\n000100\n000000\n000100\n000000\n010100\n000000\n010100\n000000\nCHAR 10493\n000000\n010100\n000000\n000100\n000000\n010100\n000000\n010100\n000000\nCHAR 10494\n000000\n000100\n000000\n010100\n000000\n010100\n000000\n010100\n000000\nCHAR 10495\n000000\n010100\n000000\n010100\n000000\n010100\n000000\n010100\n000000\nCHAR 64257\n000000\n000100\n001010\n001000\n011110\n001010\n001010\n000000\n000000\nCHAR 64258\n000000\n000110\n001010\n001010\n011110\n001010\n001010\n000000\n000000\nCHAR 65533\n011100\n110110\n101010\n111010\n110110\n110110\n111110\n110110\n011100",
@@ -12,14 +12,14 @@ var Resources = {
"input.js": "function InputSystem() {\n\tvar self = this;\n\n\tthis.Key = {\n\t\tLEFT: 37,\n\t\tRIGHT: 39,\n\t\tUP: 38,\n\t\tDOWN: 40,\n\t\tSPACE: 32,\n\t\tENTER: 13,\n\t\tW: 87,\n\t\tA: 65,\n\t\tS: 83,\n\t\tD: 68,\n\t\tR: 82,\n\t\tSHIFT: 16,\n\t\tCTRL: 17,\n\t\tALT: 18,\n\t\tCMD: 224\n\t};\n\n\tvar pressed;\n\tvar ignored;\n\tvar touchState;\n\n\tvar isRestartComboPressed = false;\n\n\tvar SwipeDir = {\n\t\tNone : -1,\n\t\tUp : 0,\n\t\tDown : 1,\n\t\tLeft : 2,\n\t\tRight : 3,\n\t};\n\n\tfunction resetAll() {\n\t\tisRestartComboPressed = false;\n\n\t\tpressed = {};\n\t\tignored = {};\n\n\t\ttouchState = {\n\t\t\tisDown : false,\n\t\t\tstartX : 0,\n\t\t\tstartY : 0,\n\t\t\tcurX : 0,\n\t\t\tcurY : 0,\n\t\t\tswipeDistance : 30,\n\t\t\tswipeDirection : SwipeDir.None,\n\t\t\ttapReleased : false\n\t\t};\n\t}\n\n\tresetAll();\n\n\tfunction stopWindowScrolling(e) {\n\t\tif (e.keyCode == self.Key.LEFT || e.keyCode == self.Key.RIGHT || e.keyCode == self.Key.UP || e.keyCode == self.Key.DOWN || !isPlayerEmbeddedInEditor) {\n\t\t\te.preventDefault();\n\t\t}\n\t}\n\n\tfunction isRestartCombo(e) {\n\t\treturn (e.keyCode === self.Key.R && (e.getModifierState(\"Control\")|| e.getModifierState(\"Meta\")));\n\t}\n\n\tfunction eventIsModifier(event) {\n\t\treturn (event.keyCode == self.Key.SHIFT || event.keyCode == self.Key.CTRL || event.keyCode == self.Key.ALT || event.keyCode == self.Key.CMD);\n\t}\n\n\tfunction isModifierKeyDown() {\n\t\treturn (self.isKeyDown(self.Key.SHIFT) || self.isKeyDown(self.Key.CTRL) || self.isKeyDown(self.Key.ALT) || self.isKeyDown(self.Key.CMD));\n\t}\n\n\tthis.ignoreHeldKeys = function() {\n\t\tfor (var key in pressed) {\n\t\t\tif (pressed[key]) { // only ignore keys that are actually held\n\t\t\t\tignored[key] = true;\n\t\t\t\t// bitsyLog(\"IGNORE -- \" + key, \"system\");\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.onkeydown = function(event) {\n\t\tenableGlobalAudioContext();\n\t\t// bitsyLog(\"KEYDOWN -- \" + event.keyCode, \"system\");\n\n\t\tstopWindowScrolling(event);\n\n\t\tisRestartComboPressed = isRestartCombo(event);\n\n\t\t// Special keys being held down can interfere with keyup events and lock movement\n\t\t// so just don't collect input when they're held\n\t\t{\n\t\t\tif (isModifierKeyDown()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (eventIsModifier(event)) {\n\t\t\t\tresetAll();\n\t\t\t}\n\t\t}\n\n\t\tif (ignored[event.keyCode]) {\n\t\t\treturn;\n\t\t}\n\n\t\tpressed[event.keyCode] = true;\n\t\tignored[event.keyCode] = false;\n\t}\n\n\tthis.onkeyup = function(event) {\n\t\t// bitsyLog(\"KEYUP -- \" + event.keyCode, \"system\");\n\t\tpressed[event.keyCode] = false;\n\t\tignored[event.keyCode] = false;\n\t}\n\n\tthis.ontouchstart = function(event) {\n\t\tenableGlobalAudioContext();\n\n\t\tevent.preventDefault();\n\n\t\tif( event.changedTouches.length > 0 ) {\n\t\t\ttouchState.isDown = true;\n\n\t\t\ttouchState.startX = touchState.curX = event.changedTouches[0].clientX;\n\t\t\ttouchState.startY = touchState.curY = event.changedTouches[0].clientY;\n\n\t\t\ttouchState.swipeDirection = SwipeDir.None;\n\t\t}\n\t}\n\n\tthis.ontouchmove = function(event) {\n\t\tevent.preventDefault();\n\n\t\tif( touchState.isDown && event.changedTouches.length > 0 ) {\n\t\t\ttouchState.curX = event.changedTouches[0].clientX;\n\t\t\ttouchState.curY = event.changedTouches[0].clientY;\n\n\t\t\tvar prevDirection = touchState.swipeDirection;\n\n\t\t\tif( touchState.curX - touchState.startX <= -touchState.swipeDistance ) {\n\t\t\t\ttouchState.swipeDirection = SwipeDir.Left;\n\t\t\t}\n\t\t\telse if( touchState.curX - touchState.startX >= touchState.swipeDistance ) {\n\t\t\t\ttouchState.swipeDirection = SwipeDir.Right;\n\t\t\t}\n\t\t\telse if( touchState.curY - touchState.startY <= -touchState.swipeDistance ) {\n\t\t\t\ttouchState.swipeDirection = SwipeDir.Up;\n\t\t\t}\n\t\t\telse if( touchState.curY - touchState.startY >= touchState.swipeDistance ) {\n\t\t\t\ttouchState.swipeDirection = SwipeDir.Down;\n\t\t\t}\n\n\t\t\tif( touchState.swipeDirection != prevDirection ) {\n\t\t\t\t// reset center so changing directions is easier\n\t\t\t\ttouchState.startX = touchState.curX;\n\t\t\t\ttouchState.startY = touchState.curY;\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.ontouchend = function(event) {\n\t\tevent.preventDefault();\n\n\t\ttouchState.isDown = false;\n\n\t\tif( touchState.swipeDirection == SwipeDir.None ) {\n\t\t\t// tap!\n\t\t\ttouchState.tapReleased = true;\n\t\t}\n\n\t\ttouchState.swipeDirection = SwipeDir.None;\n\t}\n\n\tthis.isKeyDown = function(keyCode) {\n\t\treturn pressed[keyCode] != null && pressed[keyCode] == true && (ignored[keyCode] == null || ignored[keyCode] == false);\n\t}\n\n\tthis.anyKeyDown = function() {\n\t\tvar anyKey = false;\n\n\t\tfor (var key in pressed) {\n\t\t\tif (pressed[key] && (ignored[key] == null || ignored[key] == false) &&\n\t\t\t\t!(key === self.Key.UP || key === self.Key.DOWN || key === self.Key.LEFT || key === self.Key.RIGHT) &&\n\t\t\t\t!(key === self.Key.W || key === self.Key.S || key === self.Key.A || key === self.Key.D)) {\n\t\t\t\t// detected that a key other than the d-pad keys are down!\n\t\t\t\tanyKey = true;\n\t\t\t}\n\t\t}\n\n\t\treturn anyKey;\n\t}\n\n\tthis.isRestartComboPressed = function() {\n\t\treturn isRestartComboPressed;\n\t}\n\n\tthis.swipeLeft = function() {\n\t\treturn touchState.swipeDirection == SwipeDir.Left;\n\t}\n\n\tthis.swipeRight = function() {\n\t\treturn touchState.swipeDirection == SwipeDir.Right;\n\t}\n\n\tthis.swipeUp = function() {\n\t\treturn touchState.swipeDirection == SwipeDir.Up;\n\t}\n\n\tthis.swipeDown = function() {\n\t\treturn touchState.swipeDirection == SwipeDir.Down;\n\t}\n\n\tthis.isTapReleased = function() {\n\t\treturn touchState.tapReleased;\n\t}\n\n\tthis.resetTapReleased = function() {\n\t\ttouchState.tapReleased = false;\n\t}\n\n\tthis.onblur = function() {\n\t\t// bitsyLog(\"~~~ BLUR ~~\", \"system\");\n\t\tresetAll();\n\t}\n\n\tthis.resetAll = resetAll;\n\n\tthis.listen = function(canvas) {\n\t\tdocument.addEventListener('keydown', self.onkeydown);\n\t\tdocument.addEventListener('keyup', self.onkeyup);\n\n\t\tif (isPlayerEmbeddedInEditor) {\n\t\t\tcanvas.addEventListener('touchstart', self.ontouchstart, {passive:false});\n\t\t\tcanvas.addEventListener('touchmove', self.ontouchmove, {passive:false});\n\t\t\tcanvas.addEventListener('touchend', self.ontouchend, {passive:false});\n\t\t}\n\t\telse {\n\t\t\t// creates a 'touchTrigger' element that covers the entire screen and can universally have touch event listeners added w/o issue.\n\n\t\t\t// we're checking for existing touchTriggers both at game start and end, so it's slightly redundant.\n\t\t\tvar existingTouchTrigger = document.querySelector('#touchTrigger');\n\n\t\t\tif (existingTouchTrigger === null) {\n\t\t\t\tvar touchTrigger = document.createElement(\"div\");\n\t\t\t\ttouchTrigger.setAttribute(\"id\",\"touchTrigger\");\n\n\t\t\t\t// afaik css in js is necessary here to force a fullscreen element\n\t\t\t\ttouchTrigger.setAttribute(\n\t\t\t\t\t\"style\",\"position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; overflow: hidden;\"\n\t\t\t\t);\n\n\t\t\t\tdocument.body.appendChild(touchTrigger);\n\n\t\t\t\ttouchTrigger.addEventListener('touchstart', self.ontouchstart);\n\t\t\t\ttouchTrigger.addEventListener('touchmove', self.ontouchmove);\n\t\t\t\ttouchTrigger.addEventListener('touchend', self.ontouchend);\n\t\t\t}\n\t\t}\n\n\t\twindow.onblur = self.onblur;\n\t}\n\n\tthis.unlisten = function(canvas) {\n\t\tdocument.removeEventListener('keydown', self.onkeydown);\n\t\tdocument.removeEventListener('keyup', self.onkeyup);\n\n\t\tif (isPlayerEmbeddedInEditor) {\n\t\t\tcanvas.removeEventListener('touchstart', self.ontouchstart);\n\t\t\tcanvas.removeEventListener('touchmove', self.ontouchmove);\n\t\t\tcanvas.removeEventListener('touchend', self.ontouchend);\n\t\t}\n\t\telse {\n\t\t\t//check for touchTrigger and removes it\n\n\t\t\tvar existingTouchTrigger = document.querySelector('#touchTrigger');\n\n\t\t\tif (existingTouchTrigger !== null) {\n\t\t\t\texistingTouchTrigger.removeEventListener('touchstart', self.ontouchstart);\n\t\t\t\texistingTouchTrigger.removeEventListener('touchmove', self.ontouchmove);\n\t\t\t\texistingTouchTrigger.removeEventListener('touchend', self.ontouchend);\n\n\t\t\t\texistingTouchTrigger.parentElement.removeChild(existingTouchTrigger);\n\t\t\t}\n\t\t}\n\n\t\twindow.onblur = null;\n\t}\n}",
"soundchip.js": "// init global audio context\nvar audioContext = new AudioContext();\n\nfunction enableGlobalAudioContext() {\n\taudioContext.resume();\n}\n\nfunction SoundSystem() {\n\tvar self = this;\n\n\t// volume\n\tvar maxGain = 0.15;\n\n\t// curves for different pulse wave duties (ratios between on and off)\n\tvar dutyCycle_1_8 = new Float32Array(256);\n\tfor (var i = 0; i < 256; i++) {\n\t\tdutyCycle_1_8[i] = ((i / 256) * 2) - 1.75;\n\t}\n\n\tvar dutyCycle_1_4 = new Float32Array(256);\n\tfor (var i = 0; i < 256; i++) {\n\t\tdutyCycle_1_4[i] = ((i / 256) * 2) - 1.5;\n\t}\n\n\tvar dutyCycle_1_2 = new Float32Array(256);\n\tfor (var i = 0; i < 256; i++) {\n\t\tdutyCycle_1_2[i] = ((i / 256) * 2) - 1.0;\n\t}\n\n\tvar dutyCycles = [\n\t\tdutyCycle_1_8,\n\t\tdutyCycle_1_4,\n\t\tdutyCycle_1_2 // square wave\n\t];\n\n\tfunction createPulseWidthModulator() {\n\t\t// the base oscillator: start with a sawtooth wave that we'll shape into a pulse wave\n\t\tvar oscillator = audioContext.createOscillator();\n\t\toscillator.type = \"sawtooth\";\n\n\t\t// create a gain node to control the volume of the sound\n\t\tvar volumeControl = audioContext.createGain();\n\t\tvolumeControl.gain.value = 0;\n\n\t\t// create a wave shaper that turns the sawtooth wave into a pulse\n\t\t// by mapping any negative value to -1 and any positive value to 1\n\t\tvar pulseCurve = new Float32Array(256);\n\t\tfor (var i = 0; i < 128; i++) {\n\t\t\tpulseCurve[i] = -1;\n\t\t}\n\t\tfor (var i = 128; i < 256; i++) {\n\t\t\tpulseCurve[i] = 1;\n\t\t}\n\n\t\tvar pulseShaper = audioContext.createWaveShaper();\n\t\tpulseShaper.curve = pulseCurve;\n\n\t\tvar dutyShaper = audioContext.createWaveShaper();\n\t\tdutyShaper.curve = dutyCycle_1_2;\n\n\t\toscillator.connect(dutyShaper);\n\t\tdutyShaper.connect(pulseShaper);\n\t\tpulseShaper.connect(volumeControl);\n\t\tvolumeControl.connect(audioContext.destination);\n\t\toscillator.start();\n\n\t\treturn {\n\t\t\toscillator: oscillator,\n\t\t\tvolumeControl: volumeControl,\n\t\t\tdutyShaper: dutyShaper\n\t\t};\n\t}\n\n\tvar pulseChannels = [createPulseWidthModulator(), createPulseWidthModulator()];\n\n\tthis.setPulse = function(channel, pulse) {\n\t\tvar pulseChannel = pulseChannels[channel];\n\t\tpulseChannel.dutyShaper.curve = dutyCycles[pulse];\n\t}\n\n\tthis.setFrequency = function(channel, frequencyHz) {\n\t\tvar pulseChannel = pulseChannels[channel];\n\t\t// set frequency in hertz\n\t\tpulseChannel.oscillator.frequency.setValueAtTime(frequencyHz, audioContext.currentTime);\n\t}\n\n\tthis.setVolume = function(channel, volumeNorm) {\n\t\tvar pulseChannel = pulseChannels[channel];\n\t\tpulseChannel.volumeControl.gain.value = volumeNorm * maxGain;\n\t}\n\n\tthis.mute = function() {\n\t\tfor (var i = 0; i < pulseChannels.length; i++) {\n\t\t\tpulseChannels[i].volumeControl.gain.value = 0;\n\t\t}\n\t}\n}\n\nvar sound = new SoundSystem();",
"graphics.js": "function GraphicsSystem() {\n\tvar self = this;\n\n\tvar canvas;\n\tvar ctx;\n\n\tvar scale;\n\tvar textScale;\n\tvar palette = [];\n\tvar images = [];\n\tvar imageFillColors = [];\n\n\tfunction makeFillStyle(color, isTransparent) {\n\t\tvar i = color * 3;\n\t\tif (isTransparent) {\n\t\t\treturn \"rgba(\" + palette[i + 0] + \",\" + palette[i + 1] + \",\" + palette[i + 2] + \", 0)\";\n\t\t}\n\t\telse {\n\t\t\treturn \"rgb(\" + palette[i + 0] + \",\" + palette[i + 1] + \",\" + palette[i + 2] + \")\";\n\t\t}\n\t}\n\n\tthis._images = images;\n\tthis._getPalette = function() {\n\t\treturn palette;\n\t};\n\n\t// todo : do I really need to pass in size here?\n\tthis.attachCanvas = function(c, size) {\n\t\tcanvas = c;\n\t\tcanvas.width = size * scale;\n\t\tcanvas.height = size * scale;\n\t\tctx = canvas.getContext(\"2d\");\n\t};\n\n\tthis.getCanvas = function() {\n\t\treturn canvas;\n\t};\n\n\tthis.getContext = function() {\n\t\treturn ctx;\n\t};\n\n\tthis.setScale = function(s) {\n\t\tscale = s;\n\t};\n\n\tthis.setTextScale = function(s) {\n\t\ttextScale = s;\n\t};\n\n\tthis.getTextScale = function() {\n\t\treturn textScale;\n\t};\n\n\tthis.setPalette = function(p) {\n\t\tpalette = p;\n\t};\n\n\t// todo : rename this since it doesn't always create a totally new canvas?\n\tthis.createImage = function(id, width, height, pixels, useTextScale) {\n\t\tvar imageScale = useTextScale === true ? textScale : scale;\n\t\tvar widthScaled = width * imageScale;\n\t\tvar heightScaled = height * imageScale;\n\n\t\t// try to use an existing image canvas if it is the right size,\n\t\t// instead of expensively creating a new one\n\t\tvar imageCanvas = images[id];\n\t\tif (imageCanvas === undefined || imageCanvas.width != widthScaled || imageCanvas.height != heightScaled) {\n\t\t\timageCanvas = document.createElement(\"canvas\");\n\t\t\timageCanvas.width = widthScaled;\n\t\t\timageCanvas.height = heightScaled;\n\t\t}\n\n\t\tvar imageCtx = imageCanvas.getContext(\"2d\");\n\n\t\t// if we know the fill color for this image, we can speed things up\n\t\t// by filling the whole image with that color\n\t\tvar fillColor;\n\t\tif (imageFillColors[id] != undefined) {\n\t\t\tfillColor = imageFillColors[id];\n\t\t\tvar isTransparent = (fillColor === 0);\n\t\t\tif (isTransparent) {\n\t\t\t\timageCtx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);\n\t\t\t}\n\t\t\telse {\n\t\t\t\timageCtx.fillStyle = makeFillStyle(fillColor, isTransparent);\n\t\t\t\timageCtx.fillRect(0, 0, imageCanvas.width, imageCanvas.height);\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < pixels.length; i++) {\n\t\t\tvar x = i % width;\n\t\t\tvar y = Math.floor(i / width);\n\t\t\tvar color = pixels[i];\n\t\t\tif (color != fillColor) {\n\t\t\t\tvar isTransparent = (color === 0);\n\t\t\t\timageCtx.fillStyle = makeFillStyle(color, isTransparent);\n\t\t\t\timageCtx.fillRect(x * imageScale, y * imageScale, imageScale, imageScale);\n\t\t\t}\n\t\t}\n\n\t\timages[id] = imageCanvas;\n\t};\n\n\tthis.setImageFill = function(id, color) {\n\t\timageFillColors[id] = color;\n\t};\n\n\tthis.drawImage = function(id, x, y, destId) {\n\t\tif (!images[id]) {\n\t\t\tbitsyLog(\"image doesn't exist: \" + id, \"graphics\");\n\t\t\treturn;\n\t\t}\n\n\t\tvar destCtx = ctx;\n\t\tif (destId != undefined) {\n\t\t\t// if there's a destination ID, that means we're drawing this image *onto* another image canvas\n\t\t\tvar destCanvas = images[destId];\n\t\t\tdestCtx = destCanvas.getContext(\"2d\");\n\t\t}\n\n\t\tdestCtx.drawImage(images[id], x * scale, y * scale, images[id].width, images[id].height);\n\t};\n\n\tthis.hasImage = function(id) {\n\t\treturn images[id] != undefined;\n\t};\n\n\tthis.getImage = function(id) {\n\t\treturn images[id];\n\t};\n\n\tthis.deleteImage = function(id) {\n\t\tdelete images[id];\n\t\tdelete imageFillColors[id];\n\t};\n\n\tthis.getCanvas = function() {\n\t\treturn canvas;\n\t};\n\n\tthis.clearCanvas = function(color) {\n\t\tbitsyLog(\"pal? \" + palette.length + \" / \" + color, \"graphics\");\n\t\tctx.fillStyle = makeFillStyle(color);\n\t\tctx.fillRect(0, 0, canvas.width, canvas.height);\n\t};\n}",
- "system.js": "/* LOGGING */\nvar DebugLogCategory = {\n\t// system\n\tinput: false,\n\tsound: false,\n\tgraphics: false,\n\tsystem: false,\n\n\t// engine\n\tbitsy: false,\n\n\t// editor\n\teditor: false,\n\n\t// tools\n\troom: false,\n\ttune: false,\n\tblip: false,\n};\n\nvar isLoggingVerbose = false;\n\nfunction bitsyLog(message, category) {\n\tif (!category) {\n\t\tcategory = \"bitsy\";\n\t}\n\n\tvar summary = category + \"::\" + message;\n\n\tif (DebugLogCategory[category] === true) {\n\t\tif (isLoggingVerbose) {\n\t\t\tconsole.group(summary);\n\n\t\t\tconsole.dir(message);\n\n\t\t\tconsole.group(\"stack\")\n\t\t\tconsole.trace();\n\t\t\tconsole.groupEnd();\n\n\t\t\tconsole.groupEnd();\n\t\t}\n\t\telse {\n\t\t\tconsole.log(summary);\n\t\t}\n\t}\n}\n\n/* GLOBALS */\nvar tilesize = 8;\nvar mapsize = 16;\nvar width = mapsize * tilesize;\nvar height = mapsize * tilesize;\nvar scale = 4;\nvar textScale = 2;\n\n/* SYSTEM */\nvar updateInterval = null;\nvar prevTime = 0;\nvar deltaTime = 0;\n\nfunction initSystem() {\n\tprevTime = Date.now();\n\tupdateInterval = setInterval(updateSystem, 16);\n}\n\nfunction updateSystem() {\n\tvar curTime = Date.now();\n\tdeltaTime = curTime - prevTime;\n\n\t// update all active processes\n\tfor (var i = 0; i < processes.length; i++) {\n\t\tbitsy = processes[i].system;\n\t\tif (bitsy._active) {\n\t\t\tbitsyLog(bitsy._name + \" img count: \" + bitsy._graphics._images.length, \"system\");\n\t\t\tvar shouldContinue = bitsy._update(deltaTime);\n\t\t\tif (!shouldContinue) {\n\t\t\t\t// todo : do I really care about this _exit thing?\n\t\t\t\tif (bitsy._name != \"bitsy\") {\n\t\t\t\t\tbitsy._exit();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tbitsy = mainProcess.system;\n\tprevTime = curTime;\n}\n\nfunction loadGame(canvas, gameData, defaultFontData) {\n\tbitsyLog(\"load!\", \"system\");\n\t// initialize bitsy system\n\tbitsy._attachCanvas(canvas);\n\tbitsy._write(bitsy._gameDataBlock, gameData);\n\tbitsy._write(bitsy._fontDataBlock, defaultFontData);\n\tbitsy._start();\n}\n\nfunction quitGame() {\n\t// hack to press the menu button to force game over state\n\tbitsy._injectPreLoop = function() { bitsy._poke(bitsy._buttonBlock, bitsy.BTN_MENU, 1); };\n\n\t// one last update to clean up (a little hacky to do this here?)\n\tbitsy._update(0);\n\tbitsy._exit();\n\n\t// clean up this gross hack\n\tbitsy._injectPreLoop = null;\n}\n\n/* GRAPHICS */\nvar canvas; // can I get rid of these?\nvar ctx;\n\nfunction attachCanvas(c) {\n\t// hack : tes tnew system\n\tbitsy._attachCanvas(c);\n\t// extra hacky\n\tcanvas = bitsy._getCanvas();\n\tctx = bitsy._getContext();\n}\n\n/* PROCESSES */\nvar processes = [];\n\nfunction addProcess(name) {\n\tvar proc = {};\n\tproc.system = new BitsySystem(name);\n\n\tprocesses.push(proc);\n\n\treturn proc;\n}\n\n/* == SYSTEM v0.2 === */\nfunction BitsySystem(name) {\n\tvar self = this;\n\n\tif (!name) {\n\t\tname = \"bitsy\";\n\t}\n\n\t// memory\n\tvar memory = {\n\t\tblocks: [],\n\t\tchanged: []\n\t};\n\n\t// input\n\tvar input = new InputSystem();\n\n\t// sound\n\tvar sound = new SoundSystem();\n\tvar soundDurationIndex = 0;\n\tvar soundFrequencyIndex = 1;\n\tvar soundVolumeIndex = 2;\n\tvar soundPulseIndex = 3;\n\tvar maxVolume = 15;\n\n\t// graphics\n\tvar graphics = new GraphicsSystem();\n\tgraphics.setScale(scale);\n\tgraphics.setTextScale(textScale);\n\tvar initialPaletteSize = 64;\n\tvar tilePoolStart = null;\n\tvar tilePoolSize = 512;\n\t// hack!!! (access for debugging)\n\tthis._graphics = graphics;\n\n\tfunction updateTextScale() {\n\t\t// make sure the text scale matches the text mode\n\t\tvar textMode = self._peek(modeBlock, 1);\n\t\tvar textModeScale = (textMode === self.TXT_LOREZ) ? scale : textScale;\n\t\tif (graphics.getTextScale() != textModeScale) {\n\t\t\tgraphics.setTextScale(textModeScale);\n\t\t\tmemory.changed[self.TEXTBOX] = true;\n\t\t}\n\t}\n\n\tfunction updateInput() {\n\t\t// update input flags\n\t\tself._poke(self._buttonBlock, self.BTN_UP,\n\t\t\t(input.isKeyDown(input.Key.UP) || input.isKeyDown(input.Key.W) || input.swipeUp()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_DOWN,\n\t\t\t(input.isKeyDown(input.Key.DOWN) || input.isKeyDown(input.Key.S) || input.swipeDown()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_LEFT,\n\t\t\t(input.isKeyDown(input.Key.LEFT) || input.isKeyDown(input.Key.A) || input.swipeLeft()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_RIGHT,\n\t\t\t(input.isKeyDown(input.Key.RIGHT) || input.isKeyDown(input.Key.D) || input.swipeRight()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_OK,\n\t\t\t(input.anyKeyDown() || input.isTapReleased()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_MENU,\n\t\t\t(input.isRestartComboPressed()) ? 1 : 0);\n\n\t\tinput.resetTapReleased();\n\t}\n\n\tfunction updateSound(dt) {\n\t\tvar changed0 = memory.changed[self.SOUND1];\n\t\tvar changed1 = memory.changed[self.SOUND2];\n\n\t\t// update sound channel timers\n\t\tvar timer0 = self._peek(self.SOUND1, soundDurationIndex);\n\t\ttimer0 -= dt;\n\t\tif (timer0 <= 0) {\n\t\t\ttimer0 = 0;\n\t\t\tif (self._peek(self.SOUND1, soundVolumeIndex) > 0) {\n\t\t\t\tself._poke(self.SOUND1, soundVolumeIndex, 0);\n\t\t\t\tchanged0 = true;\n\t\t\t}\n\t\t}\n\t\tself._poke(self.SOUND1, soundDurationIndex, timer0);\n\n\t\tvar timer1 = self._peek(self.SOUND2, soundDurationIndex);\n\t\ttimer1 -= dt;\n\t\tif (timer1 <= 0) {\n\t\t\ttimer1 = 0;\n\t\t\tif (self._peek(self.SOUND2, soundVolumeIndex) > 0) {\n\t\t\t\tself._poke(self.SOUND2, soundVolumeIndex, 0);\n\t\t\t\tchanged1 = true;\n\t\t\t}\n\t\t}\n\t\tself._poke(self.SOUND2, soundDurationIndex, timer1);\n\n\t\t// send updated channel attributes to the sound system\n\t\tif (changed0) {\n\t\t\tsound.setPulse(0, self._peek(self.SOUND1, soundPulseIndex));\n\n\t\t\tvar freq = self._peek(self.SOUND1, soundFrequencyIndex);\n\t\t\tvar freqHz = freq / 100;\n\t\t\tsound.setFrequency(0, freqHz);\n\n\t\t\tvar volume = self._peek(self.SOUND1, soundVolumeIndex);\n\t\t\tvolume = Math.max(0, Math.min(volume, maxVolume));\n\t\t\tvolumeNorm = (volume / maxVolume);\n\t\t\tsound.setVolume(0, volumeNorm);\n\t\t}\n\n\t\tif (changed1) {\n\t\t\tsound.setPulse(1, self._peek(self.SOUND2, soundPulseIndex));\n\n\t\t\tvar freq = self._peek(self.SOUND2, soundFrequencyIndex);\n\t\t\tvar freqHz = freq / 100;\n\t\t\tsound.setFrequency(1, freqHz);\n\n\t\t\tvar volume = self._peek(self.SOUND2, soundVolumeIndex);\n\t\t\tvolume = Math.max(0, Math.min(volume, maxVolume));\n\t\t\tvolumeNorm = (volume / maxVolume);\n\t\t\tsound.setVolume(1, volumeNorm);\n\t\t}\n\t}\n\n\tfunction updateGraphics() {\n\t\tif (self._enableGraphics === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tbitsyLog(\"update graphics\", \"system\");\n\n\t\tif (memory.changed[paletteBlock]) {\n\t\t\tgraphics.setPalette(self._dump()[paletteBlock]);\n\t\t}\n\n\t\tif (tilePoolStart != null) {\n\t\t\tfor (var i = 0; i < tilePoolSize; i++) {\n\t\t\t\tvar tile = tilePoolStart + i;\n\t\t\t\tif (memory.blocks[tile] != undefined && memory.changed[tile]) {\n\t\t\t\t\tbitsyLog(\"tile changed? \" + tile, \"system\");\n\t\t\t\t\t// update tile image\n\t\t\t\t\tgraphics.createImage(tile, self.TILE_SIZE, self.TILE_SIZE, self._dump()[tile]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar textboxChanged = memory.changed[self.TEXTBOX] || memory.changed[textboxAttributeBlock];\n\t\tif (textboxChanged) {\n\t\t\t// todo : should this be optimized in some way?\n\t\t\t// update textbox image\n\t\t\tvar w = self._peek(textboxAttributeBlock, 3); // todo : need a variable to store this index?\n\t\t\tvar h = self._peek(textboxAttributeBlock, 4);\n\t\t\tif (w > 0 && h > 0) {\n\t\t\t\tbitsyLog(\"textbox changed! \" + memory.changed[self.TEXTBOX] + \" \" + memory.changed[textboxAttributeBlock] + \" \" + w + \" \" + h, \"system\");\n\t\t\t\tvar useTextBoxScale = true; // todo : check mode here?\n\t\t\t\tgraphics.createImage(self.TEXTBOX, w, h, self._dump()[self.TEXTBOX], useTextBoxScale);\n\t\t\t}\n\t\t}\n\n\t\tvar mode = self._peek(modeBlock, 0);\n\t\tif (mode === self.GFX_VIDEO) {\n\t\t\tif (memory.changed[self.VIDEO]) {\n\t\t\t\tgraphics.clearCanvas(0);\n\t\t\t\t// update screen image\n\t\t\t\tgraphics.createImage(self.VIDEO, self.VIDEO_SIZE, self.VIDEO_SIZE, self._dump()[self.VIDEO]);\n\t\t\t\t// render screen onto canvas\n\t\t\t\tgraphics.drawImage(self.VIDEO, 0, 0);\n\t\t\t}\n\t\t}\n\t\telse if (mode === self.GFX_MAP) {\n\t\t\t// redraw any changed layers\n\t\t\tvar layers = self._getTileMapLayers();\n\t\t\tvar anyMapLayerChanged = false;\n\t\t\tfor (var i = 0; i < layers.length; i++) {\n\t\t\t\tvar layerId = layers[i];\n\t\t\t\tif (memory.changed[layerId]) {\n\t\t\t\t\t// need to redraw this map layer\n\t\t\t\t\tanyMapLayerChanged = true;\n\t\t\t\t\t// clear layer canvas\n\t\t\t\t\tgraphics.setImageFill(layerId, 0); // fill transparent\n\t\t\t\t\tgraphics.createImage(layerId, self.VIDEO_SIZE, self.VIDEO_SIZE, []);\n\t\t\t\t\t// render tiles onto layer canvas\n\t\t\t\t\tvar layerData = self._dump()[layerId];\n\t\t\t\t\tfor (var ty = 0; ty < self.MAP_SIZE; ty++) {\n\t\t\t\t\t\tfor (var tx = 0; tx < self.MAP_SIZE; tx++) {\n\t\t\t\t\t\t\tvar tileIndex = (ty * self.MAP_SIZE) + tx;\n\t\t\t\t\t\t\tvar tile = layerData[tileIndex];\n\t\t\t\t\t\t\tif (tile > 0) {\n\t\t\t\t\t\t\t\tgraphics.drawImage(tile, tx * self.TILE_SIZE, ty * self.TILE_SIZE, layerId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// redraw the main canvas\n\t\t\tif (textboxChanged || anyMapLayerChanged) {\n\t\t\t\tbitsyLog(\"map changed? \" + memory.changed[self.MAP1] + \" \" + memory.changed[self.MAP2], \"system\");\n\t\t\t\tgraphics.clearCanvas(0);\n\n\t\t\t\tfor (var i = 0; i < layers.length; i++) {\n\t\t\t\t\tvar layerId = layers[i];\n\t\t\t\t\t// draw the layer's image canvas onto the main canvas\n\t\t\t\t\tgraphics.drawImage(layerId, 0, 0);\n\t\t\t\t}\n\n\t\t\t\t// draw textbox onto canvas\n\t\t\t\tvar visible = self._peek(textboxAttributeBlock, 0)\n\t\t\t\tvar x = self._peek(textboxAttributeBlock, 1);\n\t\t\t\tvar y = self._peek(textboxAttributeBlock, 2);\n\t\t\t\tvar w = self._peek(textboxAttributeBlock, 3);\n\t\t\t\tvar h = self._peek(textboxAttributeBlock, 4);\n\t\t\t\tif (visible > 0 && w > 0 && h > 0) {\n\t\t\t\t\tgraphics.drawImage(self.TEXTBOX, x, y);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* == PRIVATE / DEBUG == */\n\tthis._name = name;\n\n\tthis._active = false;\n\n\tthis._attachCanvas = function(c) {\n\t\tgraphics.attachCanvas(c, self.VIDEO_SIZE);\n\t};\n\n\tthis._getCanvas = graphics.getCanvas;\n\tthis._getContext = graphics.getContext;\n\n\tthis._start = function() {\n\t\tinput.listen(graphics.getCanvas());\n\t\tupdateTextScale();\n\t\tself._active = true;\n\t};\n\n\t// hacky...\n\tthis._startNoInput = function() {\n\t\tupdateTextScale();\n\t\tself._active = true;\n\t};\n\n\tthis._exit = function() {\n\t\tinput.unlisten(graphics.getCanvas());\n\t\tsound.mute();\n\t\tself._active = false;\n\t};\n\n\t// hacky....\n\tthis._injectPreLoop = null;\n\tthis._injectPostDraw = null;\n\n\tthis._update = function(dt) {\n\t\tvar shouldContinue = false;\n\n\t\tupdateInput();\n\n\t\t// too hacky???\n\t\tif (self._injectPreLoop) {\n\t\t\tself._injectPreLoop();\n\t\t}\n\n\t\t// run main loop\n\t\tif (onLoopFunction) {\n\t\t\tshouldContinue = onLoopFunction(dt);\n\t\t}\n\n\t\tif (memory.changed[modeBlock]) {\n\t\t\tupdateTextScale();\n\t\t}\n\n\t\t// update output systems\n\t\tupdateSound(dt);\n\t\tupdateGraphics();\n\n\t\tif (self._injectPostDraw) {\n\t\t\tself._injectPostDraw();\n\t\t}\n\n\t\t// reset memory block changed flags\n\t\tfor (var i = 0; i < memory.changed.length; i++) {\n\t\t\tmemory.changed[i] = false;\n\t\t}\n\n\t\t// todo : should the _exit() call go in here?\n\n\t\treturn shouldContinue;\n\t};\n\n\tthis._updateGraphics = updateGraphics;\n\n\tthis._allocate = function(args) {\n\t\t// find next available block in range\n\t\tvar next = (args && args.start) ? args.start : 0;\n\t\tvar count = (args && args.max) ? args.max : -1;\n\t\twhile (memory.blocks[next] != undefined && count != 0) {\n\t\t\tnext++;\n\t\t\tcount--;\n\t\t}\n\n\t\tif (count == 0) {\n\t\t\t// couldn't find any available block\n\t\t\treturn null;\n\t\t}\n\n\t\tif (args && args.str) {\n\t\t\tmemory.blocks[next] = args.str;\n\t\t}\n\t\telse {\n\t\t\tvar size = args && args.size ? args.size : 0;\n\t\t\tmemory.blocks[next] = [];\n\t\t\tfor (var i = 0; i < size; i++) {\n\t\t\t\tmemory.blocks[next].push(0);\n\t\t\t}\n\t\t}\n\n\t\tmemory.changed[next] = false;\n\n\t\treturn next;\n\t};\n\n\tthis._free = function(block) {\n\t\tdelete memory.blocks[block];\n\t\tdelete memory.changed[block];\n\t};\n\n\tthis._peek = function(block, index) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\treturn memoryBlock.charCodeAt(index);\n\t\t}\n\t\telse {\n\t\t\treturn memoryBlock[index];\n\t\t}\n\t};\n\n\tthis._poke = function(block, index, value) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\tmemory.blocks[block] = memoryBlock.substring(0, index) + String.fromCharCode(value) + memoryBlock.substring(index + 1);\n\t\t}\n\t\telse {\n\t\t\tvar value = parseInt(value);\n\t\t\tif (!isNaN(value)) {\n\t\t\t\tmemoryBlock[index] = value;\n\t\t\t}\n\t\t}\n\t\tmemory.changed[block] = true;\n\t};\n\n\tthis._read = function(block) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\treturn memoryBlock;\n\t\t}\n\t\telse {\n\t\t\tvar str = \"\";\n\t\t\tfor (var i = 0; i < memoryBlock.length; i++) {\n\t\t\t\tstr += String.fromCharCode(memoryBlock[i]);\n\t\t\t}\n\t\t\treturn str;\n\t\t}\n\t};\n\n\tthis._write = function(block, str) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\tmemory.blocks[block] = str;\n\t\t}\n\t\telse {\n\t\t\tmemory.blocks[block] = [];\n\t\t\tfor (var i = 0; i < str.length; i++) {\n\t\t\t\tmemory.blocks[block][i] = str.charCodeAt(i);\n\t\t\t}\n\t\t}\n\t\tmemory.changed[block] = true;\n\t};\n\n\tthis._dump = function() {\n\t\treturn memory.blocks;\n\t};\n\n\t// convenience methods for hacking around with map layers\n\tvar tileMapLayers = [];\n\tthis._getTileMapLayers = function() {\n\t\treturn tileMapLayers;\n\t};\n\tthis._addTileMapLayer = function() {\n\t\tvar layer = self._allocate({\n\t\t\tstart: (tilePoolStart + tilePoolSize),\n\t\t\tsize: (self.MAP_SIZE * self.MAP_SIZE)\n\t\t});\n\n\t\ttileMapLayers.push(layer);\n\n\t\treturn layer;\n\t};\n\n\t/* == CONSTANTS == */\n\t// memory blocks (these will be initialized below)\n\tthis.VIDEO;\n\tthis.TEXTBOX;\n\tthis.MAP1;\n\tthis.MAP2;\n\tthis.SOUND1;\n\tthis.SOUND2;\n\n\t// graphics modes\n\tthis.GFX_VIDEO = 0;\n\tthis.GFX_MAP = 1;\n\n\t// text modes\n\tthis.TXT_HIREZ = 0; // 2x resolution\n\tthis.TXT_LOREZ = 1; // 1x resolution\n\n\t// size\n\tthis.TILE_SIZE = tilesize;\n\tthis.MAP_SIZE = mapsize;\n\tthis.VIDEO_SIZE = width;\n\t// todo : should text scale have a constant?\n\n\t// button codes\n\tthis.BTN_UP = 0;\n\tthis.BTN_DOWN = 1;\n\tthis.BTN_LEFT = 2;\n\tthis.BTN_RIGHT = 3;\n\tthis.BTN_OK = 4;\n\tthis.BTN_MENU = 5;\n\n\t// pulse waves\n\tthis.PULSE_1_8 = 0;\n\tthis.PULSE_1_4 = 1;\n\tthis.PULSE_1_2 = 2;\n\n\t/* == IO == */\n\tthis.log = function(message) {\n\t\tbitsyLog(message, name);\n\t};\n\n\tthis.button = function(code) {\n\t\treturn self._peek(buttonBlock, code) > 0;\n\t};\n\n\tthis.getGameData = function() {\n\t\treturn self._read(gameDataBlock);\n\t};\n\n\tthis.getFontData = function() {\n\t\treturn self._read(fontDataBlock);\n\t};\n\n\t/* == GRAPHICS == */\n\tthis.graphicsMode = function(mode) {\n\t\t// todo : store the mode flag indices somewhere?\n\t\tif (mode != undefined) {\n\t\t\tself._poke(modeBlock, 0, mode);\n\t\t}\n\n\t\treturn self._peek(modeBlock, 0);\n\t};\n\n\tthis.textMode = function(mode) {\n\t\t// todo : test whether the requested mode is supported!\n\t\tif (mode != undefined) {\n\t\t\tself._poke(modeBlock, 1, mode);\n\t\t}\n\n\t\treturn self._peek(modeBlock, 1);\n\t};\n\n\tthis.color = function(color, r, g, b) {\n\t\tself._poke(paletteBlock, (color * 3) + 0, r);\n\t\tself._poke(paletteBlock, (color * 3) + 1, g);\n\t\tself._poke(paletteBlock, (color * 3) + 2, b);\n\n\t\t// mark all graphics as changed\n\t\tmemory.changed[self.VIDEO] = true;\n\t\tmemory.changed[self.TEXTBOX] = true;\n\t\tmemory.changed[self.MAP1] = true;\n\t\tmemory.changed[self.MAP2] = true;\n\n\t\tif (tilePoolStart != null) {\n\t\t\tfor (var i = 0; i < tilePoolSize; i++) {\n\t\t\t\tif (memory.blocks[tilePoolStart + i] != undefined) {\n\t\t\t\t\tmemory.changed[tilePoolStart + i] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tthis.tile = function() {\n\t\treturn self._allocate({\n\t\t\tstart: tilePoolStart,\n\t\t\tmax: tilePoolSize,\n\t\t\tsize: (self.TILE_SIZE * self.TILE_SIZE)\n\t\t});\n\t};\n\n\tthis.delete = function(tile) {\n\t\tif (graphics.hasImage(tile)) {\n\t\t\tgraphics.deleteImage(tile);\n\t\t}\n\n\t\tself._free(tile);\n\t};\n\n\tthis.deleteAllTiles = function() {\n\t\tif (tilePoolStart != null) {\n\t\t\tfor (var i = 0; i < tilePoolSize; i++) {\n\t\t\t\tvar tile = tilePoolStart + i;\n\t\t\t\tthis.delete(tile);\n\t\t\t}\n\t\t}\n\t};\n\n\tthis.fill = function(block, value) {\n\t\tvar len = memory.blocks[block].length;\n\t\tfor (var i = 0; i < len; i++) {\n\t\t\tself._poke(block, i, value);\n\t\t}\n\n\t\tvar isImage = (block === self.VIDEO) ||\n\t\t\t(block === self.TEXTBOX) ||\n\t\t\t(block >= tilePoolStart && block < (tilePoolStart + tilePoolSize));\n\n\t\t// optimize rendering by notifying the graphics system what the fill color is for this image\n\t\tif (isImage) {\n\t\t\tgraphics.setImageFill(block, value);\n\t\t}\n\t};\n\n\tthis.set = function(block, index, value) {\n\t\tself._poke(block, index, value);\n\t};\n\n\tthis.textbox = function(visible, x, y, w, h) {\n\t\tif (visible != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 0, (visible === true) ? 1 : 0);\n\t\t}\n\t\t\n\t\tif (x != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 1, x);\n\t\t}\n\t\t\n\t\tif (y != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 2, y);\n\t\t}\n\n\t\tvar prevWidth = self._peek(textboxAttributeBlock, 3);\n\t\tvar prevHeight = self._peek(textboxAttributeBlock, 4);\n\n\t\tif (w != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 3, w);\n\t\t}\n\t\t\n\t\tif (h != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 4, h);\n\t\t}\n\n\t\tif (w != undefined && h != undefined && (prevWidth != w || prevHeight != h)) {\n\t\t\t// re-allocate the textbox block (should I have a helper function for this?)\n\t\t\tmemory.blocks[self.TEXTBOX] = [];\n\t\t\tfor (var i = 0; i < (w * h); i++) {\n\t\t\t\tmemory.blocks[self.TEXTBOX].push(0);\n\t\t\t}\n\t\t\tmemory.changed[self.TEXTBOX] = true;\n\t\t}\n\t};\n\n\t/* == SOUND == */\n\t// duration is in milliseconds (ms)\n\tthis.sound = function(channel, duration, frequency, volume, pulse) {\n\t\tself._poke(channel, soundDurationIndex, duration);\n\t\tself._poke(channel, soundFrequencyIndex, frequency);\n\t\tself._poke(channel, soundVolumeIndex, volume);\n\t\tself._poke(channel, soundPulseIndex, pulse);\n\t};\n\n\t// frequency is in decihertz (dHz)\n\tthis.frequency = function(channel, frequency) {\n\t\tself._poke(channel, soundFrequencyIndex, frequency);\n\t};\n\n\t// volume: min = 0, max = 15\n\tthis.volume = function(channel, volume) {\n\t\tself._poke(channel, soundVolumeIndex, volume);\n\t};\n\n\t/* == EVENTS == */\n\tthis.loop = function(fn) {\n\t\tonLoopFunction = fn;\n\t};\n\n\t/* == INTERNAL == */\n\t// initialize memory blocks\n\tvar gameDataBlock = this._allocate({ str: \"\" });\n\tvar fontDataBlock = this._allocate({ str: \"\" });\n\tthis.VIDEO = this._allocate({ size: self.VIDEO_SIZE * self.VIDEO_SIZE });\n\tthis.TEXTBOX = this._allocate();\n\tthis.MAP1 = this._allocate({ size: self.MAP_SIZE * self.MAP_SIZE });\n\ttileMapLayers.push(this.MAP1);\n\tthis.MAP2 = this._allocate({ size: self.MAP_SIZE * self.MAP_SIZE });\n\ttileMapLayers.push(this.MAP2);\n\tvar paletteBlock = this._allocate({ size: initialPaletteSize * 3 });\n\tvar buttonBlock = this._allocate({ size: 8 });\n\tthis.SOUND1 = this._allocate({ size: 4 });\n\tthis.SOUND2 = this._allocate({ size: 4 });\n\tvar modeBlock = this._allocate({ size: 8 });\n\tvar textboxAttributeBlock = this._allocate({ size: 8 });\n\n\ttilePoolStart = (textboxAttributeBlock + 1);\n\n\t// access for debugging\n\tthis._gameDataBlock = gameDataBlock;\n\tthis._fontDataBlock = fontDataBlock;\n\tthis._buttonBlock = buttonBlock;\n\n\t// events\n\tvar onLoopFunction = null;\n}\n\nvar mainProcess = addProcess();\nvar bitsy = mainProcess.system;",
- "world.js": "/* BITSY VERSION */\n// is this the right place for this to live?\nvar version = {\n\tmajor: 8, // major changes\n\tminor: 9, // smaller changes\n\tdevBuildPhase: \"RELEASE\",\n};\nfunction getEngineVersion() {\n\treturn version.major + \".\" + version.minor;\n}\n\n/* TEXT CONSTANTS */\nvar titleDialogId = \"title\";\n\n// todo : where should this be stored?\nvar tileColorStartIndex = 16;\n\nvar TextDirection = {\n\tLeftToRight : \"LTR\",\n\tRightToLeft : \"RTL\"\n};\n\nvar defaultFontName = \"ascii_small\";\n\n/* TUNE CONSTANTS */\nvar barLength = 16; // sixteenth notes\nvar minTuneLength = 1;\nvar maxTuneLength = 16;\n\n// chromatic notes\nvar Note = {\n\tNONE \t\t: -1,\n\tC \t\t\t: 0,\t// C\n\tC_SHARP \t: 1,\t// C sharp / D flat\n\tD \t\t\t: 2,\t// D\n\tD_SHARP \t: 3,\t// D sharp / E flat\n\tE \t\t\t: 4,\t// E\n\tF \t\t\t: 5,\t// F\n\tF_SHARP \t: 6,\t// F sharp / G flat\n\tG \t\t\t: 7,\t// G\n\tG_SHARP \t: 8,\t// G sharp / A flat\n\tA \t\t\t: 9,\t// A\n\tA_SHARP \t: 10,\t// A sharp / B flat\n\tB \t\t\t: 11,\t// B\n\tCOUNT \t\t: 12\n};\n\n// solfa notes\nvar Solfa = {\n\tNONE \t: -1,\n\tD \t\t: 0,\t// Do\n\tR \t\t: 1,\t// Re\n\tM \t\t: 2,\t// Mi\n\tF \t\t: 3,\t// Fa\n\tS \t\t: 4,\t// Sol\n\tL \t\t: 5,\t// La\n\tT \t\t: 6,\t// Ti\n\tCOUNT \t: 7\n};\n\nvar Octave = {\n\tNONE: -1,\n\t2: 0,\n\t3: 1,\n\t4: 2, // octave 4: middle C octave\n\t5: 3,\n\tCOUNT: 4\n};\n\nvar Tempo = {\n\tSLW: 0, // slow\n\tMED: 1, // medium\n\tFST: 2, // fast\n\tXFST: 3 // extra fast (aka turbo)\n};\n\nvar SquareWave = {\n\tP8: 0, // pulse 1 / 8\n\tP4: 1, // pulse 1 / 4\n\tP2: 2, // pulse 1 / 2\n\tCOUNT: 3\n};\n\nvar ArpeggioPattern = {\n\tOFF: 0,\n\tUP: 1, // ascending triad chord\n\tDWN: 2, // descending triad chord\n\tINT5: 3, // 5 step interval\n\tINT8: 4 // 8 setp interval\n};\n\nfunction createWorldData() {\n\treturn {\n\t\troom : {},\n\t\ttile : {},\n\t\tsprite : {},\n\t\titem : {},\n\t\tdialog : {},\n\t\tend : {}, // pre-7.0 ending data for backwards compatibility\n\t\tpalette : { // start off with a default palette\n\t\t\t\"default\" : {\n\t\t\t\tname : \"default\",\n\t\t\t\tcolors : [[0,0,0],[255,255,255],[255,255,255]]\n\t\t\t}\n\t\t},\n\t\tvariable : {},\n\t\ttune : {},\n\t\tblip : {},\n\t\tversionNumberFromComment : -1, // -1 indicates no version information found\n\t\tfontName : defaultFontName,\n\t\ttextDirection : TextDirection.LeftToRight,\n\t\tflags : createDefaultFlags(),\n\t\tnames : {},\n\t\t// source data for all drawings (todo: better name?)\n\t\tdrawings : {},\n\t};\n}\n\n// creates a drawing data structure with default property values for the type\nfunction createDrawingData(type, id) {\n\t// the avatar's drawing id still uses the sprite prefix (for back compat)\n\tvar drwId = (type === \"AVA\" ? \"SPR\" : type) + \"_\" + id;\n\n\tvar drawingData = {\n\t\ttype : type,\n\t\tid : id,\n\t\tname : null,\n\t\tdrw : drwId,\n\t\tcol : (type === \"TIL\") ? 1 : 2, // foreground color\n\t\tbgc : 0, // background color\n\t\tanimation : {\n\t\t\tisAnimated : false,\n\t\t\tframeIndex : 0,\n\t\t\tframeCount : 1,\n\t\t},\n\t};\n\n\t// add type specific properties\n\tif (type === \"TIL\") {\n\t\t// default null value indicates it can vary from room to room (original version)\n\t\tdrawingData.isWall = null;\n\t}\n\n\tif (type === \"AVA\" || type === \"SPR\") {\n\t\t// default sprite location is \"offstage\"\n\t\tdrawingData.room = null;\n\t\tdrawingData.x = -1;\n\t\tdrawingData.y = -1;\n\t\tdrawingData.inventory = {};\n\t}\n\n\tif (type === \"AVA\" || type === \"SPR\" || type === \"ITM\") {\n\t\tdrawingData.dlg = null;\n\t\tdrawingData.blip = null;\n\t}\n\n\treturn drawingData;\n}\n\nfunction createTuneData(id) {\n\tvar tuneData = {\n\t\tid : id,\n\t\tname : null,\n\t\tmelody : [],\n\t\tharmony : [],\n\t\tkey: null, // a null key indicates a chromatic scale (all notes enabled)\n\t\ttempo: Tempo.MED,\n\t\tinstrumentA : SquareWave.P2,\n\t\tinstrumentB : SquareWave.P2,\n\t\tarpeggioPattern : ArpeggioPattern.OFF,\n\t};\n\treturn tuneData;\n}\n\nfunction createTuneBarData() {\n\tvar bar = [];\n\tfor (var i = 0; i < barLength; i++) {\n\t\tbar.push({ beats: 0, note: Note.C, octave: Octave[4] });\n\t}\n\treturn bar;\n}\n\nfunction createTuneKeyData() {\n\tvar key = {\n\t\tnotes: [], // mapping of the solfa scale degrees to chromatic notes\n\t\tscale: [] // list of solfa notes that are enabled for this key\n\t};\n\n\t// initialize notes\n\tfor (var i = 0; i < Solfa.COUNT; i++) {\n\t\tkey.notes.push(Note.NONE);\n\t}\n\n\treturn key;\n}\n\nfunction createBlipData(id) {\n\tvar blipData = {\n\t\tid: id,\n\t\tname: null,\n\t\tpitchA: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tpitchB: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tpitchC: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tenvelope: {\n\t\t\tattack: 0, // attack time in ms\n\t\t\tdecay: 0, // decay time in ms\n\t\t\tsustain: 0, // sustain volume\n\t\t\tlength: 0, // sustain time in ms\n\t\t\trelease: 0 // release time in ms\n\t\t},\n\t\tbeat : {\n\t\t\ttime: 0, // time in ms between pitch changes\n\t\t\tdelay: 0 // time in ms *before* first pitch change\n\t\t},\n\t\tinstrument: SquareWave.P2,\n\t\tdoRepeat: false\n\t\t// TODO : consider for future update\n\t\t// doSlide: false,\n\t};\n\n\treturn blipData;\n}\n\nfunction createDefaultFlags() {\n\treturn {\n\t\t// version\n\t\tVER_MAJ: -1, // major version number (-1 = no version information found)\n\t\tVER_MIN: -1, // minor version number (-1 = no version information found)\n\t\t// compatibility\n\t\tROOM_FORMAT: 0, // 0 = non-comma separated (original), 1 = comma separated (default)\n\t\tDLG_COMPAT: 0, // 0 = default dialog behavior, 1 = pre-7.0 dialog behavior\n\t\t// config\n\t\tTXT_MODE: 0 // 0 = HIREZ (2x - default), 1 = LOREZ (1x)\n\t};\n}\n\nfunction createDialogData(id) {\n\treturn {\n\t\tsrc : \"\",\n\t\tname : null,\n\t\tid : id,\n\t};\n}\n\nfunction parseWorld(file) {\n\tbitsy.log(\"create world data\");\n\n\tvar world = createWorldData();\n\n\tbitsy.log(\"init parse state\");\n\n\tvar parseState = {\n\t\tlines : file.split(\"\\n\"),\n\t\tindex : 0,\n\t\tspriteStartLocations : {}\n\t};\n\n\tbitsy.log(\"start reading lines\");\n\n\twhile (parseState.index < parseState.lines.length) {\n\t\tvar i = parseState.index;\n\t\tvar lines = parseState.lines;\n\t\tvar curLine = lines[i];\n\n\t\t// bitsy.log(\"LN \" + i + \" xx \" + curLine);\n\n\t\tif (i == 0) {\n\t\t\ti = parseTitle(parseState, world);\n\t\t}\n\t\telse if (curLine.length <= 0 || curLine.charAt(0) === \"#\") {\n\t\t\t// collect version number from a comment (hacky but required for pre-8.0 compatibility)\n\t\t\tif (curLine.indexOf(\"# BITSY VERSION \") != -1) {\n\t\t\t\tworld.versionNumberFromComment = parseFloat(curLine.replace(\"# BITSY VERSION \", \"\"));\n\t\t\t}\n\n\t\t\t//skip blank lines & comments\n\t\t\ti++;\n\t\t}\n\t\telse if (getType(curLine) === \"PAL\") {\n\t\t\ti = parsePalette(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"ROOM\" || getType(curLine) === \"SET\") { // SET for back compat\n\t\t\ti = parseRoom(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TIL\") {\n\t\t\ti = parseTile(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"SPR\") {\n\t\t\ti = parseSprite(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"ITM\") {\n\t\t\ti = parseItem(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"DLG\") {\n\t\t\ti = parseDialog(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"END\") {\n\t\t\t// parse endings for back compat\n\t\t\ti = parseEnding(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"VAR\") {\n\t\t\ti = parseVariable(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"DEFAULT_FONT\") {\n\t\t\ti = parseFontName(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TEXT_DIRECTION\") {\n\t\t\ti = parseTextDirection(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"FONT\") {\n\t\t\ti = parseFontData(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TUNE\") {\n\t\t\ti = parseTune(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"BLIP\") {\n\t\t\ti = parseBlip(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"!\") {\n\t\t\ti = parseFlag(parseState, world);\n\t\t}\n\t\telse {\n\t\t\ti++;\n\t\t}\n\n\t\tparseState.index = i;\n\t}\n\n\tworld.names = createNameMapsForWorld(world);\n\n\tplaceSprites(parseState, world);\n\n\tif ((world.flags.VER_MAJ <= -1 || world.flags.VER_MIN <= -1) && world.versionNumberFromComment > -1) {\n\t\tvar versionNumberStr = \"\" + world.versionNumberFromComment;\n\t\tversionNumberStr = versionNumberStr.split(\".\");\n\t\tworld.flags.VER_MAJ = parseFloat(versionNumberStr[0]);\n\t\tworld.flags.VER_MIN = parseFloat(versionNumberStr[1]);\n\t}\n\n\t// starting in version v7.0, there were two major changes to dialog behavior:\n\t// 1) sprite dialog was no longer implicitly linked by the sprite and dialog IDs matching\n\t// (see this commit: 5e1adb29faad4e50603c689d2dac143074117b4e)\n\t// 2) ending dialogs no longer had their own world data type (\"END\")\n\t// for the v7.x versions I tried to automatically convert old dialog to the new format,\n\t// however, that process can be unreliable and lead to weird bugs.\n\t// with v8.0 and above I will no longer attempt to convert old files, and instead will use\n\t// a flag to indicate files that need to use the backwards compatible behavior -\n\t// this is more reliable & configurable (at the cost of making pre-7.0 games a bit harder to edit)\n\tif (world.flags.VER_MAJ < 7) {\n\t\tworld.flags.DLG_COMPAT = 1;\n\t}\n\n\treturn world;\n}\n\nfunction parseTitle(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar results;\n\tif (scriptUtils) {\n\t\tresults = scriptUtils.ReadDialogScript(lines,i);\n\t}\n\telse {\n\t\tresults = { script: lines[i], index: (i + 1) };\n\t}\n\n\tworld.dialog[titleDialogId] = createDialogData(titleDialogId);\n\tworld.dialog[titleDialogId].src = results.script;\n\n\ti = results.index;\n\ti++;\n\n\treturn i;\n}\n\nfunction parsePalette(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\tvar colors = [];\n\tvar name = null;\n\twhile (i < lines.length && lines[i].length > 0) { //look for empty line\n\t\tvar args = lines[i].split(\" \");\n\t\tif (args[0] === \"NAME\") {\n\t\t\tname = lines[i].split(/\\s(.+)/)[1];\n\t\t}\n\t\telse {\n\t\t\tvar col = [];\n\t\t\tlines[i].split(\",\").forEach(function(i) {\n\t\t\t\tcol.push(parseInt(i));\n\t\t\t});\n\t\t\tcolors.push(col);\n\t\t}\n\t\ti++;\n\t}\n\tworld.palette[id] = {\n\t\tid : id,\n\t\tname : name,\n\t\tcolors : colors\n\t};\n\treturn i;\n}\n\nfunction createRoomData(id) {\n\treturn {\n\t\tid: id,\n\t\tname: null,\n\t\ttilemap: [],\n\t\twalls: [],\n\t\texits: [],\n\t\tendings: [],\n\t\titems: [],\n\t\tpal: null,\n\t\tava: null,\n\t\ttune: \"0\"\n\t};\n}\n\nfunction createExitData(x, y, destRoom, destX, destY, transition, dlg) {\n\treturn {\n\t\tx: x,\n\t\ty: y,\n\t\tdest: {\n\t\t\troom: destRoom,\n\t\t\tx: destX,\n\t\t\ty: destY\n\t\t},\n\t\ttransition_effect: transition,\n\t\tdlg: dlg,\n\t};\n}\n\nfunction createEndingData(id, x, y) {\n\treturn {\n\t\tid: id,\n\t\tx: x,\n\t\ty: y\n\t};\n}\n\nfunction parseRoom(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\n\tvar roomData = createRoomData(id);\n\n\ti++;\n\n\t// create tile map\n\tif (world.flags.ROOM_FORMAT === 0) {\n\t\t// old way: no commas, single char tile ids\n\t\tvar end = i + bitsy.MAP_SIZE;\n\t\tvar y = 0;\n\t\tfor (; i < end; i++) {\n\t\t\troomData.tilemap.push([]);\n\t\t\tfor (x = 0; x < bitsy.MAP_SIZE; x++) {\n\t\t\t\troomData.tilemap[y].push(lines[i].charAt(x));\n\t\t\t}\n\t\t\ty++;\n\t\t}\n\t}\n\telse if (world.flags.ROOM_FORMAT === 1) {\n\t\t// new way: comma separated, multiple char tile ids\n\t\tvar end = i + bitsy.MAP_SIZE;\n\t\tvar y = 0;\n\t\tfor (; i < end; i++) {\n\t\t\troomData.tilemap.push([]);\n\t\t\tvar lineSep = lines[i].split(\",\");\n\t\t\tfor (x = 0; x < bitsy.MAP_SIZE; x++) {\n\t\t\t\troomData.tilemap[y].push(lineSep[x]);\n\t\t\t}\n\t\t\ty++;\n\t\t}\n\t}\n\n\twhile (i < lines.length && lines[i].length > 0) { //look for empty line\n\t\t// bitsy.log(getType(lines[i]));\n\t\tif (getType(lines[i]) === \"SPR\") {\n\t\t\t/* NOTE SPRITE START LOCATIONS */\n\t\t\tvar sprId = getId(lines[i]);\n\t\t\tif (sprId.indexOf(\",\") == -1 && lines[i].split(\" \").length >= 3) { //second conditional checks for coords\n\t\t\t\t/* PLACE A SINGLE SPRITE */\n\t\t\t\tvar sprCoord = lines[i].split(\" \")[2].split(\",\");\n\t\t\t\tparseState.spriteStartLocations[sprId] = {\n\t\t\t\t\troom : id,\n\t\t\t\t\tx : parseInt(sprCoord[0]),\n\t\t\t\t\ty : parseInt(sprCoord[1])\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( world.flags.ROOM_FORMAT == 0 ) { // TODO: right now this shortcut only works w/ the old comma separate format\n\t\t\t\t/* PLACE MULTIPLE SPRITES*/ \n\t\t\t\t//Does find and replace in the tilemap (may be hacky, but its convenient)\n\t\t\t\tvar sprList = sprId.split(\",\");\n\t\t\t\tfor (row in roomData.tilemap) {\n\t\t\t\t\tfor (s in sprList) {\n\t\t\t\t\t\tvar col = roomData.tilemap[row].indexOf( sprList[s] );\n\t\t\t\t\t\t//if the sprite is in this row, replace it with the \"null tile\" and set its starting position\n\t\t\t\t\t\tif (col != -1) {\n\t\t\t\t\t\t\troomData.tilemap[row][col] = \"0\";\n\t\t\t\t\t\t\tparseState.spriteStartLocations[ sprList[s] ] = {\n\t\t\t\t\t\t\t\troom : id,\n\t\t\t\t\t\t\t\tx : parseInt(col),\n\t\t\t\t\t\t\t\ty : parseInt(row)\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"ITM\") {\n\t\t\tvar itmId = getId(lines[i]);\n\t\t\tvar itmCoord = lines[i].split(\" \")[2].split(\",\");\n\t\t\tvar itm = {\n\t\t\t\tid: itmId,\n\t\t\t\tx : parseInt(itmCoord[0]),\n\t\t\t\ty : parseInt(itmCoord[1])\n\t\t\t};\n\t\t\troomData.items.push( itm );\n\t\t}\n\t\telse if (getType(lines[i]) === \"WAL\") {\n\t\t\t/* DEFINE COLLISIONS (WALLS) */\n\t\t\troomData.walls = getId(lines[i]).split(\",\");\n\t\t}\n\t\telse if (getType(lines[i]) === \"EXT\") {\n\t\t\t/* ADD EXIT */\n\t\t\tvar exitArgs = lines[i].split(\" \");\n\t\t\t//arg format: EXT 10,5 M 3,2 [AVA:7 LCK:a,9] [AVA 7 LCK a 9]\n\t\t\tvar exitCoords = exitArgs[1].split(\",\");\n\t\t\tvar destName = exitArgs[2];\n\t\t\tvar destCoords = exitArgs[3].split(\",\");\n\t\t\tvar ext = createExitData(\n\t\t\t\t/* x \t\t\t*/ parseInt(exitCoords[0]),\n\t\t\t\t/* y \t\t\t*/ parseInt(exitCoords[1]),\n\t\t\t\t/* destRoom \t*/ destName,\n\t\t\t\t/* destX \t\t*/ parseInt(destCoords[0]),\n\t\t\t\t/* destY \t\t*/ parseInt(destCoords[1]),\n\t\t\t\t/* transition \t*/ null,\n\t\t\t\t/* dlg \t\t\t*/ null);\n\n\t\t\t// optional arguments\n\t\t\tvar exitArgIndex = 4;\n\t\t\twhile (exitArgIndex < exitArgs.length) {\n\t\t\t\tif (exitArgs[exitArgIndex] == \"FX\") {\n\t\t\t\t\text.transition_effect = exitArgs[exitArgIndex+1];\n\t\t\t\t\texitArgIndex += 2;\n\t\t\t\t}\n\t\t\t\telse if (exitArgs[exitArgIndex] == \"DLG\") {\n\t\t\t\t\text.dlg = exitArgs[exitArgIndex+1];\n\t\t\t\t\texitArgIndex += 2;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\texitArgIndex += 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\troomData.exits.push(ext);\n\t\t}\n\t\telse if (getType(lines[i]) === \"END\") {\n\t\t\t/* ADD ENDING */\n\t\t\tvar endId = getId(lines[i]);\n\n\t\t\tvar endCoords = getCoord(lines[i], 2);\n\t\t\tvar end = createEndingData(\n\t\t\t\t/* id */ endId,\n\t\t\t\t/* x */ parseInt(endCoords[0]),\n\t\t\t\t/* y */ parseInt(endCoords[1]));\n\n\t\t\troomData.endings.push(end);\n\t\t}\n\t\telse if (getType(lines[i]) === \"PAL\") {\n\t\t\t/* CHOOSE PALETTE (that's not default) */\n\t\t\troomData.pal = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"AVA\") {\n\t\t\t// change avatar appearance per room\n\t\t\troomData.ava = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"TUNE\") {\n\t\t\troomData.tune = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\troomData.name = getNameArg(lines[i]);\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.room[id] = roomData;\n\n\treturn i;\n}\n\nfunction parseTile(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar tileData = createDrawingData(\"TIL\", id);\n\n\ti++;\n\n\t// read & store tile image source\n\ti = parseDrawingCore(lines, i, tileData.drw, world);\n\n\t// update animation info\n\ttileData.animation.frameCount = getDrawingFrameCount(world, tileData.drw);\n\ttileData.animation.isAnimated = tileData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\ttileData.col = parseInt(getId(lines[i]));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\ttileData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttileData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\ttileData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"WAL\") {\n\t\t\tvar wallArg = getArg(lines[i], 1);\n\t\t\tif (wallArg === \"true\") {\n\t\t\t\ttileData.isWall = true;\n\t\t\t}\n\t\t\telse if (wallArg === \"false\") {\n\t\t\t\ttileData.isWall = false;\n\t\t\t}\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store tile data\n\tworld.tile[id] = tileData;\n\n\treturn i;\n}\n\nfunction parseSprite(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar type = (id === \"A\") ? \"AVA\" : \"SPR\";\n\tvar spriteData = createDrawingData(type, id);\n\n\t// bitsy.log(spriteData);\n\n\ti++;\n\n\t// read & store sprite image source\n\ti = parseDrawingCore(lines, i, spriteData.drw, world);\n\n\t// update animation info\n\tspriteData.animation.frameCount = getDrawingFrameCount(world, spriteData.drw);\n\tspriteData.animation.isAnimated = spriteData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\t/* COLOR OFFSET INDEX */\n\t\t\tspriteData.col = parseInt(getId(lines[i]));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\t/* BACKGROUND COLOR */\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\tspriteData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tspriteData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"POS\") {\n\t\t\t/* STARTING POSITION */\n\t\t\tvar posArgs = lines[i].split(\" \");\n\t\t\tvar roomId = posArgs[1];\n\t\t\tvar coordArgs = posArgs[2].split(\",\");\n\t\t\tparseState.spriteStartLocations[id] = {\n\t\t\t\troom : roomId,\n\t\t\t\tx : parseInt(coordArgs[0]),\n\t\t\t\ty : parseInt(coordArgs[1])\n\t\t\t};\n\t\t}\n\t\telse if(getType(lines[i]) === \"DLG\") {\n\t\t\tspriteData.dlg = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\tspriteData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"ITM\") {\n\t\t\t/* ITEM STARTING INVENTORY */\n\t\t\tvar itemId = getId(lines[i]);\n\t\t\tvar itemCount = parseFloat(getArg(lines[i], 2));\n\t\t\tspriteData.inventory[itemId] = itemCount;\n\t\t}\n\t\telse if (getType(lines[i]) == \"BLIP\") {\n\t\t\tvar blipId = getId(lines[i]);\n\t\t\tspriteData.blip = blipId;\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store sprite data\n\tworld.sprite[id] = spriteData;\n\n\treturn i;\n}\n\nfunction parseItem(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar itemData = createDrawingData(\"ITM\", id);\n\n\ti++;\n\n\t// read & store item image source\n\ti = parseDrawingCore(lines, i, itemData.drw, world);\n\n\t// update animation info\n\titemData.animation.frameCount = getDrawingFrameCount(world, itemData.drw);\n\titemData.animation.isAnimated = itemData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\t/* COLOR OFFSET INDEX */\n\t\t\titemData.col = parseInt(getArg(lines[i], 1));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\t/* BACKGROUND COLOR */\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\titemData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\titemData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"DLG\") {\n\t\t\titemData.dlg = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\titemData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) == \"BLIP\") {\n\t\t\tvar blipId = getId(lines[i]);\n\t\t\titemData.blip = blipId;\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store item data\n\tworld.item[id] = itemData;\n\n\treturn i;\n}\n\nfunction parseDrawingCore(lines, i, drwId, world) {\n\tvar frameList = []; //init list of frames\n\tframeList.push( [] ); //init first frame\n\tvar frameIndex = 0;\n\tvar y = 0;\n\twhile (y < bitsy.TILE_SIZE) {\n\t\tvar line = lines[i + y];\n\t\tvar row = [];\n\n\t\tfor (x = 0; x < bitsy.TILE_SIZE; x++) {\n\t\t\trow.push(parseInt(line.charAt(x)));\n\t\t}\n\n\t\tframeList[frameIndex].push(row);\n\t\ty++;\n\n\t\tif (y === bitsy.TILE_SIZE) {\n\t\t\ti = i + y;\n\t\t\tif (lines[i] != undefined && lines[i].charAt(0) === \">\") {\n\t\t\t\t// start next frame!\n\t\t\t\tframeList.push([]);\n\t\t\t\tframeIndex++;\n\n\t\t\t\t//start the count over again for the next frame\n\t\t\t\ti++;\n\t\t\t\ty = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tstoreDrawingData(world, drwId, frameList);\n\n\treturn i;\n}\n\nfunction parseDialog(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\t// hacky but I need to store this so I can set the name below\n\tvar id = getId(lines[i]);\n\n\ti = parseScript(lines, i, world.dialog);\n\n\tif (i < lines.length && lines[i].length > 0 && getType(lines[i]) === \"NAME\") {\n\t\tworld.dialog[id].name = getNameArg(lines[i]);\n\t\ti++;\n\t}\n\n\treturn i;\n}\n\n// keeping this around to parse old files where endings were separate from dialogs\nfunction parseEnding(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\treturn parseScript(lines, i, world.end);\n}\n\nfunction parseScript(lines, i, data) {\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar results;\n\tif (scriptUtils) {\n\t\tresults = scriptUtils.ReadDialogScript(lines,i);\n\t}\n\telse {\n\t\tresults = { script: lines[i], index: (i + 1)};\n\t}\n\n\tdata[id] = createDialogData(id);\n\tdata[id].src = results.script;\n\n\ti = results.index;\n\n\treturn i;\n}\n\nfunction parseVariable(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\ti++;\n\tvar value = lines[i];\n\ti++;\n\tworld.variable[id] = value;\n\treturn i;\n}\n\nfunction parseFontName(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tworld.fontName = getArg(lines[i], 1);\n\ti++;\n\treturn i;\n}\n\nfunction parseTextDirection(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tworld.textDirection = getArg(lines[i], 1);\n\ti++;\n\treturn i;\n}\n\nfunction parseFontData(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\t// NOTE : we're not doing the actual parsing here --\n\t// just grabbing the block of text that represents the font\n\t// and giving it to the font manager to use later\n\n\tvar localFontName = getId(lines[i]);\n\tvar localFontData = lines[i];\n\ti++;\n\n\twhile (i < lines.length && lines[i] != \"\") {\n\t\tlocalFontData += \"\\n\" + lines[i];\n\t\ti++;\n\t}\n\n\tvar localFontFilename = localFontName + fontManager.GetExtension();\n\tfontManager.AddResource( localFontFilename, localFontData );\n\n\treturn i;\n}\n\nfunction parseTune(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar tuneData = createTuneData(id);\n\n\tvar barIndex = 0;\n\twhile (barIndex < maxTuneLength) {\n\t\t// MELODY\n\t\tvar melodyBar = createTuneBarData();\n\t\tvar melodyNotes = lines[i].split(\",\");\n\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t// default to a rest\n\t\t\tvar pitch = { beats: 0, note: Note.C, octave: Octave[4], };\n\n\t\t\tif (j < melodyNotes.length) {\n\t\t\t\tvar pitchSplit = melodyNotes[j].split(\"~\");\n\t\t\t\tvar pitchStr = pitchSplit[0];\n\t\t\t\tpitch = parsePitch(melodyNotes[j]);\n\n\t\t\t\t// look for effect added to the note\n\t\t\t\tif (pitchSplit.length > 1) {\n\t\t\t\t\tvar blipId = pitchSplit[1];\n\t\t\t\t\tpitch.blip = blipId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmelodyBar[j] = pitch;\n\t\t}\n\t\ttuneData.melody.push(melodyBar);\n\t\ti++;\n\n\t\t// HARMONY\n\t\tvar harmonyBar = createTuneBarData();\n\t\tvar harmonyNotes = lines[i].split(\",\");\n\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t// default to a rest\n\t\t\tvar pitch = { beats: 0, note: Note.C, octave: Octave[4], };\n\n\t\t\tif (j < harmonyNotes.length) {\n\t\t\t\tvar pitchSplit = harmonyNotes[j].split(\"~\");\n\t\t\t\tvar pitchStr = pitchSplit[0];\n\t\t\t\tpitch = parsePitch(harmonyNotes[j]);\n\n\t\t\t\t// look for effect added to the note\n\t\t\t\tif (pitchSplit.length > 1) {\n\t\t\t\t\tvar blipId = pitchSplit[1];\n\t\t\t\t\tpitch.blip = blipId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tharmonyBar[j] = pitch;\n\t\t}\n\t\ttuneData.harmony.push(harmonyBar);\n\t\ti++;\n\n\t\t// check if there's another bar after this one\n\t\tif (lines[i] === \">\") {\n\t\t\t// there is! increment the index\n\t\t\tbarIndex++;\n\t\t\ti++;\n\t\t}\n\t\telse {\n\t\t\t// we've reached the end of the tune!\n\t\t\tbarIndex = maxTuneLength;\n\t\t}\n\t}\n\n\t// parse other tune properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"KEY\") {\n\t\t\ttuneData.key = createTuneKeyData();\n\n\t\t\tvar keyNotes = getArg(lines[i], 1);\n\t\t\tif (keyNotes) {\n\t\t\t\tkeyNotes = keyNotes.split(\",\");\n\t\t\t\tfor (var j = 0; j < keyNotes.length && j < tuneData.key.notes.length; j++) {\n\t\t\t\t\tvar pitch = parsePitch(keyNotes[j]);\n\t\t\t\t\ttuneData.key.notes[j] = pitch.note;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar keyScale = getArg(lines[i], 2);\n\t\t\tif (keyScale) {\n\t\t\t\tkeyScale = keyScale.split(\",\");\n\t\t\t\tfor (var j = 0; j < keyScale.length; j++) {\n\t\t\t\t\tvar pitch = parsePitch(keyScale[j]);\n\t\t\t\t\tif (pitch.note > Solfa.NONE && pitch.note < Solfa.COUNT) {\n\t\t\t\t\t\ttuneData.key.scale.push(pitch.note);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"TMP\") {\n\t\t\tvar tempoId = getId(lines[i]);\n\t\t\tif (Tempo[tempoId] != undefined) {\n\t\t\t\ttuneData.tempo = Tempo[tempoId];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"SQR\") {\n\t\t\t// square wave instrument settings\n\t\t\tvar squareWaveIdA = getArg(lines[i], 1);\n\t\t\tif (SquareWave[squareWaveIdA] != undefined) {\n\t\t\t\ttuneData.instrumentA = SquareWave[squareWaveIdA];\n\t\t\t}\n\n\t\t\tvar squareWaveIdB = getArg(lines[i], 2);\n\t\t\tif (SquareWave[squareWaveIdB] != undefined) {\n\t\t\t\ttuneData.instrumentB = SquareWave[squareWaveIdB];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"ARP\") {\n\t\t\tvar arp = getId(lines[i]);\n\t\t\tif (ArpeggioPattern[arp] != undefined) {\n\t\t\t\ttuneData.arpeggioPattern = ArpeggioPattern[arp];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\tvar name = lines[i].split(/\\s(.+)/)[1];\n\t\t\ttuneData.name = name;\n\t\t\t// todo : add to map?\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.tune[id] = tuneData;\n\n\treturn i;\n}\n\nfunction parseBlip(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar blipData = createBlipData(id);\n\n\t// blip pitches\n\tvar notes = lines[i].split(\",\");\n\tif (notes.length >= 1) {\n\t\tblipData.pitchA = parsePitch(notes[0]);\n\t}\n\tif (notes.length >= 2) {\n\t\tblipData.pitchB = parsePitch(notes[1]);\n\t}\n\tif (notes.length >= 3) {\n\t\tblipData.pitchC = parsePitch(notes[2]);\n\t}\n\ti++;\n\n\t// blip parameters\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"ENV\") {\n\t\t\t// envelope\n\t\t\tblipData.envelope.attack = parseInt(getArg(lines[i], 1));\n\t\t\tblipData.envelope.decay = parseInt(getArg(lines[i], 2));\n\t\t\tblipData.envelope.sustain = parseInt(getArg(lines[i], 3));\n\t\t\tblipData.envelope.length = parseInt(getArg(lines[i], 4));\n\t\t\tblipData.envelope.release = parseInt(getArg(lines[i], 5));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BEAT\") {\n\t\t\t// pitch beat length\n\t\t\tblipData.beat.time = parseInt(getArg(lines[i], 1));\n\t\t\tblipData.beat.delay = parseInt(getArg(lines[i], 2));\n\t\t}\n\t\telse if (getType(lines[i]) === \"SQR\") {\n\t\t\t// square wave\n\t\t\tvar squareWaveId = getArg(lines[i], 1);\n\t\t\tif (SquareWave[squareWaveId] != undefined) {\n\t\t\t\tblipData.instrument = SquareWave[squareWaveId];\n\t\t\t}\n\t\t}\n\t\t// TODO : consider for future update\n\t\t// else if (getType(lines[i]) === \"SLD\") {\n\t\t// \t// slide mode\n\t\t// \tif (parseInt(getArg(lines[i], 1)) === 1) {\n\t\t// \t\tblipData.doSlide = true;\n\t\t// \t}\n\t\t// }\n\t\telse if (getType(lines[i]) === \"RPT\") {\n\t\t\t// repeat mode\n\t\t\tif (parseInt(getArg(lines[i], 1)) === 1) {\n\t\t\t\tblipData.doRepeat = true;\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\tvar name = lines[i].split(/\\s(.+)/)[1];\n\t\t\tblipData.name = name;\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.blip[id] = blipData;\n\n\treturn i;\n}\n\nfunction parsePitch(pitchStr) {\n\tvar pitch = { beats: 1, note: Note.C, octave: Octave[4], };\n\tvar i;\n\n\t// beats\n\tvar beatsToken = \"\";\n\tfor (i = 0; i < pitchStr.length && (\"0123456789\".indexOf(pitchStr[i]) != -1); i++) {\n\t\tbeatsToken += pitchStr[i];\n\t}\n\tif (beatsToken.length > 0) {\n\t\tpitch.beats = parseInt(beatsToken);\n\t}\n\n\t// note\n\tvar noteType;\n\tvar noteName = \"\";\n\tif (i < pitchStr.length) {\n\t\tif (pitchStr[i] === pitchStr[i].toUpperCase()) {\n\t\t\t// uppercase letters represent chromatic notes\n\t\t\tnoteType = Note;\n\t\t\tnoteName += pitchStr[i];\n\t\t\ti++;\n\n\t\t\t// check for sharp\n\t\t\tif (i < pitchStr.length && pitchStr[i] === \"#\") {\n\t\t\t\tnoteName += \"_SHARP\";\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// lowercase letters represent solfa notes\n\t\t\tnoteType = Solfa;\n\t\t\tnoteName += pitchStr[i].toUpperCase();\n\t\t\ti++;\n\t\t}\n\t}\n\n\tif (noteType != undefined && noteType[noteName] != undefined) {\n\t\tpitch.note = noteType[noteName];\n\t}\n\n\t// octave\n\tvar octaveToken = \"\";\n\tif (i < pitchStr.length) {\n\t\toctaveToken += pitchStr[i];\n\t}\n\n\tif (Octave[octaveToken] != undefined) {\n\t\tpitch.octave = Octave[octaveToken];\n\t}\n\n\treturn pitch;\n}\n\nfunction parseFlag(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\tvar valStr = lines[i].split(\" \")[2];\n\tworld.flags[id] = parseInt( valStr );\n\ti++;\n\treturn i;\n}\n\nfunction getDrawingFrameCount(world, drwId) {\n\treturn world.drawings[drwId].length;\n}\n\nfunction storeDrawingData(world, drwId, drawingData) {\n\tworld.drawings[drwId] = drawingData;\n}\n\nfunction placeSprites(parseState, world) {\n\tfor (id in parseState.spriteStartLocations) {\n\t\tworld.sprite[id].room = parseState.spriteStartLocations[id].room;\n\t\tworld.sprite[id].x = parseState.spriteStartLocations[id].x;\n\t\tworld.sprite[id].y = parseState.spriteStartLocations[id].y;\n\t}\n}\n\nfunction createNameMapsForWorld(world) {\n\tvar nameMaps = {};\n\n\tfunction createNameMap(objectStore) {\n\t\tvar map = {};\n\n\t\tfor (id in objectStore) {\n\t\t\tif (objectStore[id].name != undefined && objectStore[id].name != null) {\n\t\t\t\tmap[objectStore[id].name] = id;\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\tnameMaps.room = createNameMap(world.room);\n\tnameMaps.tile = createNameMap(world.tile);\n\tnameMaps.sprite = createNameMap(world.sprite);\n\tnameMaps.item = createNameMap(world.item);\n\tnameMaps.dialog = createNameMap(world.dialog);\n\tnameMaps.palette = createNameMap(world.palette);\n\tnameMaps.tune = createNameMap(world.tune);\n\tnameMaps.blip = createNameMap(world.blip);\n\n\treturn nameMaps;\n}\n\nfunction getType(line) {\n\treturn getArg(line,0);\n}\n\nfunction getId(line) {\n\treturn getArg(line,1);\n}\n\nfunction getCoord(line,arg) {\n\treturn getArg(line,arg).split(\",\");\n}\n\nfunction getArg(line,arg) {\n\treturn line.split(\" \")[arg];\n}\n\nfunction getNameArg(line) {\n\tvar name = line.split(/\\s(.+)/)[1];\n\treturn name;\n}",
+ "system.js": "/* LOGGING */\nvar DebugLogCategory = {\n\t// system\n\tinput: false,\n\tsound: false,\n\tgraphics: false,\n\tsystem: false,\n\n\t// engine\n\tbitsy: false,\n\n\t// editor\n\teditor: false,\n\n\t// tools\n\troom: false,\n\ttune: false,\n\tblip: false,\n};\n\nvar isLoggingVerbose = false;\n\nfunction bitsyLog(message, category) {\n\tif (!category) {\n\t\tcategory = \"bitsy\";\n\t}\n\n\tvar summary = category + \"::\" + message;\n\n\tif (DebugLogCategory[category] === true) {\n\t\tif (isLoggingVerbose) {\n\t\t\tconsole.group(summary);\n\n\t\t\tconsole.dir(message);\n\n\t\t\tconsole.group(\"stack\")\n\t\t\tconsole.trace();\n\t\t\tconsole.groupEnd();\n\n\t\t\tconsole.groupEnd();\n\t\t}\n\t\telse {\n\t\t\tconsole.log(summary);\n\t\t}\n\t}\n}\n\n/* GLOBALS */\nvar tilesize = 8;\nvar mapsize = 16;\nvar width = mapsize * tilesize;\nvar height = mapsize * tilesize;\nvar scale = 4;\nvar textScale = 2;\n\n/* SYSTEM */\nvar updateInterval = null;\nvar prevTime = 0;\nvar deltaTime = 0;\n\nfunction initSystem() {\n\tprevTime = Date.now();\n\tupdateInterval = setInterval(updateSystem, 16);\n}\n\nfunction updateSystem() {\n\tvar curTime = Date.now();\n\tdeltaTime = curTime - prevTime;\n\n\t// update all active processes\n\tfor (var i = 0; i < processes.length; i++) {\n\t\tbitsy = processes[i].system;\n\t\tif (bitsy._active) {\n\t\t\tbitsyLog(bitsy._name + \" img count: \" + bitsy._graphics._images.length, \"system\");\n\t\t\tvar shouldContinue = bitsy._update(deltaTime);\n\t\t\tif (!shouldContinue) {\n\t\t\t\t// todo : do I really care about this _exit thing?\n\t\t\t\tif (bitsy._name != \"bitsy\") {\n\t\t\t\t\tbitsy._exit();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tbitsy = mainProcess.system;\n\tprevTime = curTime;\n}\n\nfunction loadGame(canvas, gameData, defaultFontData) {\n\tbitsyLog(\"load!\", \"system\");\n\t// initialize bitsy system\n\tbitsy._attachCanvas(canvas);\n\tbitsy._write(bitsy._gameDataBlock, gameData);\n\tbitsy._write(bitsy._fontDataBlock, defaultFontData);\n\tbitsy._start();\n}\n\nfunction quitGame() {\n\t// hack to press the menu button to force game over state\n\tbitsy._injectPreLoop = function() { bitsy._poke(bitsy._buttonBlock, bitsy.BTN_MENU, 1); };\n\n\t// one last update to clean up (a little hacky to do this here?)\n\tbitsy._update(0);\n\tbitsy._exit();\n\n\t// clean up this gross hack\n\tbitsy._injectPreLoop = null;\n}\n\n/* GRAPHICS */\nvar canvas; // can I get rid of these?\nvar ctx;\n\nfunction attachCanvas(c) {\n\t// hack : tes tnew system\n\tbitsy._attachCanvas(c);\n\t// extra hacky\n\tcanvas = bitsy._getCanvas();\n\tctx = bitsy._getContext();\n}\n\n/* PROCESSES */\nvar processes = [];\n\nfunction addProcess(name) {\n\tvar proc = {};\n\tproc.system = new BitsySystem(name);\n\n\tprocesses.push(proc);\n\n\treturn proc;\n}\n\n/* == SYSTEM v0.2 === */\nfunction BitsySystem(name) {\n\tvar self = this;\n\n\tif (!name) {\n\t\tname = \"bitsy\";\n\t}\n\n\t// memory\n\tvar memory = {\n\t\tblocks: [],\n\t\tchanged: []\n\t};\n\n\t// input\n\tvar input = new InputSystem();\n\n\t// sound\n\tvar sound = new SoundSystem();\n\tvar soundDurationIndex = 0;\n\tvar soundFrequencyIndex = 1;\n\tvar soundVolumeIndex = 2;\n\tvar soundPulseIndex = 3;\n\tvar maxVolume = 15;\n\n\t// graphics\n\tvar graphics = new GraphicsSystem();\n\tgraphics.setScale(scale);\n\tgraphics.setTextScale(textScale);\n\tvar initialPaletteSize = 64;\n\tvar tilePoolStart = null;\n\tvar tilePoolSize = 512;\n\t// hack!!! (access for debugging)\n\tthis._graphics = graphics;\n\n\tfunction updateTextScale() {\n\t\t// make sure the text scale matches the text mode\n\t\tvar textMode = self._peek(modeBlock, 1);\n\t\tvar textModeScale = (textMode === self.TXT_LOREZ) ? scale : textScale;\n\t\tif (graphics.getTextScale() != textModeScale) {\n\t\t\tgraphics.setTextScale(textModeScale);\n\t\t\tmemory.changed[self.TEXTBOX] = true;\n\t\t}\n\t}\n\n\tfunction updateInput() {\n\t\t// update input flags\n\t\tself._poke(self._buttonBlock, self.BTN_UP,\n\t\t\t(input.isKeyDown(input.Key.UP) || input.isKeyDown(input.Key.W) || input.swipeUp()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_DOWN,\n\t\t\t(input.isKeyDown(input.Key.DOWN) || input.isKeyDown(input.Key.S) || input.swipeDown()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_LEFT,\n\t\t\t(input.isKeyDown(input.Key.LEFT) || input.isKeyDown(input.Key.A) || input.swipeLeft()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_RIGHT,\n\t\t\t(input.isKeyDown(input.Key.RIGHT) || input.isKeyDown(input.Key.D) || input.swipeRight()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_OK,\n\t\t\t(input.anyKeyDown() || input.isTapReleased()) ? 1 : 0);\n\n\t\tself._poke(self._buttonBlock, self.BTN_MENU,\n\t\t\t(input.isRestartComboPressed()) ? 1 : 0);\n\n\t\tinput.resetTapReleased();\n\t}\n\n\tfunction updateSound(dt) {\n\t\tvar changed0 = memory.changed[self.SOUND1];\n\t\tvar changed1 = memory.changed[self.SOUND2];\n\n\t\t// update sound channel timers\n\t\tvar timer0 = self._peek(self.SOUND1, soundDurationIndex);\n\t\ttimer0 -= dt;\n\t\tif (timer0 <= 0) {\n\t\t\ttimer0 = 0;\n\t\t\tif (self._peek(self.SOUND1, soundVolumeIndex) > 0) {\n\t\t\t\tself._poke(self.SOUND1, soundVolumeIndex, 0);\n\t\t\t\tchanged0 = true;\n\t\t\t}\n\t\t}\n\t\tself._poke(self.SOUND1, soundDurationIndex, timer0);\n\n\t\tvar timer1 = self._peek(self.SOUND2, soundDurationIndex);\n\t\ttimer1 -= dt;\n\t\tif (timer1 <= 0) {\n\t\t\ttimer1 = 0;\n\t\t\tif (self._peek(self.SOUND2, soundVolumeIndex) > 0) {\n\t\t\t\tself._poke(self.SOUND2, soundVolumeIndex, 0);\n\t\t\t\tchanged1 = true;\n\t\t\t}\n\t\t}\n\t\tself._poke(self.SOUND2, soundDurationIndex, timer1);\n\n\t\t// send updated channel attributes to the sound system\n\t\tif (changed0) {\n\t\t\tsound.setPulse(0, self._peek(self.SOUND1, soundPulseIndex));\n\n\t\t\tvar freq = self._peek(self.SOUND1, soundFrequencyIndex);\n\t\t\tvar freqHz = freq / 100;\n\t\t\tsound.setFrequency(0, freqHz);\n\n\t\t\tvar volume = self._peek(self.SOUND1, soundVolumeIndex);\n\t\t\tvolume = Math.max(0, Math.min(volume, maxVolume));\n\t\t\tvolumeNorm = (volume / maxVolume);\n\t\t\tsound.setVolume(0, volumeNorm);\n\t\t}\n\n\t\tif (changed1) {\n\t\t\tsound.setPulse(1, self._peek(self.SOUND2, soundPulseIndex));\n\n\t\t\tvar freq = self._peek(self.SOUND2, soundFrequencyIndex);\n\t\t\tvar freqHz = freq / 100;\n\t\t\tsound.setFrequency(1, freqHz);\n\n\t\t\tvar volume = self._peek(self.SOUND2, soundVolumeIndex);\n\t\t\tvolume = Math.max(0, Math.min(volume, maxVolume));\n\t\t\tvolumeNorm = (volume / maxVolume);\n\t\t\tsound.setVolume(1, volumeNorm);\n\t\t}\n\t}\n\n\tfunction updateGraphics() {\n\t\tif (self._enableGraphics === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tbitsyLog(\"update graphics\", \"system\");\n\n\t\tif (memory.changed[paletteBlock]) {\n\t\t\tgraphics.setPalette(self._dump()[paletteBlock]);\n\t\t}\n\n\t\tif (tilePoolStart != null) {\n\t\t\tfor (var i = 0; i < tilePoolSize; i++) {\n\t\t\t\tvar tile = tilePoolStart + i;\n\t\t\t\tif (memory.blocks[tile] != undefined && memory.changed[tile]) {\n\t\t\t\t\tbitsyLog(\"tile changed? \" + tile, \"system\");\n\t\t\t\t\t// update tile image\n\t\t\t\t\tgraphics.createImage(tile, self.TILE_SIZE, self.TILE_SIZE, self._dump()[tile]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar textboxChanged = memory.changed[self.TEXTBOX] || memory.changed[textboxAttributeBlock];\n\t\tif (textboxChanged) {\n\t\t\t// todo : should this be optimized in some way?\n\t\t\t// update textbox image\n\t\t\tvar w = self._peek(textboxAttributeBlock, 3); // todo : need a variable to store this index?\n\t\t\tvar h = self._peek(textboxAttributeBlock, 4);\n\t\t\tif (w > 0 && h > 0) {\n\t\t\t\tbitsyLog(\"textbox changed! \" + memory.changed[self.TEXTBOX] + \" \" + memory.changed[textboxAttributeBlock] + \" \" + w + \" \" + h, \"system\");\n\t\t\t\tvar useTextBoxScale = true; // todo : check mode here?\n\t\t\t\tgraphics.createImage(self.TEXTBOX, w, h, self._dump()[self.TEXTBOX], useTextBoxScale);\n\t\t\t}\n\t\t}\n\n\t\tvar mode = self._peek(modeBlock, 0);\n\t\tif (mode === self.GFX_VIDEO) {\n\t\t\tif (memory.changed[self.VIDEO]) {\n\t\t\t\tgraphics.clearCanvas(0);\n\t\t\t\t// update screen image\n\t\t\t\tgraphics.createImage(self.VIDEO, self.VIDEO_SIZE, self.VIDEO_SIZE, self._dump()[self.VIDEO]);\n\t\t\t\t// render screen onto canvas\n\t\t\t\tgraphics.drawImage(self.VIDEO, 0, 0);\n\t\t\t}\n\t\t}\n\t\telse if (mode === self.GFX_MAP) {\n\t\t\t// redraw any changed layers\n\t\t\tvar layers = self._getTileMapLayers();\n\t\t\tvar anyMapLayerChanged = false;\n\t\t\tfor (var i = 0; i < layers.length; i++) {\n\t\t\t\tvar layerId = layers[i];\n\t\t\t\tif (memory.changed[layerId]) {\n\t\t\t\t\t// need to redraw this map layer\n\t\t\t\t\tanyMapLayerChanged = true;\n\t\t\t\t\t// clear layer canvas\n\t\t\t\t\tgraphics.setImageFill(layerId, 0); // fill transparent\n\t\t\t\t\tgraphics.createImage(layerId, self.VIDEO_SIZE, self.VIDEO_SIZE, []);\n\t\t\t\t\t// render tiles onto layer canvas\n\t\t\t\t\tvar layerData = self._dump()[layerId];\n\t\t\t\t\tfor (var ty = 0; ty < self.MAP_SIZE; ty++) {\n\t\t\t\t\t\tfor (var tx = 0; tx < self.MAP_SIZE; tx++) {\n\t\t\t\t\t\t\tvar tileIndex = (ty * self.MAP_SIZE) + tx;\n\t\t\t\t\t\t\tvar tile = layerData[tileIndex];\n\t\t\t\t\t\t\tif (tile > 0) {\n\t\t\t\t\t\t\t\tgraphics.drawImage(tile, tx * self.TILE_SIZE, ty * self.TILE_SIZE, layerId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// redraw the main canvas\n\t\t\tif (textboxChanged || anyMapLayerChanged) {\n\t\t\t\tbitsyLog(\"map changed? \" + memory.changed[self.MAP1] + \" \" + memory.changed[self.MAP2], \"system\");\n\t\t\t\tgraphics.clearCanvas(0);\n\n\t\t\t\tfor (var i = 0; i < layers.length; i++) {\n\t\t\t\t\tvar layerId = layers[i];\n\t\t\t\t\t// draw the layer's image canvas onto the main canvas\n\t\t\t\t\tgraphics.drawImage(layerId, 0, 0);\n\t\t\t\t}\n\n\t\t\t\t// draw textbox onto canvas\n\t\t\t\tvar visible = self._peek(textboxAttributeBlock, 0)\n\t\t\t\tvar x = self._peek(textboxAttributeBlock, 1);\n\t\t\t\tvar y = self._peek(textboxAttributeBlock, 2);\n\t\t\t\tvar w = self._peek(textboxAttributeBlock, 3);\n\t\t\t\tvar h = self._peek(textboxAttributeBlock, 4);\n\t\t\t\tif (visible > 0 && w > 0 && h > 0) {\n\t\t\t\t\tgraphics.drawImage(self.TEXTBOX, x, y);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* == PRIVATE / DEBUG == */\n\tthis._name = name;\n\n\tthis._active = false;\n\n\tthis._attachCanvas = function(c) {\n\t\tgraphics.attachCanvas(c, self.VIDEO_SIZE);\n\t};\n\n\tthis._getCanvas = graphics.getCanvas;\n\tthis._getContext = graphics.getContext;\n\n\tthis._start = function() {\n\t\tinput.listen(graphics.getCanvas());\n\t\tupdateTextScale();\n\t\tself._active = true;\n\t};\n\n\t// hacky...\n\tthis._startNoInput = function() {\n\t\tupdateTextScale();\n\t\tself._active = true;\n\t};\n\n\tthis._exit = function() {\n\t\t// disable graphics\n\t\tvar canvas = graphics.getCanvas();\n\t\tif (canvas) {\n\t\t\tinput.unlisten(canvas);\t\n\t\t}\n\n\t\t// disable sound\n\t\tsound.mute();\n\n\t\tself._active = false;\n\t};\n\n\t// hacky....\n\tthis._injectPreLoop = null;\n\tthis._injectPostDraw = null;\n\n\tthis._update = function(dt) {\n\t\tvar shouldContinue = false;\n\n\t\tupdateInput();\n\n\t\t// too hacky???\n\t\tif (self._injectPreLoop) {\n\t\t\tself._injectPreLoop();\n\t\t}\n\n\t\t// run main loop\n\t\tif (onLoopFunction) {\n\t\t\tshouldContinue = onLoopFunction(dt);\n\t\t}\n\n\t\tif (memory.changed[modeBlock]) {\n\t\t\tupdateTextScale();\n\t\t}\n\n\t\t// update output systems\n\t\tupdateSound(dt);\n\t\tupdateGraphics();\n\n\t\tif (self._injectPostDraw) {\n\t\t\tself._injectPostDraw();\n\t\t}\n\n\t\t// reset memory block changed flags\n\t\tfor (var i = 0; i < memory.changed.length; i++) {\n\t\t\tmemory.changed[i] = false;\n\t\t}\n\n\t\t// todo : should the _exit() call go in here?\n\n\t\treturn shouldContinue;\n\t};\n\n\tthis._updateGraphics = updateGraphics;\n\n\tthis._allocate = function(args) {\n\t\t// find next available block in range\n\t\tvar next = (args && args.start) ? args.start : 0;\n\t\tvar count = (args && args.max) ? args.max : -1;\n\t\twhile (memory.blocks[next] != undefined && count != 0) {\n\t\t\tnext++;\n\t\t\tcount--;\n\t\t}\n\n\t\tif (count == 0) {\n\t\t\t// couldn't find any available block\n\t\t\treturn null;\n\t\t}\n\n\t\tif (args && args.str) {\n\t\t\tmemory.blocks[next] = args.str;\n\t\t}\n\t\telse {\n\t\t\tvar size = args && args.size ? args.size : 0;\n\t\t\tmemory.blocks[next] = [];\n\t\t\tfor (var i = 0; i < size; i++) {\n\t\t\t\tmemory.blocks[next].push(0);\n\t\t\t}\n\t\t}\n\n\t\tmemory.changed[next] = false;\n\n\t\treturn next;\n\t};\n\n\tthis._free = function(block) {\n\t\tdelete memory.blocks[block];\n\t\tdelete memory.changed[block];\n\t};\n\n\tthis._peek = function(block, index) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\treturn memoryBlock.charCodeAt(index);\n\t\t}\n\t\telse {\n\t\t\treturn memoryBlock[index];\n\t\t}\n\t};\n\n\tthis._poke = function(block, index, value) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\tmemory.blocks[block] = memoryBlock.substring(0, index) + String.fromCharCode(value) + memoryBlock.substring(index + 1);\n\t\t}\n\t\telse {\n\t\t\tvar value = parseInt(value);\n\t\t\tif (!isNaN(value)) {\n\t\t\t\tmemoryBlock[index] = value;\n\t\t\t}\n\t\t}\n\t\tmemory.changed[block] = true;\n\t};\n\n\tthis._read = function(block) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\treturn memoryBlock;\n\t\t}\n\t\telse {\n\t\t\tvar str = \"\";\n\t\t\tfor (var i = 0; i < memoryBlock.length; i++) {\n\t\t\t\tstr += String.fromCharCode(memoryBlock[i]);\n\t\t\t}\n\t\t\treturn str;\n\t\t}\n\t};\n\n\tthis._write = function(block, str) {\n\t\tvar memoryBlock = memory.blocks[block];\n\t\tif (typeof(memoryBlock) === \"string\") {\n\t\t\tmemory.blocks[block] = str;\n\t\t}\n\t\telse {\n\t\t\tmemory.blocks[block] = [];\n\t\t\tfor (var i = 0; i < str.length; i++) {\n\t\t\t\tmemory.blocks[block][i] = str.charCodeAt(i);\n\t\t\t}\n\t\t}\n\t\tmemory.changed[block] = true;\n\t};\n\n\tthis._dump = function() {\n\t\treturn memory.blocks;\n\t};\n\n\t// convenience methods for hacking around with map layers\n\tvar tileMapLayers = [];\n\tthis._getTileMapLayers = function() {\n\t\treturn tileMapLayers;\n\t};\n\tthis._addTileMapLayer = function() {\n\t\tvar layer = self._allocate({\n\t\t\tstart: (tilePoolStart + tilePoolSize),\n\t\t\tsize: (self.MAP_SIZE * self.MAP_SIZE)\n\t\t});\n\n\t\ttileMapLayers.push(layer);\n\n\t\treturn layer;\n\t};\n\n\t/* == CONSTANTS == */\n\t// memory blocks (these will be initialized below)\n\tthis.VIDEO;\n\tthis.TEXTBOX;\n\tthis.MAP1;\n\tthis.MAP2;\n\tthis.SOUND1;\n\tthis.SOUND2;\n\n\t// graphics modes\n\tthis.GFX_VIDEO = 0;\n\tthis.GFX_MAP = 1;\n\n\t// text modes\n\tthis.TXT_HIREZ = 0; // 2x resolution\n\tthis.TXT_LOREZ = 1; // 1x resolution\n\n\t// size\n\tthis.TILE_SIZE = tilesize;\n\tthis.MAP_SIZE = mapsize;\n\tthis.VIDEO_SIZE = width;\n\t// todo : should text scale have a constant?\n\n\t// button codes\n\tthis.BTN_UP = 0;\n\tthis.BTN_DOWN = 1;\n\tthis.BTN_LEFT = 2;\n\tthis.BTN_RIGHT = 3;\n\tthis.BTN_OK = 4;\n\tthis.BTN_MENU = 5;\n\n\t// pulse waves\n\tthis.PULSE_1_8 = 0;\n\tthis.PULSE_1_4 = 1;\n\tthis.PULSE_1_2 = 2;\n\n\t/* == IO == */\n\tthis.log = function(message) {\n\t\tbitsyLog(message, name);\n\t};\n\n\tthis.button = function(code) {\n\t\treturn self._peek(buttonBlock, code) > 0;\n\t};\n\n\tthis.getGameData = function() {\n\t\treturn self._read(gameDataBlock);\n\t};\n\n\tthis.getFontData = function() {\n\t\treturn self._read(fontDataBlock);\n\t};\n\n\t/* == GRAPHICS == */\n\tthis.graphicsMode = function(mode) {\n\t\t// todo : store the mode flag indices somewhere?\n\t\tif (mode != undefined) {\n\t\t\tself._poke(modeBlock, 0, mode);\n\t\t}\n\n\t\treturn self._peek(modeBlock, 0);\n\t};\n\n\tthis.textMode = function(mode) {\n\t\t// todo : test whether the requested mode is supported!\n\t\tif (mode != undefined) {\n\t\t\tself._poke(modeBlock, 1, mode);\n\t\t}\n\n\t\treturn self._peek(modeBlock, 1);\n\t};\n\n\tthis.color = function(color, r, g, b) {\n\t\tself._poke(paletteBlock, (color * 3) + 0, r);\n\t\tself._poke(paletteBlock, (color * 3) + 1, g);\n\t\tself._poke(paletteBlock, (color * 3) + 2, b);\n\n\t\t// mark all graphics as changed\n\t\tmemory.changed[self.VIDEO] = true;\n\t\tmemory.changed[self.TEXTBOX] = true;\n\t\tmemory.changed[self.MAP1] = true;\n\t\tmemory.changed[self.MAP2] = true;\n\n\t\tif (tilePoolStart != null) {\n\t\t\tfor (var i = 0; i < tilePoolSize; i++) {\n\t\t\t\tif (memory.blocks[tilePoolStart + i] != undefined) {\n\t\t\t\t\tmemory.changed[tilePoolStart + i] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tthis.tile = function() {\n\t\treturn self._allocate({\n\t\t\tstart: tilePoolStart,\n\t\t\tmax: tilePoolSize,\n\t\t\tsize: (self.TILE_SIZE * self.TILE_SIZE)\n\t\t});\n\t};\n\n\tthis.delete = function(tile) {\n\t\tif (graphics.hasImage(tile)) {\n\t\t\tgraphics.deleteImage(tile);\n\t\t}\n\n\t\tself._free(tile);\n\t};\n\n\tthis.fill = function(block, value) {\n\t\tvar len = memory.blocks[block].length;\n\t\tfor (var i = 0; i < len; i++) {\n\t\t\tself._poke(block, i, value);\n\t\t}\n\n\t\tvar isImage = (block === self.VIDEO) ||\n\t\t\t(block === self.TEXTBOX) ||\n\t\t\t(block >= tilePoolStart && block < (tilePoolStart + tilePoolSize));\n\n\t\t// optimize rendering by notifying the graphics system what the fill color is for this image\n\t\tif (isImage) {\n\t\t\tgraphics.setImageFill(block, value);\n\t\t}\n\t};\n\n\tthis.set = function(block, index, value) {\n\t\tself._poke(block, index, value);\n\t};\n\n\tthis.textbox = function(visible, x, y, w, h) {\n\t\tif (visible != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 0, (visible === true) ? 1 : 0);\n\t\t}\n\t\t\n\t\tif (x != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 1, x);\n\t\t}\n\t\t\n\t\tif (y != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 2, y);\n\t\t}\n\n\t\tvar prevWidth = self._peek(textboxAttributeBlock, 3);\n\t\tvar prevHeight = self._peek(textboxAttributeBlock, 4);\n\n\t\tif (w != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 3, w);\n\t\t}\n\t\t\n\t\tif (h != undefined) {\n\t\t\tself._poke(textboxAttributeBlock, 4, h);\n\t\t}\n\n\t\tif (w != undefined && h != undefined && (prevWidth != w || prevHeight != h)) {\n\t\t\t// re-allocate the textbox block (should I have a helper function for this?)\n\t\t\tmemory.blocks[self.TEXTBOX] = [];\n\t\t\tfor (var i = 0; i < (w * h); i++) {\n\t\t\t\tmemory.blocks[self.TEXTBOX].push(0);\n\t\t\t}\n\t\t\tmemory.changed[self.TEXTBOX] = true;\n\t\t}\n\t};\n\n\t/* == SOUND == */\n\t// duration is in milliseconds (ms)\n\tthis.sound = function(channel, duration, frequency, volume, pulse) {\n\t\tself._poke(channel, soundDurationIndex, duration);\n\t\tself._poke(channel, soundFrequencyIndex, frequency);\n\t\tself._poke(channel, soundVolumeIndex, volume);\n\t\tself._poke(channel, soundPulseIndex, pulse);\n\t};\n\n\t// frequency is in decihertz (dHz)\n\tthis.frequency = function(channel, frequency) {\n\t\tself._poke(channel, soundFrequencyIndex, frequency);\n\t};\n\n\t// volume: min = 0, max = 15\n\tthis.volume = function(channel, volume) {\n\t\tself._poke(channel, soundVolumeIndex, volume);\n\t};\n\n\t/* == EVENTS == */\n\tthis.loop = function(fn) {\n\t\tonLoopFunction = fn;\n\t};\n\n\t/* == INTERNAL == */\n\t// initialize memory blocks\n\tvar gameDataBlock = this._allocate({ str: \"\" });\n\tvar fontDataBlock = this._allocate({ str: \"\" });\n\tthis.VIDEO = this._allocate({ size: self.VIDEO_SIZE * self.VIDEO_SIZE });\n\tthis.TEXTBOX = this._allocate();\n\tthis.MAP1 = this._allocate({ size: self.MAP_SIZE * self.MAP_SIZE });\n\ttileMapLayers.push(this.MAP1);\n\tthis.MAP2 = this._allocate({ size: self.MAP_SIZE * self.MAP_SIZE });\n\ttileMapLayers.push(this.MAP2);\n\tvar paletteBlock = this._allocate({ size: initialPaletteSize * 3 });\n\tvar buttonBlock = this._allocate({ size: 8 });\n\tthis.SOUND1 = this._allocate({ size: 4 });\n\tthis.SOUND2 = this._allocate({ size: 4 });\n\tvar modeBlock = this._allocate({ size: 8 });\n\tvar textboxAttributeBlock = this._allocate({ size: 8 });\n\n\ttilePoolStart = (textboxAttributeBlock + 1);\n\n\t// access for debugging\n\tthis._gameDataBlock = gameDataBlock;\n\tthis._fontDataBlock = fontDataBlock;\n\tthis._buttonBlock = buttonBlock;\n\n\t// events\n\tvar onLoopFunction = null;\n}\n\nvar mainProcess = addProcess();\nvar bitsy = mainProcess.system;",
+ "world.js": "/* BITSY VERSION */\n// is this the right place for this to live?\nvar version = {\n\tmajor: 8, // major changes\n\tminor: 11, // smaller changes\n\tdevBuildPhase: \"RELEASE\",\n};\nfunction getEngineVersion() {\n\treturn version.major + \".\" + version.minor;\n}\n\n/* TEXT CONSTANTS */\nvar titleDialogId = \"title\";\n\n// todo : where should this be stored?\nvar tileColorStartIndex = 16;\n\nvar TextDirection = {\n\tLeftToRight : \"LTR\",\n\tRightToLeft : \"RTL\"\n};\n\nvar defaultFontName = \"ascii_small\";\n\n/* TUNE CONSTANTS */\nvar barLength = 16; // sixteenth notes\nvar minTuneLength = 1;\nvar maxTuneLength = 16;\n\n// chromatic notes\nvar Note = {\n\tNONE \t\t: -1,\n\tC \t\t\t: 0,\t// C\n\tC_SHARP \t: 1,\t// C sharp / D flat\n\tD \t\t\t: 2,\t// D\n\tD_SHARP \t: 3,\t// D sharp / E flat\n\tE \t\t\t: 4,\t// E\n\tF \t\t\t: 5,\t// F\n\tF_SHARP \t: 6,\t// F sharp / G flat\n\tG \t\t\t: 7,\t// G\n\tG_SHARP \t: 8,\t// G sharp / A flat\n\tA \t\t\t: 9,\t// A\n\tA_SHARP \t: 10,\t// A sharp / B flat\n\tB \t\t\t: 11,\t// B\n\tCOUNT \t\t: 12\n};\n\n// solfa notes\nvar Solfa = {\n\tNONE \t: -1,\n\tD \t\t: 0,\t// Do\n\tR \t\t: 1,\t// Re\n\tM \t\t: 2,\t// Mi\n\tF \t\t: 3,\t// Fa\n\tS \t\t: 4,\t// Sol\n\tL \t\t: 5,\t// La\n\tT \t\t: 6,\t// Ti\n\tCOUNT \t: 7\n};\n\nvar Octave = {\n\tNONE: -1,\n\t2: 0,\n\t3: 1,\n\t4: 2, // octave 4: middle C octave\n\t5: 3,\n\tCOUNT: 4\n};\n\nvar Tempo = {\n\tSLW: 0, // slow\n\tMED: 1, // medium\n\tFST: 2, // fast\n\tXFST: 3 // extra fast (aka turbo)\n};\n\nvar SquareWave = {\n\tP8: 0, // pulse 1 / 8\n\tP4: 1, // pulse 1 / 4\n\tP2: 2, // pulse 1 / 2\n\tCOUNT: 3\n};\n\nvar ArpeggioPattern = {\n\tOFF: 0,\n\tUP: 1, // ascending triad chord\n\tDWN: 2, // descending triad chord\n\tINT5: 3, // 5 step interval\n\tINT8: 4 // 8 setp interval\n};\n\nfunction createWorldData() {\n\treturn {\n\t\troom : {},\n\t\ttile : {},\n\t\tsprite : {},\n\t\titem : {},\n\t\tdialog : {},\n\t\tend : {}, // pre-7.0 ending data for backwards compatibility\n\t\tpalette : { // start off with a default palette\n\t\t\t\"default\" : {\n\t\t\t\tname : \"default\",\n\t\t\t\tcolors : [[0,0,0],[255,255,255],[255,255,255]]\n\t\t\t}\n\t\t},\n\t\tvariable : {},\n\t\ttune : {},\n\t\tblip : {},\n\t\tversionNumberFromComment : -1, // -1 indicates no version information found\n\t\tfontName : defaultFontName,\n\t\ttextDirection : TextDirection.LeftToRight,\n\t\tflags : createDefaultFlags(),\n\t\tnames : {},\n\t\t// source data for all drawings (todo: better name?)\n\t\tdrawings : {},\n\t};\n}\n\n// creates a drawing data structure with default property values for the type\nfunction createDrawingData(type, id) {\n\t// the avatar's drawing id still uses the sprite prefix (for back compat)\n\tvar drwId = (type === \"AVA\" ? \"SPR\" : type) + \"_\" + id;\n\n\tvar drawingData = {\n\t\ttype : type,\n\t\tid : id,\n\t\tname : null,\n\t\tdrw : drwId,\n\t\tcol : (type === \"TIL\") ? 1 : 2, // foreground color\n\t\tbgc : 0, // background color\n\t\tanimation : {\n\t\t\tisAnimated : false,\n\t\t\tframeIndex : 0,\n\t\t\tframeCount : 1,\n\t\t},\n\t};\n\n\t// add type specific properties\n\tif (type === \"TIL\") {\n\t\t// default null value indicates it can vary from room to room (original version)\n\t\tdrawingData.isWall = null;\n\t}\n\n\tif (type === \"AVA\" || type === \"SPR\") {\n\t\t// default sprite location is \"offstage\"\n\t\tdrawingData.room = null;\n\t\tdrawingData.x = -1;\n\t\tdrawingData.y = -1;\n\t\tdrawingData.inventory = {};\n\t}\n\n\tif (type === \"AVA\" || type === \"SPR\" || type === \"ITM\") {\n\t\tdrawingData.dlg = null;\n\t\tdrawingData.blip = null;\n\t}\n\n\treturn drawingData;\n}\n\nfunction createTuneData(id) {\n\tvar tuneData = {\n\t\tid : id,\n\t\tname : null,\n\t\tmelody : [],\n\t\tharmony : [],\n\t\tkey: null, // a null key indicates a chromatic scale (all notes enabled)\n\t\ttempo: Tempo.MED,\n\t\tinstrumentA : SquareWave.P2,\n\t\tinstrumentB : SquareWave.P2,\n\t\tarpeggioPattern : ArpeggioPattern.OFF,\n\t};\n\treturn tuneData;\n}\n\nfunction createTuneBarData() {\n\tvar bar = [];\n\tfor (var i = 0; i < barLength; i++) {\n\t\tbar.push({ beats: 0, note: Note.C, octave: Octave[4] });\n\t}\n\treturn bar;\n}\n\nfunction createTuneKeyData() {\n\tvar key = {\n\t\tnotes: [], // mapping of the solfa scale degrees to chromatic notes\n\t\tscale: [] // list of solfa notes that are enabled for this key\n\t};\n\n\t// initialize notes\n\tfor (var i = 0; i < Solfa.COUNT; i++) {\n\t\tkey.notes.push(Note.NONE);\n\t}\n\n\treturn key;\n}\n\nfunction createBlipData(id) {\n\tvar blipData = {\n\t\tid: id,\n\t\tname: null,\n\t\tpitchA: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tpitchB: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tpitchC: { beats: 0, note: Note.C, octave: Octave[4] },\n\t\tenvelope: {\n\t\t\tattack: 0, // attack time in ms\n\t\t\tdecay: 0, // decay time in ms\n\t\t\tsustain: 0, // sustain volume\n\t\t\tlength: 0, // sustain time in ms\n\t\t\trelease: 0 // release time in ms\n\t\t},\n\t\tbeat : {\n\t\t\ttime: 0, // time in ms between pitch changes\n\t\t\tdelay: 0 // time in ms *before* first pitch change\n\t\t},\n\t\tinstrument: SquareWave.P2,\n\t\tdoRepeat: false\n\t\t// TODO : consider for future update\n\t\t// doSlide: false,\n\t};\n\n\treturn blipData;\n}\n\nfunction createDefaultFlags() {\n\treturn {\n\t\t// version\n\t\tVER_MAJ: -1, // major version number (-1 = no version information found)\n\t\tVER_MIN: -1, // minor version number (-1 = no version information found)\n\t\t// compatibility\n\t\tROOM_FORMAT: 0, // 0 = non-comma separated (original), 1 = comma separated (default)\n\t\tDLG_COMPAT: 0, // 0 = default dialog behavior, 1 = pre-7.0 dialog behavior\n\t\t// config\n\t\tTXT_MODE: 0 // 0 = HIREZ (2x - default), 1 = LOREZ (1x)\n\t};\n}\n\nfunction createDialogData(id) {\n\treturn {\n\t\tsrc : \"\",\n\t\tname : null,\n\t\tid : id,\n\t};\n}\n\nfunction parseWorld(file) {\n\tbitsy.log(\"create world data\");\n\n\tvar world = createWorldData();\n\n\tbitsy.log(\"init parse state\");\n\n\tvar parseState = {\n\t\tlines : file.split(\"\\n\"),\n\t\tindex : 0,\n\t\tspriteStartLocations : {}\n\t};\n\n\tbitsy.log(\"start reading lines\");\n\n\twhile (parseState.index < parseState.lines.length) {\n\t\tvar i = parseState.index;\n\t\tvar lines = parseState.lines;\n\t\tvar curLine = lines[i];\n\n\t\t// bitsy.log(\"LN \" + i + \" xx \" + curLine);\n\n\t\tif (i == 0) {\n\t\t\ti = parseTitle(parseState, world);\n\t\t}\n\t\telse if (curLine.length <= 0 || curLine.charAt(0) === \"#\") {\n\t\t\t// collect version number from a comment (hacky but required for pre-8.0 compatibility)\n\t\t\tif (curLine.indexOf(\"# BITSY VERSION \") != -1) {\n\t\t\t\tworld.versionNumberFromComment = parseFloat(curLine.replace(\"# BITSY VERSION \", \"\"));\n\t\t\t}\n\n\t\t\t//skip blank lines & comments\n\t\t\ti++;\n\t\t}\n\t\telse if (getType(curLine) === \"PAL\") {\n\t\t\ti = parsePalette(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"ROOM\" || getType(curLine) === \"SET\") { // SET for back compat\n\t\t\ti = parseRoom(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TIL\") {\n\t\t\ti = parseTile(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"SPR\") {\n\t\t\ti = parseSprite(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"ITM\") {\n\t\t\ti = parseItem(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"DLG\") {\n\t\t\ti = parseDialog(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"END\") {\n\t\t\t// parse endings for back compat\n\t\t\ti = parseEnding(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"VAR\") {\n\t\t\ti = parseVariable(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"DEFAULT_FONT\") {\n\t\t\ti = parseFontName(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TEXT_DIRECTION\") {\n\t\t\ti = parseTextDirection(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"FONT\") {\n\t\t\ti = parseFontData(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"TUNE\") {\n\t\t\ti = parseTune(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"BLIP\") {\n\t\t\ti = parseBlip(parseState, world);\n\t\t}\n\t\telse if (getType(curLine) === \"!\") {\n\t\t\ti = parseFlag(parseState, world);\n\t\t}\n\t\telse {\n\t\t\ti++;\n\t\t}\n\n\t\tparseState.index = i;\n\t}\n\n\tworld.names = createNameMapsForWorld(world);\n\n\tplaceSprites(parseState, world);\n\n\tif ((world.flags.VER_MAJ <= -1 || world.flags.VER_MIN <= -1) && world.versionNumberFromComment > -1) {\n\t\tvar versionNumberStr = \"\" + world.versionNumberFromComment;\n\t\tversionNumberStr = versionNumberStr.split(\".\");\n\t\tworld.flags.VER_MAJ = parseFloat(versionNumberStr[0]);\n\t\tworld.flags.VER_MIN = parseFloat(versionNumberStr[1]);\n\t}\n\n\t// starting in version v7.0, there were two major changes to dialog behavior:\n\t// 1) sprite dialog was no longer implicitly linked by the sprite and dialog IDs matching\n\t// (see this commit: 5e1adb29faad4e50603c689d2dac143074117b4e)\n\t// 2) ending dialogs no longer had their own world data type (\"END\")\n\t// for the v7.x versions I tried to automatically convert old dialog to the new format,\n\t// however, that process can be unreliable and lead to weird bugs.\n\t// with v8.0 and above I will no longer attempt to convert old files, and instead will use\n\t// a flag to indicate files that need to use the backwards compatible behavior -\n\t// this is more reliable & configurable (at the cost of making pre-7.0 games a bit harder to edit)\n\tif (world.flags.VER_MAJ < 7) {\n\t\tworld.flags.DLG_COMPAT = 1;\n\t}\n\n\treturn world;\n}\n\nfunction parseTitle(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar results;\n\tif (scriptUtils) {\n\t\tresults = scriptUtils.ReadDialogScript(lines,i);\n\t}\n\telse {\n\t\tresults = { script: lines[i], index: (i + 1) };\n\t}\n\n\tworld.dialog[titleDialogId] = createDialogData(titleDialogId);\n\tworld.dialog[titleDialogId].src = results.script;\n\n\ti = results.index;\n\ti++;\n\n\treturn i;\n}\n\nfunction parsePalette(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\tvar colors = [];\n\tvar name = null;\n\twhile (i < lines.length && lines[i].length > 0) { //look for empty line\n\t\tvar args = lines[i].split(\" \");\n\t\tif (args[0] === \"NAME\") {\n\t\t\tname = lines[i].split(/\\s(.+)/)[1];\n\t\t}\n\t\telse {\n\t\t\tvar col = [];\n\t\t\tlines[i].split(\",\").forEach(function(i) {\n\t\t\t\tcol.push(parseInt(i));\n\t\t\t});\n\t\t\tcolors.push(col);\n\t\t}\n\t\ti++;\n\t}\n\tworld.palette[id] = {\n\t\tid : id,\n\t\tname : name,\n\t\tcolors : colors\n\t};\n\treturn i;\n}\n\nfunction createRoomData(id) {\n\treturn {\n\t\tid: id,\n\t\tname: null,\n\t\ttilemap: [],\n\t\twalls: [],\n\t\texits: [],\n\t\tendings: [],\n\t\titems: [],\n\t\tpal: null,\n\t\tava: null,\n\t\ttune: \"0\"\n\t};\n}\n\nfunction createExitData(x, y, destRoom, destX, destY, transition, dlg) {\n\treturn {\n\t\tx: x,\n\t\ty: y,\n\t\tdest: {\n\t\t\troom: destRoom,\n\t\t\tx: destX,\n\t\t\ty: destY\n\t\t},\n\t\ttransition_effect: transition,\n\t\tdlg: dlg,\n\t};\n}\n\nfunction createEndingData(id, x, y) {\n\treturn {\n\t\tid: id,\n\t\tx: x,\n\t\ty: y\n\t};\n}\n\nfunction parseRoom(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\n\tvar roomData = createRoomData(id);\n\n\ti++;\n\n\t// create tile map\n\tif (world.flags.ROOM_FORMAT === 0) {\n\t\t// old way: no commas, single char tile ids\n\t\tvar end = i + bitsy.MAP_SIZE;\n\t\tvar y = 0;\n\t\tfor (; i < end; i++) {\n\t\t\troomData.tilemap.push([]);\n\t\t\tfor (x = 0; x < bitsy.MAP_SIZE; x++) {\n\t\t\t\troomData.tilemap[y].push(lines[i].charAt(x));\n\t\t\t}\n\t\t\ty++;\n\t\t}\n\t}\n\telse if (world.flags.ROOM_FORMAT === 1) {\n\t\t// new way: comma separated, multiple char tile ids\n\t\tvar end = i + bitsy.MAP_SIZE;\n\t\tvar y = 0;\n\t\tfor (; i < end; i++) {\n\t\t\troomData.tilemap.push([]);\n\t\t\tvar lineSep = lines[i].split(\",\");\n\t\t\tfor (x = 0; x < bitsy.MAP_SIZE; x++) {\n\t\t\t\troomData.tilemap[y].push(lineSep[x]);\n\t\t\t}\n\t\t\ty++;\n\t\t}\n\t}\n\n\twhile (i < lines.length && lines[i].length > 0) { //look for empty line\n\t\t// bitsy.log(getType(lines[i]));\n\t\tif (getType(lines[i]) === \"SPR\") {\n\t\t\t/* NOTE SPRITE START LOCATIONS */\n\t\t\tvar sprId = getId(lines[i]);\n\t\t\tif (sprId.indexOf(\",\") == -1 && lines[i].split(\" \").length >= 3) { //second conditional checks for coords\n\t\t\t\t/* PLACE A SINGLE SPRITE */\n\t\t\t\tvar sprCoord = lines[i].split(\" \")[2].split(\",\");\n\t\t\t\tparseState.spriteStartLocations[sprId] = {\n\t\t\t\t\troom : id,\n\t\t\t\t\tx : parseInt(sprCoord[0]),\n\t\t\t\t\ty : parseInt(sprCoord[1])\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( world.flags.ROOM_FORMAT == 0 ) { // TODO: right now this shortcut only works w/ the old comma separate format\n\t\t\t\t/* PLACE MULTIPLE SPRITES*/ \n\t\t\t\t//Does find and replace in the tilemap (may be hacky, but its convenient)\n\t\t\t\tvar sprList = sprId.split(\",\");\n\t\t\t\tfor (row in roomData.tilemap) {\n\t\t\t\t\tfor (s in sprList) {\n\t\t\t\t\t\tvar col = roomData.tilemap[row].indexOf( sprList[s] );\n\t\t\t\t\t\t//if the sprite is in this row, replace it with the \"null tile\" and set its starting position\n\t\t\t\t\t\tif (col != -1) {\n\t\t\t\t\t\t\troomData.tilemap[row][col] = \"0\";\n\t\t\t\t\t\t\tparseState.spriteStartLocations[ sprList[s] ] = {\n\t\t\t\t\t\t\t\troom : id,\n\t\t\t\t\t\t\t\tx : parseInt(col),\n\t\t\t\t\t\t\t\ty : parseInt(row)\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"ITM\") {\n\t\t\tvar itmId = getId(lines[i]);\n\t\t\tvar itmCoord = lines[i].split(\" \")[2].split(\",\");\n\t\t\tvar itm = {\n\t\t\t\tid: itmId,\n\t\t\t\tx : parseInt(itmCoord[0]),\n\t\t\t\ty : parseInt(itmCoord[1])\n\t\t\t};\n\t\t\troomData.items.push( itm );\n\t\t}\n\t\telse if (getType(lines[i]) === \"WAL\") {\n\t\t\t/* DEFINE COLLISIONS (WALLS) */\n\t\t\troomData.walls = getId(lines[i]).split(\",\");\n\t\t}\n\t\telse if (getType(lines[i]) === \"EXT\") {\n\t\t\t/* ADD EXIT */\n\t\t\tvar exitArgs = lines[i].split(\" \");\n\t\t\t//arg format: EXT 10,5 M 3,2 [AVA:7 LCK:a,9] [AVA 7 LCK a 9]\n\t\t\tvar exitCoords = exitArgs[1].split(\",\");\n\t\t\tvar destName = exitArgs[2];\n\t\t\tvar destCoords = exitArgs[3].split(\",\");\n\t\t\tvar ext = createExitData(\n\t\t\t\t/* x \t\t\t*/ parseInt(exitCoords[0]),\n\t\t\t\t/* y \t\t\t*/ parseInt(exitCoords[1]),\n\t\t\t\t/* destRoom \t*/ destName,\n\t\t\t\t/* destX \t\t*/ parseInt(destCoords[0]),\n\t\t\t\t/* destY \t\t*/ parseInt(destCoords[1]),\n\t\t\t\t/* transition \t*/ null,\n\t\t\t\t/* dlg \t\t\t*/ null);\n\n\t\t\t// optional arguments\n\t\t\tvar exitArgIndex = 4;\n\t\t\twhile (exitArgIndex < exitArgs.length) {\n\t\t\t\tif (exitArgs[exitArgIndex] == \"FX\") {\n\t\t\t\t\text.transition_effect = exitArgs[exitArgIndex+1];\n\t\t\t\t\texitArgIndex += 2;\n\t\t\t\t}\n\t\t\t\telse if (exitArgs[exitArgIndex] == \"DLG\") {\n\t\t\t\t\text.dlg = exitArgs[exitArgIndex+1];\n\t\t\t\t\texitArgIndex += 2;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\texitArgIndex += 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\troomData.exits.push(ext);\n\t\t}\n\t\telse if (getType(lines[i]) === \"END\") {\n\t\t\t/* ADD ENDING */\n\t\t\tvar endId = getId(lines[i]);\n\n\t\t\tvar endCoords = getCoord(lines[i], 2);\n\t\t\tvar end = createEndingData(\n\t\t\t\t/* id */ endId,\n\t\t\t\t/* x */ parseInt(endCoords[0]),\n\t\t\t\t/* y */ parseInt(endCoords[1]));\n\n\t\t\troomData.endings.push(end);\n\t\t}\n\t\telse if (getType(lines[i]) === \"PAL\") {\n\t\t\t/* CHOOSE PALETTE (that's not default) */\n\t\t\troomData.pal = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"AVA\") {\n\t\t\t// change avatar appearance per room\n\t\t\troomData.ava = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"TUNE\") {\n\t\t\troomData.tune = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\troomData.name = getNameArg(lines[i]);\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.room[id] = roomData;\n\n\treturn i;\n}\n\nfunction parseTile(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar tileData = createDrawingData(\"TIL\", id);\n\n\ti++;\n\n\t// read & store tile image source\n\ti = parseDrawingCore(lines, i, tileData.drw, world);\n\n\t// update animation info\n\ttileData.animation.frameCount = getDrawingFrameCount(world, tileData.drw);\n\ttileData.animation.isAnimated = tileData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\ttileData.col = parseInt(getId(lines[i]));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\ttileData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttileData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\ttileData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"WAL\") {\n\t\t\tvar wallArg = getArg(lines[i], 1);\n\t\t\tif (wallArg === \"true\") {\n\t\t\t\ttileData.isWall = true;\n\t\t\t}\n\t\t\telse if (wallArg === \"false\") {\n\t\t\t\ttileData.isWall = false;\n\t\t\t}\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store tile data\n\tworld.tile[id] = tileData;\n\n\treturn i;\n}\n\nfunction parseSprite(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar type = (id === \"A\") ? \"AVA\" : \"SPR\";\n\tvar spriteData = createDrawingData(type, id);\n\n\t// bitsy.log(spriteData);\n\n\ti++;\n\n\t// read & store sprite image source\n\ti = parseDrawingCore(lines, i, spriteData.drw, world);\n\n\t// update animation info\n\tspriteData.animation.frameCount = getDrawingFrameCount(world, spriteData.drw);\n\tspriteData.animation.isAnimated = spriteData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\t/* COLOR OFFSET INDEX */\n\t\t\tspriteData.col = parseInt(getId(lines[i]));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\t/* BACKGROUND COLOR */\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\tspriteData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tspriteData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"POS\") {\n\t\t\t/* STARTING POSITION */\n\t\t\tvar posArgs = lines[i].split(\" \");\n\t\t\tvar roomId = posArgs[1];\n\t\t\tvar coordArgs = posArgs[2].split(\",\");\n\t\t\tparseState.spriteStartLocations[id] = {\n\t\t\t\troom : roomId,\n\t\t\t\tx : parseInt(coordArgs[0]),\n\t\t\t\ty : parseInt(coordArgs[1])\n\t\t\t};\n\t\t}\n\t\telse if(getType(lines[i]) === \"DLG\") {\n\t\t\tspriteData.dlg = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\tspriteData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"ITM\") {\n\t\t\t/* ITEM STARTING INVENTORY */\n\t\t\tvar itemId = getId(lines[i]);\n\t\t\tvar itemCount = parseFloat(getArg(lines[i], 2));\n\t\t\tspriteData.inventory[itemId] = itemCount;\n\t\t}\n\t\telse if (getType(lines[i]) == \"BLIP\") {\n\t\t\tvar blipId = getId(lines[i]);\n\t\t\tspriteData.blip = blipId;\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store sprite data\n\tworld.sprite[id] = spriteData;\n\n\treturn i;\n}\n\nfunction parseItem(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\tvar itemData = createDrawingData(\"ITM\", id);\n\n\ti++;\n\n\t// read & store item image source\n\ti = parseDrawingCore(lines, i, itemData.drw, world);\n\n\t// update animation info\n\titemData.animation.frameCount = getDrawingFrameCount(world, itemData.drw);\n\titemData.animation.isAnimated = itemData.animation.frameCount > 1;\n\n\t// read other properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"COL\") {\n\t\t\t/* COLOR OFFSET INDEX */\n\t\t\titemData.col = parseInt(getArg(lines[i], 1));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BGC\") {\n\t\t\t/* BACKGROUND COLOR */\n\t\t\tvar bgcId = getId(lines[i]);\n\t\t\tif (bgcId === \"*\") {\n\t\t\t\t// transparent background\n\t\t\t\titemData.bgc = (-1 * tileColorStartIndex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\titemData.bgc = parseInt(bgcId);\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"DLG\") {\n\t\t\titemData.dlg = getId(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\t/* NAME */\n\t\t\titemData.name = getNameArg(lines[i]);\n\t\t}\n\t\telse if (getType(lines[i]) == \"BLIP\") {\n\t\t\tvar blipId = getId(lines[i]);\n\t\t\titemData.blip = blipId;\n\t\t}\n\n\t\ti++;\n\t}\n\n\t// store item data\n\tworld.item[id] = itemData;\n\n\treturn i;\n}\n\nfunction parseDrawingCore(lines, i, drwId, world) {\n\tvar frameList = []; //init list of frames\n\tframeList.push( [] ); //init first frame\n\tvar frameIndex = 0;\n\tvar y = 0;\n\twhile (y < bitsy.TILE_SIZE) {\n\t\tvar line = lines[i + y];\n\t\tvar row = [];\n\n\t\tfor (x = 0; x < bitsy.TILE_SIZE; x++) {\n\t\t\trow.push(parseInt(line.charAt(x)));\n\t\t}\n\n\t\tframeList[frameIndex].push(row);\n\t\ty++;\n\n\t\tif (y === bitsy.TILE_SIZE) {\n\t\t\ti = i + y;\n\t\t\tif (lines[i] != undefined && lines[i].charAt(0) === \">\") {\n\t\t\t\t// start next frame!\n\t\t\t\tframeList.push([]);\n\t\t\t\tframeIndex++;\n\n\t\t\t\t//start the count over again for the next frame\n\t\t\t\ti++;\n\t\t\t\ty = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tstoreDrawingData(world, drwId, frameList);\n\n\treturn i;\n}\n\nfunction parseDialog(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\t// hacky but I need to store this so I can set the name below\n\tvar id = getId(lines[i]);\n\n\ti = parseScript(lines, i, world.dialog);\n\n\tif (i < lines.length && lines[i].length > 0 && getType(lines[i]) === \"NAME\") {\n\t\tworld.dialog[id].name = getNameArg(lines[i]);\n\t\ti++;\n\t}\n\n\treturn i;\n}\n\n// keeping this around to parse old files where endings were separate from dialogs\nfunction parseEnding(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\treturn parseScript(lines, i, world.end);\n}\n\nfunction parseScript(lines, i, data) {\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar results;\n\tif (scriptUtils) {\n\t\tresults = scriptUtils.ReadDialogScript(lines,i);\n\t}\n\telse {\n\t\tresults = { script: lines[i], index: (i + 1)};\n\t}\n\n\tdata[id] = createDialogData(id);\n\tdata[id].src = results.script;\n\n\ti = results.index;\n\n\treturn i;\n}\n\nfunction parseVariable(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\ti++;\n\tvar value = lines[i];\n\ti++;\n\tworld.variable[id] = value;\n\treturn i;\n}\n\nfunction parseFontName(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tworld.fontName = getArg(lines[i], 1);\n\ti++;\n\treturn i;\n}\n\nfunction parseTextDirection(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tworld.textDirection = getArg(lines[i], 1);\n\ti++;\n\treturn i;\n}\n\nfunction parseFontData(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\t// NOTE : we're not doing the actual parsing here --\n\t// just grabbing the block of text that represents the font\n\t// and giving it to the font manager to use later\n\n\tvar localFontName = getId(lines[i]);\n\tvar localFontData = lines[i];\n\ti++;\n\n\twhile (i < lines.length && lines[i] != \"\") {\n\t\tlocalFontData += \"\\n\" + lines[i];\n\t\ti++;\n\t}\n\n\tvar localFontFilename = localFontName + fontManager.GetExtension();\n\tfontManager.AddResource( localFontFilename, localFontData );\n\n\treturn i;\n}\n\nfunction parseTune(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar tuneData = createTuneData(id);\n\n\tvar barIndex = 0;\n\twhile (barIndex < maxTuneLength) {\n\t\t// MELODY\n\t\tvar melodyBar = createTuneBarData();\n\t\tvar melodyNotes = lines[i].split(\",\");\n\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t// default to a rest\n\t\t\tvar pitch = { beats: 0, note: Note.C, octave: Octave[4], };\n\n\t\t\tif (j < melodyNotes.length) {\n\t\t\t\tvar pitchSplit = melodyNotes[j].split(\"~\");\n\t\t\t\tvar pitchStr = pitchSplit[0];\n\t\t\t\tpitch = parsePitch(melodyNotes[j]);\n\n\t\t\t\t// look for effect added to the note\n\t\t\t\tif (pitchSplit.length > 1) {\n\t\t\t\t\tvar blipId = pitchSplit[1];\n\t\t\t\t\tpitch.blip = blipId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmelodyBar[j] = pitch;\n\t\t}\n\t\ttuneData.melody.push(melodyBar);\n\t\ti++;\n\n\t\t// HARMONY\n\t\tvar harmonyBar = createTuneBarData();\n\t\tvar harmonyNotes = lines[i].split(\",\");\n\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t// default to a rest\n\t\t\tvar pitch = { beats: 0, note: Note.C, octave: Octave[4], };\n\n\t\t\tif (j < harmonyNotes.length) {\n\t\t\t\tvar pitchSplit = harmonyNotes[j].split(\"~\");\n\t\t\t\tvar pitchStr = pitchSplit[0];\n\t\t\t\tpitch = parsePitch(harmonyNotes[j]);\n\n\t\t\t\t// look for effect added to the note\n\t\t\t\tif (pitchSplit.length > 1) {\n\t\t\t\t\tvar blipId = pitchSplit[1];\n\t\t\t\t\tpitch.blip = blipId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tharmonyBar[j] = pitch;\n\t\t}\n\t\ttuneData.harmony.push(harmonyBar);\n\t\ti++;\n\n\t\t// check if there's another bar after this one\n\t\tif (lines[i] === \">\") {\n\t\t\t// there is! increment the index\n\t\t\tbarIndex++;\n\t\t\ti++;\n\t\t}\n\t\telse {\n\t\t\t// we've reached the end of the tune!\n\t\t\tbarIndex = maxTuneLength;\n\t\t}\n\t}\n\n\t// parse other tune properties\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"KEY\") {\n\t\t\ttuneData.key = createTuneKeyData();\n\n\t\t\tvar keyNotes = getArg(lines[i], 1);\n\t\t\tif (keyNotes) {\n\t\t\t\tkeyNotes = keyNotes.split(\",\");\n\t\t\t\tfor (var j = 0; j < keyNotes.length && j < tuneData.key.notes.length; j++) {\n\t\t\t\t\tvar pitch = parsePitch(keyNotes[j]);\n\t\t\t\t\ttuneData.key.notes[j] = pitch.note;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar keyScale = getArg(lines[i], 2);\n\t\t\tif (keyScale) {\n\t\t\t\tkeyScale = keyScale.split(\",\");\n\t\t\t\tfor (var j = 0; j < keyScale.length; j++) {\n\t\t\t\t\tvar pitch = parsePitch(keyScale[j]);\n\t\t\t\t\tif (pitch.note > Solfa.NONE && pitch.note < Solfa.COUNT) {\n\t\t\t\t\t\ttuneData.key.scale.push(pitch.note);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"TMP\") {\n\t\t\tvar tempoId = getId(lines[i]);\n\t\t\tif (Tempo[tempoId] != undefined) {\n\t\t\t\ttuneData.tempo = Tempo[tempoId];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"SQR\") {\n\t\t\t// square wave instrument settings\n\t\t\tvar squareWaveIdA = getArg(lines[i], 1);\n\t\t\tif (SquareWave[squareWaveIdA] != undefined) {\n\t\t\t\ttuneData.instrumentA = SquareWave[squareWaveIdA];\n\t\t\t}\n\n\t\t\tvar squareWaveIdB = getArg(lines[i], 2);\n\t\t\tif (SquareWave[squareWaveIdB] != undefined) {\n\t\t\t\ttuneData.instrumentB = SquareWave[squareWaveIdB];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"ARP\") {\n\t\t\tvar arp = getId(lines[i]);\n\t\t\tif (ArpeggioPattern[arp] != undefined) {\n\t\t\t\ttuneData.arpeggioPattern = ArpeggioPattern[arp];\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\tvar name = lines[i].split(/\\s(.+)/)[1];\n\t\t\ttuneData.name = name;\n\t\t\t// todo : add to map?\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.tune[id] = tuneData;\n\n\treturn i;\n}\n\nfunction parseBlip(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\n\tvar id = getId(lines[i]);\n\ti++;\n\n\tvar blipData = createBlipData(id);\n\n\t// blip pitches\n\tvar notes = lines[i].split(\",\");\n\tif (notes.length >= 1) {\n\t\tblipData.pitchA = parsePitch(notes[0]);\n\t}\n\tif (notes.length >= 2) {\n\t\tblipData.pitchB = parsePitch(notes[1]);\n\t}\n\tif (notes.length >= 3) {\n\t\tblipData.pitchC = parsePitch(notes[2]);\n\t}\n\ti++;\n\n\t// blip parameters\n\twhile (i < lines.length && lines[i].length > 0) { // look for empty line\n\t\tif (getType(lines[i]) === \"ENV\") {\n\t\t\t// envelope\n\t\t\tblipData.envelope.attack = parseInt(getArg(lines[i], 1));\n\t\t\tblipData.envelope.decay = parseInt(getArg(lines[i], 2));\n\t\t\tblipData.envelope.sustain = parseInt(getArg(lines[i], 3));\n\t\t\tblipData.envelope.length = parseInt(getArg(lines[i], 4));\n\t\t\tblipData.envelope.release = parseInt(getArg(lines[i], 5));\n\t\t}\n\t\telse if (getType(lines[i]) === \"BEAT\") {\n\t\t\t// pitch beat length\n\t\t\tblipData.beat.time = parseInt(getArg(lines[i], 1));\n\t\t\tblipData.beat.delay = parseInt(getArg(lines[i], 2));\n\t\t}\n\t\telse if (getType(lines[i]) === \"SQR\") {\n\t\t\t// square wave\n\t\t\tvar squareWaveId = getArg(lines[i], 1);\n\t\t\tif (SquareWave[squareWaveId] != undefined) {\n\t\t\t\tblipData.instrument = SquareWave[squareWaveId];\n\t\t\t}\n\t\t}\n\t\t// TODO : consider for future update\n\t\t// else if (getType(lines[i]) === \"SLD\") {\n\t\t// \t// slide mode\n\t\t// \tif (parseInt(getArg(lines[i], 1)) === 1) {\n\t\t// \t\tblipData.doSlide = true;\n\t\t// \t}\n\t\t// }\n\t\telse if (getType(lines[i]) === \"RPT\") {\n\t\t\t// repeat mode\n\t\t\tif (parseInt(getArg(lines[i], 1)) === 1) {\n\t\t\t\tblipData.doRepeat = true;\n\t\t\t}\n\t\t}\n\t\telse if (getType(lines[i]) === \"NAME\") {\n\t\t\tvar name = lines[i].split(/\\s(.+)/)[1];\n\t\t\tblipData.name = name;\n\t\t}\n\n\t\ti++;\n\t}\n\n\tworld.blip[id] = blipData;\n\n\treturn i;\n}\n\nfunction parsePitch(pitchStr) {\n\tvar pitch = { beats: 1, note: Note.C, octave: Octave[4], };\n\tvar i;\n\n\t// beats\n\tvar beatsToken = \"\";\n\tfor (i = 0; i < pitchStr.length && (\"0123456789\".indexOf(pitchStr[i]) != -1); i++) {\n\t\tbeatsToken += pitchStr[i];\n\t}\n\tif (beatsToken.length > 0) {\n\t\tpitch.beats = parseInt(beatsToken);\n\t}\n\n\t// note\n\tvar noteType;\n\tvar noteName = \"\";\n\tif (i < pitchStr.length) {\n\t\tif (pitchStr[i] === pitchStr[i].toUpperCase()) {\n\t\t\t// uppercase letters represent chromatic notes\n\t\t\tnoteType = Note;\n\t\t\tnoteName += pitchStr[i];\n\t\t\ti++;\n\n\t\t\t// check for sharp\n\t\t\tif (i < pitchStr.length && pitchStr[i] === \"#\") {\n\t\t\t\tnoteName += \"_SHARP\";\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// lowercase letters represent solfa notes\n\t\t\tnoteType = Solfa;\n\t\t\tnoteName += pitchStr[i].toUpperCase();\n\t\t\ti++;\n\t\t}\n\t}\n\n\tif (noteType != undefined && noteType[noteName] != undefined) {\n\t\tpitch.note = noteType[noteName];\n\t}\n\n\t// octave\n\tvar octaveToken = \"\";\n\tif (i < pitchStr.length) {\n\t\toctaveToken += pitchStr[i];\n\t}\n\n\tif (Octave[octaveToken] != undefined) {\n\t\tpitch.octave = Octave[octaveToken];\n\t}\n\n\treturn pitch;\n}\n\nfunction parseFlag(parseState, world) {\n\tvar i = parseState.index;\n\tvar lines = parseState.lines;\n\tvar id = getId(lines[i]);\n\tvar valStr = lines[i].split(\" \")[2];\n\tworld.flags[id] = parseInt( valStr );\n\ti++;\n\treturn i;\n}\n\nfunction getDrawingFrameCount(world, drwId) {\n\treturn world.drawings[drwId].length;\n}\n\nfunction storeDrawingData(world, drwId, drawingData) {\n\tworld.drawings[drwId] = drawingData;\n}\n\nfunction placeSprites(parseState, world) {\n\tfor (id in parseState.spriteStartLocations) {\n\t\tworld.sprite[id].room = parseState.spriteStartLocations[id].room;\n\t\tworld.sprite[id].x = parseState.spriteStartLocations[id].x;\n\t\tworld.sprite[id].y = parseState.spriteStartLocations[id].y;\n\t}\n}\n\nfunction createNameMapsForWorld(world) {\n\tvar nameMaps = {};\n\n\tfunction createNameMap(objectStore) {\n\t\tvar map = {};\n\n\t\tfor (id in objectStore) {\n\t\t\tif (objectStore[id].name != undefined && objectStore[id].name != null) {\n\t\t\t\tmap[objectStore[id].name] = id;\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\tnameMaps.room = createNameMap(world.room);\n\tnameMaps.tile = createNameMap(world.tile);\n\tnameMaps.sprite = createNameMap(world.sprite);\n\tnameMaps.item = createNameMap(world.item);\n\tnameMaps.dialog = createNameMap(world.dialog);\n\tnameMaps.palette = createNameMap(world.palette);\n\tnameMaps.tune = createNameMap(world.tune);\n\tnameMaps.blip = createNameMap(world.blip);\n\n\treturn nameMaps;\n}\n\nfunction getType(line) {\n\treturn getArg(line,0);\n}\n\nfunction getId(line) {\n\treturn getArg(line,1);\n}\n\nfunction getCoord(line,arg) {\n\treturn getArg(line,arg).split(\",\");\n}\n\nfunction getArg(line,arg) {\n\treturn line.split(\" \")[arg];\n}\n\nfunction getNameArg(line) {\n\tvar name = line.split(/\\s(.+)/)[1];\n\treturn name;\n}",
"sound.js": "/* PITCH HELPER FUNCTIONS */\nfunction pitchToSteps(pitch) {\n\treturn (pitch.octave * Note.COUNT) + pitch.note;\n}\n\nfunction stepsToPitch(steps) {\n\tvar pitch = { beats: 1, note: Note.C, octave: Octave[2], };\n\n\twhile (steps >= Note.COUNT) {\n\t\tpitch.octave = (pitch.octave + 1) % Octave.COUNT;\n\t\tsteps -= Note.COUNT;\n\t}\n\n\tpitch.note += steps;\n\n\t// make sure pitch isn't outside a valid range\n\tif (pitch.note <= Note.NONE) {\n\t\tpitch.note = Note.C;\n\t}\n\telse if (pitch.note >= Note.COUNT) {\n\t\tpitch.note = Note.B;\n\t}\n\n\tif (pitch.octave <= Octave.NONE) {\n\t\tpitch.octave = Octave[2];\n\t}\n\telse if (pitch.octave >= Octave.COUNT) {\n\t\tpitch.octave = Octave[5];\n\t}\n\n\treturn pitch;\n}\n\nfunction adjustPitch(pitch, stepDelta) {\n\treturn stepsToPitch(pitchToSteps(pitch) + stepDelta);\n}\n\nfunction pitchDistance(pitchA, pitchB) {\n\treturn pitchToSteps(pitchB) - pitchToSteps(pitchA);\n}\n\nfunction isMinPitch(pitch) {\n\treturn pitchToSteps(pitch) <= pitchToSteps({ note: Note.C, octave: Octave[2] });\n}\n\nfunction isMaxPitch(pitch) {\n\treturn pitchToSteps(pitch) >= pitchToSteps({ note: Note.B, octave: Octave[5] });\n}\n\nfunction SoundPlayer() {\n\t// frequencies (in hertz) for octave 0 (or is it octave 4?)\n\tvar frequencies = [\n\t\t261.7, // middle C\n\t\t277.2,\n\t\t293.7,\n\t\t311.2,\n\t\t329.7,\n\t\t349.3,\n\t\t370.0,\n\t\t392.0,\n\t\t415.3,\n\t\t440.0,\n\t\t466.2,\n\t\t493.9,\n\t];\n\n\t// tempos are calculated as the duration of a 16th note, rounded to the nearest millisecond\n\tvar tempos = {};\n\ttempos[Tempo.SLW] = 250; // 60bpm (adagio)\n\ttempos[Tempo.MED] = 188; // ~80bpm (andante) [exact would be 187.5 ms]\n\ttempos[Tempo.FST] = 125; // 120bpm (moderato)\n\ttempos[Tempo.XFST] = 94; // ~160bpm (allegro) [exact would be 93.75 ms]\n\n\t// arpeggio patterns expressed in scale degrees\n\tvar arpeggioPattern = {};\n\tarpeggioPattern[ArpeggioPattern.UP] = [0, 2, 4, 7];\n\tarpeggioPattern[ArpeggioPattern.DWN] = [7, 4, 2, 0];\n\tarpeggioPattern[ArpeggioPattern.INT5] = [0, 4];\n\tarpeggioPattern[ArpeggioPattern.INT8] = [0, 7];\n\n\tthis.getArpeggioSteps = function(tune) { return arpeggioPattern[tune.arpeggioPattern]; };\n\n\tfunction isPitchPlayable(pitch, key) {\n\t\tif (pitch.beats <= 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (key === undefined || key === null) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// test if note is in the scale\n\t\treturn (key.scale.indexOf(pitch.note) > -1)\n\t\t\t&& (key.notes[pitch.note] > Note.NONE)\n\t\t\t&& (key.notes[pitch.note] < Note.COUNT);\n\t}\n\n\tfunction pitchToChromatic(pitch, key) {\n\t\tif (pitch === undefined || pitch === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (key === undefined || key === null) {\n\t\t\treturn pitch;\n\t\t}\n\n\t\t// convert from solfa\n\t\tvar octaveOffset = (pitch.note >= Solfa.COUNT) ? 1 : 0;\n\n\t\treturn {\n\t\t\tbeats: pitch.beats,\n\t\t\toctave: pitch.octave + octaveOffset,\n\t\t\t// todo : what about the scale limits?\n\t\t\tnote: key.notes[(pitch.note % Solfa.COUNT)],\n\t\t\tblip: pitch.blip\n\t\t};\n\t}\n\n\tfunction makePitchFrequency(pitch) {\n\t\t// todo : this clamp shouldn't be required.. there's a bug in the pitch shifting somewhere\n\t\tvar note = Math.max(0, pitch.note);\n\t\tvar octave = (pitch.octave != undefined ? pitch.octave : Octave[4]);\n\n\t\tvar octaveMin = Octave[2];\n\t\tvar octaveMax = Octave[5];\n\n\t\t// make sure octave is in valid range\n\t\toctave = Math.max(octaveMin, Math.min(octave, octaveMax));\n\t\tvar distFromMiddleC = octave - 2;\n\n\t\tvar freq = frequencies[note] * Math.pow(2, distFromMiddleC);\n\n\t\tif (isNaN(freq)) {\n\t\t\tbitsy.log(\"invalid frequency \" + pitch, \"sound\");\n\t\t}\n\n\t\treturn freq;\n\t}\n\n\tvar maxVolume = 15; // todo : should this be a system constant?\n\tvar noteVolume = 5;\n\n\tvar curTune = null;\n\tvar isTunePaused = false;\n\tvar barIndex = -1;\n\tvar curArpeggio = [];\n\n\tvar beat16 = 0;\n\tvar beat16Timer = 0;\n\tvar beat16Index = 0;\n\n\t// special settings\n\tvar isLooping = false;\n\tvar isMelodyMuted = false;\n\tvar maxBeatCount = null;\n\tvar muteTimer = 0; // allow temporary muting of all notes\n\n\tfunction arpeggiateBar(bar, key, pattern) {\n\t\tvar arpeggio = [];\n\n\t\tif (key != undefined && key != null && isPitchPlayable(bar[0], key)) {\n\t\t\tfor (var i = 0; i < arpeggioPattern[pattern].length; i++) {\n\t\t\t\tvar pitch = { beats: 1, note: bar[0].note + arpeggioPattern[pattern][i], octave: bar[0].octave };\n\t\t\t\tarpeggio.push(pitchToChromatic(pitch, key));\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < arpeggio.length; i++) {\n\t\t\tbitsy.log(i + \": \" + serializeNote(arpeggio[i].note));\n\t\t}\n\n\t\treturn arpeggio;\n\t};\n\n\tfunction playNote(pitch, instrument, options) {\n\t\tif (pitch.beats <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar channel = bitsy.SOUND1;\n\t\tif (options != undefined && options.channel != undefined) {\n\t\t\tchannel = options.channel;\n\t\t}\n\n\t\tvar key = null;\n\t\tif (options != undefined && options.key != undefined) {\n\t\t\tkey = options.key;\n\t\t}\n\n\t\tvar beatLen = beat16;\n\t\tif (options != undefined && options.beatLen != undefined) {\n\t\t\tbeatLen = options.beatLen;\n\t\t}\n\n\t\tif (isPitchPlayable(pitch, key)) {\n\t\t\tvar freq = makePitchFrequency(pitchToChromatic(pitch, key));\n\t\t\tbitsy.sound(channel, (pitch.beats * beatLen), freq * 100, noteVolume, instrument);\n\t\t}\n\t}\n\n\tfunction sfxFrequencyAtTime(sfx, time) {\n\t\tvar beatDelay = sfx.blip.beat.delay;\n\t\tvar beatTime = sfx.blip.beat.time;\n\t\tvar delta = Math.max(0, time - beatDelay) / beatTime;\n\n\t\tvar pitchDelta = sfx.blip.doRepeat\n\t\t\t? (delta % sfx.frequencies.length)\n\t\t\t: Math.min(delta, sfx.frequencies.length - 1);\n\n\t\tsfx.pitchIndex = Math.floor(pitchDelta);\n\t\tvar curFreq = sfx.frequencies[sfx.pitchIndex];\n\n\t\t// TODO : consider for future update\n\t\t// if (sfx.blip.doSlide) {\n\t\t// \tvar nextPitchIndex = (sfx.pitchIndex + 1) % sfx.frequencies.length;\n\t\t// \tvar nextFreq = sfx.frequencies[nextPitchIndex];\n\t\t// \tvar d = pitchDelta - sfx.pitchIndex;\n\t\t// \tcurFreq = curFreq + ((nextFreq - curFreq) * d);\n\t\t// }\n\n\t\treturn curFreq;\n\t}\n\n\tfunction sfxVolumeAtTime(sfx, time) {\n\t\tvar volume = 0;\n\n\t\t// use envelope settings to calculate volume\n\t\tvar attack = sfx.blip.envelope.attack;\n\t\tvar decay = sfx.blip.envelope.decay;\n\t\tvar length = sfx.blip.envelope.length;\n\t\tvar release = sfx.blip.envelope.release;\n\t\tif (time < attack) {\n\t\t\t// attack\n\t\t\tvar t = time / attack;\n\t\t\tvolume = Math.floor(sfxPeakVolume * t);\n\t\t}\n\t\telse if (time < attack + decay) {\n\t\t\t// decay\n\t\t\tvar t = (time - attack) / decay;\n\t\t\tvar d = sfx.blip.envelope.sustain - sfxPeakVolume;\n\t\t\tvolume = Math.floor(sfxPeakVolume + (d * t));\n\t\t}\n\t\telse if (time < attack + decay + length) {\n\t\t\t// sustain\n\t\t\tvolume = sfx.blip.envelope.sustain;\n\t\t}\n\t\telse if (time < attack + decay + length + release) {\n\t\t\t// release\n\t\t\tvar t = (time - (attack + decay + length)) / release;\n\t\t\tvolume = Math.floor(sfx.blip.envelope.sustain * (1 - t));\n\t\t}\n\t\telse {\n\t\t\tvolume = 0;\n\t\t}\n\n\t\treturn volume;\n\t}\n\n\tfunction updateSfx(dt) {\n\t\t// try limiting the max change per frame\n\t\tdt = Math.min(dt, 32);\n\t\tvar isAnyBlipPlaying = false;\n\n\t\tif (activeSfx != null) {\n\t\t\tisAnyBlipPlaying = true;\n\t\t\tvar sfx = activeSfx;\n\n\t\t\tsfx.timer += dt;\n\t\t\tif (sfx.timer >= sfx.duration) {\n\t\t\t\tsfx.timer = sfx.duration;\n\t\t\t}\n\n\t\t\tif (sfx.frequencies.length > 0) {\n\t\t\t\t// update pitch\n\t\t\t\tvar prevPitchIndex = sfx.pitchIndex;\n\t\t\t\tvar freq = sfxFrequencyAtTime(sfx, sfx.timer);\n\t\t\t\tif (prevPitchIndex != sfx.pitchIndex) {\n\t\t\t\t\t// pitch changed!\n\t\t\t\t\tbitsy.frequency(bitsy.SOUND1, freq * 100);\n\t\t\t\t}\n\n\t\t\t\t// update volume envelope\n\t\t\t\tbitsy.volume(bitsy.SOUND1, sfxVolumeAtTime(sfx, sfx.timer));\n\t\t\t}\n\n\t\t\tif (sfx.timer >= sfx.duration) {\n\t\t\t\t// turn off sound\n\t\t\t\tbitsy.volume(bitsy.SOUND1, 0);\n\t\t\t\tactiveSfx = null;\n\t\t\t}\n\t\t}\n\n\t\tif (isMusicPausedForBlip && !isAnyBlipPlaying) {\n\t\t\tisMusicPausedForBlip = false;\n\t\t}\n\t}\n\n\tfunction updateTune(dt) {\n\t\tif (curTune === undefined || curTune === null) {\n\t\t\treturn;\n\t\t}\n\n\t\tbeat16Timer += dt;\n\n\t\tif (muteTimer > 0) {\n\t\t\tmuteTimer -= dt;\n\t\t}\n\n\t\tif (beat16Timer >= beat16) {\n\t\t\tbeat16Timer = 0;\n\t\t\tbeat16Index++;\n\n\t\t\tif (beat16Index >= 16) {\n\t\t\t\tbeat16Index = 0;\n\n\t\t\t\tif (!isLooping) {\n\t\t\t\t\tbarIndex = (barIndex + 1) % curTune.melody.length;\n\n\t\t\t\t\tif (curTune.arpeggioPattern != ArpeggioPattern.OFF && curTune.key != null) {\n\t\t\t\t\t\tcurArpeggio = arpeggiateBar(curTune.harmony[barIndex], curTune.key, curTune.arpeggioPattern);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (muteTimer <= 0) {\n\t\t\t\tif (!isMelodyMuted) {\n\t\t\t\t\t// melody note\n\t\t\t\t\tvar pitchA = curTune.melody[barIndex][beat16Index];\n\t\t\t\t\tif (pitchA.beats > 0) {\n\t\t\t\t\t\t// since they're played on the same channel, any melody note will cancel a blip\n\t\t\t\t\t\tactiveSfx = null;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (pitchA.blip != undefined && pitchA.beats > 0) {\n\t\t\t\t\t\tplayBlip(blip[pitchA.blip], { interruptMusic: false, pitch: pitchA, key: curTune.key });\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tplayNote(pitchA, curTune.instrumentA, { channel: bitsy.SOUND1, key: curTune.key });\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (curTune.arpeggioPattern === ArpeggioPattern.OFF) {\n\t\t\t\t\t// harmony note\n\t\t\t\t\tvar pitchB = curTune.harmony[barIndex][beat16Index];\n\t\t\t\t\tif (pitchB.blip != undefined && pitchB.beats > 0) {\n\t\t\t\t\t\tplayBlip(blip[pitchB.blip], { interruptMusic: false, pitch: pitchB, key: curTune.key });\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tplayNote(pitchB, curTune.instrumentB, { channel: bitsy.SOUND2, key: curTune.key });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvar arpPitch = curArpeggio[beat16Index % curArpeggio.length];\n\t\t\t\t\tif (arpPitch != undefined && arpPitch.beats > 0) {\n\t\t\t\t\t\tplayNote(arpPitch, curTune.instrumentB, { channel: bitsy.SOUND2, beatLen: beat16 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (maxBeatCount != null && beat16Index >= (maxBeatCount - 1)) {\n\t\t\t\t// stop playback early\n\t\t\t\tcurTune = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.update = function(dt) {\n\t\tupdateSfx(dt);\n\t\tif (!isTunePaused && !isMusicPausedForBlip) {\n\t\t\tupdateTune(dt);\n\t\t}\n\t};\n\n\tthis.playTune = function(tune, options) {\n\t\tcurTune = tune;\n\t\tbeat16Timer = 0;\n\t\tbeat16Index = -1;\n\t\tbarIndex = 0;\n\n\t\tisLooping = false;\n\t\tisMelodyMuted = false;\n\t\tmaxBeatCount = null;\n\n\t\t// special options for the editor\n\t\tif (options != undefined) {\n\t\t\tif (options.barIndex != undefined) {\n\t\t\t\tbarIndex = options.barIndex;\n\t\t\t}\n\n\t\t\tif (options.loop != undefined) {\n\t\t\t\tisLooping = options.loop;\n\t\t\t}\n\n\t\t\tif (options.melody != undefined) {\n\t\t\t\tisMelodyMuted = !options.melody;\n\t\t\t}\n\n\t\t\tif (options.beatCount != undefined) {\n\t\t\t\tmaxBeatCount = options.beatCount;\n\t\t\t}\n\t\t}\n\n\t\t// update tempo\n\t\tbeat16 = tempos[curTune.tempo];\n\n\t\tif (curTune.arpeggioPattern != ArpeggioPattern.OFF && curTune.key != null) {\n\t\t\tcurArpeggio = arpeggiateBar(curTune.harmony[barIndex], curTune.key, curTune.arpeggioPattern);\n\t\t}\n\t};\n\n\tthis.isTunePlaying = function() {\n\t\treturn curTune != null;\n\t};\n\n\tthis.getCurTuneId = function() {\n\t\tif (curTune) {\n\t\t\treturn curTune.id;\n\t\t}\n\n\t\treturn null;\n\t};\n\n\tthis.stopTune = function() {\n\t\tcurTune = null;\n\t};\n\n\tthis.pauseTune = function() {\n\t\tisTunePaused = true;\n\t};\n\n\tthis.resumeTune = function() {\n\t\tisTunePaused = false;\n\t};\n\n\tthis.getBeat = function() {\n\t\tif (curTune == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tbar : barIndex,\n\t\t\tbeat : beat16Index,\n\t\t};\n\t};\n\n\tthis.getBlipState = function() {\n\t\treturn activeSfx;\n\t};\n\n\tthis.playNote = function(pitch, instrument, channel, key) {\n\t\tbeat16 = tempos[Tempo.SLW];\n\t\tmuteTimer = beat16;\n\t\tplayNote(pitch, instrument, { channel: channel, key: key });\n\t};\n\n\tthis.setTempo = function(tempo) {\n\t\tbeat16 = tempos[tempo];\n\t};\n\n\tthis.setLooping = function(looping) {\n\t\tisLooping = looping;\n\t};\n\n\t/* SOUND EFFECTS */\n\tvar sfxPeakVolume = 10; // todo : is this a good value?\n\tvar activeSfx = null;\n\tvar isMusicPausedForBlip = false;\n\n\tfunction createSfxState(blip, pitch, isPitchRandomized) {\n\t\t// bitsy.log(\"init sfx blip: \" + blip.id);\n\n\t\tvar sfxState = {\n\t\t\tblip : blip,\n\t\t\tpitchIndex : -1,\n\t\t\tfrequencies : [],\n\t\t\ttimer : 0,\n\t\t\tduration : 0,\n\t\t};\n\n\t\t// is it weird to track this both in the system *AND* the engine?\n\t\tsfxState.duration = (blip.envelope.attack + blip.envelope.decay + blip.envelope.length + blip.envelope.release);\n\n\t\t// adjust starting pitch\n\t\tvar step = 0;\n\t\tif (pitch != null) {\n\t\t\tstep = pitchDistance(blip.pitchA, pitch);\n\t\t}\n\t\telse if (isPitchRandomized > 0) {\n\t\t\tstep = Math.floor(Math.random() * 6);\n\t\t}\n\n\t\tif (blip.pitchA.beats > 0) {\n\t\t\tsfxState.frequencies.push(makePitchFrequency(adjustPitch(blip.pitchA, step)));\n\t\t}\n\t\tif (blip.pitchB.beats > 0) {\n\t\t\tsfxState.frequencies.push(makePitchFrequency(adjustPitch(blip.pitchB, step)));\n\t\t}\n\t\tif (blip.pitchC.beats > 0) {\n\t\t\tsfxState.frequencies.push(makePitchFrequency(adjustPitch(blip.pitchC, step)));\n\t\t}\n\n\t\treturn sfxState;\n\t}\n\n\tfunction playBlip(blip, options) {\n\t\t// default to pausing music while the blip plays (except when playing a blip as *part* of music)\n\t\tisMusicPausedForBlip = (options === undefined || options.interruptMusic === undefined) ? true : options.interruptMusic;\n\n\t\t// always play blips on channel 1\n\t\tvar channel = bitsy.SOUND1;\n\n\t\t// other options\n\t\tvar pitch = (options === undefined || options.pitch === undefined) ? null : options.pitch;\n\t\tvar isPitchRandomized = (options === undefined || options.isPitchRandomized === undefined) ? false : options.isPitchRandomized;\n\t\tvar key = (options != undefined && options.key != undefined) ? options.key : null;\n\n\t\tactiveSfx = createSfxState(blip, pitchToChromatic(pitch, key), isPitchRandomized);\n\t\tbitsy.log(\"play blip: \" + activeSfx.frequencies);\n\n\t\tbitsy.sound(\n\t\t\tchannel,\n\t\t\tactiveSfx.duration * 10, // HACK : mult by 10 is to avoid accidentally turning off early\n\t\t\tactiveSfx.frequencies.length > 0 ? (activeSfx.frequencies[0] * 100) : 0,\n\t\t\t0, // volume\n\t\t\tactiveSfx.blip.instrument);\n\t};\n\n\tthis.playBlip = playBlip;\n\n\tthis.isBlipPlaying = function() {\n\t\treturn isMusicPausedForBlip; // todo : rename this variable?\n\t};\n\n\t// todo : should any of this stuff be moved into the tool code?\n\tthis.sampleBlip = function(blip, sampleCount) {\n\t\tvar sfx = createSfxState(blip, null, false);\n\n\t\tvar minFreq = makePitchFrequency({ note: Note.C, octave: Octave[2] });\n\t\tvar maxFreq = makePitchFrequency({ note: Note.B, octave: Octave[5] });\n\n\t\t// sample the frequency of the sound\n\t\tvar frequencySamples = [];\n\t\tfor (var i = 0; i < sampleCount; i++) {\n\t\t\tif (sfx.frequencies.length > 0) {\n\t\t\t\tvar t = Math.floor((i / sampleCount) * sfx.duration);\n\t\t\t\t// get frequency at time\n\t\t\t\tvar freq = sfxFrequencyAtTime(sfx, t);\n\t\t\t\t// normalize the sample\n\t\t\t\tfreq = freq / (maxFreq - minFreq);\n\n\t\t\t\tfrequencySamples.push(freq);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfrequencySamples.push(0);\n\t\t\t}\n\t\t}\n\n\t\t// sample the volume envelope\n\t\tvar amplitudeSamples = [];\n\t\tfor (var i = 0; i < sampleCount; i++) {\n\t\t\tvar t = Math.floor((i / sampleCount) * sfx.duration);\n\t\t\tamplitudeSamples.push(sfxVolumeAtTime(sfx, t) / maxVolume);\n\t\t}\n\n\t\treturn {\n\t\t\tfrequencies: frequencySamples,\n\t\t\tamplitudes: amplitudeSamples\n\t\t};\n\t};\n}",
"font.js": "/*\nTODO:\n- can I simplify this more now that I've removed the external resources stuff?\n*/\n\nfunction FontManager(packagedFontNames) {\n\nvar self = this;\n\nvar fontExtension = \".bitsyfont\";\nthis.GetExtension = function() {\n\treturn fontExtension;\n}\n\n// place to store font data\nvar fontResources = {};\n\n// load fonts from the editor\nif (packagedFontNames != undefined && packagedFontNames != null && packagedFontNames.length > 0\n\t\t&& Resources != undefined && Resources != null) {\n\n\tfor (var i = 0; i < packagedFontNames.length; i++) {\n\t\tvar filename = packagedFontNames[i];\n\t\tfontResources[filename] = Resources[filename];\n\t}\n}\n\n// manually add resource\nthis.AddResource = function(filename, fontdata) {\n\tfontResources[filename] = fontdata;\n}\n\nthis.ContainsResource = function(filename) {\n\treturn fontResources[filename] != null;\n}\n\nfunction GetData(fontName) {\n\treturn fontResources[fontName + fontExtension];\n}\nthis.GetData = GetData;\n\nfunction Create(fontData) {\n\treturn new Font(fontData);\n}\nthis.Create = Create;\n\nthis.Get = function(fontName) {\n\tvar fontData = self.GetData(fontName);\n\treturn self.Create(fontData);\n}\n\nfunction Font(fontData) {\n\tbitsy.log(\"create font\");\n\n\tvar name = \"unknown\";\n\tvar width = 6; // default size so if you have NO font or an invalid font it displays boxes\n\tvar height = 8;\n\tvar chardata = {};\n\n\t// create invalid char data at default size in case the font is missing\n\tvar invalidCharData = {};\n\tupdateInvalidCharData();\n\n\tthis.getName = function() {\n\t\treturn name;\n\t}\n\n\tthis.getData = function() {\n\t\treturn chardata;\n\t}\n\n\tthis.getWidth = function() {\n\t\treturn width;\n\t}\n\n\tthis.getHeight = function() {\n\t\treturn height;\n\t}\n\n\tthis.hasChar = function(char) {\n\t\tvar codepoint = char.charCodeAt(0);\n\t\treturn chardata[codepoint] != null;\n\t}\n\n\tthis.getChar = function(char) {\n\n\t\tvar codepoint = char.charCodeAt(0);\n\n\t\tif (chardata[codepoint] != null) {\n\t\t\treturn chardata[codepoint];\n\t\t}\n\t\telse {\n\t\t\treturn invalidCharData;\n\t\t}\n\t}\n\n\tthis.allCharCodes = function() {\n\t\tvar codeList = [];\n\t\tfor (var code in chardata) {\n\t\t\tcodeList.push(code);\n\t\t}\n\t\treturn codeList;\n\t}\n\n\tfunction createCharData() {\n\t\treturn { \n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\toffset: {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0\n\t\t\t},\n\t\t\tspacing: width,\n\t\t\tdata: [],\n\t\t};\n\t}\n\n\tfunction updateInvalidCharData() {\n\t\tinvalidCharData = createCharData();\n\t\tfor (var y = 0; y < height; y++) {\n\t\t\tfor (var x = 0; x < width; x++) {\n\t\t\t\tif (x < width-1 && y < height-1) {\n\t\t\t\t\tinvalidCharData.data.push(1);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tinvalidCharData.data.push(0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction parseFont(fontData) {\n\t\tif (fontData == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tbitsy.log(\"split font lines\");\n\t\t// NOTE: this is where we run out of memory - split creates a lot of memory issues\n\t\t// var lines = fontData.split(\"\\n\");\n\t\tbitsy.log(\"after split lines\");\n\n\t\tvar isReadingChar = false;\n\t\tvar isReadingCharProperties = false;\n\t\tvar curCharLineCount = 0;\n\t\tvar curCharCode = 0;\n\n\t\tvar lineStart = 0;\n\t\tvar lineEnd = fontData.indexOf(\"\\n\", lineStart) != -1\n\t\t\t? fontData.indexOf(\"\\n\", lineStart)\n\t\t\t: fontData.length;\n\n\t\t// for (var i = 0; i < lines.length; i++) {\n\t\t// \tvar line = lines[i];\n\t\twhile (lineStart < fontData.length) {\n\t\t\tvar line = fontData.substring(lineStart, lineEnd);\n\t\t\t// bitsy.log(\"parse font xx \" + line);\n\n\t\t\tif (line[0] === \"#\") {\n\t\t\t\t// skip comment lines\n\t\t\t}\n\t\t\telse if (!isReadingChar) {\n\t\t\t\t// READING NON CHARACTER DATA LINE\n\t\t\t\tvar args = line.split(\" \");\n\t\t\t\tif (args[0] == \"FONT\") {\n\t\t\t\t\tname = args[1];\n\t\t\t\t}\n\t\t\t\telse if (args[0] == \"SIZE\") {\n\t\t\t\t\twidth = parseInt(args[1]);\n\t\t\t\t\theight = parseInt(args[2]);\n\t\t\t\t}\n\t\t\t\telse if (args[0] == \"CHAR\") {\n\t\t\t\t\tisReadingChar = true;\n\t\t\t\t\tisReadingCharProperties = true;\n\n\t\t\t\t\tcurCharLineCount = 0;\n\t\t\t\t\tcurCharCode = parseInt(args[1]);\n\t\t\t\t\tchardata[curCharCode] = createCharData();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// CHAR PROPERTIES\n\t\t\t\tif (isReadingCharProperties) {\n\t\t\t\t\tvar args = line.split(\" \");\n\t\t\t\t\tif (args[0].indexOf(\"CHAR_\") == 0) { // Sub-properties start with \"CHAR_\"\n\t\t\t\t\t\tif (args[0] == \"CHAR_SIZE\") {\n\t\t\t\t\t\t\t// Custom character size - overrides the default character size for the font\n\t\t\t\t\t\t\tchardata[curCharCode].width = parseInt(args[1]);\n\t\t\t\t\t\t\tchardata[curCharCode].height = parseInt(args[2]);\n\t\t\t\t\t\t\tchardata[curCharCode].spacing = parseInt(args[1]); // HACK : assumes CHAR_SIZE is always declared first\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (args[0] == \"CHAR_OFFSET\") {\n\t\t\t\t\t\t\t// Character offset - shift the origin of the character on the X or Y axis\n\t\t\t\t\t\t\tchardata[curCharCode].offset.x = parseInt(args[1]);\n\t\t\t\t\t\t\tchardata[curCharCode].offset.y = parseInt(args[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (args[0] == \"CHAR_SPACING\") {\n\t\t\t\t\t\t\t// Character spacing:\n\t\t\t\t\t\t\t// specify total horizontal space taken up by the character\n\t\t\t\t\t\t\t// lets chars take up more or less space on a line than its bitmap does\n\t\t\t\t\t\t\tchardata[curCharCode].spacing = parseInt(args[1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tisReadingCharProperties = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// CHAR DATA\n\t\t\t\tif (!isReadingCharProperties) {\n\t\t\t\t\t// READING CHARACTER DATA LINE\n\t\t\t\t\tfor (var j = 0; j < chardata[curCharCode].width; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tchardata[curCharCode].data.push( parseInt(line[j]) );\n\t\t\t\t\t}\n\n\t\t\t\t\tcurCharLineCount++;\n\t\t\t\t\tif (curCharLineCount >= chardata[curCharCode].height) {\n\t\t\t\t\t\tisReadingChar = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlineStart = lineEnd + 1;\n\t\t\tlineEnd = fontData.indexOf(\"\\n\", lineStart) != -1\n\t\t\t\t? fontData.indexOf(\"\\n\", lineStart)\n\t\t\t\t: fontData.length;\n\t\t}\n\n\t\t// re-init invalid character box at the actual font size once it's loaded\n\t\tupdateInvalidCharData();\n\t}\n\n\tbitsy.log(\"parse font\");\n\tparseFont(fontData);\n\n\tbitsy.log(\"create font\");\n}\n\n} // FontManager\n",
"transition.js": "var TransitionManager = function() {\n\tvar transitionStart = null;\n\tvar transitionEnd = null;\n\n\tvar isTransitioning = false;\n\tvar transitionTime = 0; // milliseconds\n\tvar minStepTime = 125; // cap the frame rate\n\tvar curStep = 0;\n\n\tthis.BeginTransition = function(startRoom, startX, startY, endRoom, endX, endY, effectName) {\n\t\tbitsy.log(\"--- START ROOM TRANSITION ---\");\n\n\t\tcurEffect = effectName;\n\n\t\tvar tmpRoom = player().room;\n\t\tvar tmpX = player().x;\n\t\tvar tmpY = player().y;\n\n\t\tif (transitionEffects[curEffect].showPlayerStart) {\n\t\t\tplayer().room = startRoom;\n\t\t\tplayer().x = startX;\n\t\t\tplayer().y = startY;\n\t\t}\n\t\telse {\n\t\t\tplayer().room = \"_transition_none\"; // kind of hacky!!\n\t\t}\n\n\t\tvar startRoomPixels = createRoomPixelBuffer(room[startRoom]);\n\t\tvar startPalette = getPal(room[startRoom].pal);\n\t\tvar startImage = new PostProcessImage(startRoomPixels);\n\t\ttransitionStart = new TransitionInfo(startImage, startPalette, startX, startY);\n\n\t\tif (transitionEffects[curEffect].showPlayerEnd) {\n\t\t\tplayer().room = endRoom;\n\t\t\tplayer().x = endX;\n\t\t\tplayer().y = endY;\n\t\t}\n\t\telse {\n\t\t\tplayer().room = \"_transition_none\";\n\t\t}\n\n\t\tvar endRoomPixels = createRoomPixelBuffer(room[endRoom]);\n\t\tvar endPalette = getPal(room[endRoom].pal);\n\t\tvar endImage = new PostProcessImage(endRoomPixels);\n\t\ttransitionEnd = new TransitionInfo(endImage, endPalette, endX, endY);\n\n\t\tisTransitioning = true;\n\t\ttransitionTime = 0;\n\t\tcurStep = 0;\n\n\t\tplayer().room = endRoom;\n\t\tplayer().x = endX;\n\t\tplayer().y = endY;\n\n\t\tbitsy.graphicsMode(bitsy.GFX_VIDEO);\n\t}\n\n\tthis.UpdateTransition = function(dt) {\n\t\tif (!isTransitioning) {\n\t\t\treturn;\n\t\t}\n\n\t\ttransitionTime += dt;\n\n\t\tvar maxStep = transitionEffects[curEffect].stepCount;\n\n\t\tif (transitionTime >= minStepTime) {\n\t\t\tcurStep++;\n\n\t\t\tvar step = curStep;\n\t\t\tbitsy.log(\"transition step \" + step);\n\n\t\t\tif (transitionEffects[curEffect].paletteEffectFunc) {\n\t\t\t\tvar colors = transitionEffects[curEffect].paletteEffectFunc(transitionStart, transitionEnd, (step / maxStep));\n\t\t\t\tupdatePaletteWithTileColors(colors);\n\t\t\t}\n\n\t\t\tbitsy.fill(bitsy.VIDEO, tileColorStartIndex);\n\n\t\t\tfor (var y = 0; y < bitsy.VIDEO_SIZE; y++) {\n\t\t\t\tfor (var x = 0; x < bitsy.VIDEO_SIZE; x++) {\n\t\t\t\t\tvar color = transitionEffects[curEffect].pixelEffectFunc(transitionStart, transitionEnd, x, y, (step / maxStep));\n\t\t\t\t\tbitsy.set(bitsy.VIDEO, (y * bitsy.VIDEO_SIZE) + x, color);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttransitionTime = 0;\n\t\t}\n\n\t\tif (curStep >= (maxStep - 1)) {\n\t\t\tisTransitioning = false;\n\t\t\ttransitionTime = 0;\n\t\t\ttransitionStart = null;\n\t\t\ttransitionEnd = null;\n\t\t\tcurStep = 0;\n\n\t\t\tif (transitionCompleteCallback != null) {\n\t\t\t\ttransitionCompleteCallback();\n\t\t\t}\n\t\t\ttransitionCompleteCallback = null;\n\n\t\t\tbitsy.graphicsMode(bitsy.GFX_MAP);\n\t\t}\n\t}\n\n\tthis.IsTransitionActive = function() {\n\t\treturn isTransitioning;\n\t}\n\n\t// todo : should this be part of the constructor?\n\tvar transitionCompleteCallback = null;\n\tthis.OnTransitionComplete = function(callback) {\n\t\tif (isTransitioning) { // TODO : safety check necessary?\n\t\t\ttransitionCompleteCallback = callback;\n\t\t}\n\t}\n\n\tvar transitionEffects = {};\n\tvar curEffect = \"none\";\n\tthis.RegisterTransitionEffect = function(name, effect) {\n\t\ttransitionEffects[name] = effect;\n\t}\n\n\tthis.RegisterTransitionEffect(\"none\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : false,\n\t\tpaletteEffectFunc : function() {},\n\t\tpixelEffectFunc : function() {},\n\t});\n\n\tthis.RegisterTransitionEffect(\"fade_w\", { // TODO : have it linger on full white briefly?\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 6,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\treturn delta < 0.5 ? start.Image.GetPixel(pixelX, pixelY) : end.Image.GetPixel(pixelX, pixelY);\n\t\t},\n\t\tpaletteEffectFunc : function(start, end, delta) {\n\t\t\tvar colors = [];\n\n\t\t\tif (delta < 0.5) {\n\t\t\t\tdelta = delta / 0.5;\n\n\t\t\t\tfor (var i = 0; i < start.Palette.length; i++) {\n\t\t\t\t\tcolors.push(lerpColor(start.Palette[i], [255, 255, 255], delta));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelta = ((delta - 0.5) / 0.5);\n\n\t\t\t\tfor (var i = 0; i < end.Palette.length; i++) {\n\t\t\t\t\tcolors.push(lerpColor([255, 255, 255], end.Palette[i], delta));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn colors;\n\t\t},\n\t});\n\n\tthis.RegisterTransitionEffect(\"fade_b\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 6,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\treturn delta < 0.5 ? start.Image.GetPixel(pixelX, pixelY) : end.Image.GetPixel(pixelX, pixelY);\n\t\t},\n\t\tpaletteEffectFunc : function(start, end, delta) {\n\t\t\tvar colors = [];\n\n\t\t\tif (delta < 0.5) {\n\t\t\t\tdelta = delta / 0.5;\n\n\t\t\t\tfor (var i = 0; i < start.Palette.length; i++) {\n\t\t\t\t\tcolors.push(lerpColor(start.Palette[i], [0, 0, 0], delta));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelta = ((delta - 0.5) / 0.5);\n\n\t\t\t\tfor (var i = 0; i < end.Palette.length; i++) {\n\t\t\t\t\tcolors.push(lerpColor([0, 0, 0], end.Palette[i], delta));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn colors;\n\t\t},\n\t});\n\n\tthis.RegisterTransitionEffect(\"wave\", {\n\t\tshowPlayerStart : true,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 12,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tvar waveDelta = delta < 0.5 ? delta / 0.5 : 1 - ((delta - 0.5) / 0.5);\n\n\t\t\tvar offset = (pixelY + (waveDelta * waveDelta * 0.2 * start.Image.Height));\n\t\t\tvar freq = 4;\n\t\t\tvar size = 2 + (14 * waveDelta);\n\t\t\tpixelX += Math.floor(Math.sin(offset / freq) * size);\n\n\t\t\tif (pixelX < 0) {\n\t\t\t\tpixelX += start.Image.Width;\n\t\t\t}\n\t\t\telse if (pixelX >= start.Image.Width) {\n\t\t\t\tpixelX -= start.Image.Width;\n\t\t\t}\n\n\t\t\tvar curImage = delta < 0.5 ? start.Image : end.Image;\n\t\t\treturn curImage.GetPixel(pixelX, pixelY);\n\t\t},\n\t\tpaletteEffectFunc : function(start, end, delta) {\n\t\t\treturn delta < 0.5 ? start.Palette : end.Palette;\n\t\t},\n\t});\n\n\tthis.RegisterTransitionEffect(\"tunnel\", {\n\t\tshowPlayerStart : true,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 12,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tif (delta <= 0.4) {\n\t\t\t\tvar tunnelDelta = 1 - (delta / 0.4);\n\n\t\t\t\tvar xDist = start.PlayerCenter.x - pixelX;\n\t\t\t\tvar yDist = start.PlayerCenter.y - pixelY;\n\t\t\t\tvar dist = Math.sqrt((xDist * xDist) + (yDist * yDist));\n\n\t\t\t\tif (dist > start.Image.Width * tunnelDelta) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn start.Image.GetPixel(pixelX, pixelY);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (delta <= 0.6) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvar tunnelDelta = (delta - 0.6) / 0.4;\n\n\t\t\t\tvar xDist = end.PlayerCenter.x - pixelX;\n\t\t\t\tvar yDist = end.PlayerCenter.y - pixelY;\n\t\t\t\tvar dist = Math.sqrt((xDist * xDist) + (yDist * yDist));\n\n\t\t\t\tif (dist > end.Image.Width * tunnelDelta) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn end.Image.GetPixel(pixelX, pixelY);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tpaletteEffectFunc : function(start, end, delta) {\n\t\t\treturn delta < 0.5 ? start.Palette : end.Palette;\n\t\t},\n\t});\n\n\tfunction lerpPalettes(start, end, delta) {\n\t\tvar colors = [];\n\n\t\tvar maxLength = (start.Palette.length > end.Palette.length) ?\n\t\t\tstart.Palette.length : end.Palette.length;\n\n\t\tfor (var i = 0; i < maxLength; i++) {\n\t\t\tif (i < start.Palette.length && i < end.Palette.length) {\n\t\t\t\tcolors.push(lerpColor(start.Palette[i], end.Palette[i], delta));\n\t\t\t}\n\t\t\telse if (i < start.Palette.length) {\n\t\t\t\tcolors.push(lerpColor(\n\t\t\t\t\tstart.Palette[i],\n\t\t\t\t\tend.Palette[end.Palette.length - 1],\n\t\t\t\t\tdelta));\n\t\t\t}\n\t\t\telse if (i < end.Palette.length) {\n\t\t\t\tcolors.push(lerpColor(\n\t\t\t\t\tstart.Palette[start.Palette.length - 1],\n\t\t\t\t\tend.Palette[i],\n\t\t\t\t\tdelta));\n\t\t\t}\n\t\t}\n\n\t\treturn colors;\n\t}\n\n\tthis.RegisterTransitionEffect(\"slide_u\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 8,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tvar pixelOffset = -1 * Math.floor(start.Image.Height * delta);\n\t\t\tvar slidePixelY = pixelY + pixelOffset;\n\n\t\t\tif (slidePixelY >= 0) {\n\t\t\t\treturn start.Image.GetPixel(pixelX, slidePixelY);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tslidePixelY += start.Image.Height;\n\t\t\t\treturn end.Image.GetPixel(pixelX, slidePixelY);\n\t\t\t}\n\t\t},\n\t\tpaletteEffectFunc : lerpPalettes,\n\t});\n\n\tthis.RegisterTransitionEffect(\"slide_d\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 8,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tvar pixelOffset = Math.floor(start.Image.Height * delta);\n\t\t\tvar slidePixelY = pixelY + pixelOffset;\n\n\t\t\tif (slidePixelY < start.Image.Height) {\n\t\t\t\treturn start.Image.GetPixel(pixelX, slidePixelY);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tslidePixelY -= start.Image.Height;\n\t\t\t\treturn end.Image.GetPixel(pixelX, slidePixelY);\n\t\t\t}\n\t\t},\n\t\tpaletteEffectFunc : lerpPalettes,\n\t});\n\n\tthis.RegisterTransitionEffect(\"slide_l\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 8,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tvar pixelOffset = -1 * Math.floor(start.Image.Width * delta);\n\t\t\tvar slidePixelX = pixelX + pixelOffset;\n\n\t\t\tif (slidePixelX >= 0) {\n\t\t\t\treturn start.Image.GetPixel(slidePixelX, pixelY);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tslidePixelX += start.Image.Width;\n\t\t\t\treturn end.Image.GetPixel(slidePixelX, pixelY);\n\t\t\t}\n\t\t},\n\t\tpaletteEffectFunc : lerpPalettes,\n\t});\n\n\tthis.RegisterTransitionEffect(\"slide_r\", {\n\t\tshowPlayerStart : false,\n\t\tshowPlayerEnd : true,\n\t\tstepCount : 8,\n\t\tpixelEffectFunc : function(start, end, pixelX, pixelY, delta) {\n\t\t\tvar pixelOffset = Math.floor(start.Image.Width * delta);\n\t\t\tvar slidePixelX = pixelX + pixelOffset;\n\n\t\t\tif (slidePixelX < start.Image.Width) {\n\t\t\t\treturn start.Image.GetPixel(slidePixelX, pixelY);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tslidePixelX -= start.Image.Width;\n\t\t\t\treturn end.Image.GetPixel(slidePixelX, pixelY);\n\t\t\t}\n\t\t},\n\t\tpaletteEffectFunc : lerpPalettes,\n\t});\n\n\t// todo : move to Renderer()?\n\tfunction createRoomPixelBuffer(room) {\n\t\tvar pixelBuffer = [];\n\n\t\tfor (var i = 0; i < bitsy.VIDEO_SIZE * bitsy.VIDEO_SIZE; i++) {\n\t\t\tpixelBuffer.push(tileColorStartIndex);\n\t\t}\n\n\t\tvar drawTileInPixelBuffer = function(sourceData, frameIndex, colorIndex, tx, ty, pixelBuffer) {\n\t\t\tvar frameData = sourceData[frameIndex];\n\n\t\t\tfor (var y = 0; y < bitsy.TILE_SIZE; y++) {\n\t\t\t\tfor (var x = 0; x < bitsy.TILE_SIZE; x++) {\n\t\t\t\t\tvar color = tileColorStartIndex + (frameData[y][x] === 1 ? colorIndex : 0);\n\t\t\t\t\tpixelBuffer[(((ty * bitsy.TILE_SIZE) + y) * bitsy.VIDEO_SIZE) + ((tx * bitsy.TILE_SIZE) + x)] = color;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//draw tiles\n\t\tfor (i in room.tilemap) {\n\t\t\tfor (j in room.tilemap[i]) {\n\t\t\t\tvar id = room.tilemap[i][j];\n\t\t\t\tvar x = parseInt(j);\n\t\t\t\tvar y = parseInt(i);\n\n\t\t\t\tif (id != \"0\" && tile[id] != null) {\n\t\t\t\t\tdrawTileInPixelBuffer(\n\t\t\t\t\t\trenderer.GetDrawingSource(tile[id].drw),\n\t\t\t\t\t\ttile[id].animation.frameIndex,\n\t\t\t\t\t\ttile[id].col,\n\t\t\t\t\t\tx,\n\t\t\t\t\t\ty,\n\t\t\t\t\t\tpixelBuffer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//draw items\n\t\tfor (var i = 0; i < room.items.length; i++) {\n\t\t\tvar itm = room.items[i];\n\t\t\tdrawTileInPixelBuffer(\n\t\t\t\trenderer.GetDrawingSource(item[itm.id].drw),\n\t\t\t\titem[itm.id].animation.frameIndex,\n\t\t\t\titem[itm.id].col,\n\t\t\t\titm.x,\n\t\t\t\titm.y,\n\t\t\t\tpixelBuffer);\n\t\t}\n\n\t\t//draw sprites\n\t\tfor (id in sprite) {\n\t\t\tvar spr = sprite[id];\n\t\t\tif (spr.room === room.id) {\n\t\t\t\tdrawTileInPixelBuffer(\n\t\t\t\t\trenderer.GetDrawingSource(spr.drw),\n\t\t\t\t\tspr.animation.frameIndex,\n\t\t\t\t\tspr.col,\n\t\t\t\t\tspr.x,\n\t\t\t\t\tspr.y,\n\t\t\t\t\tpixelBuffer);\n\t\t\t}\n\t\t}\n\n\t\treturn pixelBuffer;\n\t}\n\n\tfunction lerpColor(colorA, colorB, t) {\n\t\treturn [\n\t\t\tcolorA[0] + ((colorB[0] - colorA[0]) * t),\n\t\t\tcolorA[1] + ((colorB[1] - colorA[1]) * t),\n\t\t\tcolorA[2] + ((colorB[2] - colorA[2]) * t),\n\t\t];\n\t};\n}; // TransitionManager()\n\n// todo : is this wrapper still useful?\nvar PostProcessImage = function(imageData) {\n\tthis.Width = bitsy.VIDEO_SIZE;\n\tthis.Height = bitsy.VIDEO_SIZE;\n\n\tthis.GetPixel = function(x, y) {\n\t\treturn imageData[(y * bitsy.VIDEO_SIZE) + x];\n\t};\n\n\tthis.GetData = function() {\n\t\treturn imageData;\n\t};\n};\n\nvar TransitionInfo = function(image, palette, playerX, playerY) {\n\tthis.Image = image;\n\n\tthis.Palette = palette;\n\n\tthis.PlayerTilePos = {\n\t\tx: playerX,\n\t\ty: playerY\n\t};\n\n\tthis.PlayerCenter = {\n\t\tx: Math.floor((playerX * bitsy.TILE_SIZE) + (bitsy.TILE_SIZE / 2)),\n\t\ty: Math.floor((playerY * bitsy.TILE_SIZE) + (bitsy.TILE_SIZE / 2))\n\t};\n};",
"script.js": "function Script() {\n\nthis.CreateInterpreter = function() {\n\treturn new Interpreter();\n};\n\nthis.CreateUtils = function() {\n\treturn new Utils();\n};\n\nvar Interpreter = function() {\n\tvar env = new Environment();\n\tvar parser = new Parser( env );\n\n\tthis.SetDialogBuffer = function(buffer) { env.SetDialogBuffer( buffer ); };\n\n\t// TODO -- maybe this should return a string instead othe actual script??\n\tthis.Compile = function(scriptName, scriptStr) {\n\t\tvar script = parser.Parse(scriptStr, scriptName);\n\t\tenv.SetScript(scriptName, script);\n\t}\n\tthis.Run = function(scriptName, exitHandler, objectContext) { // Runs pre-compiled script\n\t\tvar localEnv = new LocalEnvironment(env);\n\n\t\tif (objectContext) {\n\t\t\tlocalEnv.SetObject(objectContext); // PROTO : should this be folded into the constructor?\n\t\t}\n\n\t\tvar script = env.GetScript(scriptName);\n\n\t\tscript.Eval( localEnv, function(result) { OnScriptReturn(localEnv, exitHandler); } );\n\t}\n\tthis.Interpret = function(scriptStr, exitHandler, objectContext) { // Compiles and runs code immediately\n\t\t// bitsy.log(\"INTERPRET\");\n\t\tvar localEnv = new LocalEnvironment(env);\n\n\t\tif (objectContext) {\n\t\t\tlocalEnv.SetObject(objectContext); // PROTO : should this be folded into the constructor?\n\t\t}\n\n\t\tvar script = parser.Parse(scriptStr, \"anonymous\");\n\t\tscript.Eval( localEnv, function(result) { OnScriptReturn(localEnv, exitHandler); } );\n\t}\n\tthis.HasScript = function(name) { return env.HasScript(name); };\n\n\tthis.ResetEnvironment = function() {\n\t\tenv = new Environment();\n\t\tparser = new Parser( env );\n\t}\n\n\tthis.Parse = function(scriptStr, rootId) { // parses a script but doesn't save it\n\t\treturn parser.Parse(scriptStr, rootId);\n\t}\n\n\tthis.Eval = function(scriptTree, exitHandler) { // runs a script stored externally\n\t\tvar localEnv = new LocalEnvironment(env); // TODO : does this need an object context?\n\t\tscriptTree.Eval(\n\t\t\tlocalEnv,\n\t\t\tfunction(result) {\n\t\t\t\tOnScriptReturn(result, exitHandler);\n\t\t\t});\n\t}\n\n\tfunction OnScriptReturn(result, exitHandler) {\n\t\tif (exitHandler != null) {\n\t\t\texitHandler(result);\n\t\t}\n\t}\n\n\tthis.CreateExpression = function(expStr) {\n\t\treturn parser.CreateExpression(expStr);\n\t}\n\n\tthis.SetVariable = function(name,value,useHandler) {\n\t\tenv.SetVariable(name,value,useHandler);\n\t}\n\n\tthis.DeleteVariable = function(name,useHandler) {\n\t\tenv.DeleteVariable(name,useHandler);\n\t}\n\tthis.HasVariable = function(name) {\n\t\treturn env.HasVariable(name);\n\t}\n\n\tthis.SetOnVariableChangeHandler = function(onVariableChange) {\n\t\tenv.SetOnVariableChangeHandler(onVariableChange);\n\t}\n\tthis.GetVariableNames = function() {\n\t\treturn env.GetVariableNames();\n\t}\n\tthis.GetVariable = function(name) {\n\t\treturn env.GetVariable(name);\n\t}\n\n\tfunction DebugVisualizeScriptTree(scriptTree) {\n\t\tvar printVisitor = {\n\t\t\tVisit : function(node,depth) {\n\t\t\t\tbitsy.log(\"-\".repeat(depth) + \"- \" + node.ToString());\n\t\t\t},\n\t\t};\n\n\t\tscriptTree.VisitAll( printVisitor );\n\t}\n\n\tthis.DebugVisualizeScriptTree = DebugVisualizeScriptTree;\n\n\tthis.DebugVisualizeScript = function(scriptName) {\n\t\tDebugVisualizeScriptTree(env.GetScript(scriptName));\n\t}\n}\n\n\nvar Utils = function() {\n\t// for editor ui\n\tthis.CreateDialogBlock = function(children,doIndentFirstLine) {\n\t\tif (doIndentFirstLine === undefined) {\n\t\t\tdoIndentFirstLine = true;\n\t\t}\n\n\t\tvar block = new DialogBlockNode(doIndentFirstLine);\n\n\t\tfor (var i = 0; i < children.length; i++) {\n\t\t\tblock.AddChild(children[i]);\n\t\t}\n\t\treturn block;\n\t}\n\n\tthis.CreateOptionBlock = function() {\n\t\tvar block = new DialogBlockNode(false);\n\t\tblock.AddChild(new FuncNode(\"say\", [new LiteralNode(\" \")]));\n\t\treturn block;\n\t}\n\n\tthis.CreateItemConditionPair = function() {\n\t\tvar itemFunc = this.CreateFunctionBlock(\"item\", [\"0\"]);\n\t\tvar condition = new ExpNode(\"==\", itemFunc, new LiteralNode(1));\n\t\tvar result = new DialogBlockNode(true);\n\t\tresult.AddChild(new FuncNode(\"say\", [new LiteralNode(\" \")]));\n\t\tvar conditionPair = new ConditionPairNode(condition, result);\n\t\treturn conditionPair;\n\t}\n\n\tthis.CreateVariableConditionPair = function() {\n\t\tvar varNode = this.CreateVariableNode(\"a\");\n\t\tvar condition = new ExpNode(\"==\", varNode, new LiteralNode(1));\n\t\tvar result = new DialogBlockNode(true);\n\t\tresult.AddChild(new FuncNode(\"say\", [new LiteralNode(\" \")]));\n\t\tvar conditionPair = new ConditionPairNode(condition, result);\n\t\treturn conditionPair;\n\t}\n\n\tthis.CreateDefaultConditionPair = function() {\n\t\tvar condition = this.CreateElseNode();\n\t\tvar result = new DialogBlockNode(true);\n\t\tresult.AddChild(new FuncNode(\"say\", [new LiteralNode(\" \")]));\n\t\tvar conditionPair = new ConditionPairNode(condition, result);\n\t\treturn conditionPair;\n\t}\n\n\tthis.CreateEmptySayFunc = function() {\n\t\treturn new FuncNode(\"say\", [new LiteralNode(\"...\")]);\n\t}\n\n\tthis.CreateFunctionBlock = function(name, initParamValues) {\n\t\tvar parameters = [];\n\t\tfor (var i = 0; i < initParamValues.length; i++) {\n\t\t\tparameters.push(new LiteralNode(initParamValues[i]));\n\t\t}\n\n\t\tvar node = new FuncNode(name, parameters);\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild(node);\n\t\treturn block;\n\t}\n\n\t// TODO : rename ParseStringToLiteralNode?\n\tthis.CreateLiteralNode = function(str) {\n\t\tif (str === \"true\") {\n\t\t\treturn new LiteralNode(true);\n\t\t}\n\t\telse if (str === \"false\") {\n\t\t\treturn new LiteralNode(false);\n\t\t}\n\t\telse if (!isNaN(parseFloat(str))) {\n\t\t\treturn new LiteralNode(parseFloat(str));\n\t\t}\n\t\telse {\n\t\t\treturn new LiteralNode(str);\n\t\t}\n\t}\n\n\tthis.CreateVariableNode = function(variableName) {\n\t\treturn new VarNode(variableName);\n\t}\n\n\tthis.CreatePropertyNode = function(propertyName, literalValue) {\n\t\tvar varNode = new VarNode(propertyName);\n\t\tvar valNode = new LiteralNode(literalValue);\n\t\tvar node = new FuncNode(\"property\", [varNode, valNode]);\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild(node);\n\t\treturn block;\n\t}\n\n\tthis.CreateElseNode = function() {\n\t\treturn new ElseNode();\n\t}\n\n\tthis.CreateStringLiteralNode = function(str) {\n\t\treturn new LiteralNode(str);\n\t}\n\n\t// TODO : need to split up code & dialog blocks :|\n\tthis.CreateCodeBlock = function() {\n\t\treturn new CodeBlockNode();\n\t}\n\n\tthis.ChangeSequenceType = function(oldSequence, type) {\n\t\tif(type === \"sequence\") {\n\t\t\treturn new SequenceNode(oldSequence.children);\n\t\t}\n\t\telse if(type === \"cycle\") {\n\t\t\treturn new CycleNode(oldSequence.children);\n\t\t}\n\t\telse if(type === \"shuffle\") {\n\t\t\treturn new ShuffleNode(oldSequence.children);\n\t\t}\n\t\treturn oldSequence;\n\t}\n\n\tthis.CreateSequenceBlock = function() {\n\t\tvar option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption1.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption2.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar sequence = new SequenceNode( [ option1, option2 ] );\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild( sequence );\n\t\treturn block;\n\t}\n\n\tthis.CreateCycleBlock = function() {\n\t\tvar option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption1.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption2.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar sequence = new CycleNode( [ option1, option2 ] );\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild( sequence );\n\t\treturn block;\n\t}\n\n\tthis.CreateShuffleBlock = function() {\n\t\tvar option1 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption1.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar option2 = new DialogBlockNode( false /*doIndentFirstLine*/ );\n\t\toption2.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar sequence = new ShuffleNode( [ option1, option2 ] );\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild( sequence );\n\t\treturn block;\n\t}\n\n\tthis.CreateIfBlock = function() {\n\t\tvar leftNode = new CodeBlockNode();\n\t\tleftNode.AddChild( new FuncNode(\"item\", [new LiteralNode(\"0\")] ) );\n\t\tvar rightNode = new LiteralNode( 1 );\n\t\tvar condition1 = new ExpNode(\"==\", leftNode, rightNode );\n\n\t\tvar condition2 = new ElseNode();\n\n\t\tvar result1 = new DialogBlockNode();\n\t\tresult1.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar result2 = new DialogBlockNode();\n\t\tresult2.AddChild(new FuncNode(\"say\", [new LiteralNode(\"...\")]));\n\n\t\tvar ifNode = new IfNode( [ condition1, condition2 ], [ result1, result2 ] );\n\t\tvar block = new CodeBlockNode();\n\t\tblock.AddChild( ifNode );\n\t\treturn block;\n\t}\n\n\tthis.ReadDialogScript = function(lines, i) {\n\t\tvar scriptStr = \"\";\n\t\tif (lines[i] === Sym.DialogOpen) {\n\t\t\tscriptStr += lines[i] + \"\\n\";\n\t\t\ti++;\n\t\t\twhile(lines[i] != Sym.DialogClose) {\n\t\t\t\tscriptStr += lines[i] + \"\\n\";\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tscriptStr += lines[i];\n\t\t\ti++;\n\t\t}\n\t\telse {\n\t\t\tscriptStr += lines[i];\n\t\t\ti++;\n\t\t}\n\t\treturn { script:scriptStr, index:i };\n\t}\n\n\t// TODO this.ReadCodeScript (reads through code open and close symbols), and this.ReadScript\n\n\tthis.EnsureDialogBlockFormat = function(dialogStr) {\n\t\t// TODO -- what if it's already enclosed in dialog symbols??\n\t\tif(dialogStr.indexOf('\\n') > -1) {\n\t\t\tdialogStr = Sym.DialogOpen + \"\\n\" + dialogStr + \"\\n\" + Sym.DialogClose;\n\t\t}\n\t\treturn dialogStr;\n\t}\n\n\tthis.RemoveDialogBlockFormat = function(source) {\n\t\tvar sourceLines = source.split(\"\\n\");\n\t\tvar dialogStr = \"\";\n\t\tif(sourceLines[0] === Sym.DialogOpen) {\n\t\t\t// multi line\n\t\t\tvar i = 1;\n\t\t\twhile (i < sourceLines.length && sourceLines[i] != Sym.DialogClose) {\n\t\t\t\tdialogStr += sourceLines[i] + (sourceLines[i+1] != Sym.DialogClose ? '\\n' : '');\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// single line\n\t\t\tdialogStr = source;\n\t\t}\n\t\treturn dialogStr;\n\t}\n\n\tthis.SerializeDialogNodeList = function(nodeList) {\n\t\tvar tempBlock = new DialogBlockNode(false);\n\t\t // set children directly to avoid breaking the parenting chain for this temp operation\n\t\ttempBlock.children = nodeList;\n\t\treturn tempBlock.Serialize();\n\t}\n\n\tthis.GetOperatorList = function() {\n\t\treturn [Sym.Set].concat(Sym.Operators);\n\t}\n\n\tthis.IsInlineCode = function(node) {\n\t\treturn isInlineCode(node);\n\t}\n}\n\n\n/* BUILT-IN FUNCTIONS */ // TODO: better way to encapsulate these?\nfunction deprecatedFunc(environment,parameters,onReturn) {\n\tbitsy.log(\"BITSY SCRIPT WARNING: Tried to use deprecated function\");\n\tonReturn(null);\n}\n\nfunction sayFunc(environment, parameters, onReturn) {\n\tif (parameters[0] != undefined && parameters[0] != null) {\n\t\tvar textStr = \"\" + parameters[0];\n\t\tenvironment.GetDialogBuffer().AddText(textStr);\n\t\tenvironment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });\n\t}\n\telse {\n\t\tonReturn(null);\n\t}\n}\n\nfunction linebreakFunc(environment, parameters, onReturn) {\n\t// bitsy.log(\"LINEBREAK FUNC\");\n\tenvironment.GetDialogBuffer().AddLinebreak();\n\tenvironment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });\n}\n\nfunction pagebreakFunc(environment, parameters, onReturn) {\n\tenvironment.GetDialogBuffer().AddPagebreak(function() { onReturn(null); });\n}\n\nfunction drawFunc(environment, parameters, onReturn) {\n\tvar drawingId = parameters[0];\n\tenvironment.GetDialogBuffer().AddDrawing(drawingId);\n\tenvironment.GetDialogBuffer().AddScriptReturn(function() { onReturn(null); });\n}\n\nfunction drawSpriteFunc(environment, parameters, onReturn) {\n\tvar spriteId = parameters[0];\n\n\t// check if id parameter is actually a name\n\tif (names.sprite[spriteId] != undefined) {\n\t\tspriteId = names.sprite[spriteId];\n\t}\n\n\tvar drawingId = sprite[spriteId].drw;\n\tdrawFunc(environment, [drawingId], onReturn);\n}\n\nfunction drawTileFunc(environment, parameters, onReturn) {\n\tvar tileId = parameters[0];\n\n\t// check if id parameter is actually a name\n\tif (names.tile[tileId] != undefined) {\n\t\ttileId = names.tile[tileId];\n\t}\n\n\tvar drawingId = tile[tileId].drw;\n\tdrawFunc(environment, [drawingId], onReturn);\n}\n\nfunction drawItemFunc(environment, parameters, onReturn) {\n\tvar itemId = parameters[0];\n\n\t// check if id parameter is actually a name\n\tif (names.item[itemId] != undefined) {\n\t\titemId = names.item[itemId];\n\t}\n\n\tvar drawingId = item[itemId].drw;\n\tdrawFunc(environment, [drawingId], onReturn);\n}\n\nfunction printFontFunc(environment, parameters, onReturn) {\n\tvar allCharacters = \"\";\n\tvar font = fontManager.Get(fontName);\n\tvar codeList = font.allCharCodes();\n\tfor (var i = 0; i < codeList.length; i++) {\n\t\tallCharacters += String.fromCharCode(codeList[i]) + \" \";\n\t}\n\tsayFunc(environment, [allCharacters], onReturn);\n}\n\nfunction itemFunc(environment,parameters,onReturn) {\n\tvar itemId = parameters[0];\n\n\tif (names.item[itemId] != undefined) {\n\t\t// id is actually a name\n\t\titemId = names.item[itemId];\n\t}\n\n\tvar curItemCount = player().inventory[itemId] ? player().inventory[itemId] : 0;\n\n\tif (parameters.length > 1) {\n\t\t// TODO : is it a good idea to force inventory to be >= 0?\n\t\tplayer().inventory[itemId] = Math.max(0, parseInt(parameters[1]));\n\t\tcurItemCount = player().inventory[itemId];\n\n\t\tif (onInventoryChanged != null) {\n\t\t\tonInventoryChanged(itemId);\n\t\t}\n\t}\n\n\tonReturn(curItemCount);\n}\n\nfunction toggleTextEffect(environment, name) {\n\tif (environment.GetDialogBuffer().hasTextEffect(name)) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(name);\n\t}\n\telse {\n\t\tenvironment.GetDialogBuffer().pushTextEffect(name, []);\n\t}\n}\n\nfunction color1Func(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"clr1\");\n\tonReturn(null);\n}\n\nfunction color2Func(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"clr2\");\n\tonReturn(null);\n}\n\nfunction color3Func(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"clr3\");\n\tonReturn(null);\n}\n\nfunction colorFunc(environment, parameters, onReturn) {\n\tenvironment.GetDialogBuffer().pushTextEffect(\"clr\", parameters);\n\tonReturn(null);\n}\n\nfunction colorPopFunc(environment, parameters, onReturn) {\n\tif (environment.GetDialogBuffer().hasTextEffect(\"clr\")) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(\"clr\");\n\t}\n\tonReturn(null);\n}\n\nfunction rainbowFunc(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"rbw\");\n\tonReturn(null);\n}\n\nfunction rainbowPopFunc(environment, parameters, onReturn) {\n\tif (environment.GetDialogBuffer().hasTextEffect(\"rbw\")) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(\"rbw\");\n\t}\n\tonReturn(null);\n}\n\nfunction wavyFunc(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"wvy\");\n\tonReturn(null);\n}\n\nfunction wavyPopFunc(environment, parameters, onReturn) {\n\tif (environment.GetDialogBuffer().hasTextEffect(\"wvy\")) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(\"wvy\");\n\t}\n\tonReturn(null);\n}\n\nfunction shakyFunc(environment, parameters, onReturn) {\n\ttoggleTextEffect(environment, \"shk\");\n\tonReturn(null);\n}\n\nfunction shakyPopFunc(environment, parameters, onReturn) {\n\tif (environment.GetDialogBuffer().hasTextEffect(\"shk\")) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(\"shk\");\n\t}\n\tonReturn(null);\n}\n\nfunction propertyFunc(environment, parameters, onReturn) {\n\tvar outValue = null;\n\n\tif (parameters.length > 0 && parameters[0]) {\n\t\tvar propertyName = parameters[0];\n\n\t\tif (environment.HasProperty(propertyName)) {\n\t\t\t// TODO : in a future update I can handle the case of initializing a new property\n\t\t\t// after which we can move this block outside the HasProperty check\n\t\t\tif (parameters.length > 1) {\n\t\t\t\tvar inValue = parameters[1];\n\t\t\t\tenvironment.SetProperty(propertyName, inValue);\n\t\t\t}\n\n\t\t\toutValue = environment.GetProperty(propertyName);\n\t\t}\n\t}\n\n\tbitsy.log(\"PROPERTY! \" + propertyName + \" \" + outValue);\n\n\tonReturn(outValue);\n}\n\nfunction endFunc(environment,parameters,onReturn) {\n\tisEnding = true;\n\tisNarrating = true;\n\tdialogRenderer.SetCentered(true);\n\tdialogRenderer.DrawTextbox();\n\tonReturn(null);\n}\n\nfunction exitFunc(environment, parameters, onReturn) {\n\tvar destRoom;\n\tvar destX;\n\tvar destY;\n\n\tif (parameters.length >= 1) {\n\t\tdestRoom = parameters[0];\n\n\t\t// is it a name?\n\t\tif (names.room[destRoom] != undefined) {\n\t\t\tdestRoom = names.room[destRoom];\n\t\t}\n\t}\n\n\tif (parameters.length >= 3) {\n\t\tdestX = parseInt(parameters[1]);\n\t\tdestY = parseInt(parameters[2]);\n\t}\n\n\tif (parameters.length >= 4) {\n\t\tvar transitionEffect = parameters[3];\n\n\t\ttransition.BeginTransition(\n\t\t\tplayer().room,\n\t\t\tplayer().x,\n\t\t\tplayer().y,\n\t\t\tdestRoom,\n\t\t\tdestX,\n\t\t\tdestY,\n\t\t\ttransitionEffect);\n\t\ttransition.UpdateTransition(0);\n\t}\n\n\tvar movePlayerAndResumeScript = function() {\n\t\tif (destRoom != undefined && destX != undefined && destY != undefined) {\n\t\t\t// update world state\n\t\t\tplayer().room = destRoom;\n\t\t\tplayer().x = destX;\n\t\t\tplayer().y = destY;\n\t\t\tstate.room = destRoom;\n\n\t\t\t// update game state\n\t\t\tinitRoom(state.room);\n\t\t}\n\n\t\tif (dialogRenderer) {\n\t\t\tdialogRenderer.updateTextboxPosition();\n\t\t}\n\n\t\t// resume dialog script\n\t\tonReturn(state.room);\n\t};\n\n\t// TODO : this doesn't play nice with pagebreak because it thinks the dialog is finished!\n\tif (transition.IsTransitionActive()) {\n\t\ttransition.OnTransitionComplete(movePlayerAndResumeScript);\n\t}\n\telse {\n\t\tmovePlayerAndResumeScript();\n\t}\n}\n\nfunction tuneFunc(environment, parameters, onReturn) {\n\tif (parameters.length > 0) {\n\t\tvar tuneId = parameters[0];\n\n\t\t// check if id parameter is actually a name\n\t\tif (names.tune[tuneId] != undefined) {\n\t\t\ttuneId = names.tune[tuneId];\n\t\t}\n\n\t\tif (soundPlayer) {\n\t\t\tif (tuneId === \"0\") {\n\t\t\t\tsoundPlayer.stopTune();\n\t\t\t}\n\t\t\telse if (state.tune != tuneId) {\n\t\t\t\tsoundPlayer.playTune(tune[tuneId]);\n\t\t\t}\n\t\t}\n\n\t\tstate.tune = tuneId;\n\t}\n\n\tonReturn(state.tune);\n}\n\nfunction blipFunc(environment, parameters, onReturn) {\n\tif (parameters.length > 0) {\n\t\tvar blipId = parameters[0];\n\n\t\t// check if id parameter is actually a name\n\t\tif (names.blip[blipId] != undefined) {\n\t\t\tblipId = names.blip[blipId];\n\t\t}\n\n\t\tsoundPlayer.playBlip(blip[blipId]);\n\t}\n\n\t// if a dialog skip is happening, stop it and force a redraw of the textbox\n\tif (dialogBuffer) {\n\t\tif (dialogBuffer.tryInterruptSkip()) {\n\t\t\tdialogRenderer.Draw(dialogBuffer, 0, true /* disableOnPrint */);\n\t\t}\n\t}\n\n\tonReturn(null);\n}\n\n/*\n// TODO : use later?\nfunction yakFunc(environment, parameters, onReturn) {\n\tif (parameters.length > 0) {\n\t\tvar blipId = parameters[0];\n\n\t\t// check if id parameter is actually a name\n\t\tif (names.blip[blipId] != undefined) {\n\t\t\tblipId = names.blip[blipId];\n\t\t}\n\n\t\tenvironment.GetDialogBuffer().pushTextEffect(\"yak\", [blipId]);\n\t}\n\n\tonReturn(null);\n}\n\nfunction yakPopFunc(environment, parameters, onReturn) {\n\tif (environment.GetDialogBuffer().hasTextEffect(\"yak\")) {\n\t\tenvironment.GetDialogBuffer().popTextEffect(\"yak\");\n\t}\n\n\tonReturn(null);\n}\n*/\n\nfunction paletteFunc(environment, parameters, onReturn) {\n\tif (parameters.length > 0) {\n\t\tvar palId = parameters[0];\n\n\t\t// check if id parameter is actually a name\n\t\tif (names.palette[palId] != undefined) {\n\t\t\tpalId = names.palette[palId];\n\t\t}\n\n\t\tupdatePalette(palId);\n\t}\n\n\tonReturn(state.pal);\n}\n\nfunction avatarFunc(environment, parameters, onReturn) {\n\tif (parameters.length > 0) {\n\t\tvar sprId = parameters[0];\n\n\t\t// check if id parameter is actually a name\n\t\tif (names.sprite[sprId] != undefined) {\n\t\t\tsprId = names.sprite[sprId];\n\t\t}\n\n\t\t// override the avatar's current appearance\n\t\tstate.ava = sprId;\n\n\t\t// redraw the avatar with its new appearance\n\t\tdrawRoom(room[state.room], { redrawAvatar: true });\n\t}\n\n\tonReturn(state.ava);\n}\n\n/* BUILT-IN OPERATORS */\nfunction setExp(environment,left,right,onReturn) {\n\t// bitsy.log(\"SET \" + left.name);\n\n\tif(left.type != \"variable\") {\n\t\t// not a variable! return null and hope for the best D:\n\t\tonReturn( null );\n\t\treturn;\n\t}\n\n\tright.Eval(environment,function(rVal) {\n\t\tenvironment.SetVariable( left.name, rVal );\n\t\t// bitsy.log(\"VAL \" + environment.GetVariable( left.name ) );\n\t\tleft.Eval(environment,function(lVal) {\n\t\t\tonReturn( lVal );\n\t\t});\n\t});\n}\nfunction equalExp(environment,left,right,onReturn) {\n\t// bitsy.log(\"EVAL EQUAL\");\n\t// bitsy.log(left);\n\t// bitsy.log(right);\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal === rVal );\n\t\t});\n\t});\n}\nfunction greaterExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal > rVal );\n\t\t});\n\t});\n}\nfunction lessExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal < rVal );\n\t\t});\n\t});\n}\nfunction greaterEqExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal >= rVal );\n\t\t});\n\t});\n}\nfunction lessEqExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal <= rVal );\n\t\t});\n\t});\n}\nfunction multExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal * rVal );\n\t\t});\n\t});\n}\nfunction divExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal / rVal );\n\t\t});\n\t});\n}\nfunction addExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal + rVal );\n\t\t});\n\t});\n}\nfunction subExp(environment,left,right,onReturn) {\n\tright.Eval(environment,function(rVal){\n\t\tleft.Eval(environment,function(lVal){\n\t\t\tonReturn( lVal - rVal );\n\t\t});\n\t});\n}\n\n/* ENVIRONMENT */\nvar Environment = function() {\n\tvar dialogBuffer = null;\n\tthis.SetDialogBuffer = function(buffer) { dialogBuffer = buffer; };\n\tthis.GetDialogBuffer = function() { return dialogBuffer; };\n\n\tvar functionMap = {};\n\n\t// dialog\n\tfunctionMap[\"say\"] = sayFunc;\n\tfunctionMap[\"br\"] = linebreakFunc;\n\tfunctionMap[\"pg\"] = pagebreakFunc;\n\n\t// text effects\n\tfunctionMap[\"wvy\"] = wavyFunc;\n\tfunctionMap[\"/wvy\"] = wavyPopFunc;\n\tfunctionMap[\"shk\"] = shakyFunc;\n\tfunctionMap[\"/shk\"] = shakyPopFunc;\n\tfunctionMap[\"rbw\"] = rainbowFunc;\n\tfunctionMap[\"/rbw\"] = rainbowPopFunc;\n\tfunctionMap[\"clr\"] = colorFunc;\n\tfunctionMap[\"/clr\"] = colorPopFunc;\n\t// drawing text effects\n\tfunctionMap[\"drwt\"] = drawTileFunc;\n\tfunctionMap[\"drws\"] = drawSpriteFunc;\n\tfunctionMap[\"drwi\"] = drawItemFunc;\n\n\t// room\n\tfunctionMap[\"end\"] = endFunc;\n\tfunctionMap[\"exit\"] = exitFunc;\n\tfunctionMap[\"pal\"] = paletteFunc;\n\tfunctionMap[\"ava\"] = avatarFunc;\n\n\t// inventory & variables\n\tfunctionMap[\"item\"] = itemFunc;\n\tfunctionMap[\"property\"] = propertyFunc;\n\n\t// sound\n\tfunctionMap[\"tune\"] = tuneFunc;\n\tfunctionMap[\"blip\"] = blipFunc;\n\n\t// legacy\n\tfunctionMap[\"clr1\"] = color1Func;\n\tfunctionMap[\"clr2\"] = color2Func;\n\tfunctionMap[\"clr3\"] = color3Func;\n\tfunctionMap[\"print\"] = sayFunc;\n\tfunctionMap[\"printTile\"] = drawTileFunc;\n\tfunctionMap[\"printSprite\"] = drawSpriteFunc;\n\tfunctionMap[\"printItem\"] = drawItemFunc;\n\n\t// DEBUG\n\tfunctionMap[\"_debugOnlyPrintFont\"] = printFontFunc;\n\n\t// EXPERIMENTAL\n\t// functionMap[\"yak\"] = yakFunc;\n\t// functionMap[\"/yak\"] = yakPopFunc;\n\n\tthis.HasFunction = function(name) { return functionMap[name] != undefined; };\n\tthis.EvalFunction = function(name,parameters,onReturn,env) {\n\t\tif (env == undefined || env == null) {\n\t\t\tenv = this;\n\t\t}\n\n\t\tfunctionMap[name](env, parameters, onReturn);\n\t}\n\n\tvar variableMap = {};\n\n\tthis.HasVariable = function(name) { return variableMap[name] != undefined; };\n\tthis.GetVariable = function(name) { return variableMap[name]; };\n\tthis.SetVariable = function(name,value,useHandler) {\n\t\t// bitsy.log(\"SET VARIABLE \" + name + \" = \" + value);\n\t\tif(useHandler === undefined) useHandler = true;\n\t\tvariableMap[name] = value;\n\t\tif(onVariableChangeHandler != null && useHandler){\n\t\t\tonVariableChangeHandler(name);\n\t\t}\n\t};\n\tthis.DeleteVariable = function(name,useHandler) {\n\t\tif(useHandler === undefined) useHandler = true;\n\t\tif(variableMap[name] != undefined) {\n\t\t\tvariableMap.delete(name);\n\t\t\tif(onVariableChangeHandler != null && useHandler) {\n\t\t\t\tonVariableChangeHandler(name);\n\t\t\t}\n\t\t}\n\t};\n\n\tvar operatorMap = {};\n\toperatorMap[\"=\"] = setExp;\n\toperatorMap[\"==\"] = equalExp;\n\toperatorMap[\">\"] = greaterExp;\n\toperatorMap[\"<\"] = lessExp;\n\toperatorMap[\">=\"] = greaterEqExp;\n\toperatorMap[\"<=\"] = lessEqExp;\n\toperatorMap[\"*\"] = multExp;\n\toperatorMap[\"/\"] = divExp;\n\toperatorMap[\"+\"] = addExp;\n\toperatorMap[\"-\"] = subExp;\n\n\tthis.HasOperator = function(sym) { return operatorMap[sym] != undefined; };\n\tthis.EvalOperator = function(sym,left,right,onReturn) {\n\t\toperatorMap[ sym ]( this, left, right, onReturn );\n\t}\n\n\tvar scriptMap = {};\n\tthis.HasScript = function(name) { return scriptMap[name] != undefined; };\n\tthis.GetScript = function(name) { return scriptMap[name]; };\n\tthis.SetScript = function(name,script) { scriptMap[name] = script; };\n\n\tvar onVariableChangeHandler = null;\n\tthis.SetOnVariableChangeHandler = function(onVariableChange) {\n\t\tonVariableChangeHandler = onVariableChange;\n\t}\n\tthis.GetVariableNames = function() {\n\t\tvar variableNames = [];\n\n\t\tfor (var key in variableMap) {\n\t\t\tvariableNames.push(key);\n\t\t}\n\n\t\treturn variableNames;\n\t}\n}\n\n// Local environment for a single run of a script: knows local context\nvar LocalEnvironment = function(parentEnvironment) {\n\t// this.SetDialogBuffer // not allowed in local environment?\n\tthis.GetDialogBuffer = function() { return parentEnvironment.GetDialogBuffer(); };\n\n\tthis.HasFunction = function(name) { return parentEnvironment.HasFunction(name); };\n\tthis.EvalFunction = function(name,parameters,onReturn,env) {\n\t\tif (env == undefined || env == null) {\n\t\t\tenv = this;\n\t\t}\n\n\t\tparentEnvironment.EvalFunction(name,parameters,onReturn,env);\n\t}\n\n\tthis.HasVariable = function(name) { return parentEnvironment.HasVariable(name); };\n\tthis.GetVariable = function(name) { return parentEnvironment.GetVariable(name); };\n\tthis.SetVariable = function(name,value,useHandler) { parentEnvironment.SetVariable(name,value,useHandler); };\n\t// this.DeleteVariable // not needed in local environment?\n\n\tthis.HasOperator = function(sym) { return parentEnvironment.HasOperator(sym); };\n\tthis.EvalOperator = function(sym,left,right,onReturn,env) {\n\t\tif (env == undefined || env == null) {\n\t\t\tenv = this;\n\t\t}\n\n\t\tparentEnvironment.EvalOperator(sym,left,right,onReturn,env);\n\t};\n\n\t// TODO : I don't *think* any of this is required by the local environment\n\t// this.HasScript\n\t// this.GetScript\n\t// this.SetScript\n\n\t// TODO : pretty sure these debug methods aren't required by the local environment either\n\t// this.SetOnVariableChangeHandler\n\t// this.GetVariableNames\n\n\t/* Here's where specific local context data goes:\n\t * this includes access to the object running the script\n\t * and any properties it may have (so far only \"locked\")\n\t */\n\n\t// The local environment knows what object called it -- currently only used to access properties\n\tvar curObject = null;\n\tthis.HasObject = function() { return curObject != undefined && curObject != null; }\n\tthis.SetObject = function(object) { curObject = object; }\n\tthis.GetObject = function() { return curObject; }\n\n\t// accessors for properties of the object that's running the script\n\tthis.HasProperty = function(name) {\n\t\tif (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t};\n\tthis.GetProperty = function(name) {\n\t\tif (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {\n\t\t\treturn curObject.property[name]; // TODO : should these be getters and setters instead?\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t};\n\tthis.SetProperty = function(name, value) {\n\t\t// NOTE : for now, we need to gaurd against creating new properties\n\t\tif (curObject && curObject.property && curObject.property.hasOwnProperty(name)) {\n\t\t\tcurObject.property[name] = value;\n\t\t}\n\t};\n}\n\nfunction leadingWhitespace(depth) {\n\tvar str = \"\";\n\tfor(var i = 0; i < depth; i++) {\n\t\tstr += \" \"; // two spaces per indent\n\t}\n\t// bitsy.log(\"WHITESPACE \" + depth + \" ::\" + str + \"::\");\n\treturn str;\n}\n\n/* NODES */\nvar TreeRelationship = function() {\n\tthis.parent = null;\n\tthis.children = [];\n\n\tthis.AddChild = function(node) {\n\t\tthis.children.push(node);\n\t\tnode.parent = this;\n\t};\n\n\tthis.AddChildren = function(nodeList) {\n\t\tfor (var i = 0; i < nodeList.length; i++) {\n\t\t\tthis.AddChild(nodeList[i]);\n\t\t}\n\t};\n\n\tthis.SetChildren = function(nodeList) {\n\t\tthis.children = [];\n\t\tthis.AddChildren(nodeList);\n\t};\n\n\tthis.VisitAll = function(visitor, depth) {\n\t\tif (depth == undefined || depth == null) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvisitor.Visit(this, depth);\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tthis.children[i].VisitAll( visitor, depth + 1 );\n\t\t}\n\t};\n\n\tthis.rootId = null; // for debugging\n\tthis.GetId = function() {\n\t\t// bitsy.log(this);\n\t\tif (this.rootId != null) {\n\t\t\treturn this.rootId;\n\t\t}\n\t\telse if (this.parent != null) {\n\t\t\tvar parentId = this.parent.GetId();\n\t\t\tif (parentId != null) {\n\t\t\t\treturn parentId + \"_\" + this.parent.children.indexOf(this);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n\nfunction DialogBlockNode(doIndentFirstLine) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"dialog_block\";\n\n\tthis.Eval = function(environment, onReturn) {\n\t\t// bitsy.log(\"EVAL BLOCK \" + this.children.length);\n\n\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\tevents.Raise(\"script_node_enter\", { id: this.GetId() });\n\t\t}\n\n\t\tvar lastVal = null;\n\t\tvar i = 0;\n\n\t\tfunction evalChildren(children, done) {\n\t\t\tif (i < children.length) {\n\t\t\t\t// bitsy.log(\">> CHILD \" + i);\n\t\t\t\tchildren[i].Eval(environment, function(val) {\n\t\t\t\t\t// bitsy.log(\"<< CHILD \" + i);\n\t\t\t\t\tlastVal = val;\n\t\t\t\t\ti++;\n\t\t\t\t\tevalChildren(children,done);\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdone();\n\t\t\t}\n\t\t};\n\n\t\tvar self = this;\n\t\tevalChildren(this.children, function() {\n\t\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\t\tevents.Raise(\"script_node_exit\", { id: self.GetId() });\n\t\t\t}\n\n\t\t\tonReturn(lastVal);\n\t\t});\n\t}\n\n\tif (doIndentFirstLine === undefined) {\n\t\tdoIndentFirstLine = true; // This is just for serialization\n\t}\n\n\tthis.Serialize = function(depth) {\n\t\tif (depth === undefined) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvar str = \"\";\n\t\tvar lastNode = null;\n\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\n\t\t\tvar curNode = this.children[i];\n\n\t\t\tvar shouldIndentFirstLine = (i == 0 && doIndentFirstLine);\n\t\t\tvar shouldIndentAfterLinebreak = (lastNode && lastNode.type === \"function\" && lastNode.name === \"br\");\n\n\t\t\tif (shouldIndentFirstLine || shouldIndentAfterLinebreak) {\n\t\t\t\tstr += leadingWhitespace(depth);\n\t\t\t}\n\n\t\t\tstr += curNode.Serialize(depth);\n\n\t\t\tlastNode = curNode;\n\t\t}\n\n\t\treturn str;\n\t}\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.GetId();\n\t};\n}\n\nfunction CodeBlockNode() {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"code_block\";\n\n\tthis.Eval = function(environment, onReturn) {\n\t\t// bitsy.log(\"EVAL BLOCK \" + this.children.length);\n\n\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\tevents.Raise(\"script_node_enter\", { id: this.GetId() });\n\t\t}\n\n\t\tvar lastVal = null;\n\t\tvar i = 0;\n\n\t\tfunction evalChildren(children, done) {\n\t\t\tif (i < children.length) {\n\t\t\t\t// bitsy.log(\">> CHILD \" + i);\n\t\t\t\tchildren[i].Eval(environment, function(val) {\n\t\t\t\t\t// bitsy.log(\"<< CHILD \" + i);\n\t\t\t\t\tlastVal = val;\n\t\t\t\t\ti++;\n\t\t\t\t\tevalChildren(children,done);\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdone();\n\t\t\t}\n\t\t};\n\n\t\tvar self = this;\n\t\tevalChildren(this.children, function() {\n\t\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\t\tevents.Raise(\"script_node_exit\", { id: self.GetId() });\n\t\t\t}\n\n\t\t\tonReturn(lastVal);\n\t\t});\n\t}\n\n\tthis.Serialize = function(depth) {\n\t\tif(depth === undefined) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\t// bitsy.log(\"SERIALIZE BLOCK!!!\");\n\t\t// bitsy.log(depth);\n\t\t// bitsy.log(doIndentFirstLine);\n\n\t\tvar str = \"{\"; // todo: increase scope of Sym?\n\n\t\t// TODO : do code blocks ever have more than one child anymore????\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tvar curNode = this.children[i];\n\t\t\tstr += curNode.Serialize(depth);\n\t\t}\n\n\t\tstr += \"}\";\n\n\t\treturn str;\n\t}\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.GetId();\n\t};\n}\n\nfunction isInlineCode(node) {\n\treturn isTextEffectBlock(node) || isUndefinedBlock(node) || isMultilineListBlock(node);\n}\n\nfunction isUndefinedBlock(node) {\n\treturn node.type === \"code_block\" && node.children.length > 0 && node.children[0].type === \"undefined\";\n}\n\nvar textEffectBlockNames = [\"clr1\", \"clr2\", \"clr3\", \"wvy\", \"shk\", \"rbw\", \"printSprite\", \"printItem\", \"printTile\", \"print\", \"say\", \"br\"];\nfunction isTextEffectBlock(node) {\n\tif (node.type === \"code_block\") {\n\t\tif (node.children.length > 0 && node.children[0].type === \"function\") {\n\t\t\tvar func = node.children[0];\n\t\t\treturn textEffectBlockNames.indexOf(func.name) != -1;\n\t\t}\n\t}\n\treturn false;\n}\n\nvar listBlockTypes = [\"sequence\", \"cycle\", \"shuffle\", \"if\"];\nfunction isMultilineListBlock(node) {\n\tif (node.type === \"code_block\") {\n\t\tif (node.children.length > 0) {\n\t\t\tvar child = node.children[0];\n\t\t\treturn listBlockTypes.indexOf(child.type) != -1;\n\t\t}\n\t}\n\treturn false;\n}\n\n// for round-tripping undefined code through the parser (useful for hacks!)\nfunction UndefinedNode(sourceStr) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"undefined\";\n\tthis.source = sourceStr;\n\n\tthis.Eval = function(environment,onReturn) {\n\t\ttoggleTextEffect(environment, \"_debug_highlight\");\n\t\tsayFunc(environment, [\"{\" + sourceStr + \"}\"], function() {\n\t\t\tonReturn(null);\n\t\t});\n\t\ttoggleTextEffect(environment, \"_debug_highlight\");\n\t}\n\n\tthis.Serialize = function(depth) {\n\t\treturn this.source;\n\t}\n\n\tthis.ToString = function() {\n\t\treturn \"undefined\" + \" \" + this.GetId();\n\t}\n}\n\nfunction FuncNode(name, args) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"function\";\n\tthis.name = name;\n\tthis.args = args;\n\n\tthis.Eval = function(environment,onReturn) {\n\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\tevents.Raise(\"script_node_enter\", { id: this.GetId() });\n\t\t}\n\n\t\tvar self = this; // hack to deal with scope (TODO : move up higher?)\n\n\t\tvar argumentValues = [];\n\t\tvar i = 0;\n\n\t\tfunction evalArgs(args, done) {\n\t\t\t// TODO : really hacky way to make we get the first\n\t\t\t// symbol's NAME instead of its variable value\n\t\t\t// if we are trying to do something with a property\n\t\t\tif (self.name === \"property\" && i === 0 && i < args.length) {\n\t\t\t\tif (args[i].type === \"variable\") {\n\t\t\t\t\targumentValues.push(args[i].name);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// first argument for a property MUST be a variable symbol\n\t\t\t\t\t// -- so skip everything if it's not!\n\t\t\t\t\ti = args.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (i < args.length) {\n\t\t\t\t// Evaluate each argument\n\t\t\t\targs[i].Eval(\n\t\t\t\t\tenvironment,\n\t\t\t\t\tfunction(val) {\n\t\t\t\t\t\targumentValues.push(val);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tevalArgs(args, done);\n\t\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdone();\n\t\t\t}\n\t\t};\n\n\t\tevalArgs(\n\t\t\tthis.args,\n\t\t\tfunction() {\n\t\t\t\tif (isPlayerEmbeddedInEditor && events != undefined && events != null) {\n\t\t\t\t\tevents.Raise(\"script_node_exit\", { id: self.GetId() });\n\t\t\t\t}\n\n\t\t\t\tenvironment.EvalFunction(self.name, argumentValues, onReturn);\n\t\t\t});\n\t}\n\n\tthis.Serialize = function(depth) {\n\t\tvar isDialogBlock = this.parent.type === \"dialog_block\";\n\t\tif (isDialogBlock && this.name === \"say\") {\n\t\t\t// TODO this could cause problems with \"real\" print functions\n\t\t\treturn this.args[0].value; // first argument should be the text of the {print} func\n\t\t}\n\t\telse if (isDialogBlock && this.name === \"br\") {\n\t\t\treturn \"\\n\";\n\t\t}\n\t\telse {\n\t\t\tvar str = \"\";\n\t\t\tstr += this.name;\n\t\t\tfor(var i = 0; i < this.args.length; i++) {\n\t\t\t\tstr += \" \";\n\t\t\t\tstr += this.args[i].Serialize(depth);\n\t\t\t}\n\t\t\treturn str;\n\t\t}\n\t}\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.name + \" \" + this.GetId();\n\t};\n}\n\nfunction LiteralNode(value) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"literal\";\n\tthis.value = value;\n\n\tthis.Eval = function(environment,onReturn) {\n\t\tonReturn(this.value);\n\t};\n\n\tthis.Serialize = function(depth) {\n\t\tvar str = \"\";\n\n\t\tif (this.value === null) {\n\t\t\treturn str;\n\t\t}\n\n\t\tif (typeof this.value === \"string\") {\n\t\t\tstr += '\"';\n\t\t}\n\n\t\tstr += this.value;\n\n\t\tif (typeof this.value === \"string\") {\n\t\t\tstr += '\"';\n\t\t}\n\n\t\treturn str;\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.value + \" \" + this.GetId();\n\t};\n}\n\nfunction VarNode(name) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"variable\";\n\tthis.name = name;\n\n\tthis.Eval = function(environment,onReturn) {\n\t\t// bitsy.log(\"EVAL \" + this.name + \" \" + environment.HasVariable(this.name) + \" \" + environment.GetVariable(this.name));\n\t\tif( environment.HasVariable(this.name) )\n\t\t\tonReturn( environment.GetVariable( this.name ) );\n\t\telse\n\t\t\tonReturn(null); // not a valid variable -- return null and hope that's ok\n\t} // TODO: might want to store nodes in the variableMap instead of values???\n\n\tthis.Serialize = function(depth) {\n\t\tvar str = \"\" + this.name;\n\t\treturn str;\n\t}\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.name + \" \" + this.GetId();\n\t};\n}\n\nfunction ExpNode(operator, left, right) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"operator\";\n\tthis.operator = operator;\n\tthis.left = left;\n\tthis.right = right;\n\n\tthis.Eval = function(environment,onReturn) {\n\t\t// bitsy.log(\"EVAL \" + this.operator);\n\t\tvar self = this; // hack to deal with scope\n\t\tenvironment.EvalOperator( this.operator, this.left, this.right, \n\t\t\tfunction(val){\n\t\t\t\t// bitsy.log(\"EVAL EXP \" + self.operator + \" \" + val);\n\t\t\t\tonReturn(val);\n\t\t\t} );\n\t\t// NOTE : sadly this pushes a lot of complexity down onto the actual operator methods\n\t};\n\n\tthis.Serialize = function(depth) {\n\t\tvar isNegativeNumber = this.operator === \"-\" && this.left.type === \"literal\" && this.left.value === null;\n\n\t\tif (!isNegativeNumber) {\n\t\t\tvar str = \"\";\n\n\t\t\tif (this.left != undefined && this.left != null) {\n\t\t\t\tstr += this.left.Serialize(depth) + \" \";\n\t\t\t}\n\n\t\t\tstr += this.operator;\n\n\t\t\tif (this.right != undefined && this.right != null) {\n\t\t\t\tstr += \" \" + this.right.Serialize(depth);\n\t\t\t}\n\n\t\t\treturn str;\n\t\t}\n\t\telse {\n\t\t\treturn this.operator + this.right.Serialize(depth); // hacky but seems to work\n\t\t}\n\t};\n\n\tthis.VisitAll = function(visitor, depth) {\n\t\tif (depth == undefined || depth == null) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvisitor.Visit( this, depth );\n\t\tif(this.left != null)\n\t\t\tthis.left.VisitAll( visitor, depth + 1 );\n\t\tif(this.right != null)\n\t\t\tthis.right.VisitAll( visitor, depth + 1 );\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.operator + \" \" + this.GetId();\n\t};\n}\n\nfunction SequenceBase() {\n\tTreeRelationship.call(this);\n\n\tthis.Serialize = function(depth) {\n\t\tvar str = \"\";\n\t\tstr += this.type + \"\\n\";\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tstr += leadingWhitespace(depth + 1) + Sym.List + \" \";\n\t\t\tstr += this.children[i].Serialize(depth + 2);\n\t\t\tstr += \"\\n\";\n\t\t}\n\t\tstr += leadingWhitespace(depth);\n\t\treturn str;\n\t};\n\n\tthis.VisitAll = function(visitor, depth) {\n\t\tif (depth == undefined || depth == null) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvisitor.Visit(this, depth);\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tthis.children[i].VisitAll( visitor, depth + 1 );\n\t\t}\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.GetId();\n\t};\n}\n\nfunction SequenceNode(options) {\n\tSequenceBase.call(this);\n\n\tthis.type = \"sequence\";\n\tthis.AddChildren(options);\n\n\tvar index = 0;\n\tthis.Eval = function(environment, onReturn) {\n\t\t// bitsy.log(\"SEQUENCE \" + index);\n\t\tthis.children[index].Eval(environment, onReturn);\n\n\t\tvar next = index + 1;\n\t\tif (next < this.children.length) {\n\t\t\tindex = next;\n\t\t}\n\t}\n}\n\nfunction CycleNode(options) {\n\tSequenceBase.call(this);\n\n\tthis.type = \"cycle\";\n\tthis.AddChildren(options);\n\n\tvar index = 0;\n\tthis.Eval = function(environment, onReturn) {\n\t\t// bitsy.log(\"CYCLE \" + index);\n\t\tthis.children[index].Eval(environment, onReturn);\n\n\t\tvar next = index + 1;\n\t\tif (next < this.children.length) {\n\t\t\tindex = next;\n\t\t}\n\t\telse {\n\t\t\tindex = 0;\n\t\t}\n\t}\n}\n\nfunction ShuffleNode(options) {\n\tSequenceBase.call(this);\n\n\tthis.type = \"shuffle\";\n\tthis.AddChildren(options);\n\n\tvar optionsShuffled = [];\n\tfunction shuffle(options) {\n\t\toptionsShuffled = [];\n\t\tvar optionsUnshuffled = options.slice();\n\t\twhile (optionsUnshuffled.length > 0) {\n\t\t\tvar i = Math.floor(Math.random() * optionsUnshuffled.length);\n\t\t\toptionsShuffled.push(optionsUnshuffled.splice(i,1)[0]);\n\t\t}\n\t}\n\tshuffle(this.children);\n\n\tvar index = 0;\n\tthis.Eval = function(environment, onReturn) {\n\t\toptionsShuffled[index].Eval(environment, onReturn);\n\t\t\n\t\tindex++;\n\t\tif (index >= this.children.length) {\n\t\t\tshuffle(this.children);\n\t\t\tindex = 0;\n\t\t}\n\t}\n}\n\n// TODO : rename? ConditionalNode?\nfunction IfNode(conditions, results, isSingleLine) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"if\";\n\n\tfor (var i = 0; i < conditions.length; i++) {\n\t\tthis.AddChild(new ConditionPairNode(conditions[i], results[i]));\n\t}\n\n\tvar self = this;\n\tthis.Eval = function(environment, onReturn) {\n\t\t// bitsy.log(\"EVAL IF\");\n\t\tvar i = 0;\n\t\tfunction TestCondition() {\n\t\t\tself.children[i].Eval(environment, function(result) {\n\t\t\t\tif (result.conditionValue == true) {\n\t\t\t\t\tonReturn(result.resultValue);\n\t\t\t\t}\n\t\t\t\telse if (i+1 < self.children.length) {\n\t\t\t\t\ti++;\n\t\t\t\t\tTestCondition();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tonReturn(null);\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\t\tTestCondition();\n\t};\n\n\tif (isSingleLine === undefined) {\n\t\tisSingleLine = false; // This is just for serialization\n\t}\n\n\tthis.Serialize = function(depth) {\n\t\tvar str = \"\";\n\t\tif(isSingleLine) {\n\t\t\t// HACKY - should I even keep this mode???\n\t\t\tstr += this.children[0].children[0].Serialize() + \" ? \" + this.children[0].children[1].Serialize();\n\t\t\tif (this.children.length > 1 && this.children[1].children[0].type === Sym.Else) {\n\t\t\t\tstr += \" \" + Sym.ElseExp + \" \" + this.children[1].children[1].Serialize();\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tstr += \"\\n\";\n\t\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\t\tstr += this.children[i].Serialize(depth);\n\t\t\t}\n\t\t\tstr += leadingWhitespace(depth);\n\t\t}\n\t\treturn str;\n\t};\n\n\tthis.IsSingleLine = function() {\n\t\treturn isSingleLine;\n\t};\n\n\tthis.VisitAll = function(visitor, depth) {\n\t\tif (depth == undefined || depth == null) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvisitor.Visit(this, depth);\n\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tthis.children[i].VisitAll(visitor, depth + 1);\n\t\t}\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.mode + \" \" + this.GetId();\n\t};\n}\n\nfunction ConditionPairNode(condition, result) {\n\tTreeRelationship.call(this);\n\n\tthis.type = \"condition_pair\";\n\n\tthis.AddChild(condition);\n\tthis.AddChild(result);\n\n\tvar self = this;\n\n\tthis.Eval = function(environment, onReturn) {\n\t\tself.children[0].Eval(environment, function(conditionSuccess) {\n\t\t\tif (conditionSuccess) {\n\t\t\t\tself.children[1].Eval(environment, function(resultValue) {\n\t\t\t\t\tonReturn({ conditionValue:true, resultValue:resultValue });\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tonReturn({ conditionValue:false });\n\t\t\t}\n\t\t});\n\t};\n\n\tthis.Serialize = function(depth) {\n\t\tvar str = \"\";\n\t\tstr += leadingWhitespace(depth + 1);\n\t\tstr += Sym.List + \" \" + this.children[0].Serialize(depth) + \" \" + Sym.ConditionEnd + Sym.Linebreak;\n\t\tstr += this.children[1].Serialize(depth + 2) + Sym.Linebreak;\n\t\treturn str;\n\t};\n\n\tthis.VisitAll = function(visitor, depth) {\n\t\tif (depth == undefined || depth == null) {\n\t\t\tdepth = 0;\n\t\t}\n\n\t\tvisitor.Visit(this, depth);\n\n\t\tfor (var i = 0; i < this.children.length; i++) {\n\t\t\tthis.children[i].VisitAll(visitor, depth + 1);\n\t\t}\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.GetId();\n\t};\n}\n\nfunction ElseNode() {\n\tTreeRelationship.call(this);\n\n\tthis.type = Sym.Else;\n\n\tthis.Eval = function(environment, onReturn) {\n\t\tonReturn(true);\n\t};\n\n\tthis.Serialize = function() {\n\t\treturn Sym.Else;\n\t};\n\n\tthis.ToString = function() {\n\t\treturn this.type + \" \" + this.mode + \" \" + this.GetId();\n\t};\n}\n\nvar Sym = {\n\tDialogOpen : '\"\"\"',\n\tDialogClose : '\"\"\"',\n\tCodeOpen : \"{\",\n\tCodeClose : \"}\",\n\tLinebreak : \"\\n\", // just call it \"break\" ?\n\tSeparator : \":\",\n\tList : \"-\",\n\tString : '\"',\n\tConditionEnd : \"?\",\n\tElse : \"else\",\n\tElseExp : \":\", // special shorthand for expressions (deprecate?)\n\tSet : \"=\",\n\tOperators : [\"==\", \">=\", \"<=\", \">\", \"<\", \"-\", \"+\", \"/\", \"*\"], // operators need to be in reverse order of precedence\n};\n\nvar Parser = function(env) {\n\tvar environment = env;\n\n\tthis.Parse = function(scriptStr, rootId) {\n\t\tvar rootNode = new DialogBlockNode();\n\t\trootNode.rootId = rootId;\n\t\tvar state = new ParserState(rootNode, scriptStr);\n\n\t\tif (state.MatchAhead(Sym.DialogOpen)) {\n\t\t\t// multi-line dialog block\n\t\t\tvar dialogStr = state.ConsumeBlock(Sym.DialogOpen + Sym.Linebreak, Sym.Linebreak + Sym.DialogClose);\n\t\t\trootNode = new DialogBlockNode();\n\t\t\trootNode.rootId = rootId; // hacky!!\n\t\t\tstate = new ParserState(rootNode, dialogStr);\n\t\t\tstate = ParseDialog(state);\n\t\t}\n\t\telse {\n\t\t\t// single-line dialog block\n\t\t\tstate = ParseDialog(state);\n\t\t}\n\n\t\treturn state.rootNode;\n\t};\n\n\tvar ParserState = function( rootNode, str ) {\n\t\tthis.rootNode = rootNode;\n\t\tthis.curNode = this.rootNode;\n\n\t\tvar sourceStr = str;\n\t\tvar i = 0;\n\t\tthis.Index = function() { return i; };\n\t\tthis.Count = function() { return sourceStr.length; };\n\t\tthis.Done = function() { return i >= sourceStr.length; };\n\t\tthis.Char = function() { return sourceStr[i]; };\n\t\tthis.Step = function(n) { if(n===undefined) n=1; i += n; };\n\t\tthis.MatchAhead = function(str) {\n\t\t\t// bitsy.log(str);\n\t\t\tstr = \"\" + str; // hack to turn single chars into strings\n\t\t\t// bitsy.log(str);\n\t\t\t// bitsy.log(str.length);\n\t\t\tfor (var j = 0; j < str.length; j++) {\n\t\t\t\tif (i + j >= sourceStr.length) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\telse if (str[j] != sourceStr[i+j]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tthis.Peak = function(end) {\n\t\t\tvar str = \"\";\n\t\t\tvar j = i;\n\t\t\t// bitsy.log(j);\n\t\t\twhile (j < sourceStr.length && end.indexOf(sourceStr[j]) == -1) {\n\t\t\t\tstr += sourceStr[j];\n\t\t\t\tj++;\n\t\t\t}\n\t\t\t// bitsy.log(\"PEAK ::\" + str + \"::\");\n\t\t\treturn str;\n\t\t}\n\t\tthis.ConsumeBlock = function(open, close, includeSymbols) {\n\t\t\tif (includeSymbols === undefined || includeSymbols === null) {\n\t\t\t\tincludeSymbols = false;\n\t\t\t}\n\n\t\t\tvar startIndex = i;\n\n\t\t\tvar matchCount = 0;\n\t\t\tif (this.MatchAhead(open)) {\n\t\t\t\tmatchCount++;\n\t\t\t\tthis.Step(open.length);\n\t\t\t}\n\n\t\t\twhile (matchCount > 0 && !this.Done()) {\n\t\t\t\tif (this.MatchAhead(close)) {\n\t\t\t\t\tmatchCount--;\n\t\t\t\t\tthis.Step( close.length );\n\t\t\t\t}\n\t\t\t\telse if (this.MatchAhead(open)) {\n\t\t\t\t\tmatchCount++;\n\t\t\t\t\tthis.Step(open.length);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Step();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (includeSymbols) {\n\t\t\t\treturn sourceStr.slice(startIndex, i);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn sourceStr.slice(startIndex + open.length, i - close.length);\n\t\t\t}\n\t\t}\n\n\t\tthis.Print = function() { bitsy.log(sourceStr); };\n\t\tthis.Source = function() { return sourceStr; };\n\t};\n\n\t/*\n\t\tParseDialog():\n\t\tThis function adds {print} nodes and linebreak {br} nodes to display text,\n\t\tinterleaved with bracketed code nodes for functions and flow control,\n\t\tsuch as text effects {shk} {wvy} or sequences like {cycle} and {shuffle}.\n\t\tThe parsing of those code blocks is handled by ParseCode.\n\n\t\tNote on parsing newline characters:\n\t\t- there should be an implicit linebreak {br} after each dialog line\n\t\t- a \"dialog line\" is defined as any line that either:\n\t\t\t- 1) contains dialog text (any text outside of a code block)\n\t\t\t- 2) is entirely empty (no text, no code)\n\t\t\t- *or* 3) contains a list block (sequence, cycle, shuffle, or conditional)\n\t\t- lines *only* containing {code} blocks are not dialog lines\n\n\t\tNOTE TO SELF: all the state I'm storing in here feels like\n\t\tevidence that the parsing system kind of broke down at this point :(\n\t\tMaybe it would feel better if I move into the \"state\" object\n\t*/\n\tfunction ParseDialog(state) {\n\t\tvar curLineNodeList = [];\n\t\tvar curText = \"\";\n\t\tvar curLineIsEmpty = true;\n\t\tvar curLineContainsDialogText = false;\n\t\tvar prevLineIsDialogLine = false;\n\n\t\tvar curLineIsDialogLine = function() {\n\t\t\treturn curLineContainsDialogText || curLineIsEmpty;\n\t\t}\n\n\t\tvar resetLineStateForNewLine = function() {\n\t\t\tprevLineIsDialogLine = curLineIsDialogLine();\n\t\t\tcurLineContainsDialogText = false;\n\t\t\tcurLineIsEmpty = true;\n\t\t\tcurText = \"\";\n\t\t\tcurLineNodeList = [];\n\t\t}\n\n\t\tvar tryAddTextNodeToList = function() {\n\t\t\tif (curText.length > 0) {\n\t\t\t\tvar sayNode = new FuncNode(\"say\", [new LiteralNode(curText)]);\n\t\t\t\tcurLineNodeList.push(sayNode);\n\n\t\t\t\tcurText = \"\";\n\t\t\t\tcurLineIsEmpty = false;\n\t\t\t\tcurLineContainsDialogText = true;\n\t\t\t}\n\t\t}\n\n\t\tvar addCodeNodeToList = function() {\n\t\t\tvar codeSource = state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose);\n\t\t\tvar codeState = new ParserState(new CodeBlockNode(), codeSource);\n\t\t\tcodeState = ParseCode(codeState);\n\t\t\tvar codeBlockNode = codeState.rootNode;\n\t\t\tcurLineNodeList.push(codeBlockNode);\n\n\t\t\tcurLineIsEmpty = false;\n\n\t\t\t// lists count as dialog text, because they can contain it\n\t\t\tif (isMultilineListBlock(codeBlockNode)) {\n\t\t\t\tcurLineContainsDialogText = true;\n\t\t\t}\n\t\t}\n\n\t\tvar tryAddLinebreakNodeToList = function() {\n\t\t\tif (prevLineIsDialogLine) {\n\t\t\t\tvar linebreakNode = new FuncNode(\"br\", []);\n\t\t\t\tcurLineNodeList.unshift(linebreakNode);\n\t\t\t}\n\t\t}\n\n\t\tvar addLineNodesToParent = function() {\n\t\t\tfor (var i = 0; i < curLineNodeList.length; i++) {\n\t\t\t\tstate.curNode.AddChild(curLineNodeList[i]);\n\t\t\t}\n\t\t}\n\n\t\twhile (!state.Done()) {\n\t\t\tif (state.MatchAhead(Sym.CodeOpen)) { // process code block\n\t\t\t\t// add any buffered text to a print node, and parse the code\n\t\t\t\ttryAddTextNodeToList();\n\t\t\t\taddCodeNodeToList();\n\t\t\t}\n\t\t\telse if (state.MatchAhead(Sym.Linebreak)) { // process new line\n\t\t\t\t// add any buffered text to a print node, \n\t\t\t\t// and add a linebreak if we are between two dialog lines\n\t\t\t\ttryAddTextNodeToList();\n\t\t\t\ttryAddLinebreakNodeToList();\n\n\t\t\t\t// since we've reached the end of a line\n\t\t\t\t// add stored nodes for this line to the parent node we are building,\n\t\t\t\t// and reset state for the next line\n\t\t\t\taddLineNodesToParent();\n\t\t\t\tresetLineStateForNewLine();\n\n\t\t\t\tstate.Step();\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// continue adding text to the current text buffer\n\t\t\t\tcurText += state.Char();\n\t\t\t\tstate.Step();\n\t\t\t}\n\t\t}\n\n\t\t// to make sure we don't leave anything behind:\n\t\t// add buffered text to a print node and add all nodes\n\t\t// to the current parent node\n\t\ttryAddTextNodeToList();\n\t\ttryAddLinebreakNodeToList();\n\t\taddLineNodesToParent();\n\n\t\treturn state;\n\t}\n\n\tfunction ParseDialogBlock(state) {\n\t\tvar dialogStr = state.ConsumeBlock( Sym.DialogOpen, Sym.DialogClose );\n\n\t\tvar dialogState = new ParserState(new DialogBlockNode(), dialogStr);\n\t\tdialogState = ParseDialog( dialogState );\n\n\t\tstate.curNode.AddChild( dialogState.rootNode );\n\n\t\treturn state;\n\t}\n\n\t/*\n\t\tParseConditional():\n\t\tA conditional contains a list of conditions that can be\n\t\tevaluated to true or false, followed by more dialog\n\t\tthat will be evaluated if the condition is true. The first\n\t\ttrue condition is the one that gets evaluated.\n\t*/\n\tfunction ParseConditional(state) {\n\t\tvar conditionStrings = [];\n\t\tvar resultStrings = [];\n\t\tvar curIndex = -1;\n\t\tvar requiredLeadingWhitespace = -1;\n\n\t\t// TODO : very similar to sequence parsing - can we share anything?\n\t\tfunction parseConditionalItemLine(state) {\n\t\t\tvar lineText = \"\";\n\t\t\tvar whitespaceCount = 0;\n\t\t\tvar isNewCondition = false;\n\t\t\tvar encounteredNonWhitespace = false;\n\t\t\tvar encounteredConditionEnd = false;\n\n\t\t\twhile (!state.Done() && !(state.Char() === Sym.Linebreak)) {\n\t\t\t\t// count whitespace until we hit the first non-whitespace character\n\t\t\t\tif (!encounteredNonWhitespace) {\n\t\t\t\t\tif (state.Char() === \" \" || state.Char() === \"\\t\") {\n\t\t\t\t\t\twhitespaceCount++;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tencounteredNonWhitespace = true;\n\n\t\t\t\t\t\tif (state.Char() === Sym.List) {\n\t\t\t\t\t\t\tisNewCondition = true;\n\t\t\t\t\t\t\twhitespaceCount += 2; // count the list seperator AND the following extra space\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if this is the condition, we need to track whether we've\n\t\t\t\t// reached the end of the condition\n\t\t\t\tif (isNewCondition && !encounteredConditionEnd) {\n\t\t\t\t\tif (state.Char() === Sym.ConditionEnd) {\n\t\t\t\t\t\tencounteredConditionEnd = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// add characters one at a time, unless it's a code block\n\t\t\t\t// since code blocks can contain additional sequences inside\n\t\t\t\t// them that will mess up our list item detection\n\t\t\t\tif (state.Char() === Sym.CodeOpen) {\n\t\t\t\t\tlineText += state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose, true /*includeSymbols*/);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (!encounteredConditionEnd) { // skip all characters including & after the condition end\n\t\t\t\t\t\tlineText += state.Char();\n\t\t\t\t\t}\n\t\t\t\t\tstate.Step();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (state.Char() === Sym.Linebreak) {\n\t\t\t\tstate.Step();\n\t\t\t}\n\n\t\t\treturn { text:lineText, whitespace:whitespaceCount, isNewCondition:isNewCondition };\n\t\t}\n\n\t\t// TODO : this is copied from sequence parsing; share?\n\t\tfunction trimLeadingWhitespace(text, trimLength) {\n\t\t\tvar textSplit = text.split(Sym.linebreak);\n\t\t\ttextSplit = textSplit.map(function(line) { return line.slice(trimLength) });\n\t\t\treturn textSplit.join(Sym.linebreak);\n\t\t}\n\n\t\twhile (!state.Done()) {\n\t\t\tvar lineResults = parseConditionalItemLine(state);\n\n\t\t\tif (lineResults.isNewCondition) {\n\t\t\t\trequiredLeadingWhitespace = lineResults.whitespace;\n\t\t\t\tcurIndex++;\n\t\t\t\tconditionStrings[curIndex] = \"\";\n\t\t\t\tresultStrings[curIndex] = \"\";\n\t\t\t}\n\n\t\t\t// to avoid extra newlines in nested conditionals, only count lines\n\t\t\t// that at least match the whitespace count of the initial line\n\t\t\t// NOTE: see the comment in sequence parsing for more details\n\t\t\tif (lineResults.whitespace >= requiredLeadingWhitespace) {\n\t\t\t\tvar trimmedText = trimLeadingWhitespace(lineResults.text, requiredLeadingWhitespace);\n\n\t\t\t\tif (lineResults.isNewCondition) {\n\t\t\t\t\tconditionStrings[curIndex] += trimmedText;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tresultStrings[curIndex] += trimmedText + Sym.Linebreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// hack: cut off the trailing newlines from all the result strings\n\t\tresultStrings = resultStrings.map(function(result) { return result.slice(0,-1); });\n\n\t\tvar conditions = [];\n\t\tfor (var i = 0; i < conditionStrings.length; i++) {\n\t\t\tvar str = conditionStrings[i].trim();\n\t\t\tif (str === Sym.Else) {\n\t\t\t\tconditions.push(new ElseNode());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvar exp = CreateExpression(str);\n\t\t\t\tconditions.push(exp);\n\t\t\t}\n\t\t}\n\n\t\tvar results = [];\n\t\tfor (var i = 0; i < resultStrings.length; i++) {\n\t\t\tvar str = resultStrings[i];\n\t\t\tvar dialogBlockState = new ParserState(new DialogBlockNode(), str);\n\t\t\tdialogBlockState = ParseDialog(dialogBlockState);\n\t\t\tvar dialogBlock = dialogBlockState.rootNode;\n\t\t\tresults.push(dialogBlock);\n\t\t}\n\n\t\tstate.curNode.AddChild(new IfNode(conditions, results));\n\n\t\treturn state;\n\t}\n\n\tfunction IsSequence(str) {\n\t\t// bitsy.log(\"IsSequence? \" + str);\n\t\treturn str === \"sequence\" || str === \"cycle\" || str === \"shuffle\";\n\t}\n\n\t/*\n\t\tParseSequence():\n\t\tSequence nodes contain a list of dialog block nodes. The order those\n\t\tnodes are evaluated is determined by the type of sequence:\n\t\t- sequence: each child node evaluated once in order\n\t\t- cycle: repeats from the beginning after all nodes evaluate\n\t\t- shuffle: evaluate in a random order\n\n\t\tEach item in a sequence is sepearated by a \"-\" character.\n\t\tThe seperator must come at the beginning of the line,\n\t\tbut may be preceded by whitespace (in any amount).\n\n\t\tAbout whitespace: Whitespace at the start of a line\n\t\tis ignored if it less than or equal to the count of\n\t\twhitespace that preceded the list separator (\"-\") at\n\t\tthe start of that item. (The count also includes the\n\t\tseperator and the extra space after the seperator.)\n\t */\n\tfunction ParseSequence(state, sequenceType) {\n\t\tvar itemStrings = [];\n\t\tvar curItemIndex = -1; // -1 indicates not reading an item yet\n\t\tvar requiredLeadingWhitespace = -1;\n\n\t\tfunction parseSequenceItemLine(state) {\n\t\t\tvar lineText = \"\";\n\t\t\tvar whitespaceCount = 0;\n\t\t\tvar isNewListItem = false;\n\t\t\tvar encounteredNonWhitespace = false;\n\n\t\t\twhile (!state.Done() && !(state.Char() === Sym.Linebreak)) {\n\t\t\t\t// count whitespace until we hit the first non-whitespace character\n\t\t\t\tif (!encounteredNonWhitespace) {\n\t\t\t\t\tif (state.Char() === \" \" || state.Char() === \"\\t\") {\n\t\t\t\t\t\twhitespaceCount++;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tencounteredNonWhitespace = true;\n\n\t\t\t\t\t\tif (state.Char() === Sym.List) {\n\t\t\t\t\t\t\tisNewListItem = true;\n\t\t\t\t\t\t\twhitespaceCount += 2; // count the list seperator AND the following extra space\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// add characters one at a time, unless it's a code block\n\t\t\t\t// since code blocks can contain additional sequences inside\n\t\t\t\t// them that will mess up our list item detection\n\t\t\t\tif (state.Char() === Sym.CodeOpen) {\n\t\t\t\t\tlineText += state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose, true /*includeSymbols*/);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlineText += state.Char();\n\t\t\t\t\tstate.Step();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (state.Char() === Sym.Linebreak) {\n\t\t\t\tstate.Step();\n\t\t\t}\n\n\t\t\treturn { text:lineText, whitespace:whitespaceCount, isNewListItem:isNewListItem };\n\t\t}\n\n\t\tfunction trimLeadingWhitespace(text, trimLength) {\n\t\t\t// the split and join is necessary because a single \"line\"\n\t\t\t// can contain sequences that may contain newlines of their own\n\t\t\t// (we treat them all as one \"line\" for sequence parsing purposes)\n\t\t\tvar textSplit = text.split(Sym.linebreak);\n\t\t\ttextSplit = textSplit.map(function(line) { return line.slice(trimLength) });\n\t\t\treturn textSplit.join(Sym.linebreak);\n\t\t}\n\n\t\twhile (!state.Done()) {\n\t\t\tvar lineResults = parseSequenceItemLine(state);\n\n\t\t\tif (lineResults.isNewListItem) {\n\t\t\t\trequiredLeadingWhitespace = lineResults.whitespace;\n\t\t\t\tcurItemIndex++;\n\t\t\t\titemStrings[curItemIndex] = \"\";\n\t\t\t}\n\n\t\t\t// to avoid double counting closing lines (empty ones ending in a curly brace)\n\t\t\t// we only allow lines that have at least as much whitespace as the start of the list item\n\t\t\t// TODO : I think right now this leads to a bug if the list item's indentation is less than\n\t\t\t// its parent code block... hopefully that won't be a big deal for now\n\t\t\t// (NOTE: I think the bug could be fixed by only applying this to the FINAL line of an item, but\n\t\t\t// that would require more consideration and testing)\n\t\t\tif (lineResults.whitespace >= requiredLeadingWhitespace) {\n\t\t\t\tvar trimmedText = trimLeadingWhitespace(lineResults.text, requiredLeadingWhitespace);\n\t\t\t\titemStrings[curItemIndex] += trimmedText + Sym.Linebreak;\n\t\t\t}\n\t\t}\n\n\t\t// a bit hacky: cut off the trailing newlines from all the items\n\t\titemStrings = itemStrings.map(function(item) { return item.slice(0,-1); });\n\n\t\tvar options = [];\n\t\tfor (var i = 0; i < itemStrings.length; i++) {\n\t\t\tvar str = itemStrings[i];\n\t\t\tvar dialogBlockState = new ParserState(new DialogBlockNode(false /* doIndentFirstLine */), str);\n\t\t\tdialogBlockState = ParseDialog(dialogBlockState);\n\t\t\tvar dialogBlock = dialogBlockState.rootNode;\n\t\t\toptions.push(dialogBlock);\n\t\t}\n\n\t\tif (sequenceType === \"sequence\") {\n\t\t\tstate.curNode.AddChild(new SequenceNode(options));\n\t\t}\n\t\telse if (sequenceType === \"cycle\") {\n\t\t\tstate.curNode.AddChild(new CycleNode(options));\n\t\t}\n\t\telse if (sequenceType === \"shuffle\") {\n\t\t\tstate.curNode.AddChild(new ShuffleNode(options));\n\t\t}\n\n\t\treturn state;\n\t}\n\n\tfunction ParseFunction(state, funcName) {\n\t\tbitsy.log(\"~~~ PARSE FUNCTION \" + funcName);\n\n\t\tvar args = [];\n\n\t\tvar curSymbol = \"\";\n\t\tfunction OnSymbolEnd() {\n\t\t\tcurSymbol = curSymbol.trim();\n\t\t\t// bitsy.log(\"PARAMTER \" + curSymbol);\n\t\t\targs.push( StringToValue(curSymbol) );\n\t\t\t// bitsy.log(args);\n\t\t\tcurSymbol = \"\";\n\t\t}\n\n\t\twhile( !( state.Char() === \"\\n\" || state.Done() ) ) {\n\t\t\tif( state.MatchAhead(Sym.CodeOpen) ) {\n\t\t\t\tvar codeBlockState = new ParserState(new CodeBlockNode(), state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose));\n\t\t\t\tcodeBlockState = ParseCode( codeBlockState );\n\t\t\t\tvar codeBlock = codeBlockState.rootNode;\n\t\t\t\targs.push( codeBlock );\n\t\t\t\tcurSymbol = \"\";\n\t\t\t}\n\t\t\telse if( state.MatchAhead(Sym.String) ) {\n\t\t\t\t/* STRING LITERAL */\n\t\t\t\tvar str = state.ConsumeBlock(Sym.String, Sym.String);\n\t\t\t\t// bitsy.log(\"STRING \" + str);\n\t\t\t\targs.push( new LiteralNode(str) );\n\t\t\t\tcurSymbol = \"\";\n\t\t\t}\n\t\t\telse if(state.Char() === \" \" && curSymbol.length > 0) {\n\t\t\t\tOnSymbolEnd();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcurSymbol += state.Char();\n\t\t\t}\n\t\t\tstate.Step();\n\t\t}\n\n\t\tif(curSymbol.length > 0) {\n\t\t\tOnSymbolEnd();\n\t\t}\n\n\t\tstate.curNode.AddChild( new FuncNode( funcName, args ) );\n\n\t\treturn state;\n\t}\n\n\tfunction IsValidVariableName(str) {\n\t\tvar reg = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;\n\t\tvar isValid = reg.test(str);\n\t\t// bitsy.log(\"VALID variable??? \" + isValid);\n\t\treturn isValid;\n\t}\n\n\tfunction StringToValue(valStr) {\n\t\tif(valStr[0] === Sym.CodeOpen) {\n\t\t\t// CODE BLOCK!!!\n\t\t\tvar codeStr = (new ParserState( null, valStr )).ConsumeBlock(Sym.CodeOpen, Sym.CodeClose); //hacky\n\t\t\tvar codeBlockState = new ParserState(new CodeBlockNode(), codeStr);\n\t\t\tcodeBlockState = ParseCode( codeBlockState );\n\t\t\treturn codeBlockState.rootNode;\n\t\t}\n\t\telse if(valStr[0] === Sym.String) {\n\t\t\t// STRING!!\n\t\t\t// bitsy.log(\"STRING\");\n\t\t\tvar str = \"\";\n\t\t\tvar i = 1;\n\t\t\twhile (i < valStr.length && valStr[i] != Sym.String) {\n\t\t\t\tstr += valStr[i];\n\t\t\t\ti++;\n\t\t\t}\n\t\t\t// bitsy.log(str);\n\t\t\treturn new LiteralNode( str );\n\t\t}\n\t\telse if(valStr === \"true\") {\n\t\t\t// BOOL\n\t\t\treturn new LiteralNode( true );\n\t\t}\n\t\telse if(valStr === \"false\") {\n\t\t\t// BOOL\n\t\t\treturn new LiteralNode( false );\n\t\t}\n\t\telse if( !isNaN(parseFloat(valStr)) ) {\n\t\t\t// NUMBER!!\n\t\t\t// bitsy.log(\"NUMBER!!! \" + valStr);\n\t\t\treturn new LiteralNode( parseFloat(valStr) );\n\t\t}\n\t\telse if(IsValidVariableName(valStr)) {\n\t\t\t// VARIABLE!!\n\t\t\t// bitsy.log(\"VARIABLE\");\n\t\t\treturn new VarNode(valStr); // TODO : check for valid potential variables\n\t\t}\n\t\telse {\n\t\t\t// uh oh\n\t\t\treturn new LiteralNode(null);\n\t\t}\n\t}\n\n\tfunction CreateExpression(expStr) {\n\t\texpStr = expStr.trim();\n\n\t\tfunction IsInsideString(index) {\n\t\t\tvar inString = false;\n\t\t\tfor(var i = 0; i < expStr.length; i++) {\n\t\t\t\tif(expStr[i] === Sym.String)\n\t\t\t\t\tinString = !inString;\n\n\t\t\t\tif(index === i)\n\t\t\t\t\treturn inString;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction IsInsideCode(index) {\n\t\t\tvar count = 0;\n\t\t\tfor(var i = 0; i < expStr.length; i++) {\n\t\t\t\tif(expStr[i] === Sym.CodeOpen)\n\t\t\t\t\tcount++;\n\t\t\t\telse if(expStr[i] === Sym.CodeClose)\n\t\t\t\t\tcount--;\n\n\t\t\t\tif(index === i)\n\t\t\t\t\treturn count > 0;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tvar operator = null;\n\n\t\t// set is special because other operator can look like it, and it has to go first in the order of operations\n\t\tvar setIndex = expStr.indexOf(Sym.Set);\n\t\tif( setIndex > -1 && !IsInsideString(setIndex) && !IsInsideCode(setIndex) ) { // it might be a set operator\n\t\t\tif( expStr[setIndex+1] != \"=\" && expStr[setIndex-1] != \">\" && expStr[setIndex-1] != \"<\" ) {\n\t\t\t\t// ok it actually IS a set operator and not ==, >=, or <=\n\t\t\t\toperator = Sym.Set;\n\t\t\t\tvar variableName = expStr.substring(0,setIndex).trim(); // TODO : valid variable name testing\n\t\t\t\tvar left = IsValidVariableName(variableName) ? new VarNode( variableName ) : new LiteralNode(null);\n\t\t\t\tvar right = CreateExpression( expStr.substring(setIndex+Sym.Set.length) );\n\t\t\t\tvar exp = new ExpNode( operator, left, right );\n\t\t\t\treturn exp;\n\t\t\t}\n\t\t}\n\n\t\t// special if \"expression\" for single-line if statements\n\t\tvar ifIndex = expStr.indexOf(Sym.ConditionEnd);\n\t\tif( ifIndex > -1 && !IsInsideString(ifIndex) && !IsInsideCode(ifIndex) ) {\n\t\t\toperator = Sym.ConditionEnd;\n\t\t\tvar conditionStr = expStr.substring(0,ifIndex).trim();\n\t\t\tvar conditions = [ CreateExpression(conditionStr) ];\n\n\t\t\tvar resultStr = expStr.substring(ifIndex+Sym.ConditionEnd.length);\n\t\t\tvar results = [];\n\t\t\tfunction AddResult(str) {\n\t\t\t\tvar dialogBlockState = new ParserState(new DialogBlockNode(), str);\n\t\t\t\tdialogBlockState = ParseDialog( dialogBlockState );\n\t\t\t\tvar dialogBlock = dialogBlockState.rootNode;\n\t\t\t\tresults.push( dialogBlock );\n\t\t\t}\n\n\t\t\tvar elseIndex = resultStr.indexOf(Sym.ElseExp); // does this need to test for strings?\n\t\t\tif(elseIndex > -1) {\n\t\t\t\tconditions.push( new ElseNode() );\n\n\t\t\t\tvar elseStr = resultStr.substring(elseIndex+Sym.ElseExp.length);\n\t\t\t\tvar resultStr = resultStr.substring(0,elseIndex);\n\n\t\t\t\tAddResult( resultStr.trim() );\n\t\t\t\tAddResult( elseStr.trim() );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tAddResult( resultStr.trim() );\n\t\t\t}\n\n\t\t\treturn new IfNode( conditions, results, true /*isSingleLine*/ );\n\t\t}\n\n\t\tfor( var i = 0; (operator == null) && (i < Sym.Operators.length); i++ ) {\n\t\t\tvar opSym = Sym.Operators[i];\n\t\t\tvar opIndex = expStr.indexOf( opSym );\n\t\t\tif( opIndex > -1 && !IsInsideString(opIndex) && !IsInsideCode(opIndex) ) {\n\t\t\t\toperator = opSym;\n\t\t\t\tvar left = CreateExpression( expStr.substring(0,opIndex) );\n\t\t\t\tvar right = CreateExpression( expStr.substring(opIndex+opSym.length) );\n\t\t\t\tvar exp = new ExpNode( operator, left, right );\n\t\t\t\treturn exp;\n\t\t\t}\n\t\t}\n\n\t\tif( operator == null ) {\n\t\t\treturn StringToValue(expStr);\n\t\t}\n\t}\n\tthis.CreateExpression = CreateExpression;\n\n\tfunction IsWhitespace(str) {\n\t\treturn ( str === \" \" || str === \"\\t\" || str === \"\\n\" );\n\t}\n\n\tfunction IsExpression(str) {\n\t\tvar tempState = new ParserState(null, str); // hacky\n\t\tvar textOutsideCodeBlocks = \"\";\n\n\t\twhile (!tempState.Done()) {\n\t\t\tif (tempState.MatchAhead(Sym.CodeOpen)) {\n\t\t\t\ttempState.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose);\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttextOutsideCodeBlocks += tempState.Char();\n\t\t\t\ttempState.Step();\n\t\t\t}\n\t\t}\n\n\t\tvar containsAnyExpressionOperators = (textOutsideCodeBlocks.indexOf(Sym.ConditionEnd) != -1) ||\n\t\t\t\t(textOutsideCodeBlocks.indexOf(Sym.Set) != -1) ||\n\t\t\t\t(Sym.Operators.some(function(opSym) { return textOutsideCodeBlocks.indexOf(opSym) != -1; }));\n\n\t\treturn containsAnyExpressionOperators;\n\t}\n\n\tfunction IsLiteral(str) {\n\t\tvar isBool = str === \"true\" || str === \"false\";\n\t\tvar isNum = !isNaN(parseFloat(str));\n\t\tvar isStr = str[0] === '\"' && str[str.length-1] === '\"';\n\t\tvar isVar = IsValidVariableName(str);\n\t\tvar isEmpty = str.length === 0;\n\t\treturn isBool || isNum || isStr || isVar || isEmpty;\n\t}\n\n\tfunction ParseExpression(state) {\n\t\tvar line = state.Source(); // state.Peak( [Sym.Linebreak] ); // TODO : remove the linebreak thing\n\t\t// bitsy.log(\"EXPRESSION \" + line);\n\t\tvar exp = CreateExpression(line);\n\t\t// bitsy.log(exp);\n\t\tstate.curNode.AddChild(exp);\n\t\tstate.Step(line.length);\n\t\treturn state;\n\t}\n\n\tfunction IsConditionalBlock(state) {\n\t\tvar peakToFirstListSymbol = state.Peak([Sym.List]);\n\n\t\tvar foundListSymbol = peakToFirstListSymbol < state.Source().length;\n\n\t\tvar areAllCharsBeforeListWhitespace = true;\n\t\tfor (var i = 0; i < peakToFirstListSymbol.length; i++) {\n\t\t\tif (!IsWhitespace(peakToFirstListSymbol[i])) {\n\t\t\t\tareAllCharsBeforeListWhitespace = false;\n\t\t\t}\n\t\t}\n\n\t\tvar peakToFirstConditionSymbol = state.Peak([Sym.ConditionEnd]);\n\t\tpeakToFirstConditionSymbol = peakToFirstConditionSymbol.slice(peakToFirstListSymbol.length);\n\t\tvar hasNoLinebreakBetweenListAndConditionEnd = peakToFirstConditionSymbol.indexOf(Sym.Linebreak) == -1;\n\n\t\treturn foundListSymbol && \n\t\t\tareAllCharsBeforeListWhitespace && \n\t\t\thasNoLinebreakBetweenListAndConditionEnd;\n\t}\n\n\tfunction ParseCode(state) {\n\t\tif (IsConditionalBlock(state)) {\n\t\t\tstate = ParseConditional(state);\n\t\t}\n\t\telse if (environment.HasFunction(state.Peak([\" \"]))) { // TODO --- what about newlines???\n\t\t\tvar funcName = state.Peak([\" \"]);\n\t\t\tstate.Step(funcName.length);\n\t\t\tstate = ParseFunction(state, funcName);\n\t\t}\n\t\telse if (IsSequence(state.Peak([\" \", Sym.Linebreak]))) {\n\t\t\tvar sequenceType = state.Peak([\" \", Sym.Linebreak]);\n\t\t\tstate.Step(sequenceType.length);\n\t\t\tstate = ParseSequence(state, sequenceType);\n\t\t}\n\t\telse if (IsLiteral(state.Source()) || IsExpression(state.Source())) {\n\t\t\tstate = ParseExpression(state);\n\t\t}\n\t\telse {\n\t\t\tvar undefinedSrc = state.Peak([]);\n\t\t\tvar undefinedNode = new UndefinedNode(undefinedSrc);\n\t\t\tstate.curNode.AddChild(undefinedNode);\n\t\t}\n\n\t\t// just go to the end now\n\t\twhile (!state.Done()) {\n\t\t\tstate.Step();\n\t\t}\n\n\t\treturn state;\n\t}\n\n\tfunction ParseCodeBlock(state) {\n\t\tvar codeStr = state.ConsumeBlock( Sym.CodeOpen, Sym.CodeClose );\n\t\tvar codeState = new ParserState(new CodeBlockNode(), codeStr);\n\t\tcodeState = ParseCode( codeState );\n\t\tstate.curNode.AddChild( codeState.rootNode );\n\t\treturn state;\n\t}\n\n}\n\n} // Script()",
"dialog.js": "function Dialog() {\n\nthis.CreateRenderer = function() {\n\treturn new DialogRenderer();\n};\n\nthis.CreateBuffer = function() {\n\treturn new DialogBuffer();\n};\n\nvar DialogRenderer = function() {\n\t// TODO : refactor this eventually? remove everything from struct.. avoid the defaults?\n\tvar textboxInfo = {\n\t\twidth : 104,\n\t\theight : 8+4+2+5, //8 for text, 4 for top-bottom padding, 2 for line padding, 5 for arrow\n\t\ttop : 12,\n\t\tleft : 12,\n\t\tbottom : 12, //for drawing it from the bottom\n\t\tpadding_vert : 2,\n\t\tpadding_horz : 4,\n\t\tarrow_height : 5,\n\t};\n\n\tvar font = null;\n\tthis.SetFont = function(f) {\n\t\tfont = f;\n\t\ttextboxInfo.height = (textboxInfo.padding_vert * 3) + (relativeFontHeight() * 2) + textboxInfo.arrow_height;\n\n\t\t// todo : clean up all the scale stuff\n\t\tvar textboxScaleW = textboxInfo.width * getTextScale();\n\t\tvar textboxScaleH = textboxInfo.height * getTextScale();\n\t\tbitsy.textbox(false, 0, 0, textboxScaleW, textboxScaleH);\n\t}\n\n\tthis.GetPixelsPerRow = function() {\n\t\treturn (textboxInfo.width - (textboxInfo.padding_horz * 2)) * getTextScale();\n\t}\n\n\t// todo : cache this value? it shouldn't really change in the middle of a game\n\tfunction getTextScale() {\n\t\treturn bitsy.textMode() === bitsy.TXT_LOREZ ? 1 : 2;\n\t}\n\n\tfunction relativeFontWidth() {\n\t\treturn Math.ceil(font.getWidth() / getTextScale());\n\t}\n\n\tfunction relativeFontHeight() {\n\t\treturn Math.ceil(font.getHeight() / getTextScale());\n\t}\n\n\tthis.ClearTextbox = function() {\n\t\tbitsy.fill(bitsy.TEXTBOX, textBackgroundIndex);\n\t};\n\n\tvar isCentered = false;\n\tthis.SetCentered = function(centered) {\n\t\tisCentered = centered;\n\t};\n\n\t// todo : I can stop doing this every frame right?\n\tthis.DrawTextbox = function() {\n\t\tif (isCentered) {\n\t\t\t// todo : will the height calculations always work?\n\t\t\tbitsy.textbox(true, textboxInfo.left, ((bitsy.VIDEO_SIZE / 2) - (textboxInfo.height / 2)));\n\t\t}\n\t\telse if (player().y < (bitsy.MAP_SIZE / 2)) {\n\t\t\t// bottom\n\t\t\tbitsy.textbox(true, textboxInfo.left, (bitsy.VIDEO_SIZE - textboxInfo.bottom - textboxInfo.height));\n\t\t}\n\t\telse {\n\t\t\t// top\n\t\t\tbitsy.textbox(true, textboxInfo.left, textboxInfo.top);\n\t\t}\n\t};\n\n\tvar arrowdata = [\n\t\t1,1,1,1,1,\n\t\t0,1,1,1,0,\n\t\t0,0,1,0,0\n\t];\n\n\tthis.DrawNextArrow = function() {\n\t\t// bitsy.log(\"draw arrow!\");\n\t\tvar text_scale = getTextScale();\n\t\tvar textboxScaleW = textboxInfo.width * text_scale;\n\t\tvar textboxScaleH = textboxInfo.height * text_scale;\n\n\t\tvar top = (textboxInfo.height - 5) * text_scale;\n\t\tvar left = (textboxInfo.width - (5 + 4)) * text_scale;\n\t\tif (textDirection === TextDirection.RightToLeft) { // RTL hack\n\t\t\tleft = 4 * text_scale;\n\t\t}\n\n\t\tfor (var y = 0; y < 3; y++) {\n\t\t\tfor (var x = 0; x < 5; x++) {\n\t\t\t\tvar i = (y * 5) + x;\n\t\t\t\tif (arrowdata[i] == 1) {\n\t\t\t\t\t//scaling nonsense\n\t\t\t\t\tfor (var sy = 0; sy < text_scale; sy++) {\n\t\t\t\t\t\tfor (var sx = 0; sx < text_scale; sx++) {\n\t\t\t\t\t\t\tvar px = left + (x * text_scale) + sx;\n\t\t\t\t\t\t\tvar py = top + (y * text_scale) + sy;\n\t\t\t\t\t\t\tbitsy.set(bitsy.TEXTBOX, (py * textboxScaleW) + px, textArrowIndex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tfunction drawCharData(charData, textScale, top, left, width, height, color) {\n\t\tfor (var y = 0; y < height; y++) {\n\t\t\tfor (var x = 0; x < width; x++) {\n\t\t\t\tvar i = (y * width) + x;\n\t\t\t\tif (charData[i] == 1) {\n\t\t\t\t\tbitsy.set(bitsy.TEXTBOX, ((top + y) * (textboxInfo.width * textScale)) + (left + x), color);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.DrawChar = function(char, row, col, leftPos) {\n\t\t// characters with effects need to be redrawn every frame\n\t\tif (char.effectList.length > 0) {\n\t\t\tchar.redraw = true;\n\t\t}\n\n\t\t// skip characters that are already drawn and don't need to be updated\n\t\tif (!char.redraw) {\n\t\t\treturn;\n\t\t}\n\t\tchar.redraw = false;\n\n\t\tvar text_scale = getTextScale();\n\t\tvar charData = char.bitmap;\n\t\tvar top;\n\t\tvar left;\n\n\t\tif (char.effectList.length > 0) {\n\t\t\t// clear the pixels from the previous frame\n\t\t\ttop = (4 * text_scale) + (row * 2 * text_scale) + (row * font.getHeight()) + Math.floor(char.offset.y);\n\t\t\tleft = (4 * text_scale) + leftPos + Math.floor(char.offset.x);\n\t\t\tdrawCharData(charData, text_scale, top, left, char.width, char.height, textBackgroundIndex);\n\t\t}\n\n\t\t// compute render offset *every* frame\n\t\tchar.offset = {\n\t\t\tx: char.base_offset.x,\n\t\t\ty: char.base_offset.y\n\t\t};\n\t\tchar.SetPosition(row, col);\n\t\tchar.ApplyEffects(effectTime);\n\n\t\ttop = (4 * text_scale) + (row * 2 * text_scale) + (row * font.getHeight()) + Math.floor(char.offset.y);\n\t\tleft = (4 * text_scale) + leftPos + Math.floor(char.offset.x);\n\n\t\tdrawCharData(charData, text_scale, top, left, char.width, char.height, char.color);\n\n\t\t// TODO : consider for a future update?\n\t\t/*\n\t\tif (soundPlayer && char.blip && char.hasPlayedBlip != true) {\n\t\t\tsoundPlayer.playBlip(blip[char.blip], { isPitchRandomized: true });\n\t\t\tchar.hasPlayedBlip = true;\n\t\t}\n\t\t*/\n\n\t\t// call printHandler for character\n\t\tif (!disableOnPrintHandlers) {\n\t\t\tchar.OnPrint();\n\t\t}\n\t};\n\n\tvar effectTime = 0; // TODO this variable should live somewhere better\n\n\tvar shouldUpdateTextboxSettings = true;\n\tvar shouldClearTextbox = true;\n\tvar shouldDrawArrow = true;\n\n\tvar disableOnPrintHandlers = false;\n\n\tthis.Draw = function(buffer, dt, disableOnPrint) {\n\t\tdisableOnPrintHandlers = (disableOnPrint === true);\n\n\t\t// bitsy.log(\"draw dialog\");\n\t\tif (buffer.DidFlipPageThisFrame()) {\n\t\t\tshouldClearTextbox = true;\n\t\t\tshouldDrawArrow = true;\n\t\t}\n\n\t\teffectTime += dt;\n\n\t\tif (shouldUpdateTextboxSettings) {\n\t\t\tbitsy.log(\"draw textbox\");\n\t\t\tthis.DrawTextbox(); // todo : rename to something more accurate\n\t\t\tshouldUpdateTextboxSettings = false;\n\t\t}\n\n\t\tif (shouldClearTextbox) {\n\t\t\t// bitsy.log(\"clear textbox\");\n\t\t\tthis.ClearTextbox();\n\t\t\tshouldClearTextbox = false;\n\t\t}\n\n\t\t// bitsy.log(\"draw chars\");\n\t\tbuffer.ForEachActiveChar(this.DrawChar);\n\n\t\tif (buffer.CanContinue() && shouldDrawArrow) {\n\t\t\t// bitsy.log(\"draw next arrow\");\n\t\t\tthis.DrawNextArrow();\n\t\t\tshouldDrawArrow = false;\n\t\t}\n\n\t\tif (buffer.DidPageFinishThisFrame() && onPageFinish != null) {\n\t\t\tbitsy.log(\"page finished\");\n\t\t\tonPageFinish();\n\t\t}\n\n\t\t// bitsy.log(\"draw dialog end\");\n\t};\n\n\t/* this is a hook for GIF rendering */\n\tvar onPageFinish = null;\n\tthis.SetPageFinishHandler = function(handler) {\n\t\tonPageFinish = handler;\n\t};\n\n\tthis.Reset = function() {\n\t\teffectTime = 0;\n\t\t// TODO - anything else?\n\n\t\tshouldUpdateTextboxSettings = true;\n\t\tshouldClearTextbox = true;\n\t\tshouldDrawArrow = true;\n\t}\n\n\tthis.updateTextboxPosition = function() {\n\t\tshouldUpdateTextboxSettings = true;\n\t};\n\n\t// this.CharsPerRow = function() {\n\t// \treturn textboxInfo.charsPerRow;\n\t// }\n}\n\nvar DialogBuffer = function() {\n\tvar buffer = [[[]]]; // holds dialog in an array buffer\n\tvar pageIndex = 0;\n\tvar rowIndex = 0;\n\tvar charIndex = 0;\n\tvar nextCharTimer = 0;\n\tvar nextCharMaxTime = 50; // in milliseconds\n\tvar isDialogReadyToContinue = false;\n\tvar activeTextEffects = [];\n\tvar activeTextEffectParameters = [];\n\tvar font = null;\n\tvar arabicHandler = new ArabicHandler();\n\tvar onDialogEndCallbacks = [];\n\n\tthis.SetFont = function(f) {\n\t\tfont = f;\n\t};\n\n\tthis.SetPixelsPerRow = function(n) {\n\t\tpixelsPerRow = n;\n\t};\n\n\tthis.CurPage = function() { return buffer[ pageIndex ]; };\n\tthis.CurRow = function() { return this.CurPage()[ rowIndex ]; };\n\tthis.CurChar = function() { return this.CurRow()[ charIndex ]; };\n\tthis.CurPageCount = function() { return buffer.length; };\n\tthis.CurRowCount = function() { return this.CurPage().length; };\n\tthis.CurCharCount = function() { return this.CurRow().length; };\n\n\tthis.ForEachActiveChar = function(handler) { // Iterates over visible characters on the active page\n\t\tvar rowCount = rowIndex + 1;\n\t\tfor (var i = 0; i < rowCount; i++) {\n\t\t\tvar row = this.CurPage()[i];\n\t\t\tvar charCount = (i == rowIndex) ? charIndex+1 : row.length;\n\t\t\t// bitsy.log(charCount);\n\n\t\t\tvar leftPos = 0;\n\t\t\tif (textDirection === TextDirection.RightToLeft) {\n\t\t\t\tleftPos = 24 * 8; // hack -- I think this is correct?\n\t\t\t}\n\n\t\t\tfor(var j = 0; j < charCount; j++) {\n\t\t\t\tvar char = row[j];\n\t\t\t\tif(char) {\n\t\t\t\t\tif (textDirection === TextDirection.RightToLeft) {\n\t\t\t\t\t\tleftPos -= char.spacing;\n\t\t\t\t\t}\n\t\t\t\t\t// bitsy.log(j + \" \" + leftPos);\n\n\t\t\t\t\t// handler( char, i /*rowIndex*/, j /*colIndex*/ );\n\t\t\t\t\thandler(char, i /*rowIndex*/, j /*colIndex*/, leftPos)\n\n\t\t\t\t\tif (textDirection === TextDirection.LeftToRight) {\n\t\t\t\t\t\tleftPos += char.spacing;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.Reset = function() {\n\t\tbuffer = [[[]]];\n\t\tpageIndex = 0;\n\t\trowIndex = 0;\n\t\tcharIndex = 0;\n\t\tisDialogReadyToContinue = false;\n\n\t\tafterManualPagebreak = false;\n\n\t\tactiveTextEffects = [];\n\n\t\tonDialogEndCallbacks = [];\n\n\t\tisActive = false;\n\t};\n\n\tthis.DoNextChar = function() {\n\t\tnextCharTimer = 0; //reset timer\n\n\t\t//time to update characters\n\t\tif (charIndex + 1 < this.CurCharCount()) {\n\t\t\t//add char to current row\n\t\t\tcharIndex++;\n\t\t}\n\t\telse if (rowIndex + 1 < this.CurRowCount()) {\n\t\t\t//start next row\n\t\t\trowIndex++;\n\t\t\tcharIndex = 0;\n\t\t}\n\t\telse {\n\t\t\t//the page is full!\n\t\t\tisDialogReadyToContinue = true;\n\t\t\tdidPageFinishThisFrame = true;\n\t\t}\n\n\t\tif (this.CurChar() != null) {\n\t\t\tif (this.CurChar().isPageBreak) {\n\t\t\t\t// special case for page break marker character!\n\t\t\t\tisDialogReadyToContinue = true;\n\t\t\t\tdidPageFinishThisFrame = true;\n\t\t\t}\n\t\t\t\n\t\t\tthis.CurChar().OnPrint(); // make sure we hit the callback before we run out of text\n\t\t}\n\t};\n\n\tthis.Update = function(dt) {\n\t\tdidPageFinishThisFrame = false;\n\t\tdidFlipPageThisFrame = false;\n\t\t// this.Draw(dt); // TODO move into a renderer object\n\t\tif (isDialogReadyToContinue) {\n\t\t\treturn; //waiting for dialog to be advanced by player\n\t\t}\n\n\t\tnextCharTimer += dt; //tick timer\n\n\t\tif (nextCharTimer > nextCharMaxTime) {\n\t\t\tthis.DoNextChar();\n\t\t}\n\t};\n\n\tvar isSkipping = false;\n\n\tthis.Skip = function() {\n\t\tbitsy.log(\"SKIPPP\");\n\t\tisSkipping = true;\n\n\t\tdidPageFinishThisFrame = false;\n\t\tdidFlipPageThisFrame = false;\n\n\t\t// add new characters until you get to the end of the current line of dialog\n\t\twhile (rowIndex < this.CurRowCount() && isSkipping) {\n\t\t\tthis.DoNextChar();\n\n\t\t\tif (isDialogReadyToContinue) {\n\t\t\t\t//make sure to push the rowIndex past the end to break out of the loop\n\t\t\t\trowIndex++;\n\t\t\t\tcharIndex = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (isSkipping) {\n\t\t\trowIndex = this.CurRowCount() - 1;\n\t\t\tcharIndex = this.CurCharCount() - 1;\n\t\t}\n\n\t\tisSkipping = false;\n\t};\n\n\tthis.tryInterruptSkip = function() {\n\t\tif (isSkipping) {\n\t\t\tisSkipping = false;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t};\n\n\tthis.FlipPage = function() {\n\t\tdidFlipPageThisFrame = true;\n\t\tisDialogReadyToContinue = false;\n\t\tpageIndex++;\n\t\trowIndex = 0;\n\t\tcharIndex = 0;\n\t}\n\n\tthis.EndDialog = function() {\n\t\tisActive = false; // no more text to show... this should be a sign to stop rendering dialog\n\n\t\tfor (var i = 0; i < onDialogEndCallbacks.length; i++) {\n\t\t\tonDialogEndCallbacks[i]();\n\t\t}\n\t}\n\n\tvar afterManualPagebreak = false; // is it bad to track this state like this?\n\n\tthis.Continue = function() {\n\t\tbitsy.log(\"CONTINUE\");\n\n\t\t// if we used a page break character to continue we need\n\t\t// to run whatever is in the script afterwards! // TODO : make this comment better\n\t\tif (this.CurChar().isPageBreak) {\n\t\t\t// hacky: always treat a page break as the end of dialog\n\t\t\t// if there's more dialog later we re-activate the dialog buffer\n\t\t\tthis.EndDialog();\n\t\t\tafterManualPagebreak = true;\n\t\t\tthis.CurChar().OnContinue();\n\t\t\treturn false;\n\t\t}\n\t\tif (pageIndex + 1 < this.CurPageCount()) {\n\t\t\tbitsy.log(\"FLIP PAGE!\");\n\t\t\t//start next page\n\t\t\tthis.FlipPage();\n\t\t\treturn true; /* hasMoreDialog */\n\t\t}\n\t\telse {\n\t\t\tbitsy.log(\"END DIALOG!\");\n\t\t\tbitsy.textbox(false);\n\t\t\t//end dialog mode\n\t\t\tthis.EndDialog();\n\t\t\treturn false; /* hasMoreDialog */\n\t\t}\n\t};\n\n\tvar isActive = false;\n\tthis.IsActive = function() { return isActive; };\n\n\tthis.OnDialogEnd = function(callback) {\n\t\tif (!isActive) {\n\t\t\tcallback();\n\t\t}\n\t\telse {\n\t\t\tonDialogEndCallbacks.push(callback);\n\t\t}\n\t}\n\n\tthis.CanContinue = function() { return isDialogReadyToContinue; };\n\n\tfunction DialogChar() {\n\t\tthis.redraw = true;\n\n\t\tthis.effectList = [];\n\t\tthis.effectParameterList = [];\n\n\t\tthis.color = textColorIndex; // white\n\t\tthis.offset = { x:0, y:0 }; // in pixels (screen pixels?)\n\n\t\tthis.col = 0;\n\t\tthis.row = 0;\n\n\t\tthis.SetPosition = function(row,col) {\n\t\t\t// bitsy.log(\"SET POS\");\n\t\t\t// bitsy.log(this);\n\t\t\tthis.row = row;\n\t\t\tthis.col = col;\n\t\t};\n\n\t\tthis.ApplyEffects = function(time) {\n\t\t\t// bitsy.log(\"APPLY EFFECTS! \" + time);\n\t\t\tfor (var i = 0; i < this.effectList.length; i++) {\n\t\t\t\tvar effectName = this.effectList[i];\n\t\t\t\t// bitsy.log(\"FX \" + effectName);\n\t\t\t\tTextEffects[effectName].doEffect(this, time, this.effectParameterList[i]);\n\t\t\t}\n\t\t};\n\n\t\tvar printHandler = null; // optional function to be called once on printing character\n\t\tthis.SetPrintHandler = function(handler) {\n\t\t\tprintHandler = handler;\n\t\t};\n\t\tthis.OnPrint = function() {\n\t\t\tif (printHandler != null) {\n\t\t\t\t// bitsy.log(\"PRINT HANDLER ---- DIALOG BUFFER\");\n\t\t\t\tprintHandler();\n\t\t\t\tprintHandler = null; // only call handler once (hacky)\n\t\t\t}\n\t\t};\n\n\t\tthis.bitmap = [];\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t\tthis.base_offset = { // hacky name\n \t\t\tx: 0,\n\t\t\ty: 0\n\t\t};\n\t\tthis.spacing = 0;\n\t}\n\n\tfunction DialogFontChar(font, char, effectList, effectParameterList) {\n\t\tDialogChar.call(this);\n\n\t\tthis.effectList = effectList.slice(); // clone effect list (since it can change between chars)\n\t\tthis.effectParameterList = effectParameterList.slice();\n\n\t\tvar charData = font.getChar(char);\n\t\tthis.char = char;\n\t\tthis.bitmap = charData.data;\n\t\tthis.width = charData.width;\n\t\tthis.height = charData.height;\n\t\tthis.base_offset.x = charData.offset.x;\n\t\tthis.base_offset.y = charData.offset.y;\n\t\tthis.spacing = charData.spacing;\n\t\tthis.blip = null;\n\t\tthis.hasPlayedBlip = false;\n\t}\n\n\tfunction DialogDrawingChar(drawingId, effectList, effectParameterList) {\n\t\tDialogChar.call(this);\n\n\t\tthis.effectList = effectList.slice(); // clone effect list (since it can change between chars)\n\t\tthis.effectParameterList = effectParameterList.slice();\n\n\t\t// get the first frame of the drawing and flatten it\n\t\tvar drawingData = renderer.GetDrawingSource(drawingId)[0];\n\t\tvar drawingDataFlat = [];\n\t\tfor (var i = 0; i < drawingData.length; i++) {\n\t\t\tdrawingDataFlat = drawingDataFlat.concat(drawingData[i]);\n\t\t}\n\n\t\tthis.bitmap = drawingDataFlat;\n\t\tthis.width = 8;\n\t\tthis.height = 8;\n\t\tthis.spacing = 8;\n\t}\n\n\tfunction DialogScriptControlChar() {\n\t\tDialogChar.call(this);\n\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t\tthis.spacing = 0;\n\t}\n\n\t// is a control character really the best way to handle page breaks?\n\tfunction DialogPageBreakChar() {\n\t\tDialogChar.call(this);\n\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t\tthis.spacing = 0;\n\n\t\tthis.isPageBreak = true;\n\n\t\tvar continueHandler = null;\n\n\t\tthis.SetContinueHandler = function(handler) {\n\t\t\tcontinueHandler = handler;\n\t\t};\n\n\t\tthis.OnContinue = function() {\n\t\t\tif (continueHandler) {\n\t\t\t\tcontinueHandler();\n\t\t\t}\n\t\t};\n\t}\n\n\tfunction AddWordToCharArray(charArray, word, effectList, effectParameterList) {\n\t\t// bitsy.log(\"add char array\");\n\t\tfor (var i = 0; i < word.length; i++) {\n\t\t\tcharArray.push(new DialogFontChar(font, word[i], effectList, effectParameterList));\n\t\t}\n\t\t// bitsy.log(\"add char array end\");\n\t\treturn charArray;\n\t}\n\n\tfunction GetCharArrayWidth(charArray) {\n\t\tvar width = 0;\n\t\tfor(var i = 0; i < charArray.length; i++) {\n\t\t\twidth += charArray[i].spacing;\n\t\t}\n\t\treturn width;\n\t}\n\n\tfunction GetStringWidth(str) {\n\t\tvar width = 0;\n\t\tfor (var i = 0; i < str.length; i++) {\n\t\t\tvar charData = font.getChar(str[i]);\n\t\t\twidth += charData.spacing;\n\t\t}\n\t\treturn width;\n\t}\n\n\tvar pixelsPerRow = 192; // hard-coded fun times!!!\n\n\tthis.AddScriptReturn = function(onReturnHandler) {\n\t\tvar curPageIndex = buffer.length - 1;\n\t\tvar curRowIndex = buffer[curPageIndex].length - 1;\n\t\tvar curRowArr = buffer[curPageIndex][curRowIndex];\n\n\t\tvar controlChar = new DialogScriptControlChar();\n\t\tcontrolChar.SetPrintHandler(onReturnHandler);\n\n\t\tcurRowArr.push(controlChar);\n\n\t\tisActive = true;\n\t}\n\n\tthis.AddDrawing = function(drawingId) {\n\t\t// bitsy.log(\"DRAWING ID \" + drawingId);\n\n\t\tvar curPageIndex = buffer.length - 1;\n\t\tvar curRowIndex = buffer[curPageIndex].length - 1;\n\t\tvar curRowArr = buffer[curPageIndex][curRowIndex];\n\n\t\tvar drawingChar = new DialogDrawingChar(drawingId, activeTextEffects, activeTextEffectParameters);\n\n\t\tvar rowLength = GetCharArrayWidth(curRowArr);\n\n\t\t// TODO : clean up copy-pasted code here :/\n\t\tif (afterManualPagebreak) {\n\t\t\tthis.FlipPage(); // hacky\n\n\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\tbuffer.push([]);\n\t\t\tcurPageIndex++;\n\t\t\tbuffer[curPageIndex].push([]);\n\t\t\tcurRowIndex = 0;\n\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\tcurRowArr.push(drawingChar);\n\n\t\t\tafterManualPagebreak = false;\n\t\t}\n\t\telse if (rowLength + drawingChar.spacing <= pixelsPerRow || rowLength <= 0) {\n\t\t\t//stay on same row\n\t\t\tcurRowArr.push(drawingChar);\n\t\t}\n\t\telse if (curRowIndex == 0) {\n\t\t\t//start next row\n\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\tbuffer[curPageIndex].push([]);\n\t\t\tcurRowIndex++;\n\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\tcurRowArr.push(drawingChar);\n\t\t}\n\t\telse {\n\t\t\t//start next page\n\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\tbuffer.push([]);\n\t\t\tcurPageIndex++;\n\t\t\tbuffer[curPageIndex].push([]);\n\t\t\tcurRowIndex = 0;\n\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\tcurRowArr.push(drawingChar);\n\t\t}\n\n\t\tisActive = true; // this feels like a bad way to do this???\n\t}\n\n\t// TODO : convert this into something that takes DialogChar arrays\n\tthis.AddText = function(textStr) {\n\t\tbitsy.log(\"ADD TEXT >>\" + textStr + \"<<\");\n\n\t\t//process dialog so it's easier to display\n\t\tvar words = textStr.split(\" \");\n\n\t\t// var curPageIndex = this.CurPageCount() - 1;\n\t\t// var curRowIndex = this.CurRowCount() - 1;\n\t\t// var curRowArr = this.CurRow();\n\n\t\tvar curPageIndex = buffer.length - 1;\n\t\tvar curRowIndex = buffer[curPageIndex].length - 1;\n\t\tvar curRowArr = buffer[curPageIndex][curRowIndex];\n\n\t\tfor (var i = 0; i < words.length; i++) {\n\t\t\tvar word = words[i];\n\t\t\tif (arabicHandler.ContainsArabicCharacters(word)) {\n\t\t\t\tword = arabicHandler.ShapeArabicCharacters(word);\n\t\t\t}\n\n\t\t\tvar wordWithPrecedingSpace = ((i == 0) ? \"\" : \" \") + word;\n\t\t\tvar wordLength = GetStringWidth(wordWithPrecedingSpace);\n\n\t\t\tvar rowLength = GetCharArrayWidth(curRowArr);\n\n\t\t\tif (afterManualPagebreak) {\n\t\t\t\tthis.FlipPage();\n\n\t\t\t\t// hacky copied bit for page breaks\n\t\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\t\tbuffer.push([]);\n\t\t\t\tcurPageIndex++;\n\t\t\t\tbuffer[curPageIndex].push([]);\n\t\t\t\tcurRowIndex = 0;\n\t\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\t\tcurRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects, activeTextEffectParameters);\n\n\t\t\t\tafterManualPagebreak = false;\n\t\t\t}\n\t\t\telse if (rowLength + wordLength <= pixelsPerRow || rowLength <= 0) {\n\t\t\t\t//stay on same row\n\t\t\t\tcurRowArr = AddWordToCharArray(curRowArr, wordWithPrecedingSpace, activeTextEffects, activeTextEffectParameters);\n\t\t\t}\n\t\t\telse if (curRowIndex == 0) {\n\t\t\t\t//start next row\n\t\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\t\tbuffer[curPageIndex].push([]);\n\t\t\t\tcurRowIndex++;\n\t\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\t\tcurRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects, activeTextEffectParameters);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t//start next page\n\t\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\t\tbuffer.push([]);\n\t\t\t\tcurPageIndex++;\n\t\t\t\tbuffer[curPageIndex].push([]);\n\t\t\t\tcurRowIndex = 0;\n\t\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\t\t\t\tcurRowArr = AddWordToCharArray(curRowArr, word, activeTextEffects, activeTextEffectParameters);\n\t\t\t}\n\t\t}\n\n\t\t//destroy any empty stuff\n\t\tvar lastPage = buffer[buffer.length-1];\n\t\tvar lastRow = lastPage[lastPage.length-1];\n\t\tif (lastRow.length == 0) {\n\t\t\tlastPage.splice(lastPage.length-1, 1);\n\t\t}\n\t\tif (lastPage.length == 0) {\n\t\t\tbuffer.splice(buffer.length-1, 1);\n\t\t}\n\n\t\t//finish up \n\t\tlastPage = buffer[buffer.length-1];\n\t\tlastRow = lastPage[lastPage.length-1];\n\t\tif (lastRow.length > 0) {\n\t\t\tvar lastChar = lastRow[lastRow.length-1];\n\t\t}\n\n\t\t// bitsy.log(buffer);\n\n\t\tbitsy.log(\"add text finished\");\n\n\t\tisActive = true;\n\t};\n\n\tthis.AddLinebreak = function() {\n\t\tvar lastPage = buffer[buffer.length-1];\n\t\tif (lastPage.length <= 1) {\n\t\t\t// bitsy.log(\"LINEBREAK - NEW ROW \");\n\t\t\t// add new row\n\t\t\tlastPage.push([]);\n\t\t}\n\t\telse {\n\t\t\t// add new page\n\t\t\tbuffer.push([[]]);\n\t\t}\n\t\t// bitsy.log(buffer);\n\n\t\tisActive = true;\n\t}\n\n\tthis.AddPagebreak = function(onReturnHandler) {\n\t\tvar curPageIndex = buffer.length - 1;\n\t\tvar curRowIndex = buffer[curPageIndex].length - 1;\n\t\tvar curRowArr = buffer[curPageIndex][curRowIndex];\n\n\t\t// need to actually create a whole new page if following another pagebreak character\n\t\tif (afterManualPagebreak) {\n\t\t\tthis.FlipPage(); // hacky\n\n\t\t\tbuffer[curPageIndex][curRowIndex] = curRowArr;\n\t\t\tbuffer.push([]);\n\t\t\tcurPageIndex++;\n\t\t\tbuffer[curPageIndex].push([]);\n\t\t\tcurRowIndex = 0;\n\t\t\tcurRowArr = buffer[curPageIndex][curRowIndex];\n\n\t\t\tafterManualPagebreak = false;\n\t\t}\n\n\t\tvar pagebreakChar = new DialogPageBreakChar();\n\t\tpagebreakChar.SetContinueHandler(onReturnHandler);\n\n\t\tcurRowArr.push(pagebreakChar);\n\n\t\tisActive = true;\n\t}\n\n\tthis.hasTextEffect = function(name) {\n\t\treturn activeTextEffects.indexOf(name) != -1;\n\t};\n\n\tthis.pushTextEffect = function(name, parameters) {\n\t\tactiveTextEffects.push(name);\n\t\tactiveTextEffectParameters.push(parameters);\n\t};\n\n\tthis.popTextEffect = function(name) {\n\t\tvar i = activeTextEffects.lastIndexOf(name);\n\t\tactiveTextEffects.splice(i, 1);\n\t\tactiveTextEffectParameters.splice(i, 1);\n\t};\n\n\t/* this is a hook for GIF rendering */\n\tvar didPageFinishThisFrame = false;\n\tthis.DidPageFinishThisFrame = function(){ return didPageFinishThisFrame; };\n\n\tvar didFlipPageThisFrame = false;\n\tthis.DidFlipPageThisFrame = function(){ return didFlipPageThisFrame; };\n\n\t// this.SetCharsPerRow = function(num){ charsPerRow = num; }; // hacky\n};\n\n/* ARABIC */\nvar ArabicHandler = function() {\n\n\tvar arabicCharStart = 0x0621;\n\tvar arabicCharEnd = 0x064E;\n\n\tvar CharacterForm = {\n\t\tIsolated : 0,\n\t\tFinal : 1,\n\t\tInitial : 2,\n\t\tMiddle : 3\n\t};\n\n\t// map glyphs to their character forms\n\tvar glyphForms = {\n\t\t/*\t\t Isolated, Final, Initial, Middle Forms\t*/\n\t\t0x0621: [0xFE80,0xFE80,0xFE80,0xFE80], /* HAMZA */ \n\t\t0x0622: [0xFE81,0xFE82,0xFE81,0xFE82], /* ALEF WITH MADDA ABOVE */ \n\t\t0x0623: [0xFE83,0xFE84,0xFE83,0xFE84], /* ALEF WITH HAMZA ABOVE */ \n\t\t0x0624: [0xFE85,0xFE86,0xFE85,0xFE86], /* WAW WITH HAMZA ABOVE */ \n\t\t0x0625: [0xFE87,0xFE88,0xFE87,0xFE88], /* ALEF WITH HAMZA BELOW */ \n\t\t0x0626: [0xFE89,0xFE8A,0xFE8B,0xFE8C], /* YEH WITH HAMZA ABOVE */ \n\t\t0x0627: [0xFE8D,0xFE8E,0xFE8D,0xFE8E], /* ALEF */ \n\t\t0x0628: [0xFE8F,0xFE90,0xFE91,0xFE92], /* BEH */ \n\t\t0x0629: [0xFE93,0xFE94,0xFE93,0xFE94], /* TEH MARBUTA */ \n\t\t0x062A: [0xFE95,0xFE96,0xFE97,0xFE98], /* TEH */ \n\t\t0x062B: [0xFE99,0xFE9A,0xFE9B,0xFE9C], /* THEH */ \n\t\t0x062C: [0xFE9D,0xFE9E,0xFE9F,0xFEA0], /* JEEM */ \n\t\t0x062D: [0xFEA1,0xFEA2,0xFEA3,0xFEA4], /* HAH */ \n\t\t0x062E: [0xFEA5,0xFEA6,0xFEA7,0xFEA8], /* KHAH */ \n\t\t0x062F: [0xFEA9,0xFEAA,0xFEA9,0xFEAA], /* DAL */ \n\t\t0x0630: [0xFEAB,0xFEAC,0xFEAB,0xFEAC], /* THAL */ \n\t\t0x0631: [0xFEAD,0xFEAE,0xFEAD,0xFEAE], /* RAA */ \n\t\t0x0632: [0xFEAF,0xFEB0,0xFEAF,0xFEB0], /* ZAIN */ \n\t\t0x0633: [0xFEB1,0xFEB2,0xFEB3,0xFEB4], /* SEEN */ \n\t\t0x0634: [0xFEB5,0xFEB6,0xFEB7,0xFEB8], /* SHEEN */ \n\t\t0x0635: [0xFEB9,0xFEBA,0xFEBB,0xFEBC], /* SAD */ \n\t\t0x0636: [0xFEBD,0xFEBE,0xFEBF,0xFEC0], /* DAD */ \n\t\t0x0637: [0xFEC1,0xFEC2,0xFEC3,0xFEC4], /* TAH */ \n\t\t0x0638: [0xFEC5,0xFEC6,0xFEC7,0xFEC8], /* ZAH */ \n\t\t0x0639: [0xFEC9,0xFECA,0xFECB,0xFECC], /* AIN */ \n\t\t0x063A: [0xFECD,0xFECE,0xFECF,0xFED0], /* GHAIN */ \n\t\t0x063B: [0x0000,0x0000,0x0000,0x0000], /* space */\n\t\t0x063C: [0x0000,0x0000,0x0000,0x0000], /* space */\n\t\t0x063D: [0x0000,0x0000,0x0000,0x0000], /* space */\n\t\t0x063E: [0x0000,0x0000,0x0000,0x0000], /* space */\n\t\t0x063F: [0x0000,0x0000,0x0000,0x0000], /* space */\n\t\t0x0640: [0x0640,0x0640,0x0640,0x0640], /* TATWEEL */ \n\t\t0x0641: [0xFED1,0xFED2,0xFED3,0xFED4], /* FAA */ \n\t\t0x0642: [0xFED5,0xFED6,0xFED7,0xFED8], /* QAF */ \n\t\t0x0643: [0xFED9,0xFEDA,0xFEDB,0xFEDC], /* KAF */ \n\t\t0x0644: [0xFEDD,0xFEDE,0xFEDF,0xFEE0], /* LAM */ \n\t\t0x0645: [0xFEE1,0xFEE2,0xFEE3,0xFEE4], /* MEEM */ \n\t\t0x0646: [0xFEE5,0xFEE6,0xFEE7,0xFEE8], /* NOON */ \n\t\t0x0647: [0xFEE9,0xFEEA,0xFEEB,0xFEEC], /* HEH */ \n\t\t0x0648: [0xFEED,0xFEEE,0xFEED,0xFEEE], /* WAW */ \n\t\t0x0649: [0xFEEF,0xFEF0,0xFBE8,0xFBE9], /* ALEF MAKSURA */ \n\t\t0x064A: [0xFEF1,0xFEF2,0xFEF3,0xFEF4], /* YEH */ \n\t\t0x064B: [0xFEF5,0xFEF6,0xFEF5,0xFEF6], /* LAM ALEF MADD*/\n\t\t0x064C: [0xFEF7,0xFEF8,0xFEF7,0xFEF8], /* LAM ALEF HAMZA ABOVE*/\n\t\t0x064D: [0xFEF9,0xFEFa,0xFEF9,0xFEFa], /* LAM ALEF HAMZA BELOW*/\n\t\t0x064E: [0xFEFb,0xFEFc,0xFEFb,0xFEFc], /* LAM ALEF */\n\t};\n\n\tvar disconnectedCharacters = [0x0621,0x0622,0x0623,0x0624,0x0625,0x0627,0x062f,0x0630,0x0631,0x0632,0x0648,0x0649,0x064b,0x064c,0x064d,0x064e];\n\n\tfunction IsArabicCharacter(char) {\n\t\tvar code = char.charCodeAt(0);\n\t\treturn (code >= arabicCharStart && code <= arabicCharEnd);\n\t}\n\n\tfunction ContainsArabicCharacters(word) {\n\t\tfor (var i = 0; i < word.length; i++) {\n\t\t\tif (IsArabicCharacter(word[i])) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tfunction IsDisconnectedCharacter(char) {\n\t\tvar code = char.charCodeAt(0);\n\t\treturn disconnectedCharacters.indexOf(code) != -1;\n\t}\n\n\tfunction ShapeArabicCharacters(word) {\n\t\tvar shapedWord = \"\";\n\n\t\tfor (var i = 0; i < word.length; i++) {\n\t\t\tif (!IsArabicCharacter(word[i])) {\n\t\t\t\tshapedWord += word[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvar connectedToPreviousChar = i-1 >= 0 && IsArabicCharacter(word[i-1]) && !IsDisconnectedCharacter(word[i-1]);\n\n\t\t\tvar connectedToNextChar = i+1 < word.length && IsArabicCharacter(word[i+1]) && !IsDisconnectedCharacter(word[i]);\n\n\t\t\tvar form;\n\t\t\tif (!connectedToPreviousChar && !connectedToNextChar) {\n\t\t\t\tform = CharacterForm.Isolated;\n\t\t\t}\n\t\t\telse if (connectedToPreviousChar && !connectedToNextChar) {\n\t\t\t\tform = CharacterForm.Final;\n\t\t\t}\n\t\t\telse if (!connectedToPreviousChar && connectedToNextChar) {\n\t\t\t\tform = CharacterForm.Initial;\n\t\t\t}\n\t\t\telse if (connectedToPreviousChar && connectedToNextChar) {\n\t\t\t\tform = CharacterForm.Middle;\n\t\t\t}\n\n\t\t\tvar code = word[i].charCodeAt(0);\n\n\t\t\t// handle lam alef special case\n\t\t\tif (code == 0x0644 && connectedToNextChar) {\n\t\t\t\tvar nextCode = word[i+1].charCodeAt(0);\n\t\t\t\tvar specialCode = null;\n\t\t\t\tif (nextCode == 0x0622) {\n\t\t\t\t\t// alef madd\n\t\t\t\t\tspecialCode = glyphForms[0x064b][form];\n\t\t\t\t}\n\t\t\t\telse if (nextCode == 0x0623) {\n\t\t\t\t\t// hamza above\n\t\t\t\t\tspecialCode = glyphForms[0x064c][form];\n\t\t\t\t}\n\t\t\t\telse if (nextCode == 0x0625) {\n\t\t\t\t\t// hamza below\n\t\t\t\t\tspecialCode = glyphForms[0x064d][form];\n\t\t\t\t}\n\t\t\t\telse if (nextCode == 0x0627) {\n\t\t\t\t\t// alef\n\t\t\t\t\tspecialCode = glyphForms[0x064e][form];\n\t\t\t\t}\n\n\t\t\t\tif (specialCode != null) {\n\t\t\t\t\tshapedWord += String.fromCharCode(specialCode);\n\t\t\t\t\ti++; // skip a step\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// hacky?\n\t\t\tif (form === CharacterForm.Isolated) {\n\t\t\t\tshapedWord += word[i];\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvar shapedCode = glyphForms[code][form];\n\t\t\tshapedWord += String.fromCharCode(shapedCode);\n\t\t}\n\n\t\treturn shapedWord;\n\t}\n\n\tthis.ContainsArabicCharacters = ContainsArabicCharacters;\n\tthis.ShapeArabicCharacters = ShapeArabicCharacters;\n}\n\n/* TEXT EFFECTS */\nvar TextEffects = {};\n\nfunction RainbowEffect() {\n\tfunction positiveModulo(number, divisor) {\n\t\treturn ((number % divisor) + divisor) % divisor;\n\t}\n\n\tthis.doEffect = function(char, time, parameters) {\n\t\tchar.color = rainbowColorStartIndex + Math.floor(positiveModulo((time / 100) - char.col * 0.5, rainbowColorCount));\n\t};\n}\n\nTextEffects[\"rbw\"] = new RainbowEffect();\n\nfunction ColorEffect(index) {\n\tthis.doEffect = function(char, time, parameters) {\n\t\tif (parameters && parameters.length > 0) {\n\t\t\tchar.color = tileColorStartIndex + parameters[0];\n\t\t}\n\t\telse {\n\t\t\tchar.color = tileColorStartIndex + index;\n\t\t}\n\t};\n}\n\nTextEffects[\"clr\"] = new ColorEffect();\nTextEffects[\"clr1\"] = new ColorEffect(0);\nTextEffects[\"clr2\"] = new ColorEffect(1);\nTextEffects[\"clr3\"] = new ColorEffect(2);\n\nfunction WavyEffect() {\n\tthis.doEffect = function(char, time, parameters) {\n\t\tchar.offset.y += Math.sin((time / 250) - (char.col / 2)) * 2;\n\t};\n}\n\nTextEffects[\"wvy\"] = new WavyEffect();\n\nfunction ShakyEffect() {\n\tfunction disturb(func, time, offset, mult1, mult2) {\n\t\treturn func((time * mult1) - (offset * mult2));\n\t}\n\n\tthis.doEffect = function(char, time, parameters) {\n\t\tchar.offset.y += 1.5\n\t\t\t\t\t\t* disturb(Math.sin, time, char.col, 0.1, 0.5)\n\t\t\t\t\t\t* disturb(Math.cos, time, char.col, 0.3, 0.2)\n\t\t\t\t\t\t* disturb(Math.sin, time, char.row, 2.0, 1.0);\n\t\tchar.offset.x += 1.5\n\t\t\t\t\t\t* disturb(Math.cos, time, char.row, 0.1, 1.0)\n\t\t\t\t\t\t* disturb(Math.sin, time, char.col, 3.0, 0.7)\n\t\t\t\t\t\t* disturb(Math.cos, time, char.col, 0.2, 0.3);\n\t};\n}\n\nTextEffects[\"shk\"] = new ShakyEffect();\n\n/*\n// TODO : maybe use this in a future update?\nfunction YakEffect() {\n\tthis.doEffect = function(char, time, parameters) {\n\t\tif (char.char != \" \") {\n\t\t\tchar.blip = parameters[0];\n\t\t}\n\t};\n}\n\nTextEffects[\"yak\"] = new YakEffect();\n*/\n\nvar DebugHighlightEffect = function() {\n\tthis.doEffect = function(char, time, parameters) {\n\t\tchar.color = tileColorStartIndex;\n\t};\n}\n\nTextEffects[\"_debug_highlight\"] = new DebugHighlightEffect();\n\n} // Dialog()",
- "renderer.js": "function TileRenderer() {\nbitsy.log(\"!!!!! NEW TILE RENDERER\");\n\nvar drawingCache = {\n\tsource: {},\n\trender: {},\n};\n\n// var debugRenderCount = 0;\n\nfunction createRenderCacheId(drawingId, colorIndex) {\n\treturn drawingId + \"_\" + colorIndex;\n}\n\nfunction renderDrawing(drawing) {\n\t// debugRenderCount++;\n\t// bitsy.log(\"RENDER COUNT \" + debugRenderCount);\n\n\tvar col = drawing.col;\n\tvar bgc = drawing.bgc;\n\tvar drwId = drawing.drw;\n\tvar drawingFrames = drawingCache.source[drwId];\n\n\t// initialize render cache entry\n\tvar cacheId = createRenderCacheId(drwId, col);\n\tif (drawingCache.render[cacheId] === undefined) {\n\t\t// initialize array of frames for drawing\n\t\tdrawingCache.render[cacheId] = [];\n\t}\n\n\tfor (var i = 0; i < drawingFrames.length; i++) {\n\t\tvar frameData = drawingFrames[i];\n\t\tvar frameTileId = renderTileFromDrawingData(frameData, col, bgc);\n\t\tdrawingCache.render[cacheId].push(frameTileId);\n\t}\n}\n\nfunction renderTileFromDrawingData(drawingData, col, bgc) {\n\tvar tileId = bitsy.tile();\n\n\tvar backgroundColor = tileColorStartIndex + bgc;\n\tvar foregroundColor = tileColorStartIndex + col;\n\n\tbitsy.fill(tileId, backgroundColor);\n\n\tfor (var y = 0; y < bitsy.TILE_SIZE; y++) {\n\t\tfor (var x = 0; x < bitsy.TILE_SIZE; x++) {\n\t\t\tvar px = drawingData[y][x];\n\t\t\tif (px === 1) {\n\t\t\t\tbitsy.set(tileId, (y * bitsy.TILE_SIZE) + x, foregroundColor);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn tileId;\n}\n\n// TODO : move into core\nfunction undefinedOrNull(x) {\n\treturn x === undefined || x === null;\n}\n\nfunction isDrawingRendered(drawing) {\n\tvar cacheId = createRenderCacheId(drawing.drw, drawing.col);\n\treturn drawingCache.render[cacheId] != undefined;\n}\n\nfunction getRenderedDrawingFrames(drawing) {\n\tvar cacheId = createRenderCacheId(drawing.drw, drawing.col);\n\treturn drawingCache.render[cacheId];\n}\n\nfunction getDrawingFrameTileId(drawing, frameOverride) {\n\tvar frameIndex = 0;\n\n\tif (drawing != null && drawing.animation.isAnimated) {\n\t\tif (frameOverride != undefined && frameOverride != null) {\n\t\t\tframeIndex = frameOverride;\n\t\t}\n\t\telse {\n\t\t\tframeIndex = drawing.animation.frameIndex;\n\t\t}\n\t}\n\n\treturn getRenderedDrawingFrames(drawing)[frameIndex];\n}\n\nfunction getOrRenderDrawingFrame(drawing, frameOverride) {\n\t// bitsy.log(\"frame render: \" + drawing.type + \" \" + drawing.id + \" f:\" + frameOverride);\n\n\tif (!isDrawingRendered(drawing)) {\n\t\tbitsy.log(\"frame render: doesn't exist \" + drawing.id);\n\t\trenderDrawing(drawing);\n\t}\n\n\treturn getDrawingFrameTileId(drawing, frameOverride);\n}\n\nfunction deleteRenders(drawingId) {\n\tfor (var cacheId in drawingCache.render) {\n\t\tif (cacheId.indexOf(drawingId) === 0) {\n\t\t\tvar tiles = drawingCache.render[cacheId];\n\t\t\tfor (var i = 0; i < tiles.length; i++) {\n\t\t\t\tbitsy.delete(tiles[i]);\n\t\t\t}\n\t\t\tdelete drawingCache.render[cacheId];\n\t\t}\n\t}\n}\n\n/* PUBLIC INTERFACE */\nthis.GetDrawingFrame = getOrRenderDrawingFrame;\n\n// todo : leave individual get and set stuff for now - should I remove later?\n// todo : better name for function?\nthis.SetDrawings = function(drawingSource) {\n\tdrawingCache.source = drawingSource;\n\t// need to reset entire render cache when all the drawings are changed\n\tdrawingCache.render = {};\n};\n\nthis.SetDrawingSource = function(drawingId, drawingData) {\n\tdeleteRenders(drawingId);\n\tdrawingCache.source[drawingId] = drawingData;\n};\n\nthis.GetDrawingSource = function(drawingId) {\n\treturn drawingCache.source[drawingId];\n};\n\nthis.GetFrameCount = function(drawingId) {\n\treturn drawingCache.source[drawingId].length;\n};\n\n// todo : forceReset option is hacky?\nthis.ClearCache = function(forceReset) {\n\tif (forceReset === undefined || forceReset === true) {\n\t\tbitsy.deleteAllTiles();\n\t}\n\n\tdrawingCache.render = {};\n};\n\nthis.deleteDrawing = deleteRenders;\n\n} // Renderer()",
+ "renderer.js": "function TileRenderer() {\nbitsy.log(\"!!!!! NEW TILE RENDERER\");\n\nvar drawingCache = {\n\tsource: {},\n\trender: {},\n};\n\n// var debugRenderCount = 0;\n\nfunction createRenderCacheId(drawingId, colorIndex) {\n\treturn drawingId + \"_\" + colorIndex;\n}\n\nfunction renderDrawing(drawing) {\n\t// debugRenderCount++;\n\t// bitsy.log(\"RENDER COUNT \" + debugRenderCount);\n\n\tvar col = drawing.col;\n\tvar bgc = drawing.bgc;\n\tvar drwId = drawing.drw;\n\tvar drawingFrames = drawingCache.source[drwId];\n\n\t// initialize render cache entry\n\tvar cacheId = createRenderCacheId(drwId, col);\n\tif (drawingCache.render[cacheId] === undefined) {\n\t\t// initialize array of frames for drawing\n\t\tdrawingCache.render[cacheId] = [];\n\t}\n\n\tfor (var i = 0; i < drawingFrames.length; i++) {\n\t\tvar frameData = drawingFrames[i];\n\t\tvar frameTileId = renderTileFromDrawingData(frameData, col, bgc);\n\t\tdrawingCache.render[cacheId].push(frameTileId);\n\t}\n}\n\nfunction renderTileFromDrawingData(drawingData, col, bgc) {\n\tvar tileId = bitsy.tile();\n\n\tvar backgroundColor = tileColorStartIndex + bgc;\n\tvar foregroundColor = tileColorStartIndex + col;\n\n\tbitsy.fill(tileId, backgroundColor);\n\n\tfor (var y = 0; y < bitsy.TILE_SIZE; y++) {\n\t\tfor (var x = 0; x < bitsy.TILE_SIZE; x++) {\n\t\t\tvar px = drawingData[y][x];\n\t\t\tif (px === 1) {\n\t\t\t\tbitsy.set(tileId, (y * bitsy.TILE_SIZE) + x, foregroundColor);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn tileId;\n}\n\n// TODO : move into core\nfunction undefinedOrNull(x) {\n\treturn x === undefined || x === null;\n}\n\nfunction isDrawingRendered(drawing) {\n\tvar cacheId = createRenderCacheId(drawing.drw, drawing.col);\n\treturn drawingCache.render[cacheId] != undefined;\n}\n\nfunction getRenderedDrawingFrames(drawing) {\n\tvar cacheId = createRenderCacheId(drawing.drw, drawing.col);\n\treturn drawingCache.render[cacheId];\n}\n\nfunction getDrawingFrameTileId(drawing, frameOverride) {\n\tvar frameIndex = 0;\n\n\tif (drawing != null && drawing.animation.isAnimated) {\n\t\tif (frameOverride != undefined && frameOverride != null) {\n\t\t\tframeIndex = frameOverride;\n\t\t}\n\t\telse {\n\t\t\tframeIndex = drawing.animation.frameIndex;\n\t\t}\n\t}\n\n\treturn getRenderedDrawingFrames(drawing)[frameIndex];\n}\n\nfunction getOrRenderDrawingFrame(drawing, frameOverride) {\n\t// bitsy.log(\"frame render: \" + drawing.type + \" \" + drawing.id + \" f:\" + frameOverride);\n\n\tif (!isDrawingRendered(drawing)) {\n\t\tbitsy.log(\"frame render: doesn't exist \" + drawing.id);\n\t\trenderDrawing(drawing);\n\t}\n\n\treturn getDrawingFrameTileId(drawing, frameOverride);\n}\n\nfunction deleteRenders(drawingId) {\n\tfor (var cacheId in drawingCache.render) {\n\t\tif (cacheId.indexOf(drawingId) === 0) {\n\t\t\tvar tiles = drawingCache.render[cacheId];\n\t\t\tfor (var i = 0; i < tiles.length; i++) {\n\t\t\t\tbitsy.delete(tiles[i]);\n\t\t\t}\n\t\t\tdelete drawingCache.render[cacheId];\n\t\t}\n\t}\n}\n\n/* PUBLIC INTERFACE */\nthis.GetDrawingFrame = getOrRenderDrawingFrame;\n\n// todo : leave individual get and set stuff for now - should I remove later?\n// todo : better name for function?\nthis.SetDrawings = function(drawingSource) {\n\tdrawingCache.source = drawingSource;\n\t// need to reset entire render cache when all the drawings are changed\n\tdrawingCache.render = {};\n};\n\nthis.SetDrawingSource = function(drawingId, drawingData) {\n\tdeleteRenders(drawingId);\n\tdrawingCache.source[drawingId] = drawingData;\n};\n\nthis.GetDrawingSource = function(drawingId) {\n\treturn drawingCache.source[drawingId];\n};\n\nthis.GetFrameCount = function(drawingId) {\n\treturn drawingCache.source[drawingId].length;\n};\n\n// todo : forceReset option is hacky?\nthis.ClearCache = function(forceReset) {\n\tif (forceReset === undefined || forceReset === true) {\n\t\t// delete all tiles from system memory before clearing the cache\n\t\tfor (var cacheId in drawingCache.render) {\n\t\t\tvar tiles = drawingCache.render[cacheId];\n\t\t\tfor (var i = 0; i < tiles.length; i++) {\n\t\t\t\tbitsy.delete(tiles[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tdrawingCache.render = {};\n};\n\nthis.deleteDrawing = deleteRenders;\n\n} // Renderer()",
"bitsy.js": "/* WORLD DATA */\nvar room = {};\nvar tile = {};\nvar sprite = {};\nvar item = {};\nvar dialog = {};\nvar end = {}; // for backwards compatibility\nvar palette = { // start off with a default palette\n\t\t\"default\" : {\n\t\t\tname : \"default\",\n\t\t\tcolors : [[0,0,0],[255,255,255],[255,255,255]]\n\t\t}\n\t};\nvar variable = {}; // these are starting variable values -- they don't update (or I don't think they will)\nvar tune = {};\nvar blip = {};\nvar playerId = \"A\";\nvar fontName = defaultFontName;\nvar textDirection = TextDirection.LeftToRight;\n\n/* NAME-TO-ID MAPS */\nvar names = {\n\troom : {},\n\ttile : {},\n\tsprite : {},\n\titem : {},\n\tdialog : {},\n\tpalette : {},\n\ttune : {},\n\tblip : {},\n};\n\n// todo : this is basically a copy of the one in world.js - can I remove it?\nfunction updateNamesFromCurData() {\n\n\tfunction createNameMap(objectStore) {\n\t\tvar map = {};\n\n\t\tfor (id in objectStore) {\n\t\t\tif (objectStore[id].name != undefined && objectStore[id].name != null) {\n\t\t\t\tmap[objectStore[id].name] = id;\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\tnames.room = createNameMap(room);\n\tnames.tile = createNameMap(tile);\n\tnames.sprite = createNameMap(sprite);\n\tnames.item = createNameMap(item);\n\tnames.dialog = createNameMap(dialog);\n\tnames.palette = createNameMap(palette);\n\tnames.tune = createNameMap(tune);\n\tnames.blip = createNameMap(blip);\n}\n\n/* GAME STATE */\nvar state = {}\nfunction resetGameState() {\n\tstate.room = \"0\";\n\tstate.ava = playerId; // avatar appearance override\n\tstate.pal = \"0\"; // current palette id\n\tstate.tune = \"0\"; // current tune id (\"0\" === off)\n\tstate.exits = []; // exits in current room\n\tstate.endings = []; // endings in current room\n}\n\n// title helper functions\nfunction getTitle() {\n\treturn dialog[titleDialogId].src;\n}\nfunction setTitle(titleSrc) {\n\tdialog[titleDialogId] = { src:titleSrc, name:null };\n}\n\n/* FLAGS */\nvar flags = createDefaultFlags();\n\n// feature flags for testing purposes\nvar engineFeatureFlags = {\n\tisSoundEnabled : true,\n\tisFontEnabled : true,\n\tisTransitionEnabled : true,\n\tisScriptEnabled : true,\n\tisDialogEnabled : true,\n\tisRendererEnabled : true,\n};\n\nfunction clearGameData() {\n\troom = {};\n\ttile = {};\n\tsprite = {};\n\titem = {};\n\tdialog = {};\n\tpalette = { //start off with a default palette\n\t\t\"default\" : {\n\t\t\tname : \"default\",\n\t\t\tcolors : [[0,0,0],[255,255,255],[255,255,255]]\n\t\t}\n\t};\n\tisEnding = false; //todo - correct place for this?\n\tvariable = {};\n\n\tupdateNamesFromCurData();\n\n\tfontName = defaultFontName; // TODO : reset font manager too?\n\ttextDirection = TextDirection.LeftToRight;\n\n\tresetGameState();\n\n\tisGameLoaded = false;\n\tisGameOver = false;\n}\n\n// engine event hooks for the editor\nvar onInventoryChanged = null;\nvar onVariableChanged = null;\nvar onGameReset = null;\nvar onInitRoom = null;\n\nvar isPlayerEmbeddedInEditor = false;\n\nvar renderer;\nif (engineFeatureFlags.isRendererEnabled) {\n\trenderer = new TileRenderer();\n}\n\nvar curGameData = null;\nvar curDefaultFontData = null;\n\nvar isGameLoaded = false;\nvar isGameOver = false;\n\nfunction load_game(gameData, defaultFontData, startWithTitle) {\n\t// bitsy.log(\"game data in: \\n\" + gameData);\n\n\tcurGameData = gameData; //remember the current game (used to reset the game)\n\n\tif (dialogBuffer) {\n\t\tdialogBuffer.Reset();\n\t}\n\n\tif (scriptInterpreter) {\n\t\tscriptInterpreter.ResetEnvironment(); // ensures variables are reset -- is this the best way?\n\t}\n\n\tloadWorldFromGameData(gameData);\n\n\tbitsy.log(\"world loaded\");\n\n\tif (fontManager && !isPlayerEmbeddedInEditor && defaultFontData) {\n\t\tbitsy.log(\"load font\");\n\n\t\tcurDefaultFontData = defaultFontData; // store for resetting game\n\n\t\t// todo : consider replacing this with a more general system for requesting resources from the system?\n\t\t// hack to ensure default font is available\n\t\tfontManager.AddResource(defaultFontName + fontManager.GetExtension(), defaultFontData);\n\n\t\tbitsy.log(\"load font end\");\n\t}\n\n\t// request text mode\n\tif (flags.TXT_MODE === 1) {\n\t\tbitsy.textMode(bitsy.TXT_LOREZ);\n\t}\n\telse {\n\t\t// default to 2x scale for text rendering\n\t\tbitsy.textMode(bitsy.TXT_HIREZ);\n\t}\n\n\tif (fontManager && dialogBuffer) {\n\t\tbitsy.log(\"get font\");\n\n\t\tvar font = fontManager.Get( fontName );\n\t\tdialogBuffer.SetFont(font);\n\t\tdialogRenderer.SetFont(font);\n\n\t\tbitsy.log(\"get font end\");\n\t}\n\n\tif (dialogBuffer) {\n\t\t// this feels a little silly to me - oh well??\n\t\tdialogBuffer.SetPixelsPerRow(dialogRenderer.GetPixelsPerRow());\n\t}\n\n\tsetInitialVariables();\n\n\tbitsy.log(\"ready\");\n\n\tonready(startWithTitle);\n\n\tisGameLoaded = true;\n}\n\nfunction loadWorldFromGameData(gameData) {\n\tbitsy.log(\"load world from game data\");\n\n\tvar world = parseWorld(gameData);\n\n\tbitsy.log(\"parse world done\");\n\n\t// move world data into global scope\n\tpalette = world.palette;\n\troom = world.room;\n\ttile = world.tile;\n\tsprite = world.sprite;\n\titem = world.item;\n\tdialog = world.dialog;\n\tend = world.end; // back compat endings\n\tvariable = world.variable;\n\tfontName = world.fontName;\n\ttextDirection = world.textDirection;\n\ttune = world.tune;\n\tblip = world.blip;\n\tflags = world.flags;\n\tnames = world.names;\n\n\tif (renderer) {\n\t\trenderer.SetDrawings(world.drawings);\n\t}\n\n\t// find starting room and initialize it\n\tvar roomIds = Object.keys(room);\n\n\tif (player() != undefined && player().room != null && roomIds.indexOf(player().room) != -1) {\n\t\t// player has valid room\n\t\tstate.room = player().room;\n\t}\n\telse if (roomIds.length > 0) {\n\t\t// player not in any room! what the heck\n\t\tstate.room = roomIds[0];\n\t}\n\telse {\n\t\t// uh oh there are no rooms I guess???\n\t\tstate.room = null;\n\t}\n\n\tif (state.room != null) {\n\t\tbitsy.log(\"INIT ROOM \" + state.room);\n\t\tinitRoom(state.room);\n\t}\n}\n\nfunction reset_cur_game() {\n\tif (curGameData == null) {\n\t\treturn; //can't reset if we don't have the game data\n\t}\n\n\tstopGame();\n\tclearGameData();\n\n\tif (isPlayerEmbeddedInEditor && onGameReset != null) {\n\t\tonGameReset();\n\t}\n}\n\nfunction onready(startWithTitle) {\n\tbitsy.log(\"game ready!\");\n\n\tif (startWithTitle === undefined || startWithTitle === null) {\n\t\tstartWithTitle = true;\n\t}\n\n\tif (startWithTitle) { // used by editor \n\t\tstartNarrating(getTitle());\n\t}\n}\n\nfunction setInitialVariables() {\n\tif (!scriptInterpreter) {\n\t\treturn;\n\t}\n\n\tfor(id in variable) {\n\t\tvar value = variable[id]; // default to string\n\t\tif(value === \"true\") {\n\t\t\tvalue = true;\n\t\t}\n\t\telse if(value === \"false\") {\n\t\t\tvalue = false;\n\t\t}\n\t\telse if(!isNaN(parseFloat(value))) {\n\t\t\tvalue = parseFloat(value);\n\t\t}\n\t\tscriptInterpreter.SetVariable(id,value);\n\t}\n\tscriptInterpreter.SetOnVariableChangeHandler( onVariableChanged );\n}\n\nfunction getOffset(evt) {\n\tvar offset = { x:0, y:0 };\n\n\tvar el = evt.target;\n\tvar rect = el.getBoundingClientRect();\n\n\toffset.x += rect.left + el.scrollLeft;\n\toffset.y += rect.top + el.scrollTop;\n\n\toffset.x = evt.clientX - offset.x;\n\toffset.y = evt.clientY - offset.y;\n\n\treturn offset;\n}\n\nfunction stopGame() {\n\tif (soundPlayer) {\n\t\tsoundPlayer.stopTune();\n\t}\n\tbitsy.log(\"stop GAME!\");\n}\n\nfunction update(dt) {\n\tif (!isGameLoaded) {\n\t\tload_game(bitsy.getGameData(), bitsy.getFontData());\n\t}\n\n\tif (state.room == null) {\n\t\t// in the special case where there is no valid room, end the game\n\t\tstartNarrating( \"\", true /*isEnding*/ );\n\t}\n\n\tif (!transition || !transition.IsTransitionActive()) {\n\t\tupdateInput();\n\t}\n\n\tif (transition && transition.IsTransitionActive()) {\n\t\t// transition animation takes over everything!\n\t\ttransition.UpdateTransition(dt);\n\t}\n\telse {\n\t\tif (bitsy.graphicsMode() != bitsy.GFX_MAP) {\n\t\t\tbitsy.graphicsMode(bitsy.GFX_MAP);\n\t\t}\n\n\t\tif (soundPlayer) {\n\t\t\tsoundPlayer.update(dt);\n\t\t}\n\n\t\tif (!isNarrating && !isEnding) {\n\t\t\t// draw world if game has begun\n\t\t\tvar didAnimate = updateAnimation(dt);\n\n\t\t\t// test whether player moved so we can redraw just the avatar\n\t\t\tplayerCurX = player().x;\n\t\t\tplayerCurY = player().y;\n\t\t\tvar didPlayerMove = (playerPrevX != playerCurX) || (playerPrevY != playerCurY);\n\n\t\t\tdrawRoom(room[state.room], { redrawAnimated: didAnimate, redrawAvatar: didPlayerMove });\n\n\t\t\t// store player's position for next frame\n\t\t\tplayerPrevX = playerCurX;\n\t\t\tplayerPrevY = playerCurY;\n\t\t}\n\t\telse {\n\t\t\tclearRoom();\n\t\t}\n\n\t\tif (dialogBuffer && dialogBuffer.IsActive() && !(soundPlayer && soundPlayer.isBlipPlaying())) {\n\t\t\t// bitsy.log(\"update dialog\");\n\t\t\t// bitsy.log(\"renderer\");\n\t\t\tdialogRenderer.Draw(dialogBuffer, dt);\n\t\t\t// bitsy.log(\"buffer\");\n\t\t\tdialogBuffer.Update(dt);\n\t\t\t// bitsy.log(\"update dialog end\");\n\t\t}\n\n\t\t// keep moving avatar if player holds down button\n\t\tif ((!dialogBuffer || !dialogBuffer.IsActive()) && !isEnding) {\n\t\t\tif (curPlayerDirection != Direction.None) {\n\t\t\t\tplayerHoldToMoveTimer -= dt;\n\n\t\t\t\tif (playerHoldToMoveTimer <= 0) {\n\t\t\t\t\tmovePlayer(curPlayerDirection, false /* isFirstMove */);\n\t\t\t\t\tplayerHoldToMoveTimer = 150;\n\t\t\t\t\t// playerHoldToMoveTimer = 16; // PERF TEST\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// clean up state if the game is ending\n\tif (isGameOver) {\n\t\tbitsy.log(\"game over\");\n\t\treset_cur_game();\n\t}\n\n\treturn true;\n}\n\nvar isAnyButtonHeld = false;\nvar isIgnoringInput = false;\n\nfunction isAnyButtonDown() {\n\treturn bitsy.button(bitsy.BTN_UP) ||\n\t\tbitsy.button(bitsy.BTN_DOWN) ||\n\t\tbitsy.button(bitsy.BTN_LEFT) ||\n\t\tbitsy.button(bitsy.BTN_RIGHT) ||\n\t\tbitsy.button(bitsy.BTN_OK);\n}\n\nfunction updateInput() {\n\tif (dialogBuffer && dialogBuffer.IsActive()) {\n\t\tif (!(soundPlayer && soundPlayer.isBlipPlaying())) {\n\t\t\tif (!isAnyButtonHeld && isAnyButtonDown()) {\n\t\t\t\t/* CONTINUE DIALOG */\n\t\t\t\tif (dialogBuffer.CanContinue()) {\n\t\t\t\t\tvar hasMoreDialog = dialogBuffer.Continue();\n\t\t\t\t\tif (!hasMoreDialog) {\n\t\t\t\t\t\t// ignore currently held keys UNTIL they are released (stops player from insta-moving)\n\t\t\t\t\t\tisIgnoringInput = true;\n\t\t\t\t\t\tcurPlayerDirection = Direction.None;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdialogBuffer.Skip();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (isEnding) {\n\t\tif (!isAnyButtonHeld && isAnyButtonDown()) {\n\t\t\t// tell game to restart\n\t\t\tisGameOver = true;\n\t\t}\n\t}\n\telse if (!isIgnoringInput) {\n\t\t/* WALK */\n\t\tvar prevPlayerDirection = curPlayerDirection;\n\n\t\tif (bitsy.button(bitsy.BTN_UP)) {\n\t\t\tcurPlayerDirection = Direction.Up;\n\t\t}\n\t\telse if (bitsy.button(bitsy.BTN_DOWN)) {\n\t\t\tcurPlayerDirection = Direction.Down;\n\t\t}\n\t\telse if (bitsy.button(bitsy.BTN_LEFT)) {\n\t\t\tcurPlayerDirection = Direction.Left;\n\t\t}\n\t\telse if (bitsy.button(bitsy.BTN_RIGHT)) {\n\t\t\tcurPlayerDirection = Direction.Right;\n\t\t}\n\t\telse {\n\t\t\tcurPlayerDirection = Direction.None;\n\t\t}\n\n\t\tif (curPlayerDirection != Direction.None && curPlayerDirection != prevPlayerDirection) {\n\t\t\tmovePlayer(curPlayerDirection, true /* isFirstMove */);\n\t\t\tplayerHoldToMoveTimer = 500;\n\t\t\t// playerHoldToMoveTimer = 32; // PERF TEST\n\t\t}\n\t}\n\n\tif (!isAnyButtonDown()) {\n\t\tisIgnoringInput = false;\n\t}\n\n\t// quit if the user ever presses the restart button\n\t// todo : should I rename it bitsy.BTN_RESTART or bitsy.BTN_QUIT or bitsy.BTN_OFF?\n\tif (bitsy.button(bitsy.BTN_MENU)) {\n\t\tisGameOver = true;\n\t}\n\n\tisAnyButtonHeld = isAnyButtonDown();\n}\n\nvar animationCounter = 0;\nvar animationTime = 400;\nfunction updateAnimation(dt) {\n\tanimationCounter += dt;\n\t// bitsy.log(\"anim \" + animationCounter);\n\tif (animationCounter >= animationTime) {\n\t\t// animate sprites\n\t\tfor (id in sprite) {\n\t\t\tvar spr = sprite[id];\n\t\t\tif (spr.animation.isAnimated) {\n\t\t\t\tspr.animation.frameIndex = (spr.animation.frameIndex + 1) % spr.animation.frameCount;\n\t\t\t}\n\t\t}\n\n\t\t// animate tiles\n\t\tfor (id in tile) {\n\t\t\tvar til = tile[id];\n\t\t\tif (til.animation.isAnimated) {\n\t\t\t\ttil.animation.frameIndex = (til.animation.frameIndex + 1) % til.animation.frameCount;\n\t\t\t}\n\t\t}\n\n\t\t// animate items\n\t\tfor (id in item) {\n\t\t\tvar itm = item[id];\n\t\t\tif (itm.animation.isAnimated) {\n\t\t\t\titm.animation.frameIndex = (itm.animation.frameIndex + 1) % itm.animation.frameCount;\n\t\t\t}\n\t\t}\n\n\t\t// reset counter\n\t\tanimationCounter = 0;\n\n\t\t// updated animations this frame\n\t\treturn true;\n\t}\n\n\t// did *not* update animations this frame\n\treturn false;\n}\n\nfunction resetAllAnimations() {\n\tfor (id in sprite) {\n\t\tvar spr = sprite[id];\n\t\tif (spr.animation.isAnimated) {\n\t\t\tspr.animation.frameIndex = 0;\n\t\t}\n\t}\n\n\tfor (id in tile) {\n\t\tvar til = tile[id];\n\t\tif (til.animation.isAnimated) {\n\t\t\ttil.animation.frameIndex = 0;\n\t\t}\n\t}\n\n\tfor (id in item) {\n\t\tvar itm = item[id];\n\t\tif (itm.animation.isAnimated) {\n\t\t\titm.animation.frameIndex = 0;\n\t\t}\n\t}\n}\n\nfunction getSpriteAt(x, y, roomId) {\n\tif (roomId === undefined) {\n\t\troomId = state.room;\n\t}\n\n\tfor (id in sprite) {\n\t\tvar spr = sprite[id];\n\t\tif (spr.room === roomId) {\n\t\t\tif (spr.x == x && spr.y == y) {\n\t\t\t\treturn id;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nvar Direction = {\n\tNone : -1,\n\tUp : 0,\n\tDown : 1,\n\tLeft : 2,\n\tRight : 3\n};\n\nvar curPlayerDirection = Direction.None;\nvar playerHoldToMoveTimer = 0;\nvar playerPrevX = 0;\nvar playerPrevY = 0;\n\nfunction movePlayer(direction, isFirstMove) {\n\tdidPlayerMove = false;\n\tvar roomIds = Object.keys(room);\n\n\tif (player().room == null || roomIds.indexOf(player().room) < 0) {\n\t\treturn; // player room is missing or invalid.. can't move them!\n\t}\n\n\tvar spr = null;\n\n\tif (direction == Direction.Left && !(spr = getSpriteLeft()) && !isWallLeft()) {\n\t\tplayer().x -= 1;\n\t}\n\telse if (direction == Direction.Right && !(spr = getSpriteRight()) && !isWallRight()) {\n\t\tplayer().x += 1;\n\t}\n\telse if (direction == Direction.Up && !(spr = getSpriteUp()) && !isWallUp()) {\n\t\tplayer().y -= 1;\n\t}\n\telse if (direction == Direction.Down && !(spr = getSpriteDown()) && !isWallDown()) {\n\t\tplayer().y += 1;\n\t}\n\n\tvar ext = getExit( player().room, player().x, player().y );\n\tvar end = getEnding( player().room, player().x, player().y );\n\tvar itmIndex = getItemIndex( player().room, player().x, player().y );\n\n\t// only play one sound effect per \"turn\"\n\tvar blipId = null;\n\n\t// do items first, because you can pick up an item AND go through a door\n\tif (itmIndex > -1) {\n\t\tvar itm = room[player().room].items[itmIndex];\n\t\tvar itemRoom = player().room;\n\n\t\t// play sound on pitck up item\n\t\tif (item[itm.id].blip != null) {\n\t\t\tblipId = item[itm.id].blip;\n\t\t}\n\n\t\tstartItemDialog(itm.id, function() {\n\t\t\t// remove item from room\n\t\t\troom[itemRoom].items.splice(itmIndex, 1);\n\n\t\t\t// update player inventory\n\t\t\tif (player().inventory[itm.id]) {\n\t\t\t\tplayer().inventory[itm.id] += 1;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tplayer().inventory[itm.id] = 1;\n\t\t\t}\n\n\t\t\t// show inventory change in UI\n\t\t\tif (onInventoryChanged != null) {\n\t\t\t\tonInventoryChanged(itm.id);\n\t\t\t}\n\t\t});\n\t}\n\n\tif (end) {\n\t\tstartEndingDialog(end);\n\t}\n\telse if (ext) {\n\t\tmovePlayerThroughExit(ext);\n\t}\n\telse if (spr) {\n\t\t// play sound on greet sprite\n\t\tif (sprite[spr].blip != null) {\n\t\t\tblipId = sprite[spr].blip;\n\t\t}\n\n\t\tstartSpriteDialog(spr /*spriteId*/);\n\t}\n\n\t// TODO : maybe add in a future update?\n\t/*\n\t// play sound when player moves (if no other sound selected)\n\tif (isFirstMove && blipId === null && sprite[state.ava].blip != null) {\n\t\tblipId = sprite[state.ava].blip;\n\t\trandomizeBlip = true;\n\t\tblipChannel = bitsy.SOUND2; // play walking sfx *under* the tune melody\n\t}\n\t*/\n\n\tif (soundPlayer && blipId != null && blip[blipId]) {\n\t\tsoundPlayer.playBlip(blip[blipId]);\n\t}\n}\n\nvar transition;\nif (engineFeatureFlags.isTransitionEnabled) {\n\ttransition = new TransitionManager();\n}\n\nfunction movePlayerThroughExit(ext) {\n\tvar GoToDest = function() {\n\t\tif (transition && ext.transition_effect != null) {\n\t\t\ttransition.BeginTransition(\n\t\t\t\tplayer().room,\n\t\t\t\tplayer().x,\n\t\t\t\tplayer().y,\n\t\t\t\text.dest.room,\n\t\t\t\text.dest.x,\n\t\t\t\text.dest.y,\n\t\t\t\text.transition_effect);\n\n\t\t\ttransition.UpdateTransition(0);\n\n\t\t\ttransition.OnTransitionComplete(function() {\n\t\t\t\tplayer().room = ext.dest.room;\n\t\t\t\tplayer().x = ext.dest.x;\n\t\t\t\tplayer().y = ext.dest.y;\n\t\t\t\tstate.room = ext.dest.room;\n\t\t\t\tinitRoom(state.room);\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\tplayer().room = ext.dest.room;\n\t\t\tplayer().x = ext.dest.x;\n\t\t\tplayer().y = ext.dest.y;\n\t\t\tstate.room = ext.dest.room;\n\n\t\t\tinitRoom(state.room);\n\t\t}\n\t};\n\n\tif (ext.dlg != undefined && ext.dlg != null) {\n\t\t// TODO : I need to simplify dialog code,\n\t\t// so I don't have to get the ID and the source str\n\t\t// every time!\n\t\tstartDialog(\n\t\t\tdialog[ext.dlg].src,\n\t\t\text.dlg,\n\t\t\tfunction(result) {\n\t\t\t\tvar isLocked = ext.property && ext.property.locked === true;\n\t\t\t\tif (!isLocked) {\n\t\t\t\t\tGoToDest();\n\t\t\t\t}\n\t\t\t},\n\t\t\text);\n\t}\n\telse {\n\t\tGoToDest();\n\t}\n}\n\n/* PALETTE INDICES */\nvar backgroundIndex = 0;\nvar textBackgroundIndex = 1;\nvar textArrowIndex = 2;\nvar textColorIndex = 3;\n\n// precalculated rainbow colors\nvar rainbowColorStartIndex = 4;\nvar rainbowColorCount = 10;\nvar rainbowColors = [\n\t[255,0,0],\n\t[255,217,0],\n\t[78,255,0],\n\t[0,255,125],\n\t[0,192,255],\n\t[0,18,255],\n\t[136,0,255],\n\t[255,0,242],\n\t[255,0,138],\n\t[255,0,61],\n];\n\nfunction updatePaletteWithTileColors(tileColors) {\n\t// the screen background color should match the first tile color\n\tif (tileColors.length > 0) {\n\t\tvar color = tileColors[0];\n\t\tbitsy.color(backgroundIndex, color[0], color[1], color[2]);\n\t}\n\telse {\n\t\t// as a fallback, use black as the background\n\t\tbitsy.log(\"no tile colors!\");\n\t\tbitsy.color(backgroundIndex, 0, 0, 0);\n\t}\n\n\t// textbox colors\n\tbitsy.color(textBackgroundIndex, 0, 0, 0); // black\n\tbitsy.color(textArrowIndex, 255, 255, 255); // white\n\tbitsy.color(textColorIndex, 255, 255, 255); // white\n\n\t// rainbow colors\n\tfor (var i = 0; i < rainbowColorCount; i++) {\n\t\tvar color = rainbowColors[i];\n\t\tbitsy.color(rainbowColorStartIndex + i, color[0], color[1], color[2]);\n\t}\n\n\t// tile colors\n\tfor (var i = 0; i < tileColors.length; i++) {\n\t\tvar color = tileColors[i];\n\t\tbitsy.color(tileColorStartIndex + i, color[0], color[1], color[2]);\n\t}\n}\n\nfunction updatePalette(palId) {\n\tstate.pal = palId;\n\tvar pal = palette[state.pal];\n\tupdatePaletteWithTileColors(pal.colors);\n}\n\nfunction initRoom(roomId) {\n\tbitsy.log(\"init room \" + roomId);\n\n\tupdatePalette(getRoomPal(roomId));\n\n\t// update avatar appearance\n\tstate.ava = (room[roomId].ava != null) ? room[roomId].ava : playerId;\n\n\tif (renderer) {\n\t\trenderer.ClearCache();\n\t}\n\n\t// init exit properties\n\tstate.exits = [];\n\tfor (var i = 0; i < room[roomId].exits.length; i++) {\n\t\tvar exit = createExitData(\n\t\t\t/* x \t\t\t*/ room[roomId].exits[i].x,\n\t\t\t/* y \t\t\t*/ room[roomId].exits[i].y,\n\t\t\t/* destRoom \t*/ room[roomId].exits[i].dest.room,\n\t\t\t/* destX \t\t*/ room[roomId].exits[i].dest.x,\n\t\t\t/* destY \t\t*/ room[roomId].exits[i].dest.y,\n\t\t\t/* transition \t*/ room[roomId].exits[i].transition_effect,\n\t\t\t/* dlg \t\t\t*/ room[roomId].exits[i].dlg);\n\t\texit.property = { locked: false };\n\n\t\tstate.exits.push(exit);\n\t}\n\n\t// init ending properties\n\tstate.endings = [];\n\tfor (var i = 0; i < room[roomId].endings.length; i++) {\n\t\tvar end = createEndingData(\n\t\t\t/* id */ room[roomId].endings[i].id,\n\t\t\t/* x */ room[roomId].endings[i].x,\n\t\t\t/* y */ room[roomId].endings[i].y);\n\t\tend.property = { locked: false };\n\n\t\tstate.endings.push(end);\n\t}\n\n\tif (soundPlayer) {\n\t\tif (!room[roomId].tune || room[roomId].tune === \"0\" || !tune[room[roomId].tune]) {\n\t\t\t// stop music\n\t\t\tstate.tune = \"0\";\n\t\t\tsoundPlayer.stopTune();\n\t\t}\n\t\telse if (room[roomId].tune != state.tune) {\n\t\t\t// start music\n\t\t\tstate.tune = room[roomId].tune;\n\t\t\tsoundPlayer.playTune(tune[state.tune]);\n\t\t}\n\t}\n\n\tvar drawArgs = { redrawAll: true };\n\tdrawRoom(room[roomId], drawArgs);\n\n\tif (onInitRoom) {\n\t\tonInitRoom(roomId);\n\t}\n}\n\nfunction getItemIndex( roomId, x, y ) {\n\tfor( var i = 0; i < room[roomId].items.length; i++ ) {\n\t\tvar itm = room[roomId].items[i];\n\t\tif ( itm.x == x && itm.y == y)\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n\nfunction getSpriteLeft() { //repetitive?\n\treturn getSpriteAt( player().x - 1, player().y );\n}\n\nfunction getSpriteRight() {\n\treturn getSpriteAt( player().x + 1, player().y );\n}\n\nfunction getSpriteUp() {\n\treturn getSpriteAt( player().x, player().y - 1 );\n}\n\nfunction getSpriteDown() {\n\treturn getSpriteAt( player().x, player().y + 1 );\n}\n\nfunction isWallLeft() {\n\treturn (player().x - 1 < 0) || isWall( player().x - 1, player().y );\n}\n\nfunction isWallRight() {\n\treturn (player().x + 1 >= bitsy.MAP_SIZE) || isWall(player().x + 1, player().y);\n}\n\nfunction isWallUp() {\n\treturn (player().y - 1 < 0) || isWall( player().x, player().y - 1 );\n}\n\nfunction isWallDown() {\n\treturn (player().y + 1 >= bitsy.MAP_SIZE) || isWall(player().x, player().y + 1);\n}\n\nfunction isWall(x, y, roomId) {\n\tif (roomId == undefined || roomId == null) {\n\t\troomId = state.room;\n\t}\n\n\tvar tileId = getTile(x, y, roomId);\n\tif (tileId === '0') {\n\t\treturn false; // Blank spaces aren't walls, ya doofus\n\t}\n\n\tif (tile[tileId].isWall === undefined || tile[tileId].isWall === null) {\n\t\t// No wall-state defined: check room-specific walls\n\t\tvar i = room[roomId].walls.indexOf(getTile(x, y, roomId));\n\t\treturn (i > -1);\n\t}\n\n\t// Otherwise, use the tile's own wall-state\n\treturn tile[tileId].isWall;\n}\n\nfunction getItem(roomId,x,y) {\n\tfor (i in room[roomId].items) {\n\t\tvar item = room[roomId].items[i];\n\t\tif (x == item.x && y == item.y) {\n\t\t\treturn item;\n\t\t}\n\t}\n\treturn null;\n}\n\n// todo : roomId isn't useful in these functions anymore! safe to remove?\nfunction getExit(roomId, x, y) {\n\tfor (i in state.exits) {\n\t\tvar e = state.exits[i];\n\t\tif (x == e.x && y == e.y) {\n\t\t\treturn e;\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction getEnding(roomId, x, y) {\n\tfor (i in state.endings) {\n\t\tvar e = state.endings[i];\n\t\tif (x == e.x && y == e.y) {\n\t\t\treturn e;\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction getTile(x, y, roomId) {\n\t// bitsy.log(x + \" \" + y);\n\tvar t = getRoom(roomId).tilemap[y][x];\n\treturn t;\n}\n\nfunction player() {\n\treturn sprite[playerId];\n}\n\n// Sort of a hack for legacy palette code (when it was just an array)\nfunction getPal(id) {\n\tif (palette[id] === undefined) {\n\t\tid = \"default\";\n\t}\n\n\treturn palette[ id ].colors;\n}\n\nfunction getRoom(id) {\n\treturn room[id === undefined ? state.room : id];\n}\n\nfunction isSpriteOffstage(id) {\n\treturn sprite[id].room == null;\n}\n\nfunction serializeNote(note, key, useFriendlyName) {\n\tvar isSolfa = (key != undefined && key != null);\n\tvar noteType = (isSolfa === true) ? Solfa : Note;\n\n\tif (isSolfa && key.scale.indexOf(note) === -1) {\n\t\t// no matching note in key\n\t\treturn null;\n\t}\n\n\tif (isSolfa && useFriendlyName != true) {\n\t\tfor (var name in Solfa) {\n\t\t\tif (Solfa[name] === note) {\n\t\t\t\treturn name.toLowerCase();\n\t\t\t}\n\t\t}\n\n\t\t// no solfa note found\n\t\treturn null;\n\t}\n\n\t// for a solfa note's \"friendly name\" convert to the chromatic equivalent\n\tif (isSolfa && useFriendlyName === true) {\n\t\tnote = key.notes[note];\n\t}\n\n\t// from this point on, we know the note we're looking for is chromatic\n\tfor (var name in Note) {\n\t\tif (Note[name] === note) {\n\t\t\tname = name.replace(\"_SHARP\", \"#\");\n\t\t\tif (useFriendlyName === true && name === \"H\") {\n\t\t\t\tname = \"C\";\n\t\t\t}\n\t\t\treturn name;\n\t\t}\n\t}\n\n\t// no note found\n\treturn symbol;\n}\n\nfunction serializeOctave(octave) {\n\tfor (var symbol in Octave) {\n\t\tif (Octave[symbol] === octave) {\n\t\t\treturn symbol;\n\t\t}\n\t}\n\n\t// default to middle octave\n\treturn \"4\";\n}\n\n//TODO this is in progress and doesn't support all features\nfunction serializeWorld(skipFonts) {\n\tif (skipFonts === undefined || skipFonts === null) {\n\t\tskipFonts = false;\n\t}\n\n\t// update version flags\n\tflags.VER_MAJ = version.major;\n\tflags.VER_MIN = version.minor;\n\n\tvar worldStr = \"\";\n\t/* TITLE */\n\tworldStr += getTitle() + \"\\n\";\n\tworldStr += \"\\n\";\n\t/* VERSION */\n\tworldStr += \"# BITSY VERSION \" + getEngineVersion() + \"\\n\"; // add version as a comment for debugging purposes\n\tif (version.devBuildPhase != \"RELEASE\") {\n\t\tworldStr += \"# DEVELOPMENT BUILD -- \" + version.devBuildPhase;\n\t}\n\tworldStr += \"\\n\";\n\t/* FLAGS */\n\tfor (f in flags) {\n\t\tworldStr += \"! \" + f + \" \" + flags[f] + \"\\n\";\n\t}\n\tworldStr += \"\\n\"\n\t/* FONT */\n\tif (fontName != defaultFontName) {\n\t\tworldStr += \"DEFAULT_FONT \" + fontName + \"\\n\";\n\t\tworldStr += \"\\n\"\n\t}\n\tif (textDirection != TextDirection.LeftToRight) {\n\t\tworldStr += \"TEXT_DIRECTION \" + textDirection + \"\\n\";\n\t\tworldStr += \"\\n\"\n\t}\n\t/* PALETTE */\n\tfor (id in palette) {\n\t\tif (id != \"default\") {\n\t\t\tworldStr += \"PAL \" + id + \"\\n\";\n\t\t\tfor (i in getPal(id)) {\n\t\t\t\tfor (j in getPal(id)[i]) {\n\t\t\t\t\tworldStr += getPal(id)[i][j];\n\t\t\t\t\tif (j < 2) worldStr += \",\";\n\t\t\t\t}\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t\tif (palette[id].name != null) {\n\t\t\t\tworldStr += \"NAME \" + palette[id].name + \"\\n\";\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\t\t}\n\t}\n\t/* ROOM */\n\tfor (id in room) {\n\t\tworldStr += \"ROOM \" + id + \"\\n\";\n\t\tif ( flags.ROOM_FORMAT == 0 ) {\n\t\t\t// old non-comma separated format\n\t\t\tfor (i in room[id].tilemap) {\n\t\t\t\tfor (j in room[id].tilemap[i]) {\n\t\t\t\t\tworldStr += room[id].tilemap[i][j];\t\n\t\t\t\t}\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t}\n\t\telse if ( flags.ROOM_FORMAT == 1 ) {\n\t\t\t// new comma separated format\n\t\t\tfor (i in room[id].tilemap) {\n\t\t\t\tfor (j in room[id].tilemap[i]) {\n\t\t\t\t\tworldStr += room[id].tilemap[i][j];\n\t\t\t\t\tif (j < room[id].tilemap[i].length-1) worldStr += \",\"\n\t\t\t\t}\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (room[id].name != null) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + room[id].name + \"\\n\";\n\t\t}\n\t\tif (room[id].walls.length > 0) {\n\t\t\t/* WALLS */\n\t\t\tworldStr += \"WAL \";\n\t\t\tfor (j in room[id].walls) {\n\t\t\t\tworldStr += room[id].walls[j];\n\t\t\t\tif (j < room[id].walls.length-1) {\n\t\t\t\t\tworldStr += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\t\t}\n\t\tif (room[id].items.length > 0) {\n\t\t\t/* ITEMS */\n\t\t\tfor (j in room[id].items) {\n\t\t\t\tvar itm = room[id].items[j];\n\t\t\t\tworldStr += \"ITM \" + itm.id + \" \" + itm.x + \",\" + itm.y;\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (room[id].exits.length > 0) {\n\t\t\t/* EXITS */\n\t\t\tfor (j in room[id].exits) {\n\t\t\t\tvar e = room[id].exits[j];\n\t\t\t\tif ( isExitValid(e) ) {\n\t\t\t\t\tworldStr += \"EXT \" + e.x + \",\" + e.y + \" \" + e.dest.room + \" \" + e.dest.x + \",\" + e.dest.y;\n\t\t\t\t\tif (e.transition_effect != undefined && e.transition_effect != null) {\n\t\t\t\t\t\tworldStr += \" FX \" + e.transition_effect;\n\t\t\t\t\t}\n\t\t\t\t\tif (e.dlg != undefined && e.dlg != null) {\n\t\t\t\t\t\tworldStr += \" DLG \" + e.dlg;\n\t\t\t\t\t}\n\t\t\t\t\tworldStr += \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (room[id].endings.length > 0) {\n\t\t\t/* ENDINGS */\n\t\t\tfor (j in room[id].endings) {\n\t\t\t\tvar e = room[id].endings[j];\n\t\t\t\t// todo isEndingValid\n\t\t\t\tworldStr += \"END \" + e.id + \" \" + e.x + \",\" + e.y;\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (room[id].pal != null && room[id].pal != \"default\") {\n\t\t\t/* PALETTE */\n\t\t\tworldStr += \"PAL \" + room[id].pal + \"\\n\";\n\t\t}\n\t\tif (room[id].ava != null) {\n\t\t\t/* AVATAR SPRITE */\n\t\t\tworldStr += \"AVA \" + room[id].ava + \"\\n\";\n\t\t}\n\t\tif (room[id].tune != null && room[id].tune != \"0\") {\n\t\t\t/* TUNE */\n\t\t\tworldStr += \"TUNE \" + room[id].tune + \"\\n\";\n\t\t}\n\t\tworldStr += \"\\n\";\n\t}\n\t/* TILES */\n\tfor (id in tile) {\n\t\tworldStr += \"TIL \" + id + \"\\n\";\n\t\tworldStr += serializeDrawing( \"TIL_\" + id );\n\t\tif (tile[id].name != null && tile[id].name != undefined) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + tile[id].name + \"\\n\";\n\t\t}\n\t\tif (tile[id].isWall != null && tile[id].isWall != undefined) {\n\t\t\t/* WALL */\n\t\t\tworldStr += \"WAL \" + tile[id].isWall + \"\\n\";\n\t\t}\n\t\tif (tile[id].col != null && tile[id].col != undefined && tile[id].col != 1) {\n\t\t\t/* COLOR OVERRIDE */\n\t\t\tworldStr += \"COL \" + tile[id].col + \"\\n\";\n\t\t}\n\t\tif (tile[id].bgc != null && tile[id].bgc != undefined && tile[id].bgc != 0) {\n\t\t\t/* BACKGROUND COLOR OVERRIDE */\n\t\t\tif (tile[id].bgc < 0) {\n\t\t\t\t// transparent background\n\t\t\t\tworldStr += \"BGC *\\n\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tworldStr += \"BGC \" + tile[id].bgc + \"\\n\";\n\t\t\t}\n\t\t}\n\t\tworldStr += \"\\n\";\n\t}\n\t/* SPRITES */\n\tfor (id in sprite) {\n\t\tworldStr += \"SPR \" + id + \"\\n\";\n\t\tworldStr += serializeDrawing( \"SPR_\" + id );\n\t\tif (sprite[id].name != null && sprite[id].name != undefined) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + sprite[id].name + \"\\n\";\n\t\t}\n\t\tif (sprite[id].dlg != null) {\n\t\t\tworldStr += \"DLG \" + sprite[id].dlg + \"\\n\";\n\t\t}\n\t\tif (sprite[id].room != null) {\n\t\t\t/* SPRITE POSITION */\n\t\t\tworldStr += \"POS \" + sprite[id].room + \" \" + sprite[id].x + \",\" + sprite[id].y + \"\\n\";\n\t\t}\n\t\tif (sprite[id].inventory != null) {\n\t\t\tfor(itemId in sprite[id].inventory) {\n\t\t\t\tworldStr += \"ITM \" + itemId + \" \" + sprite[id].inventory[itemId] + \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (sprite[id].col != null && sprite[id].col != undefined && sprite[id].col != 2) {\n\t\t\t/* COLOR OVERRIDE */\n\t\t\tworldStr += \"COL \" + sprite[id].col + \"\\n\";\n\t\t}\n\t\tif (sprite[id].bgc != null && sprite[id].bgc != undefined && sprite[id].bgc != 0) {\n\t\t\t/* BACKGROUND COLOR OVERRIDE */\n\t\t\tif (sprite[id].bgc < 0) {\n\t\t\t\t// transparent background\n\t\t\t\tworldStr += \"BGC *\\n\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tworldStr += \"BGC \" + sprite[id].bgc + \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (sprite[id].blip != null && sprite[id].blip != undefined) {\n\t\t\t/* BLIP */\n\t\t\tworldStr += \"BLIP \" + sprite[id].blip + \"\\n\";\n\t\t}\n\t\tworldStr += \"\\n\";\n\t}\n\t/* ITEMS */\n\tfor (id in item) {\n\t\tworldStr += \"ITM \" + id + \"\\n\";\n\t\tworldStr += serializeDrawing( \"ITM_\" + id );\n\t\tif (item[id].name != null && item[id].name != undefined) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + item[id].name + \"\\n\";\n\t\t}\n\t\tif (item[id].dlg != null) {\n\t\t\tworldStr += \"DLG \" + item[id].dlg + \"\\n\";\n\t\t}\n\t\tif (item[id].col != null && item[id].col != undefined && item[id].col != 2) {\n\t\t\t/* COLOR OVERRIDE */\n\t\t\tworldStr += \"COL \" + item[id].col + \"\\n\";\n\t\t}\n\t\tif (item[id].bgc != null && item[id].bgc != undefined && item[id].bgc != 0) {\n\t\t\t/* BACKGROUND COLOR OVERRIDE */\n\t\t\tif (item[id].bgc < 0) {\n\t\t\t\t// transparent background\n\t\t\t\tworldStr += \"BGC *\\n\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tworldStr += \"BGC \" + item[id].bgc + \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (item[id].blip != null && item[id].blip != undefined) {\n\t\t\t/* BLIP */\n\t\t\tworldStr += \"BLIP \" + item[id].blip + \"\\n\";\n\t\t}\n\t\tworldStr += \"\\n\";\n\t}\n\t/* DIALOG */\n\tfor (id in dialog) {\n\t\tif (id != titleDialogId) {\n\t\t\tworldStr += \"DLG \" + id + \"\\n\";\n\t\t\tworldStr += dialog[id].src + \"\\n\";\n\t\t\tif (dialog[id].name != null) {\n\t\t\t\tworldStr += \"NAME \" + dialog[id].name + \"\\n\";\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\t\t}\n\t}\n\t/* ENDINGS (for backwards compability only) */\n\tfor (id in end) {\n\t\tworldStr += \"END \" + id + \"\\n\";\n\t\tworldStr += end[id].src + \"\\n\";\n\t\tworldStr += \"\\n\";\n\t}\n\t/* VARIABLES */\n\tfor (id in variable) {\n\t\tworldStr += \"VAR \" + id + \"\\n\";\n\t\tworldStr += variable[id] + \"\\n\";\n\t\tworldStr += \"\\n\";\n\t}\n\t/* TUNES */\n\tfor (id in tune) {\n\t\tif (id === \"0\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tworldStr += \"TUNE \" + id + \"\\n\";\n\t\tfor (var i = 0; i < maxTuneLength && i < tune[id].melody.length; i++) {\n\t\t\t// MELODY\n\t\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t\tvar noteStr = serializeNote(tune[id].melody[i][j].note, tune[id].key);\n\t\t\t\tif (noteStr === null) {\n\t\t\t\t\ttune[id].melody[i][j].beats = 0;\n\t\t\t\t}\n\t\t\t\tif (tune[id].melody[i][j].beats != 1) {\n\t\t\t\t\tworldStr += tune[id].melody[i][j].beats;\n\t\t\t\t}\n\t\t\t\tif (tune[id].melody[i][j].beats > 0) {\n\t\t\t\t\tworldStr += noteStr;\n\t\t\t\t}\n\t\t\t\tif (tune[id].melody[i][j].beats > 0 && tune[id].melody[i][j].octave != Octave[4]) {\n\t\t\t\t\tworldStr += serializeOctave(tune[id].melody[i][j].octave);\n\t\t\t\t}\n\t\t\t\tif (tune[id].melody[i][j].beats > 0 && tune[id].melody[i][j].blip != undefined) {\n\t\t\t\t\t// todo : create constant for the blip separator?\n\t\t\t\t\tworldStr += \"~\" + tune[id].melody[i][j].blip;\n\t\t\t\t}\n\t\t\t\tif (j < 15) {\n\t\t\t\t\tworldStr += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\n\t\t\t// HARMONY\n\t\t\t// todo : lots of copy-pasting - I could probably make some helper functions to simplify this\n\t\t\tfor (var j = 0; j < barLength; j++) {\n\t\t\t\tvar noteStr = serializeNote(tune[id].harmony[i][j].note, tune[id].key);\n\t\t\t\tif (noteStr === null) {\n\t\t\t\t\ttune[id].harmony[i][j].beats = 0;\n\t\t\t\t}\n\t\t\t\tif (tune[id].harmony[i][j].beats != 1) {\n\t\t\t\t\tworldStr += tune[id].harmony[i][j].beats;\n\t\t\t\t}\n\t\t\t\tif (tune[id].harmony[i][j].beats > 0) {\n\t\t\t\t\tworldStr += noteStr;\n\t\t\t\t}\n\t\t\t\tif (tune[id].harmony[i][j].beats > 0 && tune[id].harmony[i][j].octave != Octave[4]) {\n\t\t\t\t\tworldStr += serializeOctave(tune[id].harmony[i][j].octave);\n\t\t\t\t}\n\t\t\t\tif (tune[id].harmony[i][j].beats > 0 && tune[id].harmony[i][j].blip != undefined) {\n\t\t\t\t\tworldStr += \"~\" + tune[id].harmony[i][j].blip;\n\t\t\t\t}\n\t\t\t\tif (j < 15) {\n\t\t\t\t\tworldStr += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\n\t\t\tif (i < (tune[id].melody.length - 1)) {\n\t\t\t\tworldStr += \">\";\n\t\t\t\tworldStr += \"\\n\";\n\t\t\t}\n\t\t}\n\t\tif (tune[id].name != null) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + tune[id].name + \"\\n\";\n\t\t}\n\t\tif (tune[id].key != undefined && tune[id].key != null) {\n\t\t\tworldStr += \"KEY \";\n\t\t\tfor (var i = 0; i < Solfa.COUNT; i++) {\n\t\t\t\tworldStr += serializeNote(tune[id].key.notes[i]);\n\t\t\t\tif (i < Solfa.COUNT - 1) {\n\t\t\t\t\tworldStr += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tworldStr += \" \";\n\t\t\tfor (var i = 0; i < tune[id].key.scale.length; i++) {\n\t\t\t\tworldStr += serializeNote(tune[id].key.scale[i], tune[id].key);\n\t\t\t\tif (i < tune[id].key.scale.length - 1) {\n\t\t\t\t\tworldStr += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tworldStr += \"\\n\";\n\t\t}\n\t\tworldStr += \"TMP \";\n\t\tswitch (tune[id].tempo) {\n\t\t\tcase Tempo.SLW:\n\t\t\t\tworldStr += \"SLW\";\n\t\t\t\tbreak;\n\t\t\tcase Tempo.MED:\n\t\t\t\tworldStr += \"MED\";\n\t\t\t\tbreak;\n\t\t\tcase Tempo.FST:\n\t\t\t\tworldStr += \"FST\";\n\t\t\t\tbreak;\n\t\t\tcase Tempo.XFST:\n\t\t\t\tworldStr += \"XFST\";\n\t\t\t\tbreak;\n\t\t}\n\t\tworldStr += \"\\n\";\n\t\tworldStr += \"SQR \";\n\t\tswitch (tune[id].instrumentA) {\n\t\t\tcase SquareWave.P8:\n\t\t\t\tworldStr += \"P8\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P4:\n\t\t\t\tworldStr += \"P4\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P2:\n\t\t\t\tworldStr += \"P2\";\n\t\t\t\tbreak;\n\t\t}\n\t\tworldStr += \" \";\n\t\tswitch (tune[id].instrumentB) {\n\t\t\tcase SquareWave.P8:\n\t\t\t\tworldStr += \"P8\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P4:\n\t\t\t\tworldStr += \"P4\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P2:\n\t\t\t\tworldStr += \"P2\";\n\t\t\t\tbreak;\n\t\t}\n\t\tworldStr += \"\\n\";\n\t\tif (tune[id].key != undefined && tune[id].key != null && tune[id].arpeggioPattern != ArpeggioPattern.OFF) {\n\t\t\tswitch (tune[id].arpeggioPattern) {\n\t\t\t\tcase ArpeggioPattern.UP:\n\t\t\t\t\tworldStr += \"ARP UP\\n\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ArpeggioPattern.DWN:\n\t\t\t\t\tworldStr += \"ARP DWN\\n\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ArpeggioPattern.INT5:\n\t\t\t\t\tworldStr += \"ARP INT5\\n\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ArpeggioPattern.INT8:\n\t\t\t\t\tworldStr += \"ARP INT8\\n\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tworldStr += \"\\n\";\n\t}\n\t/* BLIP */\n\tfor (id in blip) {\n\t\tif (id === \"0\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tworldStr += \"BLIP \" + id + \"\\n\";\n\t\t// pitches\n\t\tif (blip[id].pitchA.beats > 0) {\n\t\t\tworldStr += serializeNote(blip[id].pitchA.note);\n\t\t\tif (blip[id].pitchA.octave != Octave[4]) {\n\t\t\t\tworldStr += serializeOctave(blip[id].pitchA.octave);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tworldStr += blip[id].pitchA.beats;\n\t\t}\n\t\tworldStr += \",\";\n\t\tif (blip[id].pitchB.beats > 0) {\n\t\t\tworldStr += serializeNote(blip[id].pitchB.note);\n\t\t\tif (blip[id].pitchB.octave != Octave[4]) {\n\t\t\t\tworldStr += serializeOctave(blip[id].pitchB.octave);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tworldStr += blip[id].pitchB.beats;\n\t\t}\n\t\tworldStr += \",\";\n\t\tif (blip[id].pitchC.beats > 0) {\n\t\t\tworldStr += serializeNote(blip[id].pitchC.note);\n\t\t\tif (blip[id].pitchC.octave != Octave[4]) {\n\t\t\t\tworldStr += serializeOctave(blip[id].pitchC.octave);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tworldStr += blip[id].pitchC.beats;\n\t\t}\n\t\tworldStr += \"\\n\";\n\t\tif (blip[id].name != null) {\n\t\t\t/* NAME */\n\t\t\tworldStr += \"NAME \" + blip[id].name + \"\\n\";\n\t\t}\n\t\t// envelope\n\t\tworldStr += \"ENV \" + blip[id].envelope.attack\n\t\t\t+ \" \" + blip[id].envelope.decay\n\t\t\t+ \" \" + blip[id].envelope.sustain\n\t\t\t+ \" \" + blip[id].envelope.length\n\t\t\t+ \" \" + blip[id].envelope.release + \"\\n\";\n\t\t// beat\n\t\tworldStr += \"BEAT \" + blip[id].beat.time\n\t\t\t+ \" \" + blip[id].beat.delay + \"\\n\";\n\t\t// instrument (square wave type)\n\t\tworldStr += \"SQR \";\n\t\tswitch (blip[id].instrument) {\n\t\t\tcase SquareWave.P8:\n\t\t\t\tworldStr += \"P8\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P4:\n\t\t\t\tworldStr += \"P4\";\n\t\t\t\tbreak;\n\t\t\tcase SquareWave.P2:\n\t\t\t\tworldStr += \"P2\";\n\t\t\t\tbreak;\n\t\t}\n\t\tworldStr += \"\\n\";\n\t\t// other parameters\n\t\tif (blip[id].doRepeat === true) {\n\t\t\tworldStr += \"RPT 1\\n\";\n\t\t}\n\t\t// TODO : consider for future update\n\t\t// if (blip[id].doSlide === true) {\n\t\t// \tworldStr += \"SLD 1\\n\";\n\t\t// }\n\t\tworldStr += \"\\n\";\n\t}\n\t/* FONT */\n\t// TODO : support multiple fonts\n\tif (fontManager && fontName != defaultFontName && !skipFonts) {\n\t\tworldStr += fontManager.GetData(fontName);\n\t}\n\n\treturn worldStr;\n}\n\nfunction serializeDrawing(drwId) {\n\tif (!renderer) {\n\t\treturn \"\";\n\t}\n\n\tvar drawingData = renderer.GetDrawingSource(drwId);\n\tvar drwStr = \"\";\n\tfor (f in drawingData) {\n\t\tfor (y in drawingData[f]) {\n\t\t\tvar rowStr = \"\";\n\t\t\tfor (x in drawingData[f][y]) {\n\t\t\t\trowStr += drawingData[f][y][x];\n\t\t\t}\n\t\t\tdrwStr += rowStr + \"\\n\";\n\t\t}\n\t\tif (f < (drawingData.length-1)) drwStr += \">\\n\";\n\t}\n\treturn drwStr;\n}\n\nfunction isExitValid(e) {\n\tvar hasValidStartPos = e.x >= 0 && e.x < bitsy.MAP_SIZE && e.y >= 0 && e.y < bitsy.MAP_SIZE;\n\tvar hasDest = e.dest != null;\n\tvar hasValidRoomDest = (e.dest.room != null && e.dest.x >= 0 && e.dest.x < bitsy.MAP_SIZE && e.dest.y >= 0 && e.dest.y < bitsy.MAP_SIZE);\n\treturn hasValidStartPos && hasDest && hasValidRoomDest;\n}\n\nfunction setTile(mapId, x, y, tileId) {\n\tbitsy.set(mapId, (y * bitsy.MAP_SIZE) + x, tileId);\n}\n\nfunction drawTile(tileId, x, y) {\n\tsetTile(bitsy.MAP1, x, y, tileId);\n}\n\nfunction drawSprite(tileId, x, y) {\n\tsetTile(bitsy.MAP2, x, y, tileId);\n}\n\nfunction drawItem(tileId, x, y) {\n\tsetTile(bitsy.MAP2, x, y, tileId);\n}\n\n// var debugLastRoomDrawn = \"0\";\n\nfunction clearRoom() {\n\tvar paletteId = \"default\";\n\n\tif (room === undefined) {\n\t\t// protect against invalid rooms\n\t\treturn;\n\t}\n\n\tif (room.pal != null && palette[paletteId] != undefined) {\n\t\tpaletteId = room.pal;\n\t}\n\n\t// clear background & foreground\n\tbitsy.fill(bitsy.MAP1, 0);\n\tbitsy.fill(bitsy.MAP2, 0);\n}\n\nfunction drawRoomBackground(room, frameIndex, redrawAnimatedOnly) {\n\tif (!redrawAnimatedOnly) {\n\t\t// clear background map\n\t\tbitsy.fill(bitsy.MAP1, 0);\n\t}\n\n\t// NOTE: interestingly the slowest part of this is iterating over all the tiles, not actually drawing them\n\tfor (var y = 0; y < bitsy.MAP_SIZE; y++) {\n\t\tfor (var x = 0; x < bitsy.MAP_SIZE; x++) {\n\t\t\tvar id = room.tilemap[y][x];\n\n\t\t\tif (id != \"0\" && tile[id] == null) { // hack-around to avoid corrupting files (not a solution though!)\n\t\t\t\tid = \"0\";\n\t\t\t\troom.tilemap[y][x] = id;\n\t\t\t}\n\n\t\t\tif (id != \"0\" && (!redrawAnimatedOnly || tile[id].animation.isAnimated)) {\n\t\t\t\tdrawTile(getTileFrame(tile[id], frameIndex), x, y);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction drawRoomForeground(room, frameIndex, redrawAnimatedOnly) {\n\tif (!redrawAnimatedOnly) {\n\t\t// clear foreground map\n\t\tbitsy.fill(bitsy.MAP2, 0);\n\t}\n\n\t// draw items\n\tfor (var i = 0; i < room.items.length; i++) {\n\t\tvar itm = room.items[i];\n\t\tif (!redrawAnimatedOnly || item[itm.id].animation.isAnimated) {\n\t\t\tdrawItem(getItemFrame(item[itm.id], frameIndex), itm.x, itm.y);\n\t\t}\n\t}\n\n\t// draw sprites\n\tfor (id in sprite) {\n\t\tvar spr = sprite[id];\n\t\tif (id != playerId && spr.room === room.id && (!redrawAnimatedOnly || spr.animation.isAnimated)) {\n\t\t\tdrawSprite(getSpriteFrame(spr, frameIndex), spr.x, spr.y);\n\t\t}\n\t}\n}\n\nfunction drawRoomForegroundTile(room, frameIndex, x, y) {\n\t// draw items\n\tfor (var i = 0; i < room.items.length; i++) {\n\t\tvar itm = room.items[i];\n\t\tif (itm.x === x && itm.y === y) {\n\t\t\tdrawItem(getItemFrame(item[itm.id], frameIndex), itm.x, itm.y);\n\t\t}\n\t}\n\n\t// draw sprites\n\tfor (id in sprite) {\n\t\tvar spr = sprite[id];\n\t\tif (id != playerId && spr.room === room.id && spr.x === x && spr.y === y) {\n\t\t\tdrawSprite(getSpriteFrame(spr, frameIndex), spr.x, spr.y);\n\t\t}\n\t}\n}\n\nfunction drawRoom(room, args) {\n\tif (room === undefined || isNarrating) {\n\t\t// protect against invalid rooms\n\t\treturn;\n\t}\n\n\tvar redrawAll = args && (args.redrawAll === true);\n\tvar redrawAnimated = args && (args.redrawAnimated === true);\n\tvar redrawAvatar = args && (args.redrawAvatar === true);\n\tvar frameIndex = args ? args.frameIndex : undefined;\n\n\t// if *only* redrawing the avatar, first clear its previous position\n\tif (redrawAvatar) {\n\t\tsetTile(bitsy.MAP2, playerPrevX, playerPrevY, 0);\n\t\t// also redraw any sprite or item that might be \"under\" the player (todo: possible perf issue?)\n\t\tdrawRoomForegroundTile(room, frameIndex, playerPrevX, playerPrevY);\n\t}\n\n\t// draw background & foreground tiles\n\tif (redrawAll || redrawAnimated) {\n\t\t// draw tiles\n\t\tdrawRoomBackground(room, frameIndex, redrawAnimated);\n\t\t// draw sprites & items\n\t\tdrawRoomForeground(room, frameIndex, redrawAnimated);\n\t}\n\n\t// draw the player's avatar at its current position\n\tif ((redrawAll || redrawAnimated || redrawAvatar) && sprite[playerId] && sprite[playerId].room === room.id) {\n\t\tvar spr = sprite[playerId];\n\t\tvar x = spr.x;\n\t\tvar y = spr.y;\n\n\t\t// get the avatar override sprite (if there is one)\n\t\tif (state.ava && state.ava != playerId && sprite[state.ava]) {\n\t\t\tspr = sprite[state.ava];\n\t\t}\n\n\t\tdrawSprite(getSpriteFrame(spr, frameIndex), x, y);\n\t}\n}\n\n// TODO : remove these get*Image methods\nfunction getTileFrame(t, frameIndex) {\n\tif (!renderer) {\n\t\treturn null;\n\t}\n\treturn renderer.GetDrawingFrame(t, frameIndex);\n}\n\nfunction getSpriteFrame(s, frameIndex) {\n\tif (!renderer) {\n\t\treturn null;\n\t}\n\treturn renderer.GetDrawingFrame(s, frameIndex);\n}\n\nfunction getItemFrame(itm, frameIndex) {\n\tif (!renderer) {\n\t\treturn null;\n\t}\n\treturn renderer.GetDrawingFrame(itm, frameIndex);\n}\n\nfunction curDefaultPal() {\n\treturn getRoomPal(state.room);\n}\n\nfunction getRoomPal(roomId) {\n\tvar defaultId = \"default\";\n\n\tif (roomId == null) {\n\t\treturn defaultId;\n\t}\n\telse if (room[roomId].pal != null) {\n\t\t//a specific palette was chosen\n\t\treturn room[roomId].pal;\n\t}\n\telse {\n\t\tif (roomId in palette) {\n\t\t\t//there is a palette matching the name of the room\n\t\t\treturn roomId;\n\t\t}\n\t\telse {\n\t\t\t//use the default palette\n\t\t\treturn defaultId;\n\t\t}\n\t}\n\treturn defaultId;\n}\n\nvar isDialogMode = false;\nvar isNarrating = false;\nvar isEnding = false;\n\nvar dialogModule;\nvar dialogRenderer;\nvar dialogBuffer;\nif (engineFeatureFlags.isDialogEnabled) {\n\tdialogModule = new Dialog();\n\tdialogRenderer = dialogModule.CreateRenderer();\n\tdialogBuffer = dialogModule.CreateBuffer();\n}\n\nvar fontManager;\nif (engineFeatureFlags.isFontEnabled) {\n\tfontManager = new FontManager();\n}\n\n// TODO : is this scriptResult thing being used anywhere???\nfunction onExitDialog(scriptResult, dialogCallback) {\n\tisDialogMode = false;\n\tbitsy.textbox(false);\n\n\tif (isNarrating) {\n\t\tisNarrating = false;\n\n\t\t// redraw the room\n\t\tdrawRoom(room[state.room], { redrawAll: true });\n\t}\n\n\tif (isDialogPreview) {\n\t\tisDialogPreview = false;\n\n\t\tif (onDialogPreviewEnd != null) {\n\t\t\tonDialogPreviewEnd();\n\t\t}\n\t}\n\n\tif (dialogCallback != undefined && dialogCallback != null) {\n\t\tdialogCallback(scriptResult);\n\t}\n\n\tif (soundPlayer) {\n\t\tsoundPlayer.resumeTune();\n\t}\n}\n\n/*\nTODO\n- titles and endings should also take advantage of the script pre-compilation if possible??\n- could there be a namespace collision?\n- what about dialog NAMEs vs IDs?\n- what about a special script block separate from DLG?\n*/\nfunction startNarrating(dialogStr, end) {\n\tbitsy.log(\"NARRATE \" + dialogStr);\n\n\tif(end === undefined) {\n\t\tend = false;\n\t}\n\n\tisNarrating = true;\n\tisEnding = end;\n\n\tif (isEnding && soundPlayer) {\n\t\tsoundPlayer.stopTune();\n\t}\n\n\t// clear the room tiles before narrating\n\tbitsy.fill(bitsy.MAP1, 0);\n\tbitsy.fill(bitsy.MAP2, 0);\n\n\tstartDialog(dialogStr);\n}\n\nfunction startEndingDialog(ending) {\n\tisNarrating = true;\n\tisEnding = true;\n\n\tvar endingScriptId = ending.id;\n\tvar endingDialogStr = dialog[ending.id].src;\n\n\t// compatibility with pre-7.0 endings\n\tif (flags.DLG_COMPAT === 1 && end[ending.id]) {\n\t\tendingScriptId = \"end_compat_\" + ending.id;\n\t\tendingDialogStr = end[ending.id].src;\n\t}\n\n\tvar tmpTuneId = null;\n\tif (isEnding && soundPlayer) {\n\t\ttmpTuneId = soundPlayer.getCurTuneId();\n\t\tsoundPlayer.stopTune();\n\t}\n\n\tstartDialog(\n\t\tendingDialogStr,\n\t\tendingScriptId,\n\t\tfunction() {\n\t\t\tvar isLocked = ending.property && ending.property.locked === true;\n\t\t\tif (isLocked) {\n\t\t\t\tisEnding = false;\n\n\t\t\t\t// if the ending was cancelled, restart the music\n\t\t\t\t// todo : should it resume from where it started? (right now it starts over)\n\t\t\t\tif (tmpTuneId && soundPlayer && !soundPlayer.isTunePlaying()) {\n\t\t\t\t\tsoundPlayer.playTune(tune[tmpTuneId]);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tending);\n}\n\nfunction startItemDialog(itemId, dialogCallback) {\n\tvar dialogId = item[itemId].dlg;\n\t// bitsy.log(\"START ITEM DIALOG \" + dialogId);\n\tif (dialog[dialogId]) {\n\t\tvar dialogStr = dialog[dialogId].src;\n\t\tstartDialog(dialogStr, dialogId, dialogCallback);\n\t}\n\telse {\n\t\tdialogCallback();\n\t}\n}\n\nfunction startSpriteDialog(spriteId) {\n\tvar spr = sprite[spriteId];\n\tvar dialogId = spr.dlg;\n\n\t// back compat for when dialog IDs were implicitly the same as sprite IDs\n\tif (flags.DLG_COMPAT === 1 && (dialogId === undefined || dialogId === null)) {\n\t\tdialogId = spr.id;\n\t}\n\n\t// bitsy.log(\"START SPRITE DIALOG \" + dialogId);\n\tif (dialog[dialogId]){\n\t\tvar dialogStr = dialog[dialogId].src;\n\t\tstartDialog(dialogStr, dialogId);\n\t}\n}\n\nfunction startDialog(dialogStr, scriptId, dialogCallback, objectContext) {\n\tbitsy.log(\"START DIALOG\");\n\n\tif (soundPlayer) {\n\t\tsoundPlayer.pauseTune();\n\t}\n\n\tif (dialogStr.length <= 0) {\n\t\tonExitDialog(null, dialogCallback);\n\t\treturn;\n\t}\n\n\tif (!dialogBuffer) {\n\t\tbitsy.log(dialogStr);\n\t\tonExitDialog(null, dialogCallback);\n\t\treturn;\n\t}\n\n\tif (!scriptInterpreter) {\n\t\tdialogRenderer.Reset();\n\t\tdialogRenderer.SetCentered(isNarrating /*centered*/);\n\t\tdialogBuffer.Reset();\n\t\tdialogBuffer.AddText(dialogStr);\n\t\tdialogBuffer.OnDialogEnd(function() {\n\t\t\tonExitDialog(null, dialogCallback);\n\t\t});\n\t\tbitsy.log(\"dialog start end\");\n\t\treturn;\n\t};\n\n\tisDialogMode = true;\n\n\tdialogRenderer.Reset();\n\tdialogRenderer.SetCentered(isNarrating /*centered*/);\n\tdialogBuffer.Reset();\n\tscriptInterpreter.SetDialogBuffer(dialogBuffer);\n\n\tvar onScriptEnd = function(scriptResult) {\n\t\tdialogBuffer.OnDialogEnd(function() {\n\t\t\tonExitDialog(scriptResult, dialogCallback);\n\t\t});\n\t};\n\n\tif (scriptId === undefined) { // TODO : what's this for again?\n\t\tscriptInterpreter.Interpret(dialogStr, onScriptEnd);\n\t}\n\telse {\n\t\tif (!scriptInterpreter.HasScript(scriptId)) {\n\t\t\tscriptInterpreter.Compile(scriptId, dialogStr);\n\t\t}\n\t\t// scriptInterpreter.DebugVisualizeScript(scriptId);\n\t\tscriptInterpreter.Run(scriptId, onScriptEnd, objectContext);\n\t}\n\n}\n\nvar isDialogPreview = false;\nfunction startPreviewDialog(script, dialogCallback) {\n\tif (!scriptInterpreter || !dialogBuffer) {\n\t\treturn;\n\t}\n\n\tisNarrating = true;\n\n\tisDialogMode = true;\n\n\tisDialogPreview = true;\n\n\tdialogRenderer.Reset();\n\tdialogRenderer.SetCentered(true);\n\tdialogBuffer.Reset();\n\tscriptInterpreter.SetDialogBuffer(dialogBuffer);\n\n\t// TODO : do I really need a seperate callback for this debug mode??\n\tonDialogPreviewEnd = dialogCallback;\n\n\tvar onScriptEndCallback = function(scriptResult) {\n\t\tdialogBuffer.OnDialogEnd(function() {\n\t\t\tonExitDialog(scriptResult, null);\n\t\t});\n\t};\n\n\tscriptInterpreter.Eval(script, onScriptEndCallback);\n}\n\n/* NEW SCRIPT STUFF */\nvar scriptModule;\nvar scriptInterpreter;\nvar scriptUtils;\n// scriptInterpreter.SetDialogBuffer( dialogBuffer );\nif (engineFeatureFlags.isScriptEnabled) {\n\tbitsy.log(\"init script module\");\n\tscriptModule = new Script();\n\tbitsy.log(\"init interpreter\");\n\tscriptInterpreter = scriptModule.CreateInterpreter();\n\tbitsy.log(\"init utils\");\n\tscriptUtils = scriptModule.CreateUtils(); // TODO: move to editor.js?\n\tbitsy.log(\"init script module end\");\n}\n\n/* SOUND */\nvar soundPlayer;\nif (engineFeatureFlags.isSoundEnabled) {\n\tsoundPlayer = new SoundPlayer();\n}\n\n/* EVENTS */\nbitsy.loop(update);",
"icon_about.svg": "\n\n",
"icon_add.svg": "\n\n",
@@ -95,6 +95,7 @@ var Resources = {
"icon_record_stop.svg": "\n\n",
"icon_redo.svg": "\n\n",
"icon_room.svg": "\n\n",
+ "icon_save.svg": "\n\n",
"icon_search.svg": "\n\n",
"icon_set_exit_location.svg": "\n\n",
"icon_settings.svg": "\n\n",
diff --git a/editor/script/menu.js b/editor/script/menu.js
index e06616cc..5f6aacdc 100644
--- a/editor/script/menu.js
+++ b/editor/script/menu.js
@@ -1,13 +1,83 @@
/* shared HTML element creation functions */
+function tryGetMenuString(menuText) {
+ var menuString;
+
+ if (menuText && menuText.id && localization) {
+ menuString = localization.GetStringOrFallback(menuText.id, menuText.text);
+ }
+ else if (menuText && menuText.text) {
+ // no localization available
+ menuString = menuText.text;
+ }
+ else if (menuText) {
+ // fallback to raw string
+ menuString = menuText;
+ }
+
+ return menuString;
+}
+
function createGroupElement(options) {
- var groupDiv = document.createElement("div");
- groupDiv.classList.add("bitsy-menu-group");
+ // todo : better setting name?
+ var hasHeader = options && (options.text || options.icon);
+ var isExpandable = options && (options.expandable === true);
+
+ var groupElement;
+ var groupHeaderLabel;
+
+ if (hasHeader) {
+ groupHeaderLabel = createLabelElement({
+ text: options.text,
+ description: options.description,
+ icon: options.icon,
+ style: "bitsy-menu-header",
+ elementType: "span", // override the element type to make it clickable
+ });
+ }
+
+ if (isExpandable) {
+ var groupDetails = document.createElement("details");
+
+ if (options && options.open === true) {
+ groupDetails.setAttribute("open", "");
+ }
+
+ groupDetails.ontoggle = function(e) {
+ if (options.ontoggle) {
+ options.ontoggle(e);
+ }
+ };
+
+ if (hasHeader) {
+ var groupSummary = document.createElement("summary");
+ groupSummary.appendChild(groupHeaderLabel);
+ groupDetails.appendChild(groupSummary);
+ }
+
+ groupElement = groupDetails;
+ }
+ else {
+ var groupDiv = document.createElement("div");
+
+ if (hasHeader) {
+ groupDiv.appendChild(groupHeaderLabel);
+ }
+
+ groupElement = groupDiv;
+ }
+
+ groupElement.classList.add("bitsy-menu-group");
if (options && options.enabled === false) {
- groupDiv.classList.add("disabled");
+ groupElement.classList.add("disabled");
+ }
+
+ // todo : set via class instead? (name: row/column or horizontal/vertical?)
+ if (options && options.direction) {
+ groupElement.setAttribute("flex-direction", options.direction);
}
- return groupDiv;
+ return groupElement;
}
function createIconElement(id) {
@@ -15,7 +85,8 @@ function createIconElement(id) {
}
function createLabelElement(options) {
- var label = document.createElement("label");
+ var elementType = (options && options.elementType) ? options.elementType : "label";
+ var label = document.createElement(elementType);
label.classList.add("bitsy-menu-label");
if (options.style) {
@@ -26,9 +97,10 @@ function createLabelElement(options) {
label.appendChild(createIconElement(options.icon));
}
- if (options.text) {
+ var labelText = tryGetMenuString(options.text);
+ if (labelText) {
var textSpan = document.createElement("span");
- textSpan.innerText = options.text;
+ textSpan.innerText = labelText;
if (options.id) {
textSpan.id = options.id + "Text";
@@ -37,8 +109,9 @@ function createLabelElement(options) {
label.appendChild(textSpan);
}
- if (options.description) {
- label.title = options.description;
+ var labelTooltip = tryGetMenuString(options.description);
+ if (labelTooltip) {
+ label.title = labelTooltip;
}
if (options.for) {
@@ -76,6 +149,65 @@ function createTextInputElement(options) {
return input;
}
+function createTextAreaElement(options) {
+ var textarea = document.createElement("textarea");
+ textarea.setAttribute("spellcheck", false);
+
+ if (options.rows) {
+ textarea.setAttribute("rows", options.rows);
+ }
+
+ if (options.value) {
+ textarea.value = options.value;
+ }
+
+ // todo : description
+
+ if (options.onchange) {
+ textarea.onchange = options.onchange;
+ }
+
+ return textarea;
+}
+
+function createNumberInputElement(options) {
+ var input = document.createElement("input");
+ input.type = "number";
+
+ if (options.style) {
+ input.classList.add("bitsy-input-style-" + options.style);
+ }
+
+ if (options.value) {
+ input.value = options.value;
+ }
+
+ if (options.onchange) {
+ input.onchange = options.onchange;
+ }
+
+ return input;
+}
+
+function createColorInputElement(options) {
+ var input = document.createElement("input");
+ input.type = "color";
+
+ if (options.style) {
+ input.classList.add("bitsy-input-style-" + options.style);
+ }
+
+ if (options.value) {
+ input.value = options.value;
+ }
+
+ if (options.onchange) {
+ input.onchange = options.onchange;
+ }
+
+ return input;
+}
+
function createRadioElement(options) {
var radioForm = document.createElement("form");
@@ -145,8 +277,13 @@ function createSelectElement(options) {
var option = options.options[i];
var value = (option.value != undefined ? option.value : null);
var optionElement = document.createElement("option");
- optionElement.innerText = option.text;
- optionElement.title = option.description;
+
+ var optionText = tryGetMenuString(option.text);
+ optionElement.innerText = optionText;
+
+ var optionTooltip = tryGetMenuString(option.description);
+ optionElement.title = optionTooltip;
+
optionElement.value = value;
optionElement.selected = (value === options.value);
selectElement.appendChild(optionElement);
@@ -162,14 +299,16 @@ function createButtonElement(options) {
button.appendChild(createIconElement(options.icon));
}
- if (options.text) {
+ var buttonText = tryGetMenuString(options.text);
+ if (buttonText) {
var textSpan = document.createElement("span");
- textSpan.innerText = options.text;
+ textSpan.innerText = buttonText;
button.appendChild(textSpan);
}
- if (options.description) {
- button.title = options.description;
+ var buttonTooltip = tryGetMenuString(options.description);
+ if (buttonTooltip) {
+ button.title = buttonTooltip;
}
if (options.enabled != undefined) {
@@ -191,6 +330,7 @@ function createButtonElement(options) {
function createToggleElement(options) {
var toggleSpan = document.createElement("span");
+ toggleSpan.id = options.id + "Span";
var checkboxInput = document.createElement("input");
checkboxInput.type = "checkbox";
@@ -222,6 +362,42 @@ function createToggleElement(options) {
return toggleSpan;
}
+function createFileInputElement(options) {
+ var fileInputSpan = document.createElement("span");
+
+ var fileInput = document.createElement("input");
+ fileInput.type = "file";
+ fileInput.style = "display:none;"; // hack! should add to default style
+ fileInput.accept = options.accept;
+ fileInput.id = options.id; // todo : auto-generate IDs?
+ fileInput.onchange = function (e) {
+ // load file chosen by user
+ var files = e.target.files;
+ var file = files[0];
+ var reader = new FileReader();
+ reader.readAsText(file);
+
+ reader.onloadend = function() {
+ if (options.onload) {
+ options.onload(reader.result);
+ }
+ }
+ }
+ fileInputSpan.appendChild(fileInput);
+
+ var fileInputLabel = createLabelElement({
+ icon: options.icon,
+ id: options.id + "Label",
+ text: options.text,
+ for: fileInput.id,
+ description: options.description,
+ style: "filePickerLabel" // hack - add to default style!
+ });
+ fileInputSpan.appendChild(fileInputLabel);
+
+ return fileInputSpan;
+}
+
function createOptionsForFindCategory(categoryId, noneOption) {
var options = [];
@@ -266,7 +442,7 @@ function MenuInterface(tool) {
self.tool.menuUpdate();
};
- var curGroupElement = null;
+ var groupStack = [];
function wrapEventHandler(handler) {
return function(e) {
@@ -309,6 +485,23 @@ function MenuInterface(tool) {
createFunction = createRadioElement;
}
break;
+ case "text":
+ createFunction = createTextInputElement;
+ break;
+ case "textarea":
+ // todo : combine with text element?
+ createFunction = createTextAreaElement;
+ break;
+ case "number":
+ createFunction = createNumberInputElement;
+ break;
+ case "color":
+ createFunction = createColorInputElement;
+ break;
+ // todo : should this be a special element? or wrap in a function?
+ case "file":
+ createFunction = createFileInputElement;
+ break;
case "group":
createFunction = createGroupElement;
break;
@@ -323,17 +516,21 @@ function MenuInterface(tool) {
message.onchange = wrapEventHandler(message.onchange);
}
+ if (message.onload) {
+ message.onload = wrapEventHandler(message.onload);
+ }
+
var element = createFunction(message);
- if (curGroupElement === null || message.control === "group") {
- self.tool.menuElement.appendChild(element);
+ if (groupStack.length > 0) {
+ groupStack[groupStack.length - 1].appendChild(element);
}
else {
- curGroupElement.appendChild(element);
+ self.tool.menuElement.appendChild(element);
}
if (message.control === "group") {
- curGroupElement = element;
+ groupStack.push(element);
}
}
}
@@ -341,7 +538,7 @@ function MenuInterface(tool) {
this.pop = function(message) {
if (message.control === "group") {
- curGroupElement = null;
+ groupStack.pop();
}
};
}
\ No newline at end of file
diff --git a/editor/script/system/system.js b/editor/script/system/system.js
index fafb56e9..30cd645a 100644
--- a/editor/script/system/system.js
+++ b/editor/script/system/system.js
@@ -374,8 +374,15 @@ function BitsySystem(name) {
};
this._exit = function() {
- input.unlisten(graphics.getCanvas());
+ // disable graphics
+ var canvas = graphics.getCanvas();
+ if (canvas) {
+ input.unlisten(canvas);
+ }
+
+ // disable sound
sound.mute();
+
self._active = false;
};
diff --git a/editor/script/tools/game.js b/editor/script/tools/game.js
new file mode 100644
index 00000000..9c8dc937
--- /dev/null
+++ b/editor/script/tools/game.js
@@ -0,0 +1,634 @@
+function makeGameTool() {
+ return makeToolCard("game", function(tool) {
+ tool.id = "game";
+ tool.icon = "save";
+ // todo : localize
+ tool.name = function() { return "game"; };
+ // TODO : this is kind of a hack! should I just indicate it with a undefined "loop"? or would that cause other problems?
+ tool.disableCanvas = true;
+ // TODO : where is the right place for this metadata?
+ tool.size = "m";
+ tool.insertBefore = "roomCheckSpan"; // todo : need a better way of defining the order of the tool buttons
+
+ tool.loop = function(dt) {};
+
+ // sub-menus
+ var GameMenu = {
+ FILE : 0,
+ SETTINGS: 1,
+ DATA : 2,
+ };
+ var curMenu = GameMenu.FILE;
+
+ // game data
+ var showFontDataInGameData = false;
+
+ // tool settings
+ var languageList = localization.GetLanguageList();
+
+ // default export settings
+ var exportSettings = {
+ pageColor : "#ffffff",
+ isFixedSize : false,
+ fixedSize : 512
+ };
+ // load saved export settings
+ exportSettings = Store.get("exportSettings", exportSettings);
+
+ // setting groups
+ var isToolSettingsGroupOpen = true;
+ var isGameSettingsGroupOpen = true;
+ var isExportSettingsGroupOpen = true;
+
+ tool.menuUpdate = function() {
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "select",
+ value: curMenu,
+ options: [
+ // TODO : ?
+ // {
+ // text: "title",
+ // icon: "text_edit",
+ // description: "share or backup your game",
+ // value: GameMenu.FILE,
+ // },
+ {
+ text: { id: "download_tool_name", text: "save" },
+ icon: "save",
+ description: "share your game, make a backup, or start a new project",
+ value: GameMenu.FILE,
+ },
+ {
+ text: { id: "settings_tool_name", text: "settings" },
+ icon: "settings",
+ description: "tool, game, and export settings",
+ value: GameMenu.SETTINGS,
+ },
+ {
+ text: { id: "data_tool_name", text: "data" },
+ icon: "game_data",
+ description: "view and edit bitsy game data",
+ value: GameMenu.DATA,
+ }
+ ],
+ onchange: function(e) {
+ curMenu = parseInt(e.target.value);
+ },
+ });
+ tool.menu.pop({ control: "group" });
+
+ if (curMenu === GameMenu.FILE) {
+ updateFileMenu();
+ }
+ else if (curMenu === GameMenu.SETTINGS) {
+ updateSettingsMenu();
+ }
+ else if (curMenu === GameMenu.DATA) {
+ updateDataMenu();
+ }
+ };
+
+ function updateFileMenu() {
+ tool.menu.push({
+ control: "group",
+ text: ".html", // todo : localize
+ icon: "file",
+ description: "html file format: browser playable game"
+ });
+ tool.menu.push({
+ control: "button",
+ text: { id: "download_game", text: "save game" },
+ icon: "download",
+ description: "save game as .html file",
+ onclick: exportGame
+ });
+ tool.menu.push({
+ control: "file",
+ id: "import_html",
+ text: { id: "upload_game", text: "load game" },
+ icon: "upload",
+ description: "load existing game from .html file",
+ accept: ".html",
+ onload: importGameFromFile
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({
+ control: "group",
+ text: ".bitsy", // todo : localize
+ icon: "game_data",
+ description: "bitsy file format: plaintext game data"
+ });
+ tool.menu.push({
+ control: "button",
+ text: { id: "download_data", text: "save data" },
+ icon: "download",
+ description: "save game data as .bitsy file",
+ onclick: exportGameData
+ });
+ tool.menu.push({
+ control: "file",
+ id: "import_data",
+ text: "load data", // todo : localize
+ icon: "upload",
+ description: "load game data from a .bitsy file",
+ accept: ".bitsy",
+ onload: importGameDataFromFile
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({
+ control: "button",
+ text: { id: "reset_game", text: "new game" },
+ icon: "new_game",
+ description: "reset everything and start new game",
+ onclick: newGameDialog
+ });
+ }
+
+ function updateSettingsMenu() {
+ updateToolSettings();
+ updateGameSettings();
+ updateExportSettings();
+ }
+
+ function updateDataMenu() {
+ // todo : not updating after all changes...
+ tool.menu.push({
+ control: "textarea",
+ description: "raw text of the bitsy game data file (be careful when editing)",
+ rows: 26,
+ value: serializeWorld(!showFontDataInGameData),
+ onchange: function(e) {
+ var gamedataChanged = e.target.value;
+ Store.set("game_data", gamedataChanged);
+
+ // TODO : is there a better way to handle the global editor update?
+ on_game_data_change();
+ }
+ });
+ }
+
+ function exportGame() {
+ // make sure game data is up to date
+ refreshGameData();
+ var gameData = serializeWorld();
+
+ // download as html file
+ exporter.exportGame(
+ gameData,
+ getTitle(),
+ exportSettings.pageColor,
+ filenameFromGameTitle() + ".html",
+ exportSettings.isFixedSize,
+ exportSettings.fixedSize);
+ }
+
+ function importGameFromFile(fileText) {
+ resetGameData();
+
+ var gamedataImported = exporter.importGame(fileText);
+ Store.set("game_data", gamedataImported);
+
+ // TODO : is there a better way to handle the global editor update?
+ on_game_data_change();
+ }
+
+ function exportGameData() {
+ refreshGameData();
+ var gamedataExported = serializeWorld();
+ ExporterUtils.DownloadFile(filenameFromGameTitle() + ".bitsy", gamedataExported);
+ }
+
+ function importGameDataFromFile(fileText) {
+ resetGameData();
+
+ var gamedataImported = fileText;
+ Store.set("game_data", gamedataImported);
+
+ on_game_data_change();
+ }
+
+ function newGameDialog() {
+ var resetMessage = localization.GetStringOrFallback("reset_game_message", "Starting a new game will erase your old data. Consider exporting your work first! Are you sure you want to start over?");
+ // todo : move the confirm dialog into tool.menu code?
+ if (confirm(resetMessage)) {
+ // todo : move into file.js
+ resetGameData();
+ }
+ }
+
+ function resetGameData() {
+ // todo : can this be moved into file.js?
+ setDefaultGameState();
+
+ // re-apply language settings
+ updateLanguage(localization.GetLanguage());
+
+ on_game_data_change();
+ }
+
+ function filenameFromGameTitle() {
+ var filename = getTitle().replace(/[^a-zA-Z]/g, "_"); // replace non alphabet characters
+ filename = filename.toLowerCase();
+ filename = filename.substring(0, 32); // keep it from getting too long
+ return filename;
+ }
+
+ function updateToolSettings() {
+ var languageOptions = [];
+ for (var i = 0; i < languageList.length; i++) {
+ languageOptions.push({
+ text: languageList[i].name,
+ description: languageList[i].id,
+ value: languageList[i].id,
+ });
+ }
+
+ tool.menu.push({
+ control: "group",
+ text: { id: "editor_settings", text: "tool settings" },
+ direction: "column",
+ expandable: true, // todo : is this redundant now that we have "open"?
+ open: isToolSettingsGroupOpen,
+ ontoggle: function(e) {
+ isToolSettingsGroupOpen = e.target.open;
+ }
+ });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({ control: "label", text: "language:" });
+ tool.menu.push({
+ control: "select",
+ name: "languageSelect",
+ value: localization.GetLanguage(),
+ options: languageOptions,
+ onchange: onChangeLanguage,
+ });
+
+ tool.menu.push({
+ control: "label",
+ text: { id: "language_translator_credit" }
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: "show font in game data?",
+ description: "font data visibility (does not change what is exported)"
+ });
+ tool.menu.push({
+ control: "toggle",
+ id: "font_data_toggle",
+ icon: showFontDataInGameData ? "visibility" : "visibility_off",
+ text: showFontDataInGameData ? "font data visible" : "font data hidden", // todo : localize
+ description: "show / hide font data (WARNING: can be very slow for large fonts)",
+ checked: showFontDataInGameData,
+ onclick: function(e) {
+ showFontDataInGameData = e.target.checked;
+ },
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.pop({ control: "group" });
+ }
+
+ function updateGameSettings() {
+ tool.menu.push({
+ control: "group",
+ text: { id: "game_settings", text: "game settings" },
+ direction: "column",
+ expandable: true,
+ open: isGameSettingsGroupOpen,
+ ontoggle: function(e) {
+ isGameSettingsGroupOpen = e.target.open;
+ }
+ });
+
+ var selectedFontValue = fontName; // NOTE: this is a global var from the engine!
+ var fontStorage = Store.get('custom_font', null);
+ var fontOptions = [
+ {
+ text: { id: "font_ascii_small", text: "ASCII Small" },
+ description: { id: "font_ascii_small_description", text: "Small font limited to ASCII, which includes English characters and some symbols." },
+ value: "ascii_small"
+ },
+ {
+ text: { id: "font_unicode_european_small", text: "Unicode European Small" },
+ description: { id: "font_unicode_european_small_description", text: "Small font with some unicode support. Includes characters for most European languages." },
+ value: "unicode_european_small"
+ },
+ {
+ text: { id: "font_unicode_european_large", text: "Unicode European Large" },
+ description: { id: "font_unicode_european_large_description", text: "Large font with more unicode support. Includes characters for all European languages." },
+ value: "unicode_european_large"
+ },
+ {
+ text: { id: "font_unicode_asian", text: "Unicode Asian" },
+ description: { id: "font_unicode_asian_description", text: "Large font which includes characters for Asian languages such as Chinese, Japanese, and Korean." },
+ value: "unicode_asian"
+ },
+ {
+ text: { id: "font_arabic", text: "Arabic" },
+ description: { id: "font_arabic_description", text: "Pixel font with Arabic characters" },
+ value: "arabic"
+ },
+ ];
+
+ if (fontStorage != null) {
+ var customFontOption = {
+ text: "Custom Font - " + fontStorage.name, // TODO : localize
+ description: { id: "font_custom_description", text: 'Custom font in the ".bitsyfont" format' },
+ value: "custom"
+ };
+
+ fontOptions.push(customFontOption);
+
+ if (fontName === fontStorage.name) {
+ selectedFontValue = "custom";
+ }
+ }
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: { id: "font_label", text: "font" }
+ });
+ tool.menu.push({
+ control: "select",
+ name: "fontSelect",
+ value: selectedFontValue,
+ options: fontOptions,
+ onchange: onChangeFont,
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({ control: "label", text: "font file" }); // todo : localize
+ tool.menu.push({
+ control: "button",
+ icon: "download",
+ text: { id: "download_font", text: "save font" },
+ description: "save the selected font as a .bitsyfont file",
+ onclick: function() {
+ exportFont();
+ }
+ });
+ tool.menu.push({
+ // todo : can i do this with a button instead??
+ control: "file",
+ id: "fontFilePicker",
+ accept: ".bitsyfont",
+ icon: "upload",
+ text: { id: "upload_font", text: "load font" },
+ description: "load a custom font from a .bitsyfont file",
+ onload: importFontFromFile,
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: { id: "text_direction_label", text: "text direction" },
+ description: "Option for languages that read right to left"
+ });
+ tool.menu.push({
+ control: "select",
+ name: "textDirectionSelect",
+ value: textDirection, // NOTE: this is a global var from the engine!
+ options: [
+ {
+ text: { id: "text_direction_ltr", text: "left to right" },
+ value: TextDirection.LeftToRight
+ },
+ {
+ text: { id: "text_direction_rtl", text: "right to left" },
+ value: TextDirection.RightToLeft
+ },
+ ],
+ onchange: function(e) {
+ updateEditorTextDirection(e.target.value);
+ textDirection = e.target.value;
+ refreshGameData();
+ }
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: { id: "settings_text_mode", text: "text mode" }
+ });
+ tool.menu.push({
+ control: "select",
+ name: "textModeSelect",
+ value: flags.TXT_MODE, // NOTE: this is a global var from the engine!
+ options: [
+ {
+ text: { id: "settings_text_hirez", text: "default" },
+ description: "2x pixel resolution",
+ value: bitsy.TXT_HIREZ
+ },
+ // todo : not sure I like "chunky"
+ {
+ text: { id: "settings_text_lorez", text: "chunky" },
+ description: "1x pixel resolution",
+ value: bitsy.TXT_LOREZ
+ },
+ ],
+ onchange: function(e) {
+ flags.TXT_MODE = parseInt(e.target.value);
+ refreshGameData();
+ }
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.pop({ control: "group" });
+ }
+
+ function updateExportSettings() {
+ tool.menu.push({
+ control: "group",
+ text: "export settings", // TODO : localize
+ direction: "column",
+ expandable: true,
+ open: isExportSettingsGroupOpen,
+ ontoggle: function(e) {
+ isExportSettingsGroupOpen = e.target.open;
+ }
+ });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: { id: "option_page_color", text: "html page color:" }
+ });
+ tool.menu.push({
+ control: "color",
+ value: exportSettings.pageColor,
+ onchange: function(e) {
+ exportSettings.pageColor = e.target.value;
+ Store.set("exportSettings", exportSettings);
+ },
+ });
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.push({ control: "group" });
+ tool.menu.push({
+ control: "label",
+ text: { id: "option_window_size", text: "game window size:" }
+ });
+ tool.menu.push({
+ control: "select",
+ name: "windowSizeSelect",
+ value: exportSettings.isFixedSize ? "fixed" : "full",
+ options: [
+ {
+ icon: "pagesize_full",
+ text: { id: "option_full_size", text: "full page" },
+ value: "full"
+ },
+ {
+ icon: "pagesize_fixed",
+ text: { id: "option_fixed_size", text: "fixed size" },
+ value: "fixed"
+ },
+ ],
+ onchange: function(e) {
+ exportSettings.isFixedSize = (e.target.value === "fixed");
+ Store.set("exportSettings", exportSettings);
+ },
+ });
+
+ if (exportSettings.isFixedSize) {
+ tool.menu.push({
+ control: "number",
+ value: exportSettings.fixedSize,
+ onchange: function(e) {
+ exportSettings.fixedSize = parseInt(e.target.value);
+ Store.set("exportSettings", exportSettings);
+ }
+ });
+ }
+ tool.menu.pop({ control: "group" });
+
+ tool.menu.pop({ control: "group" });
+ }
+
+ function onChangeLanguage(e) {
+ var language = e.target.value;
+ updateLanguage(language);
+ }
+
+ function updateLanguage(language) {
+ pickDefaultFontForLanguage(language);
+
+ localization.ChangeLanguage(language);
+
+ // update title in new language IF the user hasn't made any changes to the default title
+ if (localization.LocalizationContains("default_title", getTitle())) {
+ setTitle(localization.GetStringOrFallback("default_title", "Write your game's title here"));
+ }
+
+ // update default sprite
+ var defaultSpriteDlgExists = dialog["0"] != null && localization.LocalizationContains("default_sprite_dlg", dialog["0"]);
+ if (defaultSpriteDlgExists) {
+ dialog["0"] = {
+ src: localization.GetStringOrFallback("default_sprite_dlg", "I'm a cat"),
+ name: null,
+ };
+ }
+
+ // update default item
+ var defaultItemDlgExists = dialog["1"] != null && localization.LocalizationContains("default_item_dlg", dialog["1"]);
+ if (defaultItemDlgExists) {
+ dialog["1"] = {
+ src: localization.GetStringOrFallback("default_item_dlg", "You found a nice warm cup of tea"),
+ name: null,
+ };
+ }
+
+ // hacky global editor stuff
+ updateEditorLanguageStyle(language);
+ updateInventoryUI();
+ reloadDialogUI();
+ hackUpdatePlaceholderText();
+ hackUpdateEditorToolMenusOnLanguageChange();
+
+ refreshGameData();
+ }
+
+ function pickDefaultFontForLanguage(lang) {
+ if (lang === "en") {
+ switchFont("ascii_small", true /*doPickTextDirection*/);
+ }
+ else if (lang === "ar") {
+ switchFont("arabic", true /*doPickTextDirection*/);
+ }
+ else if (lang === "zh" || lang === "ja") {
+ switchFont("unicode_asian", true /*doPickTextDirection*/);
+ }
+ else {
+ switchFont("unicode_european_small", true /*doPickTextDirection*/);
+ }
+ }
+
+ function switchFont(newFontName, doPickTextDirection) {
+ if (doPickTextDirection === undefined || doPickTextDirection === null) {
+ doPickTextDirection = false;
+ }
+
+ fontName = newFontName;
+
+ if (doPickTextDirection) {
+ bitsyLog("PICK TEXT DIR", "editor");
+ pickDefaultTextDirectionForFont(newFontName);
+ }
+
+ refreshGameData();
+ }
+
+ function pickDefaultTextDirectionForFont(newFontName) {
+ var newTextDirection = TextDirection.LeftToRight;
+ if (newFontName === "arabic") {
+ newTextDirection = TextDirection.RightToLeft;
+ }
+ updateEditorTextDirection(newTextDirection);
+ textDirection = newTextDirection;
+ }
+
+ function onChangeFont(e) {
+ if (e.target.value != "custom") {
+ switchFont(e.target.value, true /*doPickTextDirection*/);
+ }
+ else {
+ var fontStorage = Store.get('custom_font', { name: 'ascii_small' });
+ switchFont(fontStorage.name, true /*doPickTextDirection*/);
+ }
+ }
+
+ function exportFont() {
+ var fontData = fontManager.GetData(fontName);
+ ExporterUtils.DownloadFile(fontName + ".bitsyfont", fontData);
+ }
+
+ function importFontFromFile(fileText) {
+ bitsyLog(fileText, "editor");
+
+ var customFontName = (fontManager.Create(fileText)).getName();
+
+ fontManager.AddResource(customFontName + fontManager.GetExtension(), fileText);
+ switchFont(customFontName); // bitsy engine setting
+
+ var fontStorage = {
+ name : customFontName,
+ fontdata : fileText
+ };
+ Store.set('custom_font', fontStorage);
+
+ refreshGameData();
+ }
+ });
+}
\ No newline at end of file
diff --git a/editor/style/bitsyEditorStyle.css b/editor/style/bitsyEditorStyle.css
index 3272a132..6c4d5a1b 100644
--- a/editor/style/bitsyEditorStyle.css
+++ b/editor/style/bitsyEditorStyle.css
@@ -94,6 +94,21 @@ label {
textarea {
font-family: monospace;
font-size: 100%;
+ background: black;
+ color: white;
+ resize: none;
+ border: none
+ padding: var(--bitsy-space-s);
+ border-radius: var(--bitsy-space-xs);
+ flex-grow: 1;
+ scrollbar-color: white black;
+ white-space: pre;
+ overflow-wrap: normal;
+ overflow-x: scroll;
+}
+
+.bitsy-menu textarea {
+ margin-bottom: var(--bitsy-space-l);
}
/* form */
@@ -107,7 +122,11 @@ input {
font-family: monospace;
}
-input + label {
+input[type=checkbox] + label {
+ background: var(--bitsy-color-accent-1);
+}
+
+input[type=radio] + label {
background: var(--bitsy-color-accent-1);
}
@@ -199,6 +218,11 @@ input[type=checkbox]:disabled + label {
opacity: 0.5;
}
+/* number */
+input[type=number] {
+ width: 4em;
+}
+
/* icons */
.bitsy_icon svg {
width: 1em;
@@ -247,18 +271,22 @@ input[type=checkbox]:disabled + label {
.bitsy-card-xs .bitsy-card-main {
width: var(--bitsy-size-xs);
+ min-width: var(--bitsy-size-xs);
}
.bitsy-card-s .bitsy-card-main {
width: var(--bitsy-size-s);
+ min-width: var(--bitsy-size-xs);
}
.bitsy-card-m .bitsy-card-main {
width: var(--bitsy-size-m);
+ min-width: var(--bitsy-size-xs);
}
.bitsy-card-l .bitsy-card-main {
width: var(--bitsy-size-l);
+ min-width: var(--bitsy-size-xs);
}
.bitsy-card-titlebar {
@@ -302,7 +330,7 @@ input[type=checkbox]:disabled + label {
}
.bitsy-card .bitsy-card-main {
- width: calc(100% - (2 * var(--bitsy-space-m)));
+ width: calc(100% - (2 * var(--bitsy-space-m))) !important;
}
.bitsy-card-main {
@@ -365,12 +393,16 @@ input[type=checkbox]:disabled + label {
/* menu */
.bitsy-menu {
- /* this should be the root of menus - do what with it? */
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
}
.bitsy-menu-group {
display: flex;
+ flex-direction: row;
flex-wrap: wrap;
+ align-items: center;
margin-bottom: var(--bitsy-space-s);
}
@@ -386,10 +418,22 @@ input[type=checkbox]:disabled + label {
opacity: 0.5;
}
+details.bitsy-menu-group {
+ margin-bottom: var(--bitsy-space-m);
+}
+
+details.bitsy-menu-group summary {
+ margin-bottom: var(--bitsy-space-s);
+}
+
.bitsy-menu-label .bitsy_icon {
margin-right: var(--bitsy-space-s);
}
+.bitsy-menu-header {
+ font-weight: bold;
+}
+
.bitsy-label-style-badge {
background: var(--bitsy-color-neutral-1);
color: var(--bitsy-color-neutral-2);
@@ -605,7 +649,11 @@ input[type=checkbox]:disabled + label {
color: var(--bitsy-color-main-1);
}
-.controlBox input + label {
+.controlBox input[type=checkbox] + label {
+ background: var(--bitsy-color-accent-2);
+}
+
+.controlBox input[type=radio] + label {
background: var(--bitsy-color-accent-2);
}
diff --git a/package.json b/package.json
index 19cd3c90..9a05186a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bitsy",
- "version": "8.10.0",
+ "version": "8.11.0",
"description": "make tiny games, worlds, and stories",
"author": "Adam Le Doux",
"devDependencies": {