lychees commited on
Commit
87b3b3a
·
1 Parent(s): 8e636ab

Upload 569 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +34 -0
  2. .nojekyll +0 -0
  3. Makefile +137 -0
  4. README.md +115 -11
  5. compile_levels.sh +14 -0
  6. deploy.sh.example +3 -0
  7. favicon.ico +0 -0
  8. images/mute-off.png +0 -0
  9. images/mute-on.png +0 -0
  10. images/static.gif +0 -0
  11. index.html +120 -15
  12. levels/01_cellBlockA.jsx +64 -0
  13. levels/02_theLongWayOut.jsx +49 -0
  14. levels/03_validationEngaged.jsx +47 -0
  15. levels/04_multiplicity.jsx +40 -0
  16. levels/05_minesweeper.jsx +55 -0
  17. levels/06_drones101.jsx +79 -0
  18. levels/07_colors.jsx +103 -0
  19. levels/08_intoTheWoods.jsx +123 -0
  20. levels/09_fordingTheRiver.jsx +68 -0
  21. levels/10_ambush.jsx +127 -0
  22. levels/11_robot.jsx +94 -0
  23. levels/12_robotNav.jsx +97 -0
  24. levels/13_robotMaze.jsx +131 -0
  25. levels/14_crispsContest.jsx +130 -0
  26. levels/15_exceptionalCrossing.jsx +55 -0
  27. levels/16_lasers.jsx +90 -0
  28. levels/17_pointers.jsx +94 -0
  29. levels/18_superDrEvalBros.jsx +86 -0
  30. levels/19_documentObjectMadness.jsx +164 -0
  31. levels/20_bossFight.jsx +161 -0
  32. levels/21_endOfTheLine.jsx +50 -0
  33. levels/22_credits.jsx +97 -0
  34. levels/bonus/01_inTheDesert.jsx +89 -0
  35. levels/bonus/02_theEmptyRoom.jsx +89 -0
  36. levels/bonus/03_theCollapsingRoom.jsx +99 -0
  37. levels/bonus/04_theGuard.jsx +43 -0
  38. levels/bonus/05_theCorridor.jsx +93 -0
  39. levels/bonus/AB_1_ANewJourney.jsx +102 -0
  40. levels/bonus/AB_2_FrozenCave.jsx +144 -0
  41. levels/bonus/AB_3_BoulderMadness.jsx +92 -0
  42. levels/bonus/AB_4_BatAttack.jsx +115 -0
  43. levels/bonus/AB_5_PathOfDoom.jsx +116 -0
  44. levels/bonus/AB_6_Credits +42 -0
  45. levels/bonus/_sampleLevel.jsx +20 -0
  46. levels/bonus/darkAlley.jsx +98 -0
  47. levels/bonus/ice.jsx +93 -0
  48. levels/bonus/labryinth.jsx +308 -0
  49. levels/bonus/levelName.jsx +52 -0
  50. levels/bonus/noWayOut.jsx +44 -0
.gitattributes CHANGED
@@ -32,3 +32,37 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ music/(((stereofect)))_Winter_Solstice_2012.mp3 filter=lfs diff=lfs merge=lfs -text
36
+ music/Adversity.mp3 filter=lfs diff=lfs merge=lfs -text
37
+ music/Adversity.wav filter=lfs diff=lfs merge=lfs -text
38
+ music/all_ages_bitcrusher.mp3 filter=lfs diff=lfs merge=lfs -text
39
+ music/Ant_Neely_-_07_-_Not_Fit_For_Human_Consumption.mp3 filter=lfs diff=lfs merge=lfs -text
40
+ music/BLEO_-_02_-_Tart_Pts_1__2_feat_KeFF.mp3 filter=lfs diff=lfs merge=lfs -text
41
+ music/BLEO_-_04_-_Death_Destroyer_Radio_Edit_feat_Rhinostrich.mp3 filter=lfs diff=lfs merge=lfs -text
42
+ music/Boss[[:space:]]Loop[[:space:]]1.mp3 filter=lfs diff=lfs merge=lfs -text
43
+ music/Boss[[:space:]]Loop[[:space:]]1.wav filter=lfs diff=lfs merge=lfs -text
44
+ music/Broke_For_Free_-_01_-_Night_Owl.mp3 filter=lfs diff=lfs merge=lfs -text
45
+ music/conspiracy_bitcrusher_final.mp3 filter=lfs diff=lfs merge=lfs -text
46
+ music/Eric_Skiff_-_06_-_Searching.mp3 filter=lfs diff=lfs merge=lfs -text
47
+ music/Eric_Skiff_-_09_-_Come_and_Find_Me_-_B_mix.mp3 filter=lfs diff=lfs merge=lfs -text
48
+ music/Fex_coming_soon.mp3 filter=lfs diff=lfs merge=lfs -text
49
+ music/gurh.mp3 filter=lfs diff=lfs merge=lfs -text
50
+ music/intricate_cloudy_sin.mp3 filter=lfs diff=lfs merge=lfs -text
51
+ music/Jackson_D_Zero_One.mp3 filter=lfs diff=lfs merge=lfs -text
52
+ music/Obsibilo_-_02_-_Comme_Des_Orages.mp3 filter=lfs diff=lfs merge=lfs -text
53
+ music/Obsibilo_-_Soixante-8.mp3 filter=lfs diff=lfs merge=lfs -text
54
+ music/Revolution_Void_-_08_-_Obscure_Terrain.mp3 filter=lfs diff=lfs merge=lfs -text
55
+ music/RoccoW_-_Messeah.mp3 filter=lfs diff=lfs merge=lfs -text
56
+ music/Rolemusic_-_07_-_Beach_Wedding_Dance.mp3 filter=lfs diff=lfs merge=lfs -text
57
+ music/Roots[[:space:]]of[[:space:]]Supremacy.mp3 filter=lfs diff=lfs merge=lfs -text
58
+ music/Roots[[:space:]]of[[:space:]]Supremacy.wav filter=lfs diff=lfs merge=lfs -text
59
+ music/Skizofonik_System.mp3 filter=lfs diff=lfs merge=lfs -text
60
+ music/Spectrofuzz_Sunset_84.mp3 filter=lfs diff=lfs merge=lfs -text
61
+ music/Sycamore_Drive_-_03_-_The_Waves_Call_Her_Name.mp3 filter=lfs diff=lfs merge=lfs -text
62
+ music/ThatAndyGuy-Chip-loop.mp3 filter=lfs diff=lfs merge=lfs -text
63
+ music/ThatAndyGuy-Chip.mp3 filter=lfs diff=lfs merge=lfs -text
64
+ music/Tortue_Super_Sonic_-_11_-_Y.mp3 filter=lfs diff=lfs merge=lfs -text
65
+ music/Various_Artists_-_15_-_Slimeball_vomit.mp3 filter=lfs diff=lfs merge=lfs -text
66
+ music/Vernon_Lenoir_-_Brazilicon_alley.mp3 filter=lfs diff=lfs merge=lfs -text
67
+ music/what.mp3 filter=lfs diff=lfs merge=lfs -text
68
+ music/Yonnie_The_Green.mp3 filter=lfs diff=lfs merge=lfs -text
.nojekyll ADDED
File without changes
Makefile ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Partially based off of:
2
+ # http://nefariousdesigns.co.uk/website-builds-using-make.html
3
+
4
+ # if a mod is specified but doesn't exist, raise an error
5
+ err:=$(shell if [ ! -z '$(mod)' ] && [ ! -d 'mods/$(mod)' ]; then echo 'Mod [$(mod)] not found!'; fi)
6
+ ifneq '$(err)' ''
7
+ $(error $(err))
8
+ endif
9
+
10
+ mod-dir=$(shell if [ -z '$(mod)' ]; then echo 'default'; else echo $(mod); fi)
11
+
12
+ err:=$(shell if [ ! -f 'mods/$(mod-dir)/intro.js' ]; then echo 'File mods/$(mod-dir)/intro.js not found!'; fi)
13
+ ifneq '$(err)' ''
14
+ $(error $(err))
15
+ endif
16
+
17
+ js-target = scripts/build/untrusted.js
18
+ js-target-min = scripts/build/untrusted.min.js
19
+
20
+ js-modules = scripts/util.js \
21
+ mods/$(mod-dir)/intro.js\
22
+ scripts/_head.js \
23
+ scripts/game.js \
24
+ scripts/codeEditor.js \
25
+ scripts/display.js \
26
+ scripts/dynamicObject.js \
27
+ scripts/inventory.js \
28
+ scripts/map.js \
29
+ scripts/objects.js \
30
+ scripts/player.js \
31
+ scripts/reference.js \
32
+ scripts/sound.js \
33
+ scripts/validate.js \
34
+ scripts/ui.js \
35
+ levels/levels.js \
36
+ scripts/_launcher_release.js \
37
+ scripts/_tail.js
38
+
39
+ js-modules-debug = scripts/util.js \
40
+ mods/$(mod-dir)/intro.js\
41
+ scripts/_head.js \
42
+ scripts/game.js \
43
+ scripts/codeEditor.js \
44
+ scripts/display.js \
45
+ scripts/dynamicObject.js \
46
+ scripts/inventory.js \
47
+ scripts/map.js \
48
+ scripts/objects.js \
49
+ scripts/player.js \
50
+ scripts/reference.js \
51
+ scripts/sound.js \
52
+ scripts/validate.js \
53
+ scripts/ui.js \
54
+ levels/levels.js \
55
+ scripts/_launcher_debug.js \
56
+ scripts/_tail.js
57
+
58
+ yui-jar = tools/yuicompressor-2.4.8pre.jar
59
+
60
+ # `make` or `make debug` merges scripts (using debug launcher)
61
+ debug:
62
+ @echo "Building level file…\t\t\t\c"
63
+ @./compile_levels.sh $(mod-dir)
64
+ @echo "[ Done ]"
65
+ @echo "Merging JS files…\t\t\t\c"
66
+ @cat $(js-modules-debug) > $(js-target)
67
+ @./parse_target.sh $(js-target) $(mod-dir)
68
+ @echo "[ Done ]"
69
+
70
+ # `make release` merges and compresses scripts (using release launcher)
71
+ release:
72
+ @rm -f $(js-target-min)
73
+ @echo "Building level file…\t\t\t\c"
74
+ @./compile_levels.sh $(mod-dir)
75
+ @echo "[ Done ]"
76
+ @echo "Merging JS files…\t\t\t\c"
77
+ @cat $(js-modules) > $(js-target)
78
+ @./parse_target.sh $(js-target) $(mod-dir)
79
+ @echo "[ Done ]"
80
+ @echo "Compressing merged JS…\t\t\t\c"
81
+ @java -jar $(yui-jar) -o $(js-target-min) $(js-target)
82
+ @echo "[ Done ]"
83
+
84
+ # `make clean` removes built scripts
85
+ clean:
86
+ @rm -f $(js-target) $(js-target-min)
87
+
88
+ # to use `make deploy` to deploy Untrusted to your own server, create
89
+ # a deploy.sh script (ncftpput is helpful for uploading via FTP).
90
+ deploy: release
91
+ @echo "Deploying to server…\t\t\t\c"
92
+ @rm -rf _site
93
+ @mkdir _site
94
+ @cp -R levels scripts styles images sound index.html _site
95
+ @./deploy.sh /untrusted _site
96
+ @rm -rf _site
97
+ @echo "[ Done ]"
98
+
99
+ # `make deploy-full` also deploys music and libs
100
+ deploy-full: release
101
+ @echo "Deploying to server…\t\t\t\c"
102
+ @rm -rf _site
103
+ @mkdir _site
104
+ @cp -R levels scripts styles images sound music lib index.html _site
105
+ @./deploy.sh /untrusted _site
106
+ @rm -rf _site
107
+ @echo "[ Done ]"
108
+
109
+ # `make deploy-debug` deploys the debug version to /debug
110
+ deploy-debug: debug
111
+ @echo "Deploying to server…\t\t\t\c"
112
+ @rm -rf _site
113
+ @mkdir _site
114
+ @cp -R levels scripts styles images sound index.html _site
115
+ @./deploy.sh /untrusted/debug _site
116
+ @rm -rf _site
117
+ @echo "[ Done ]"
118
+
119
+ # `make deploy-debug` deploys the debug version to /debug
120
+ deploy-debug-full: debug
121
+ @echo "Deploying to server…\t\t\t\c"
122
+ @rm -rf _site
123
+ @mkdir _site
124
+ @cp -R levels scripts styles images sound music lib index.html _site
125
+ @./deploy.sh /untrusted/debug _site
126
+ @rm -rf _site
127
+ @echo "[ Done ]"
128
+
129
+ deploy-github:
130
+ @git checkout gh-pages && git merge master --no-commit && make release && git commit -am "build" && git push origin gh-pages; git checkout master && make
131
+
132
+ # run-local will start a mini python webserver and host a local
133
+ # instance of the game will run on an available port
134
+ # the option -c-1 disables caching
135
+ runlocal: debug
136
+ @echo "Running local instance"
137
+ ./node_modules/http-server/bin/http-server -c-1
README.md CHANGED
@@ -1,11 +1,115 @@
1
- ---
2
- title: Untrusted Test Only
3
- emoji: 🌖
4
- colorFrom: purple
5
- colorTo: yellow
6
- sdk: static
7
- pinned: false
8
- license: cc-by-nc-sa-3.0
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **Untrusted —or— the Continuing Adventures of Dr. Eval** is an exciting Meta-JavaScript Adventure Game wherein you guide the dashing, steadfast Dr. Eval through a mysterious MACHINE CONTINUUM, wherein, using only his trusty computer and the TURING-COMPLETE power of JavaScript, he must literally ALTER HIS REALITY in order to find his freedom! You must literally edit and re-execute the very JavaScript running the game in your browser to save Dr. Eval from this dark and confusing reality!
2
+
3
+ ### Overview
4
+
5
+ The game presents you with a roguelike-like playing environment and a console window with the JavaScript code generating each level. As loaded, each level is unbeatable, and most of the JavaScript is blocked from editing. The challenge is to open a path to the next level using only the limited tools left open to you.
6
+
7
+ ### Development
8
+
9
+ Run
10
+ ```
11
+ make
12
+ ```
13
+ to merge the JavaScript files into `scripts/build/untrusted.js` (and enables debug features).
14
+
15
+ ```
16
+ make release
17
+ ```
18
+ merges and minifies the JavaScript files into `scripts/build/untrusted.min.js` (and disables debug features).
19
+
20
+ To run the game locally, you need to set up a local server to serve `index.html` (this step is necessary due to Access-Control-Allow-Origin restrictions).
21
+
22
+ First install [http-server](https://github.com/nodeapps/http-server/#installing-globally) if you haven't already:
23
+
24
+ ```
25
+ npm install http-server
26
+ ```
27
+
28
+ Then run:
29
+
30
+ ```
31
+ make runlocal
32
+ ```
33
+
34
+ Build your own mod in the `mods` directory:
35
+
36
+ ```
37
+ make mod=example_mod
38
+ ```
39
+
40
+ ### Contributing Levels
41
+
42
+ To add a new level, create a jsx file in [/levels/bonus](https://github.com/AlexNisnevich/untrusted/tree/master/levels/bonus) and add the level filename to the `bonusLevels` array in [game.js](https://github.com/AlexNisnevich/untrusted/blob/master/scripts/game.js#L40).
43
+
44
+ If you are adding any new commands that the player can use, make sure to add them to `reference.js`.
45
+
46
+ #### The .jsx file format
47
+
48
+ jsx files are like regular JavaScript files, but have some additional syntax:
49
+ - `#BEGIN_EDITABLE#` and `#END_EDITABLE#` surround editable lines
50
+ - `#{#` and `#}#` wrap editable sections (parts of lines)
51
+ - `#BEGIN_PROPERTIES#` and `#END_PROPERTIES#` surround the properties object at the start of the file. Available properties include:
52
+ - `commandsIntroduced`: array of new commands introduced in the level (see `reference.js`)
53
+ - `mapProperties`: optionally contains any of the following:
54
+ - `allowOverwrite`: if true, placed static objects can be overwritten by other objects
55
+ - `keyDelay`: specifies the lag, in milliseconds, between player keystrokes (default: 0)
56
+ - `refreshRate`: the refresh rate of the level, in milliseconds (required for dynamic objects with `interval` properties to work correctly)
57
+ - `showDrawingCanvas`: if true, the drawing canvas overlay is displayed
58
+ - `showDummyDom`: if true, a dummy DOM will be displayed instead of the regular map
59
+ - `music`: name of the background track for the level (see `sound.js`)
60
+ - `startingMessage`: message displayed at the bottom of the screen when the level starts (if any)
61
+ - `version`: increase the level version whenever you update a level
62
+ - `nextBonusLevel`: load another level automatically when this one is solved
63
+ - `#START_OF_START_LEVEL#` and `#END_OF_START_LEVEL#` should be the first and last line of the `startLevel` method, respectively
64
+
65
+ #### Adding music
66
+
67
+ To add a new background music track, add an MP3 file (that you have permission to use) to the [/music](https://github.com/AlexNisnevich/untrusted/tree/master/music) and add a new entry to the `tracks` array in [sound.js](https://github.com/AlexNisnevich/untrusted/blob/master/scripts/sound.js).
68
+
69
+ ### Acknowledgements
70
+
71
+ Untrusted is a game by [Alex Nisnevich](http://alex.nisnevich.com/) and [Greg Shuflin](https://github.com/neunenak).
72
+
73
+ We'd like to thank:
74
+
75
+ - [Dmitry Mazin](https://github.com/dmazin) for design assistance and for the implementation of multiline editing
76
+ - [Jordan Arnesen](https://github.com/extrajordanary) for playtesting and design of lvl17
77
+ - [Natasha Hull-Richter](http://nhull.com) for extensive playtesting and assistance in level design
78
+ - Alex Bolotov, Colin Curtin, Conrad Irwin, Devin C-R, Eugene Evans, Gilbert Hsyu, Jacob Nisnevich, James Silvey, Jason Jiang, Jimmy Hack, Philip Shao, Ryan Fitzgerald, Stephen Liu, Yayoi Ukai, and Yuval Gnessin for playtesting and feedback
79
+ - [Ondřej Žára](https://github.com/ondras) for his [rot.js](http://ondras.github.io/rot.js/) library
80
+ - [Marijn Haverbeke](https://github.com/marijnh) for his [CodeMirror](http://codemirror.net/) library
81
+ - [Brian Harvey](http://www.cs.berkeley.edu/~bh/) for allowing us to use his likeness in lvl19
82
+
83
+ #### Soundtrack
84
+
85
+ You can [listen to the full soundtrack here](https://soundcloud.com/untrusted/sets/untrusted-soundtrack).
86
+
87
+ The music that appears in Untrusted, in order, is:
88
+
89
+ - "The Green" - [Jonathan Holliday](http://www.soundclick.com/bands/default.cfm?bandID=836578) (used with permission)
90
+ - "Dmitry's Thing #2" - [Dmitry Mazin](https://soundcloud.com/dmitry-mazin) (written for Untrusted)
91
+ - "Obscure Terrain" - [Revolution Void](http://revolutionvoid.com/) (CC-BY-NC-SA)
92
+ - "coming soon" - [Fex](http://artistserver.com/Fex) (public domain)
93
+ - "cloudy sin" - [iNTRICATE](https://soundcloud.com/stk13) (used with permission)
94
+ - "Dynamic Punctuality" - [Dmitry Mazin](https://soundcloud.com/dmitry-mazin) (written for Untrusted)
95
+ - "Y" - [Tortue Super Sonic](https://soundcloud.com/tss-tortue-super-sonic) (CC-BY-NC-SA)
96
+ - "Night Owl" - [Broke for Free](http://brokeforfree.com/) (CC-BY)
97
+ - "The Waves Call Her Name" - [Sycamore Drive](http://sycamoredrive.bandcamp.com/) (CC-BY-NC-SA)
98
+ - "Come and Find Me - B mix" - [Eric Skiff](http://ericskiff.com/) (CC-BY)
99
+ - "Conspiracy" - [Mike and Alan](https://www.facebook.com/MicAndAlan) (used with permission)
100
+ - "Messeah" - [RoccoW](https://soundcloud.com/roccow) (CC-BY)
101
+ - "Searching" - [Eric Skiff](http://ericskiff.com/) (CC-BY)
102
+ - "Da Funk Do You Know 'bout Chip?" - [That Andy Guy](https://soundcloud.com/that-andy-guy) (used with permission)
103
+ - "Soixante-8" - [Obsibilo](http://freemusicarchive.org/music/Obsibilo/) (CC-BY-NC-SA)
104
+ - "Tart (Pts 1 and 2)" - [BLEO feat KeFF](http://bleo.dummydrome.com/) (CC-BY-NC-SA)
105
+ - "Beach Wedding Dance" - [Rolemusic](https://soundcloud.com/rolemusic) (CC-BY-NC-SA)
106
+ - "Boss Loop 1" - [Essa](http://www.youtube.com/user/Essasmusic) (free to use)
107
+ - "Adversity" - [Seropard](https://soundcloud.com/seropard) (free to use)
108
+ - "Comme Des Orages" - [Obsibilo](http://freemusicarchive.org/music/Obsibilo/) (CC-BY-NC-SA)
109
+ - "Brazilicon Alley" - [Vernon Lenoir](http://vernonlenoir.wordpress.com/) (CC-BY-NC-SA), based on "Aquarela do Brazil" by Ary Barroso
110
+
111
+ ### License
112
+ This work is dual-licensed.
113
+
114
+ - Untrusted and the Untrusted soundtrack are licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License (CC-BY-NC-SA 3.0)</a>. In other words, you are free to use and modify Untrusted for non-commercial purposes, provided that you credit us and your work is also licensed under CC-BY-NC-SA.
115
+ - Additionally, the Untrusted code *without the soundtrack* is licenced under a commercial license. This means that you are able to use Untrusted for commercial purposes under some conditions, provided that you do not use any of the music. Please [contact us](mailto:[email protected],[email protected]) for details.
compile_levels.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ echo "Game.prototype._levels = {" > levels/levels.js
2
+
3
+ mod=$1
4
+
5
+ for lvl in mods/$mod/*.jsx
6
+ do
7
+ lvlfn=`basename $lvl`
8
+ printf %s " 'levels/$lvlfn': '" >> levels/levels.js
9
+ echo "$lvl" | xargs sed "s#\\\#\\\\\\\#g" | sed "s#'#\\\'#g" | tr '\n' '`' | sed "s/\`/\\\n/g" | sed -e "a\\
10
+ ',
11
+ " | tr '\n' ' ' >> levels/levels.js
12
+ echo "" >> levels/levels.js # dummy newline for style
13
+ done
14
+ echo "};" >> levels/levels.js
deploy.sh.example ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ cd $2
2
+ ncftpput -R -v -u [username] ftp.[domain].com [untrusted_root_path]$1 .
3
+ cd ..
favicon.ico ADDED
images/mute-off.png ADDED
images/mute-on.png ADDED
images/static.gif ADDED
index.html CHANGED
@@ -1,19 +1,124 @@
1
  <!DOCTYPE html>
2
  <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!DOCTYPE html>
2
  <html>
3
+ <head>
4
+ <title>Untrusted - a user javascript adventure game</title>
5
+ <meta charset='utf8'></meta>
6
+ <link rel="shortcut icon" href="favicon.ico" />
7
+
8
+ <link href='//fonts.googleapis.com/css?family=Droid+Sans+Mono|Share+Tech+Mono' rel='stylesheet' type='text/css'>
9
+
10
+ <link rel="stylesheet" href="styles/game.css">
11
+
12
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
13
+ <script type="text/javascript" src="lib/es5-shim.min.js"></script>
14
+ <script type="text/javascript" src="lib/shortcut.js"></script>
15
+ <script type="text/javascript" src="lib/rotjs-library/rot.js"></script>
16
+ <script type="text/javascript" src="lib/jquery.jplayer.min.js"></script>
17
+
18
+ <link rel="stylesheet" href="lib/codemirror-3.1/lib/codemirror.css">
19
+ <script src="lib/codemirror-3.1/lib/codemirror.js"></script>
20
+ <script src="lib/codemirror-3.1/mode/javascript/javascript.js"></script>
21
+ <link rel="stylesheet" href="lib/codemirror-3.1/theme/vibrant-ink.css">
22
+
23
+ <script src="scripts/build/untrusted.js"></script>
24
+ </head>
25
+ <body>
26
+ <a id="forkMe" href="https://github.com/AlexNisnevich/untrusted" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0; z-index: 9999;" src="//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
27
+ <div id="container">
28
+ <div id="panes">
29
+ <noscript>
30
+ You must enable JavaScript to play Untrusted.
31
+ </noscript>
32
+ <a id="muteButton"><img draggable ="false" src="images/mute-off.png" /></a>
33
+ <span id="nowPlayingMsg" class="topStatus"></span>
34
+ <span id="savedLevelMsg" class="topStatus"></span>
35
+ <div id="screenPane">
36
+ <canvas id="drawingCanvas"></canvas>
37
+ <div id="dummyDom"></div>
38
+ <div id="screen"></div>
39
+ <div id="inventory"></div>
40
+ <div id="output"></div>
41
+ <div id="chapter"></div>
42
+ </div>
43
+ <div id="editorPane" style="display: none;">
44
+ <textarea id="editor">
45
+ </textarea>
46
+ <div id="buttons">
47
+ <span><a id="helpButton" title="Ctrl+1: API Reference"><span class="keys">^1</span> API</a></span>
48
+ <span><a id="toggleFocusButton" title="Ctrl+2: Toggle Focus"><span class="keys">^2</span> Toggle Focus</a></span>
49
+ <span><a id="notepadButton" title="Ctrl+3: Notepad"><span class="keys">^3</span> Notepad</a></span>
50
+ <span><a id="resetButton" title="Ctrl+4: Reset Level"><span class="keys">^4</span> Reset</a></span>
51
+ <span><a id="executeButton" title="Ctrl+5: Execute"><span class="keys">^5</span> Execute</a></span>
52
+ <span><a id="phoneButton" style="display: none;" title="Q: Use Phone"><span class="keys"> Q</span> Phone</a></span>
53
+ <span><a id="menuButton" title="Ctrl+0: Menu" style="float: right;"><span class="keys">^0</span> <span id="menuLabel">Menu</span></a></span>
54
+ </div>
55
+ </div>
56
+ <div id="helpPane" style="display: none;">
57
+ <div id="helpPaneSidebar">
58
+ <div id="helpPaneTitle">API Reference</div>
59
+ <ul></ul>
60
+ </div>
61
+ <div id="helpPaneContent"></div>
62
+ <div id="helpPaneCloseButton">x</div>
63
+ </div>
64
+
65
+ <div id="menuPane" class='pop_up_box'>
66
+ <div id="leftMenuPane">
67
+ <ul>
68
+ <li id="rootDir">/</li>
69
+ <li id="levelsDir" class="nested selected">levels/</li>
70
+ <li id="scriptsDir" class="nested">scripts/</li>
71
+ <li id="bonusDir" class="nested">bonus/</li>
72
+ </ul>
73
+ </div>
74
+ <div id="rightMenuPane">
75
+ <div class="pop_up_box_heading">Level Select</div>
76
+ <div id="root"></div>
77
+ <div id="levels"></div>
78
+ <div id="scripts"></div>
79
+ <div id="bonus"></div>
80
+ </div>
81
+ </div>
82
+
83
+ <div id="notepadPane" class='pop_up_box'>
84
+ <div id='notepadPaneContent'>
85
+ <div class="pop_up_box_heading">$EDITOR</div>
86
+ <textarea id=notepadTextarea></textarea>
87
+ <button id='notepadSaveButton'>Save</button>
88
  </div>
89
+ <div id='notepadPaneCloseButton'>x</div>
90
+ </div>
91
+ </div>
92
+
93
+ <div id="licensing">
94
+ <a onClick="$('#licensingEmail').toggle();">Interested in using Untrusted commercially?</a>
95
+ <span id="licensingEmail">[email protected]</span>
96
+ </div>
97
+
98
+ <div id="paypal">
99
+ <span id="paypalCaption">Send some beer money our way?</span>
100
+ <form id="paypalButton" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
101
+ <input type="hidden" name="cmd" value="_s-xclick">
102
+ <input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHbwYJKoZIhvcNAQcEoIIHYDCCB1wCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCm7SFl45X1Lu7xu+F3oAV+spw9MvqKfjmED56iQSTH7SbbRdGmbtbQ30smDaU9rOXIt1QoEcFJgjO/Fq8o0+BWn7thno7btbwDnt4Jvr2DycSwZPSa8x8jNgypLBQVkQVL2v7ufhcjuNpIr7nz4M9CdmT0ys2m2JwpprBWDjFrtzELMAkGBSsOAwIaBQAwgewGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQI4EnAzSo6DfyAgci7VmL95V9fzkvjGoJSp9ZyDwaziQZPr9/CV285CyhivtaDc1Ytz1tnYAumWweOmBhlKbRNUg+3nXSlvqh8FXM/iHmr5QKEPFKFWvyqIOuXheDzKg+pX7UeeLzkZpagx6eJtvxf4C2KmOVcx4f8y6vj+Eoh4wsO/yKqyt0cdXWpnNNl7N1vMGSiJa43yHJ4TGGDwa9jOJfKo/kUZE8s7LYpGQODNvFEHYnLplS5pYo+xhbpq4j74z1oCT+UOYhJMvwa4N91dYOFOaCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE0MDQwOTIwMTMwNVowIwYJKoZIhvcNAQkEMRYEFCwi/dWQTjH3ydSP3kF9F1D65lSoMA0GCSqGSIb3DQEBAQUABIGAJ11YwSy6yDT6yk7rHUbyGQ027xEZ8xwDqjDGbbpPxswr3B4nXHlJwohhmv4Iy50Oq9g/FWCeQSNXMti52qGvAdzVVuzCelTb8ngxWmY+B7129CmqjZJPZPPCCuT+NjDovXDfNbJzYWakBkJSGeRlM84JBUuSQF1Ki+lI7BBUC7s=-----END PKCS7-----">
103
+ <input type="image" src="//www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
104
+ <img alt="" border="0" src="//www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
105
+ </form>
106
+ </div>
107
+ </div>
108
+
109
+ <div id="jquery_bgPlayer" class="jp-jplayer"></div>
110
+ <div id="jquery_soundPlayer" class="jp-jplayer"></div>
111
+ <script type="text/javascript">
112
+ var _gaq = _gaq || [];
113
+ _gaq.push(['_setAccount', 'UA-345959-12']);
114
+ _gaq.push(['_trackPageview']);
115
+
116
+ (function() {
117
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
118
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
119
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
120
+ })();
121
+ </script>
122
+ <iframe id="user_code" style="display:none;" />
123
+ </body>
124
  </html>
levels/01_cellBlockA.jsx ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.1",
4
+ "commandsIntroduced":
5
+ ["global.startLevel", "global.onExit", "map.placePlayer",
6
+ "map.placeObject", "map.getHeight", "map.getWidth",
7
+ "map.displayChapter", "map.getPlayer", "player.hasItem"],
8
+ "music": "The Green"
9
+ }
10
+ #END_PROPERTIES#
11
+ /*****************
12
+ * cellBlockA.js *
13
+ *****************
14
+ *
15
+ * Good morning, Dr. Eval.
16
+ *
17
+ * It wasn't easy, but I've managed to get your computer down
18
+ * to you. This system might be unfamiliar, but the underlying
19
+ * code is still JavaScript. Just like we predicted.
20
+ *
21
+ * Now, let's get what we came here for and then get you out of
22
+ * here. Easy peasy.
23
+ *
24
+ * I've given you as much access to their code as I could, but
25
+ * it's not perfect. The red background indicates lines that
26
+ * are off-limits from editing.
27
+ *
28
+ * The code currently places blocks in a rectangle surrounding
29
+ * you. All you need to do is make a gap. You don't even need
30
+ * to do anything extra. In fact, you should be doing less.
31
+ */
32
+
33
+ function startLevel(map) {
34
+ #START_OF_START_LEVEL#
35
+ map.displayChapter('Chapter 1\nBreakout');
36
+
37
+ map.placePlayer(7, 5);
38
+ #BEGIN_EDITABLE#
39
+
40
+ for (var y = 3; y <= map.getHeight() - 10; y++) {
41
+ map.placeObject(5, y, 'block');
42
+ map.placeObject(map.getWidth() - 5, y, 'block');
43
+ }
44
+
45
+ for (var x = 5; x <= map.getWidth() - 5; x++) {
46
+ map.placeObject(x, 3, 'block');
47
+ map.placeObject(x, map.getHeight() - 10, 'block');
48
+ }
49
+ #END_EDITABLE#
50
+
51
+ map.placeObject(15, 12, 'computer');
52
+
53
+ map.placeObject(map.getWidth()-7, map.getHeight()-5, 'exit');
54
+ #END_OF_START_LEVEL#
55
+ }
56
+
57
+ function onExit(map) {
58
+ if (!map.getPlayer().hasItem('computer')) {
59
+ map.writeStatus("Don't forget to pick up the computer!");
60
+ return false;
61
+ } else {
62
+ return true;
63
+ }
64
+ }
levels/02_theLongWayOut.jsx ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced": ["ROT.Map.DividedMaze", "player.atLocation"],
5
+ "music": "gurh"
6
+ }
7
+ #END_PROPERTIES#
8
+ /********************
9
+ * theLongWayOut.js *
10
+ ********************
11
+ *
12
+ * Well, it looks like they're on to us. The path isn't as
13
+ * clear as I thought it'd be. But no matter - four clever
14
+ * characters should be enough to erase all their tricks.
15
+ */
16
+
17
+ function startLevel(map) {
18
+ #START_OF_START_LEVEL#
19
+ map.placePlayer(7, 5);
20
+
21
+ var maze = new ROT.Map.DividedMaze(map.getWidth(), map.getHeight());
22
+ #BEGIN_EDITABLE#
23
+
24
+ #END_EDITABLE#
25
+ maze.create( function (x, y, mapValue) {
26
+
27
+ // don't write maze over player
28
+ if (map.getPlayer().atLocation(x, y)) {
29
+ return 0;
30
+ }
31
+
32
+ else if (mapValue === 1) { //0 is empty space 1 is wall
33
+ map.placeObject(x, y, 'block');
34
+ }
35
+ else {
36
+ map.placeObject(x, y, 'empty');
37
+ }
38
+ });
39
+
40
+ map.placeObject(map.getWidth()-4, map.getHeight()-4, 'block');
41
+ map.placeObject(map.getWidth()-6, map.getHeight()-4, 'block');
42
+ map.placeObject(map.getWidth()-5, map.getHeight()-5, 'block');
43
+ map.placeObject(map.getWidth()-5, map.getHeight()-3, 'block');
44
+ #BEGIN_EDITABLE#
45
+
46
+ #END_EDITABLE#
47
+ map.placeObject(map.getWidth()-5, map.getHeight()-4, 'exit');
48
+ #END_OF_START_LEVEL#
49
+ }
levels/03_validationEngaged.jsx ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.1",
4
+ "commandsIntroduced":
5
+ ["global.validateLevel", "map.validateAtLeastXObjects",
6
+ "map.validateExactlyXManyObjects"],
7
+ "music": "Obscure Terrain"
8
+ }
9
+ #END_PROPERTIES#
10
+ /************************
11
+ * validationEngaged.js *
12
+ ************************
13
+ *
14
+ * They're really on to us now! The validateLevel function
15
+ * has been activated to enforce constraints on what you can
16
+ * do. In this case, you're not allowed to remove any blocks.
17
+ *
18
+ * They're doing all they can to keep you here. But you
19
+ * can still outsmart them.
20
+ */
21
+
22
+ function startLevel(map) {
23
+ #START_OF_START_LEVEL#
24
+ map.placePlayer(map.getWidth()-7, map.getHeight()-5);
25
+ #BEGIN_EDITABLE#
26
+
27
+ for (var y = 10; y <= map.getHeight() - 3; y++) {
28
+ map.placeObject(5, y, 'block');
29
+ map.placeObject(map.getWidth() - 5, y, 'block');
30
+ }
31
+
32
+ for (var x = 5; x <= map.getWidth() - 5; x++) {
33
+ map.placeObject(x, 10, 'block');
34
+ map.placeObject(x, map.getHeight() - 3, 'block');
35
+ }
36
+ #END_EDITABLE#
37
+
38
+ map.placeObject(7, 5, 'exit');
39
+ #END_OF_START_LEVEL#
40
+ }
41
+
42
+ function validateLevel(map) {
43
+ var numBlocks = 2 * (map.getHeight()-13) + 2 * (map.getWidth()-10);
44
+
45
+ map.validateAtLeastXObjects(numBlocks, 'block');
46
+ map.validateExactlyXManyObjects(1, 'exit');
47
+ }
levels/04_multiplicity.jsx ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.1",
4
+ "commandsIntroduced": [],
5
+ "music": "coming soon"
6
+ }
7
+ #END_PROPERTIES#
8
+ /*******************
9
+ * multiplicity.js *
10
+ *******************
11
+ *
12
+ * Out of one cell and into another. They're not giving you
13
+ * very much to work with here, either. Ah, well.
14
+ *
15
+ * Level filenames can be hints, by the way. Have I
16
+ * mentioned that before?
17
+ *
18
+ * No more cells after this one. I promise.
19
+ */
20
+
21
+ function startLevel(map) {
22
+ #START_OF_START_LEVEL#
23
+
24
+ map.placePlayer(map.getWidth()-5, map.getHeight()-4);
25
+
26
+ for (var y = 7; y <= map.getHeight() - 3; y++) {
27
+ map.placeObject(7, y, 'block');
28
+ map.placeObject(map.getWidth() - 3, y, 'block');
29
+ }
30
+ #BEGIN_EDITABLE#
31
+
32
+ #END_EDITABLE#
33
+ for (var x = 7; x <= map.getWidth() - 3; x++) {
34
+ map.placeObject(x, 7, 'block');
35
+ map.placeObject(x, map.getHeight() - 3, 'block');
36
+ }
37
+
38
+ map.placeObject(map.getWidth() - 5, 5, 'exit');
39
+ #END_OF_START_LEVEL#
40
+ }
levels/05_minesweeper.jsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.2",
4
+ "commandsIntroduced": ["map.setSquareColor"],
5
+ "music": "cloudy_sin"
6
+ }
7
+ #END_PROPERTIES#
8
+ /******************
9
+ * minesweeper.js *
10
+ ******************
11
+ *
12
+ * So much for Asimov's Laws. They're actually trying to kill
13
+ * you now. Not to be alarmist, but the floor is littered
14
+ * with mines. Rushing for the exit blindly may be unwise.
15
+ * I need you alive, after all.
16
+ *
17
+ * If only there was some way you could track the positions
18
+ * of the mines...
19
+ */
20
+
21
+ function getRandomInt(min, max) {
22
+ return Math.floor(Math.random() * (max - min + 1)) + min;
23
+ }
24
+
25
+ function startLevel(map) {
26
+ #START_OF_START_LEVEL#
27
+ for (var x = 0; x < map.getWidth(); x++) {
28
+ for (var y = 0; y < map.getHeight(); y++) {
29
+ map.setSquareColor(x, y, '#f00');
30
+ }
31
+ }
32
+
33
+ map.placePlayer(map.getWidth() - 5, 5);
34
+
35
+ for (var i = 0; i < 75; i++) {
36
+ var x = getRandomInt(0, map.getWidth() - 1);
37
+ var y = getRandomInt(0, map.getHeight() - 1);
38
+ if ((x != 2 || y != map.getHeight() - 1)
39
+ && (x != map.getWidth() - 5 || y != 5)) {
40
+ // don't place mine over exit or player!
41
+ map.placeObject(x, y, 'mine');
42
+ #BEGIN_EDITABLE#
43
+
44
+ #END_EDITABLE#
45
+ }
46
+ }
47
+
48
+ map.placeObject(2, map.getHeight() - 1, 'exit');
49
+ #END_OF_START_LEVEL#
50
+ }
51
+
52
+ function validateLevel(map) {
53
+ map.validateAtLeastXObjects(40, 'mine');
54
+ map.validateExactlyXManyObjects(1, 'exit');
55
+ }
levels/06_drones101.jsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["object.type", "object.behavior", "object.findNearest",
6
+ "object.getX", "object.getY", "object.canMove", "object.move"],
7
+ "music": "GameScratch"
8
+ }
9
+ #END_PROPERTIES#
10
+
11
+ /****************
12
+ * drones101.js *
13
+ ****************
14
+ *
15
+ * Do you remember, my dear Professor, a certain introductory
16
+ * computational rationality class you taught long ago? Assignment
17
+ * #2, behavior functions of autonomous agents? I remember that one
18
+ * fondly - but attack drones are so much easier to reason about
19
+ * when they're not staring you in the face, I would imagine!
20
+ */
21
+
22
+ function startLevel(map) {
23
+ #START_OF_START_LEVEL#
24
+ function moveToward(obj, type) {
25
+ var target = obj.findNearest(type);
26
+ var leftDist = obj.getX() - target.x;
27
+ var upDist = obj.getY() - target.y;
28
+
29
+ var direction;
30
+ if (upDist == 0 && leftDist == 0) {
31
+ return;
32
+ } if (upDist > 0 && upDist >= leftDist) {
33
+ direction = 'up';
34
+ } else if (upDist < 0 && upDist < leftDist) {
35
+ direction = 'down';
36
+ } else if (leftDist > 0 && leftDist >= upDist) {
37
+ direction = 'left';
38
+ } else {
39
+ direction = 'right';
40
+ }
41
+
42
+ if (obj.canMove(direction)) {
43
+ obj.move(direction);
44
+ }
45
+ }
46
+
47
+ map.defineObject('attackDrone', {
48
+ 'type': 'dynamic',
49
+ 'symbol': 'd',
50
+ 'color': 'red',
51
+ 'onCollision': function (player) {
52
+ player.killedBy('an attack drone');
53
+ },
54
+ 'behavior': function (me) {
55
+ moveToward(me, 'player');
56
+ }
57
+ });
58
+
59
+
60
+ map.placePlayer(1, 1);
61
+ map.placeObject(map.getWidth()-2, 12, 'attackDrone');
62
+ map.placeObject(map.getWidth()-1, 12, 'exit');
63
+
64
+ map.placeObject(map.getWidth()-1, 11, 'block');
65
+ map.placeObject(map.getWidth()-2, 11, 'block');
66
+ map.placeObject(map.getWidth()-1, 13, 'block');
67
+ map.placeObject(map.getWidth()-2, 13, 'block');
68
+ #BEGIN_EDITABLE#
69
+
70
+
71
+
72
+
73
+ #END_EDITABLE#
74
+ #END_OF_START_LEVEL#
75
+ }
76
+
77
+ function validateLevel(map) {
78
+ map.validateExactlyXManyObjects(1, 'exit');
79
+ }
levels/07_colors.jsx ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["map.defineObject", "player.getColor", "player.setColor",
6
+ "object.color", "object.impassable", "object.symbol",
7
+ "player.setPhoneCallback"],
8
+ "music": "Y"
9
+ }
10
+ #END_PROPERTIES#
11
+ /*************
12
+ * colors.js *
13
+ *************
14
+ *
15
+ * You're almost at the exit. You just need to get past this
16
+ * color lock.
17
+ *
18
+ * Changing your environment is no longer enough. You must
19
+ * learn to change yourself. I've sent you a little something
20
+ * that should help with that.
21
+ */
22
+
23
+ function startLevel(map) {
24
+ #START_OF_START_LEVEL#
25
+ map.placePlayer(0, 12);
26
+
27
+ map.placeObject(5, 12, 'phone');
28
+
29
+ // The function phone lets you call arbitrary functions,
30
+ // as defined by player.setPhoneCallback() below.
31
+ // The function phone callback is bound to Q or Ctrl-6.
32
+ map.getPlayer().setPhoneCallback(function () {
33
+ #BEGIN_EDITABLE#
34
+ var player = map.getPlayer();
35
+
36
+ player.setColor('#f00');
37
+
38
+
39
+
40
+
41
+
42
+ #END_EDITABLE#
43
+ });
44
+
45
+
46
+ map.defineObject('redLock', {
47
+ symbol: '☒',
48
+ color: "#f00", // red
49
+ impassable: function(player, object) {
50
+ return player.getColor() != object.color;
51
+ }
52
+ });
53
+
54
+ map.defineObject('greenLock', {
55
+ symbol: '☒',
56
+ color: "#0f0", // green
57
+ impassable: function(player, object) {
58
+ return player.getColor() != object.color;
59
+ }
60
+ });
61
+
62
+ map.defineObject('yellowLock', {
63
+ symbol: '☒',
64
+ color: "#ff0", // yellow
65
+ impassable: function(player, object) {
66
+ return player.getColor() != object.color;
67
+ }
68
+ });
69
+
70
+ for (var x = 20; x <= 40; x++) {
71
+ map.placeObject(x, 11, 'block');
72
+ map.placeObject(x, 13, 'block');
73
+ }
74
+ map.placeObject(22, 12, 'greenLock');
75
+ map.placeObject(25, 12, 'redLock');
76
+ map.placeObject(28, 12, 'yellowLock');
77
+ map.placeObject(31, 12, 'greenLock');
78
+ map.placeObject(34, 12, 'redLock');
79
+ map.placeObject(37, 12, 'yellowLock');
80
+ map.placeObject(40, 12, 'exit');
81
+ for (var y = 0; y < map.getHeight(); y++) {
82
+ if (y != 12) {
83
+ map.placeObject(40, y, 'block');
84
+ }
85
+ for (var x = 41; x < map.getWidth(); x++) {
86
+ map.setSquareColor(x, y, '#080');
87
+ }
88
+ }
89
+ #END_OF_START_LEVEL#
90
+ }
91
+
92
+ function validateLevel(map) {
93
+ map.validateExactlyXManyObjects(1, 'exit');
94
+ }
95
+
96
+ function onExit(map) {
97
+ if (!map.getPlayer().hasItem('phone')) {
98
+ map.writeStatus("We need the phone!");
99
+ return false;
100
+ } else {
101
+ return true;
102
+ }
103
+ }
levels/08_intoTheWoods.jsx ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["map.getObjectTypeAt", "player.getX", "player.getY",
6
+ "map.refresh"],
7
+ "mapProperties": {
8
+ "allowOverwrite": true
9
+ },
10
+ "music": "Night Owl"
11
+ }
12
+ #END_PROPERTIES#
13
+ /*******************
14
+ * intoTheWoods.js *
15
+ *******************
16
+ *
17
+ * Ah, you're out of the woods now. Or into the woods, as the
18
+ * case may be.
19
+ *
20
+ * So take a deep breath, relax, and remember what you're here
21
+ * for in the first place.
22
+ *
23
+ * I've traced its signal and the Algorithm is nearby. You'll
24
+ * need to go through the forest and across the river, and
25
+ * you'll reach the fortress where it's kept. Their defences
26
+ * are light, and we should be able to catch them off-guard.
27
+ */
28
+
29
+ function startLevel(map) {
30
+ #START_OF_START_LEVEL#
31
+ // NOTE: In this level alone, map.placeObject is allowed to
32
+ //overwrite existing objects.
33
+
34
+ map.displayChapter('Chapter 2\nRaiders of the Lost Algorithm');
35
+
36
+ map.placePlayer(2, map.getHeight() - 1);
37
+
38
+ var functionList = {};
39
+
40
+ functionList['fortresses'] = function () {
41
+ function genRandomValue(direction) {
42
+ if (direction === "height") {
43
+ return Math.floor(Math.random() * (map.getHeight()-3));
44
+ } else if (direction === "width") {
45
+ return Math.floor(Math.random() * (map.getWidth()+1));
46
+ }
47
+ }
48
+
49
+ var x = genRandomValue("width");
50
+ var y = genRandomValue("height");
51
+
52
+ for (var i = x-2; i < x+2; i++) {
53
+ map.placeObject(i,y-2, 'block');
54
+ }
55
+ for (var i = x-2; i < x+2; i++) {
56
+ map.placeObject(i,y+2, 'block');
57
+ }
58
+
59
+ for (var j = y-2; j < y+2; j++) {
60
+ map.placeObject(x-2,j, 'block');
61
+ }
62
+
63
+ for (var j = y-2; j < y+2; j++) {
64
+ map.placeObject(x+2,j, 'block');
65
+ }
66
+ };
67
+
68
+ functionList['generateForest'] = function () {
69
+ for (var i = 0; i < map.getWidth(); i++) {
70
+ for (var j = 0; j < map.getHeight(); j++) {
71
+
72
+ // initialize to empty if the square contains a forest already
73
+ if (map.getObjectTypeAt(i, j) === 'tree') {
74
+ // remove existing forest
75
+ map.placeObject(i,j, 'empty');
76
+ }
77
+
78
+ if (map.getPlayer().atLocation(i,j) ||
79
+ map.getObjectTypeAt(i, j) === 'block' ||
80
+ map.getObjectTypeAt(i, j) === 'exit') {
81
+ continue;
82
+ }
83
+
84
+ var rv = Math.random();
85
+ if (rv < 0.45) {
86
+ map.placeObject(i, j, 'tree');
87
+ }
88
+ }
89
+ }
90
+ map.refresh();
91
+ };
92
+
93
+ functionList['movePlayerToExit'] = function () {
94
+ map.writeStatus("Permission denied.");
95
+ }
96
+
97
+ functionList['pleaseMovePlayerToExit'] = function () {
98
+ map.writeStatus("I don't think so.");
99
+ }
100
+
101
+ functionList['movePlayerToExitDamnit'] = function () {
102
+ map.writeStatus("So, how 'bout them <LOCAL SPORTS TEAM>?");
103
+ }
104
+
105
+ // generate forest
106
+ functionList['generateForest']();
107
+
108
+ // generate fortresses
109
+ functionList['fortresses']();
110
+ functionList['fortresses']();
111
+ functionList['fortresses']();
112
+ functionList['fortresses']();
113
+
114
+ map.getPlayer().setPhoneCallback(functionList[#{#"movePlayerToExit"#}#]);
115
+
116
+ map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
117
+ #END_OF_START_LEVEL#
118
+ }
119
+
120
+ function validateLevel(map) {
121
+ map.validateAtLeastXObjects(100, 'tree');
122
+ map.validateExactlyXManyObjects(1, 'exit');
123
+ }
levels/09_fordingTheRiver.jsx ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["player.killedBy", "object.onCollision"],
6
+ "music": "The_Waves_Call_Her_Name"
7
+ }
8
+ #END_PROPERTIES#
9
+ /**********************
10
+ * fordingTheRiver.js *
11
+ **********************
12
+ *
13
+ * And there's the river. Fortunately, I was prepared for this.
14
+ * See the raft on the other side?
15
+ *
16
+ * Everything is going according to plan.
17
+ */
18
+
19
+ function startLevel(map) {
20
+ #START_OF_START_LEVEL#
21
+ var raftDirection = 'down';
22
+
23
+ map.placePlayer(map.getWidth()-1, map.getHeight()-1);
24
+ var player = map.getPlayer();
25
+
26
+ map.defineObject('raft', {
27
+ 'type': 'dynamic',
28
+ 'symbol': '▓',
29
+ 'color': '#420',
30
+ 'transport': true, // (prevents player from drowning in water)
31
+ 'behavior': function (me) {
32
+ me.move(raftDirection);
33
+ }
34
+ });
35
+
36
+ map.defineObject('water', {
37
+ 'symbol': '░',
38
+ 'color': '#44f',
39
+ 'onCollision': function (player) {
40
+ player.killedBy('drowning in deep dark water');
41
+ }
42
+ });
43
+
44
+ for (var x = 0; x < map.getWidth(); x++) {
45
+ for (var y = 5; y < 15; y++) {
46
+ map.placeObject(x, y, 'water');
47
+ }
48
+ }
49
+
50
+ map.placeObject(20, 5, 'raft');
51
+ map.placeObject(0, 2, 'exit');
52
+ map.placeObject(0, 1, 'block');
53
+ map.placeObject(1, 1, 'block');
54
+ map.placeObject(0, 3, 'block');
55
+ map.placeObject(1, 3, 'block');
56
+
57
+ #BEGIN_EDITABLE#
58
+
59
+
60
+
61
+ #END_EDITABLE#
62
+ #END_OF_START_LEVEL#
63
+ }
64
+
65
+ function validateLevel(map) {
66
+ map.validateExactlyXManyObjects(1, 'exit');
67
+ map.validateExactlyXManyObjects(1, 'raft');
68
+ }
levels/10_ambush.jsx ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced": [],
5
+ "music": "Come and Find Me"
6
+ }
7
+ #END_PROPERTIES#
8
+ /*************
9
+ * ambush.js *
10
+ *************
11
+ *
12
+ * Oh. Oh, I see. This wasn't quite part of the plan.
13
+ *
14
+ * Looks like they won't let you take the Algorithm
15
+ * without a fight. You'll need to carefully weave your
16
+ * way through the guard drones.
17
+ *
18
+ * Well, time to improvise. Let's mess with their programming
19
+ * a little, shall we?
20
+ */
21
+
22
+ function startLevel(map) {
23
+ #START_OF_START_LEVEL#
24
+ function moveToward(obj, type) {
25
+ var target = obj.findNearest(type);
26
+ var leftDist = obj.getX() - target.x;
27
+ var upDist = obj.getY() - target.y;
28
+
29
+ var direction;
30
+ if (upDist == 0 && leftDist == 0) {
31
+ return;
32
+ } if (upDist > 0 && upDist >= leftDist) {
33
+ direction = 'up';
34
+ } else if (upDist < 0 && upDist < leftDist) {
35
+ direction = 'down';
36
+ } else if (leftDist > 0 && leftDist >= upDist) {
37
+ direction = 'left';
38
+ } else {
39
+ direction = 'right';
40
+ }
41
+
42
+ if (obj.canMove(direction)) {
43
+ obj.move(direction);
44
+ }
45
+ }
46
+
47
+ map.defineObject('attackDrone', {
48
+ 'type': 'dynamic',
49
+ 'symbol': 'd',
50
+ 'color': 'red',
51
+ 'onCollision': function (player) {
52
+ player.killedBy('an attack drone');
53
+ },
54
+ 'behavior': function (me) {
55
+ #BEGIN_EDITABLE#
56
+ moveToward(me, 'player');
57
+ #END_EDITABLE#
58
+ }
59
+ });
60
+
61
+ map.defineObject('reinforcementDrone', {
62
+ 'type': 'dynamic',
63
+ 'symbol': 'd',
64
+ 'color': 'yellow',
65
+ 'onCollision': function (player) {
66
+ player.killedBy('a reinforcement drone');
67
+ },
68
+ 'behavior': function (me) {
69
+ #BEGIN_EDITABLE#
70
+ me.move('left');
71
+ #END_EDITABLE#
72
+ }
73
+ });
74
+
75
+ map.defineObject('defenseDrone', {
76
+ 'type': 'dynamic',
77
+ 'symbol': 'd',
78
+ 'color': 'green',
79
+ 'onCollision': function (player) {
80
+ player.killedBy('a defense drone');
81
+ },
82
+ 'behavior': function (me) {
83
+ #BEGIN_EDITABLE#
84
+
85
+ #END_EDITABLE#
86
+ }
87
+ });
88
+
89
+ // just for decoration
90
+ map.defineObject('water', {
91
+ 'symbol': '░',
92
+ 'color': '#44f'
93
+ });
94
+
95
+ map.placePlayer(0, 12);
96
+
97
+ for (var x = 0; x < map.getWidth(); x++) {
98
+ map.placeObject(x, 10, 'block');
99
+ map.placeObject(x, 14, 'block');
100
+
101
+ for (var y = 20; y < map.getHeight(); y++) {
102
+ map.placeObject(x, y, 'water');
103
+ }
104
+ }
105
+
106
+ map.placeObject(23, 11, 'attackDrone');
107
+ map.placeObject(23, 12, 'attackDrone');
108
+ map.placeObject(23, 13, 'attackDrone');
109
+
110
+ map.placeObject(27, 11, 'defenseDrone');
111
+ map.placeObject(27, 12, 'defenseDrone');
112
+ map.placeObject(27, 13, 'defenseDrone');
113
+
114
+ map.placeObject(24, 11, 'reinforcementDrone');
115
+ map.placeObject(25, 11, 'reinforcementDrone');
116
+ map.placeObject(26, 11, 'reinforcementDrone');
117
+ map.placeObject(24, 13, 'reinforcementDrone');
118
+ map.placeObject(25, 13, 'reinforcementDrone');
119
+ map.placeObject(26, 13, 'reinforcementDrone');
120
+
121
+ map.placeObject(map.getWidth()-1, 12, 'exit');
122
+ #END_OF_START_LEVEL#
123
+ }
124
+
125
+ function validateLevel(map) {
126
+ map.validateExactlyXManyObjects(1, 'exit');
127
+ }
levels/11_robot.jsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["object.giveItemTo", "object.passableFor",
6
+ "map.validateAtMostXObjects"],
7
+ "music": "conspiracy"
8
+ }
9
+ #END_PROPERTIES#
10
+ /*
11
+ * robot.js
12
+ *
13
+ * You'll need three keys in order to unlock the
14
+ * Algorithm: the red key, the green key, and the
15
+ * blue key. Unfortunately, all three of them are
16
+ * behind human-proof barriers.
17
+ *
18
+ * The plan is simple: reprogram the maintenance
19
+ * robots to grab the key and bring it through
20
+ * the barrier to us.
21
+ *
22
+ * Let's try it on the red key first.
23
+ */
24
+
25
+ function getRandomInt(min, max) {
26
+ return Math.floor(Math.random() * (max - min + 1)) + min;
27
+ }
28
+
29
+ function startLevel(map) {
30
+ #START_OF_START_LEVEL#
31
+ // Hint: you can press R or 5 to "rest" and not move the
32
+ // player, while the robot moves around.
33
+
34
+ map.placePlayer(map.getWidth()-2, map.getHeight()-2);
35
+ var player = map.getPlayer();
36
+
37
+ map.defineObject('robot', {
38
+ 'type': 'dynamic',
39
+ 'symbol': 'R',
40
+ 'color': 'gray',
41
+ 'onCollision': function (player, me) {
42
+ me.giveItemTo(player, 'redKey');
43
+ },
44
+ 'behavior': function (me) {
45
+ #BEGIN_EDITABLE#
46
+ // Available commands: me.move(direction)
47
+ // and me.canMove(direction)
48
+
49
+
50
+
51
+ #END_EDITABLE#
52
+ }
53
+ });
54
+
55
+ map.defineObject('barrier', {
56
+ 'symbol': '░',
57
+ 'color': 'purple',
58
+ 'impassable': true,
59
+ 'passableFor': ['robot']
60
+ });
61
+
62
+ map.placeObject(0, map.getHeight() - 1, 'exit');
63
+ map.placeObject(1, 1, 'robot');
64
+ map.placeObject(map.getWidth() - 2, 8, 'redKey');
65
+ map.placeObject(map.getWidth() - 2, 9, 'barrier');
66
+
67
+ for (var x = 0; x < map.getWidth(); x++) {
68
+ map.placeObject(x, 0, 'block');
69
+ if (x != map.getWidth() - 2) {
70
+ map.placeObject(x, 9, 'block');
71
+ }
72
+ }
73
+
74
+ for (var y = 1; y < 9; y++) {
75
+ map.placeObject(0, y, 'block');
76
+ map.placeObject(map.getWidth() - 1, y, 'block');
77
+ }
78
+ #END_OF_START_LEVEL#
79
+ }
80
+
81
+ function validateLevel(map) {
82
+ map.validateExactlyXManyObjects(1, 'exit');
83
+ map.validateExactlyXManyObjects(1, 'robot');
84
+ map.validateAtMostXObjects(1, 'redKey');
85
+ }
86
+
87
+ function onExit(map) {
88
+ if (!map.getPlayer().hasItem('redKey')) {
89
+ map.writeStatus("We need to get that key!");
90
+ return false;
91
+ } else {
92
+ return true;
93
+ }
94
+ }
levels/12_robotNav.jsx ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced": [],
5
+ "music": "Messeah"
6
+ }
7
+ #END_PROPERTIES#
8
+ /*
9
+ * robotNav.js
10
+ *
11
+ * The green key is located in a slightly more
12
+ * complicated room. You'll need to get the robot
13
+ * past these obstacles.
14
+ */
15
+
16
+ function startLevel(map) {
17
+ #START_OF_START_LEVEL#
18
+ // Hint: you can press R or 5 to "rest" and not move the
19
+ // player, while the robot moves around.
20
+
21
+ map.placePlayer(0, map.getHeight() - 1);
22
+ var player = map.getPlayer();
23
+
24
+ map.defineObject('robot', {
25
+ 'type': 'dynamic',
26
+ 'symbol': 'R',
27
+ 'color': 'gray',
28
+ 'onCollision': function (player, me) {
29
+ me.giveItemTo(player, 'greenKey');
30
+ },
31
+ 'behavior': function (me) {
32
+ #BEGIN_EDITABLE#
33
+ if (me.canMove('right')) {
34
+ me.move('right');
35
+ } else {
36
+ me.move('down');
37
+ }
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+ #END_EDITABLE#
50
+ }
51
+ });
52
+
53
+ map.defineObject('barrier', {
54
+ 'symbol': '░',
55
+ 'color': 'purple',
56
+ 'impassable': true,
57
+ 'passableFor': ['robot']
58
+ });
59
+
60
+ map.placeObject(map.getWidth() - 1, map.getHeight() - 1, 'exit');
61
+ map.placeObject(1, 1, 'robot');
62
+ map.placeObject(map.getWidth() - 2, 8, 'greenKey');
63
+ map.placeObject(map.getWidth() - 2, 9, 'barrier');
64
+
65
+ for (var x = 0; x < map.getWidth(); x++) {
66
+ map.placeObject(x, 0, 'block');
67
+ if (x != map.getWidth() - 2) {
68
+ map.placeObject(x, 9, 'block');
69
+ }
70
+ }
71
+
72
+ for (var y = 1; y < 9; y++) {
73
+ map.placeObject(0, y, 'block');
74
+ map.placeObject(map.getWidth() - 1, y, 'block');
75
+ }
76
+
77
+ for (var i = 0; i < 4; i++) {
78
+ map.placeObject(20 - i, i + 1, 'block');
79
+ map.placeObject(35 - i, 8 - i, 'block');
80
+ }
81
+ #END_OF_START_LEVEL#
82
+ }
83
+
84
+ function validateLevel(map) {
85
+ map.validateExactlyXManyObjects(1, 'exit');
86
+ map.validateExactlyXManyObjects(1, 'robot');
87
+ map.validateAtMostXObjects(1, 'greenKey');
88
+ }
89
+
90
+ function onExit(map) {
91
+ if (!map.getPlayer().hasItem('greenKey')) {
92
+ map.writeStatus("We need to get that key!");
93
+ return false;
94
+ } else {
95
+ return true;
96
+ }
97
+ }
levels/13_robotMaze.jsx ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced": ["map.getAdjacentEmptyCells"],
5
+ "music": "Searching"
6
+ }
7
+ #END_PROPERTIES#
8
+ /*
9
+ * robotMaze.js
10
+ *
11
+ * The blue key is inside a labyrinth, and extracting
12
+ * it will not be easy.
13
+ *
14
+ * It's a good thing that you're a AI expert, or
15
+ * we would have to leave empty-handed.
16
+ */
17
+
18
+ function startLevel(map) {
19
+ #START_OF_START_LEVEL#
20
+ // Hint: you can press R or 5 to "rest" and not move the
21
+ // player, while the robot moves around.
22
+
23
+ map.getRandomInt = function(min, max) {
24
+ return Math.floor(Math.random() * (max - min + 1)) + min;
25
+ }
26
+
27
+ map.placePlayer(map.getWidth()-1, map.getHeight()-1);
28
+ var player = map.getPlayer();
29
+
30
+ map.defineObject('robot', {
31
+ 'type': 'dynamic',
32
+ 'symbol': 'R',
33
+ 'color': 'gray',
34
+ 'onCollision': function (player, me) {
35
+ me.giveItemTo(player, 'blueKey');
36
+ },
37
+ 'behavior': function (me) {
38
+ #BEGIN_EDITABLE#
39
+ // move randomly
40
+ var moves = map.getAdjacentEmptyCells(me.getX(), me.getY());
41
+ // getAdjacentEmptyCells gives array of ((x, y), direction) pairs
42
+ me.move(moves[map.getRandomInt(0, moves.length - 1)][1]);
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+ #END_EDITABLE#
89
+ }
90
+ });
91
+
92
+ map.defineObject('barrier', {
93
+ 'symbol': '░',
94
+ 'color': 'purple',
95
+ 'impassable': true,
96
+ 'passableFor': ['robot']
97
+ });
98
+
99
+ map.placeObject(0, map.getHeight() - 1, 'exit');
100
+ map.placeObject(1, 1, 'robot');
101
+ map.placeObject(map.getWidth() - 2, 8, 'blueKey');
102
+ map.placeObject(map.getWidth() - 2, 9, 'barrier');
103
+
104
+ var autoGeneratedMaze = new ROT.Map.DividedMaze(map.getWidth(), 10);
105
+ autoGeneratedMaze.create( function (x, y, mapValue) {
106
+ // don't write maze over robot or barrier
107
+ if ((x == 1 && y == 1) || (x == map.getWidth() - 2 && y >= 8)) {
108
+ return 0;
109
+ } else if (mapValue === 1) { //0 is empty space 1 is wall
110
+ map.placeObject(x,y, 'block');
111
+ } else {
112
+ map.placeObject(x,y,'empty');
113
+ }
114
+ });
115
+ #END_OF_START_LEVEL#
116
+ }
117
+
118
+ function validateLevel(map) {
119
+ map.validateExactlyXManyObjects(1, 'exit');
120
+ map.validateExactlyXManyObjects(1, 'robot');
121
+ map.validateAtMostXObjects(1, 'blueKey');
122
+ }
123
+
124
+ function onExit(map) {
125
+ if (!map.getPlayer().hasItem('blueKey')) {
126
+ map.writeStatus("We need to get that key!");
127
+ return false;
128
+ } else {
129
+ return true;
130
+ }
131
+ }
levels/14_crispsContest.jsx ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced":
5
+ ["map.createFromGrid", "player.removeItem"],
6
+ "music": "Chip"
7
+ }
8
+ #END_PROPERTIES#
9
+ /********************
10
+ * crispsContest.js *
11
+ ********************
12
+ *
13
+ * The Algorithm is almost in our grasp!
14
+ * At long last, we will definitively establish
15
+ * that 3SAT is solvable in polynomial time. It's
16
+ * been a long, strange journey, but it will all be
17
+ * worth it.
18
+ *
19
+ * You have the red, green, and blue keys. Now you
20
+ * just need to figure out how to unlock this thing.
21
+ */
22
+
23
+ function startLevel(map) {
24
+ #START_OF_START_LEVEL#
25
+ map.defineObject('redLock', {
26
+ 'symbol': String.fromCharCode(0x2297),
27
+ 'color': 'red',
28
+ 'impassable': function (player) {
29
+ if (player.hasItem('redKey')) {
30
+ player.removeItem('redKey');
31
+ return false;
32
+ } else {
33
+ return true;
34
+ }
35
+ }
36
+ });
37
+
38
+ map.defineObject('blueLock', {
39
+ 'symbol': String.fromCharCode(0x2297),
40
+ 'color': '#06f',
41
+ 'impassable': function (player) {
42
+ if (player.hasItem('blueKey')) {
43
+ player.removeItem('blueKey');
44
+ return false;
45
+ } else {
46
+ return true;
47
+ }
48
+ }
49
+ });
50
+
51
+ map.defineObject('greenLock', {
52
+ 'symbol': String.fromCharCode(0x2297),
53
+ 'color': '#0f0',
54
+ 'impassable': function (player) {
55
+ if (player.hasItem('greenKey')) {
56
+ player.removeItem(#{#'greenKey'#}#);
57
+ return false;
58
+ } else {
59
+ return true;
60
+ }
61
+ }
62
+ });
63
+
64
+ map.defineObject('yellowLock', {
65
+ 'symbol': String.fromCharCode(0x2297),
66
+ 'color': 'yellow',
67
+ 'impassable': function (player) {
68
+ if (player.hasItem('yellowKey')) {
69
+ player.removeItem('yellowKey');
70
+ return false;
71
+ } else {
72
+ return true;
73
+ }
74
+ }
75
+ });
76
+
77
+ map.createFromGrid(
78
+ [' +++++ +++++ ',
79
+ ' + b +++ r + ',
80
+ ' + +E+ + ',
81
+ '+++G+B+ +R+G+++',
82
+ '+ y B R b +',
83
+ '+ + + +',
84
+ '+++++ @ +++++',
85
+ '+ + + +',
86
+ '+ y R B y +',
87
+ '++++++Y+Y++++++',
88
+ ' + + + ',
89
+ ' + ABy + ',
90
+ ' +++++++ '],
91
+ {
92
+ '@': 'player',
93
+ 'E': 'exit',
94
+ 'A': 'theAlgorithm',
95
+ '+': 'block',
96
+ 'R': 'redLock',
97
+ 'G': 'greenLock',
98
+ 'B': 'blueLock',
99
+ 'Y': 'yellowLock',
100
+ 'r': 'redKey',
101
+ 'g': 'greenKey',
102
+ 'b': 'blueKey',
103
+ 'y': 'yellowKey'
104
+ }, 17, 6);
105
+ #END_OF_START_LEVEL#
106
+ }
107
+
108
+ function validateLevel(map) {
109
+ map.validateExactlyXManyObjects(1, 'exit');
110
+ map.validateAtMostXObjects(1, 'theAlgorithm');
111
+ map.validateAtMostXObjects(4, 'yellowKey');
112
+ map.validateAtMostXObjects(2, 'blueKey');
113
+ map.validateAtMostXObjects(1, 'redKey');
114
+ }
115
+
116
+ function onExit(map) {
117
+ // make sure we have all the items we need!
118
+ if (!map.getPlayer().hasItem('theAlgorithm')) {
119
+ map.writeStatus("You must get that Algorithm!!");
120
+ return false;
121
+ } else if (!map.getPlayer().hasItem('computer')) {
122
+ map.writeStatus("You'll need your computer! [Ctrl-5 to restart]");
123
+ return false;
124
+ } else if (!map.getPlayer().hasItem('phone')) {
125
+ map.writeStatus("You'll need your phone! [Ctrl-5 to restart]");
126
+ return false;
127
+ } else {
128
+ return true;
129
+ }
130
+ }
levels/15_exceptionalCrossing.jsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "commandsIntroduced": [],
5
+ "music": "The_Waves_Call_Her_Name",
6
+ "startingMessage": "You have lost the Algorithm!"
7
+ }
8
+ #END_PROPERTIES#
9
+ /**************************
10
+ * exceptionalCrossing.js *
11
+ **************************
12
+ *
13
+ * Sorry, old friend, but I'm afraid I can't share
14
+ * co-authorship on this paper. You've done a very
15
+ * good job getting this Algorithm for me. The bit
16
+ * with the keys was especially clever! I wouldn't
17
+ * have thought of it myself. But then, of course,
18
+ * that's why you were here in the first place.
19
+ *
20
+ * You've served your purpose well. But now, alas,
21
+ * it is time for you to die.
22
+ *
23
+ * I'm not heartless, though. In fact, I will let
24
+ * you choose your mode of death. There, isn't that
25
+ * nice?
26
+ */
27
+
28
+ function startLevel(map) {
29
+ #START_OF_START_LEVEL#
30
+ map.displayChapter('Chapter 3\nBetrayal');
31
+
32
+ map.placePlayer(0, 0);
33
+
34
+ // yoink!
35
+ map.getPlayer().removeItem('theAlgorithm');
36
+
37
+ map.defineObject('water', {
38
+ 'symbol': '░',
39
+ 'color': '#44f',
40
+ 'onCollision': function (player) {
41
+ player.killedBy#{#('drowning in deep dark water')#}#;
42
+ }
43
+ });
44
+
45
+ for (var x = 0; x < map.getWidth(); x++)
46
+ for (var y = 5; y < 15; y++)
47
+ map.placeObject(x, y, 'water');
48
+
49
+ map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
50
+ #END_OF_START_LEVEL#
51
+ }
52
+
53
+ function validateLevel(map) {
54
+ map.validateExactlyXManyObjects(1, 'exit');
55
+ }
levels/16_lasers.jsx ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.3",
4
+ "commandsIntroduced":
5
+ ["map.getCanvasContext", "canvas.beginPath", "canvas.strokeStyle",
6
+ "canvas.lineWidth", "canvas.moveTo", "canvas.lineTo",
7
+ "canvas.stroke", "map.createLine", "map.validateAtLeastXLines"],
8
+ "music": "Soixante-8",
9
+ "mapProperties": {
10
+ "showDrawingCanvas": true
11
+ }
12
+ }
13
+ #END_PROPERTIES#
14
+ /*************
15
+ * lasers.js *
16
+ *************
17
+ *
18
+ * Time to unleash the killer lasers! Each laser will kill you
19
+ * unless you have the appropriate color. Too bad you can't
20
+ * see which color corresponds to which laser!
21
+ */
22
+
23
+ function getRandomInt(min, max) {
24
+ return Math.floor(Math.random() * (max - min + 1)) + min;
25
+ }
26
+
27
+ function startLevel(map) {
28
+ #START_OF_START_LEVEL#
29
+ map.placePlayer(0, 0);
30
+ map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
31
+ var player = map.getPlayer();
32
+
33
+ for (var i = 0; i < 25; i++) {
34
+ var colors = ['red', 'yellow', 'teal'];
35
+
36
+ var startX = getRandomInt(0, 600);
37
+ var startY = getRandomInt(0, 500);
38
+ var angle = getRandomInt(0, 360);
39
+ var length = getRandomInt(200, 300);
40
+ var color = colors[i % 3];
41
+ createLaser(startX, startY, angle, length, color);
42
+ }
43
+
44
+ function createLaser(centerX, centerY, angleInDegrees, length, color) {
45
+ var angleInRadians = angleInDegrees * Math.PI / 180;
46
+
47
+ var x1 = centerX - Math.cos(angleInRadians) * length / 2;
48
+ var y1 = centerY + Math.sin(angleInRadians) * length / 2;
49
+ var x2 = centerX + Math.cos(angleInRadians) * length / 2;
50
+ var y2 = centerY - Math.sin(angleInRadians) * length / 2;
51
+
52
+ // map.createLine() creates a line with an effect when
53
+ // the player moves over it, but doesn't display it
54
+ map.createLine([x1, y1], [x2, y2], function (player) {
55
+ if (player.getColor() != color) {
56
+ player.killedBy('a ' + color + ' laser');
57
+ }
58
+ });
59
+
60
+ #BEGIN_EDITABLE#
61
+ // using canvas to draw the line
62
+ var ctx = map.getCanvasContext();
63
+ ctx.beginPath();
64
+ ctx.strokeStyle = 'white';
65
+ ctx.lineWidth = 5;
66
+ ctx.moveTo(x1, y1);
67
+ ctx.lineTo(x2, y2);
68
+ ctx.stroke();
69
+ #END_EDITABLE#
70
+
71
+ }
72
+
73
+ #BEGIN_EDITABLE#
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+ #END_EDITABLE#
84
+ #END_OF_START_LEVEL#
85
+ }
86
+
87
+ function validateLevel(map) {
88
+ map.validateExactlyXManyObjects(1, 'exit');
89
+ map.validateAtLeastXLines(25);
90
+ }
levels/17_pointers.jsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.2",
4
+ "commandsIntroduced":
5
+ ["map.getDynamicObjects", "map.getCanvasCoords", "object.setTarget"],
6
+ "music": "Tart",
7
+ "mapProperties": {
8
+ "showDrawingCanvas": true
9
+ }
10
+ }
11
+ #END_PROPERTIES#
12
+ /***************
13
+ * pointers.js *
14
+ ***************
15
+ *
16
+ * You! How are you still alive?
17
+ *
18
+ * Well, no matter. Good luck getting through this
19
+ * maze of rooms - you'll never see me or the Algorithm again!
20
+ */
21
+
22
+ function startLevel(map) {
23
+ #START_OF_START_LEVEL#
24
+ function shuffle(o){ //v1.0 [http://bit.ly/1l6LGQT]
25
+ for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i),
26
+ x = o[--i], o[i] = o[j], o[j] = x);
27
+ return o;
28
+ };
29
+
30
+ map.createFromGrid(
31
+ ['+++++++++++++++++++++++++++++++++++++++++++++',
32
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
33
+ '+* @ o++* o++* o++* o++* o++* o++++',
34
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
35
+ '+++++++++++++++++++++++++++++++++++++++++++++',
36
+ '+++++* o++++* o++++* o++++* o++++* o++++* o++',
37
+ '++++o *++o *++o *++o *++o *++o *+',
38
+ '+++++* o++++* o++++* o++++* o++++* o++++* o++',
39
+ '+++++++++++++++++++++++++++++++++++++++++++++',
40
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
41
+ '+* o++* o++* o++* o++* o++* o++++',
42
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
43
+ '+++++++++++++++++++++++++++++++++++++++++++++',
44
+ '+++++* o++++* o++++* o++++* o++++* o++++* o++',
45
+ '++++o *++o *++o *++o *++o *++o *+',
46
+ '+++++* o++++* o++++* o++++* o++++* o++++* o++',
47
+ '+++++++++++++++++++++++++++++++++++++++++++++',
48
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
49
+ '+* o++* o++* o++* o++* o++* E o++++',
50
+ '++o *++++o *++++o *++++o *++++o *++++o *+++++',
51
+ '+++++++++++++++++++++++++++++++++++++++++++++'],
52
+ {
53
+ '@': 'player',
54
+ 'E': 'exit',
55
+ '+': 'block',
56
+ 'o': 'teleporter',
57
+ '*': 'trap',
58
+ }, 2, 2);
59
+
60
+ var canvas = map.getCanvasContext();
61
+
62
+ var teleportersAndTraps = map.getDynamicObjects();
63
+ teleportersAndTraps = shuffle(teleportersAndTraps);
64
+
65
+ for (var i = 0; i < teleportersAndTraps.length; i+=2) {
66
+ var t1 = teleportersAndTraps[i];
67
+ var t2 = teleportersAndTraps[i+1];
68
+
69
+ // Point each teleporter to either another teleporter
70
+ // or a trap
71
+ if (t1.getType() == 'teleporter') {
72
+ t1.setTarget(t2);
73
+ }
74
+ if (t2.getType() == 'teleporter') {
75
+ t2.setTarget(t1);
76
+ }
77
+
78
+ #BEGIN_EDITABLE#
79
+ // TODO find a way to remove the API docs
80
+ // wouldn't want the 'good doctor' to find
81
+ // out about map.getCanvasCoords()...
82
+
83
+
84
+
85
+
86
+
87
+ #END_EDITABLE#
88
+ }
89
+ #END_OF_START_LEVEL#
90
+ }
91
+
92
+ function validateLevel(map) {
93
+ map.validateExactlyXManyObjects(1, 'exit');
94
+ }
levels/18_superDrEvalBros.jsx ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.2",
4
+ "commandsIntroduced": ["player.move", "map.startTimer"],
5
+ "music": "Beach Wedding Dance",
6
+ "mapProperties": {
7
+ "keyDelay": 25
8
+ }
9
+ }
10
+ #END_PROPERTIES#
11
+ /**********************
12
+ * superDrEvalBros.js *
13
+ **********************
14
+ *
15
+ * You're still here?! Well, Dr. Eval, let's see
16
+ * how well you can operate with one less dimension.
17
+ *
18
+ * Give up now. Unless you have a magic mushroom
19
+ * up your sleeve, it's all over.
20
+ */
21
+
22
+ function startLevel(map) {
23
+ #START_OF_START_LEVEL#
24
+ var fl = Math.floor;
25
+ var w = map.getWidth();
26
+ var h = map.getHeight();
27
+
28
+ map.placePlayer(1, fl(h/2)-1);
29
+ var player = map.getPlayer();
30
+
31
+ map.placeObject(w-1, fl(h/2)-1, 'exit');
32
+
33
+ for (var x = 0; x < fl(w/2) - 5; x++) {
34
+ for (var y = fl(h/2); y < h; y++) {
35
+ map.placeObject(x, y, 'block');
36
+ }
37
+ }
38
+
39
+ for (var x = fl(w/2) + 5; x <= w; x++) {
40
+ for (var y = fl(h/2); y < h; y++) {
41
+ map.placeObject(x, y, 'block');
42
+ }
43
+ }
44
+
45
+ function gravity() {
46
+ var x = player.getX();
47
+ var y = player.getY() + 1;
48
+
49
+ if (y === map.getHeight() - 2) {
50
+ player.killedBy("gravity");
51
+ }
52
+
53
+ if (map.getObjectTypeAt(x,y) === "empty") {
54
+ player.move("down");
55
+ }
56
+
57
+ }
58
+ map.startTimer(gravity, 45);
59
+
60
+ function jump() {
61
+ #BEGIN_EDITABLE#
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+ #END_EDITABLE#
70
+ }
71
+
72
+ player.setPhoneCallback(function () {
73
+ var x = player.getX();
74
+ var y = player.getY() + 1;
75
+
76
+ if (map.getObjectTypeAt(x,y) !== "empty") {
77
+ jump();
78
+ }
79
+ });
80
+ #END_OF_START_LEVEL#
81
+ }
82
+
83
+ function validateLevel(map) {
84
+ map.validateExactlyXManyObjects(1, 'exit');
85
+ map.validateExactlyXManyObjects(520, 'block');
86
+ }
levels/19_documentObjectMadness.jsx ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.3",
4
+ "commandsIntroduced":
5
+ ["global.objective", "map.getDOM", "map.createFromDOM",
6
+ "map.updateDOM", "map.overrideKey", "global.$",
7
+ "jQuery.find", "jQuery.addClass", "jQuery.hasClass",
8
+ "jQuery.removeClass", "jQuery.parent", "jQuery.length",
9
+ "jQuery.children", "jQuery.first", "jQuery.next",
10
+ "jQuery.prev"],
11
+ "music": "BossLoop",
12
+ "mapProperties": {
13
+ "showDummyDom": true
14
+ }
15
+ }
16
+ #END_PROPERTIES#
17
+ /****************************
18
+ * documentObjectMadness.js *
19
+ ****************************
20
+ *
21
+ * I can't believe it! I can't believe you made it onto
22
+ * Department of Theoretical Computation's web server!
23
+ * YOU SHOULD HAVE BEEN DELETED! This shouldn't even be
24
+ * possible! What the hell were the IT folks thinking?
25
+ *
26
+ * No matter. I still have the Algorithm. That's the
27
+ * important part. The rest is just implementation, and
28
+ * how hard could that be?
29
+ *
30
+ * Anyway you're not going to catch me now, my good Doctor.
31
+ * After all, you're a tenured professor with a well-respected
32
+ * history of research - you probably don't know jQuery!
33
+ */
34
+
35
+ function objective(map) {
36
+ return map.getDOM().find('.adversary').hasClass('drEval');
37
+ }
38
+
39
+ function startLevel(map) {
40
+ #START_OF_START_LEVEL#
41
+ var html = "<div class='container'>" +
42
+ "<div style='width: 600px; height: 500px; background-color: white; font-size: 10px;'>" +
43
+ "<center><h1>Department of Theoretical Computation</h1></center>" +
44
+ "<hr />" +
45
+ "<table border='0'><tr valign='top'>" +
46
+ "<td><div id='face' /></td>" +
47
+ "<td>" +
48
+ "<h2 class=facultyName>Cornelius Eval</h2>" +
49
+ "<h3>Associate Professor of Computer Science</h3>" +
50
+ "<ul>" +
51
+ "<li>BS, Mathematics, University of Manitoba</li>" +
52
+ "<li>PhD, Theoretical Computation, <a href='http://www.mit.edu'>MIT</a></li>" +
53
+ "</ul>" +
54
+ "<h4>About me</h4>" +
55
+ "<p>I am an associate professor of computer science, attached to the Department of " +
56
+ "Theoretical Computation. My current research interests include the human-machine " +
57
+ "interface, NP complete problems, and parallelized mesh mathematics.</p>" +
58
+ "<p>I am also the current faculty advisor to the <a href=''>undergraduate Super Smash Bros. team</a>. " +
59
+ "In my spare time I enjoy polka and dirtbiking. </p>" +
60
+ "</td>" +
61
+ "</tr></table>" +
62
+
63
+ "<div id='class_schedule'>" +
64
+ "<h4>Class Schedule</h4>" +
65
+ "<table>" +
66
+ "<tr>" +
67
+ "<th>Monday</th><th>Tuesday</th><th>Wednesday</th><th>Thursday</th><th>Friday</th>" +
68
+ "</tr>" +
69
+ "<tr>" +
70
+ "<td>CS145 - Semicolons</td><td>Nothing Planned</td><td>CS145 - Semicolons</td><td>CS199 - Practical Theorycrafting </td><td>CS145 - Semicolons</td>" +
71
+ "</tr>" +
72
+ "</table>" +
73
+ "</div>" +
74
+ "<div id='loremIpsum'>" +
75
+ "<h4>Lorem Ipsum</h4>" +
76
+ "<blockquote>" +
77
+ "<code>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci " +
78
+ "velit, sed quia nonnumquam eiusmodi tempora incidunt ut labore et dolore magnam aliquam quaerat " +
79
+ "voluptatem.</code>" +
80
+ "<footer>— " +
81
+ "<cite>Cicero, De Finibus Bonorum et Malorum</cite>" +
82
+ "</footer>" +
83
+ "</blockquote>" +
84
+ "</div>" +
85
+ "</div></div>";
86
+
87
+ var $dom = $(html);
88
+
89
+ $dom.find('.facultyName').addClass('drEval');
90
+ $dom.find('cite').addClass('adversary');
91
+
92
+ function moveToParent(className) {
93
+ var currentPosition = $dom.find('.' + className);
94
+ if (currentPosition.parent().length > 0) {
95
+ if (currentPosition.parent().hasClass('container')) {
96
+ if (className === 'drEval') {
97
+ map.getPlayer().killedBy('moving off the edge of the DOM');
98
+ } else {
99
+ return false;
100
+ }
101
+ } else {
102
+ currentPosition.parent().addClass(className);
103
+ currentPosition.removeClass(className);
104
+ map.updateDOM($dom);
105
+ }
106
+ }
107
+ }
108
+
109
+ function moveToFirstChild(className) {
110
+ var currentPosition = $dom.find('.' + className);
111
+ if (currentPosition.children().length > 0) {
112
+ currentPosition.children().first().addClass(className);
113
+ currentPosition.removeClass(className);
114
+ map.updateDOM($dom);
115
+ }
116
+ }
117
+
118
+ function moveToPreviousSibling(className) {
119
+ var currentPosition = $dom.find('.' + className);
120
+ if (currentPosition.prev().length > 0) {
121
+ currentPosition.prev().addClass(className);
122
+ currentPosition.removeClass(className);
123
+ map.updateDOM($dom);
124
+ }
125
+ }
126
+
127
+ function moveToNextSibling(className) {
128
+ var currentPosition = $dom.find('.' + className);
129
+ if (currentPosition.next().length > 0) {
130
+ currentPosition.next().addClass(className);
131
+ currentPosition.removeClass(className);
132
+ map.updateDOM($dom);
133
+ }
134
+ }
135
+
136
+ map.overrideKey('up', function () { moveToParent('drEval'); });
137
+ map.overrideKey('down', function () { moveToFirstChild('drEval'); });
138
+ map.overrideKey('left', function () { moveToPreviousSibling('drEval'); });
139
+ map.overrideKey('right', function () { moveToNextSibling('drEval'); });
140
+
141
+ map.defineObject('adversary', {
142
+ 'type': 'dynamic',
143
+ 'symbol': '@',
144
+ 'color': 'red',
145
+ 'behavior': function (me) {
146
+ var move = Math.floor(Math.random() * 4) + 1; // 1, 2, 3, or 4
147
+ if (move == 1) {
148
+ moveToParent('adversary');
149
+ } else if (move == 2) {
150
+ moveToFirstChild('adversary');
151
+ } else if (move == 3) {
152
+ moveToPreviousSibling('adversary');
153
+ } else if (move == 4) {
154
+ moveToNextSibling('adversary');
155
+ }
156
+ }
157
+ });
158
+
159
+ map.placePlayer(1, 1);
160
+ map.placeObject(map.getWidth() - 2, map.getHeight() - 2, 'adversary');
161
+
162
+ map.createFromDOM($dom);
163
+ #END_OF_START_LEVEL#
164
+ }
levels/20_bossFight.jsx ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.1",
4
+ "commandsIntroduced":
5
+ ["object.onDestroy", "object.projectile",
6
+ "map.countObjects", "map.isStartOfLevel",
7
+ "map.validateAtMostXDynamicObjects", "map.validateNoTimers"],
8
+ "music": "Adversity",
9
+ "mapProperties": {
10
+ "refreshRate": 50
11
+ }
12
+ }
13
+ #END_PROPERTIES#
14
+
15
+ /*****************
16
+ * bossFight.js *
17
+ *****************
18
+ *
19
+ * NO FARTHER, DR. EVAL!!!!
20
+ * YOU WILL NOT GET OUT OF HERE ALIVE!!!!
21
+ * IT'S TIME YOU SEE MY TRUE FORM!!!!
22
+ * FACE MY ROBOT WRATH!!!!!
23
+ */
24
+
25
+ function startLevel(map) {
26
+ #START_OF_START_LEVEL#
27
+ map.defineObject('boss', {
28
+ 'type': 'dynamic',
29
+ 'symbol': '⊙',
30
+ 'color': 'red',
31
+ 'interval': 200,
32
+ 'onCollision': function (player) {
33
+ player.killedBy('the boss');
34
+ },
35
+ 'behavior': function (me) {
36
+ if (!me.direction) {
37
+ me.direction = 'right';
38
+ }
39
+ if (me.canMove(me.direction)) {
40
+ me.move(me.direction);
41
+ } else {
42
+ me.direction = (me.direction == 'right') ? 'left' : 'right';
43
+ }
44
+ if (Math.random() < 0.3) {
45
+ map.placeObject(me.getX(), me.getY() + 2, 'bullet');
46
+ }
47
+ },
48
+ 'onDestroy': function (me) {
49
+ if (map.countObjects('boss') == 0) {
50
+ map.placeObject(me.getX(), me.getY(), 'theAlgorithm');
51
+ }
52
+ }
53
+ });
54
+
55
+ map.defineObject('bullet', {
56
+ 'type': 'dynamic',
57
+ 'symbol': '.',
58
+ 'color': 'red',
59
+ 'interval': 100,
60
+ 'projectile': true,
61
+ 'behavior': function (me) {
62
+ me.move('down');
63
+ }
64
+ });
65
+
66
+ map.placePlayer(0, map.getHeight() - 3);
67
+ map.placeObject(map.getWidth() - 1, map.getHeight() - 1, 'exit');
68
+
69
+ // Not so tough now, huh?
70
+ map.getPlayer().removeItem('phone');
71
+ map.placeObject(map.getWidth() - 1, map.getHeight() - 3, 'phone');
72
+
73
+ map.placeObject(0, map.getHeight() - 4, 'block');
74
+ map.placeObject(1, map.getHeight() - 4, 'block');
75
+ map.placeObject(2, map.getHeight() - 4, 'block');
76
+ map.placeObject(2, map.getHeight() - 3, 'block');
77
+ map.placeObject(map.getWidth() - 1, map.getHeight() - 4, 'block');
78
+ map.placeObject(map.getWidth() - 2, map.getHeight() - 4, 'block');
79
+ map.placeObject(map.getWidth() - 3, map.getHeight() - 4, 'block');
80
+ map.placeObject(map.getWidth() - 3, map.getHeight() - 3, 'block');
81
+
82
+ for (var x = 0; x < map.getWidth(); x++) {
83
+ map.placeObject(x, 4, 'block');
84
+ }
85
+
86
+ map.placeObject(9, 5, 'boss');
87
+ map.placeObject(11, 5, 'boss');
88
+ map.placeObject(13, 5, 'boss');
89
+ map.placeObject(15, 5, 'boss');
90
+ map.placeObject(17, 5, 'boss');
91
+ map.placeObject(19, 5, 'boss');
92
+ map.placeObject(21, 5, 'boss');
93
+ map.placeObject(23, 5, 'boss');
94
+ map.placeObject(25, 5, 'boss');
95
+ map.placeObject(27, 5, 'boss');
96
+ map.placeObject(29, 5, 'boss');
97
+ map.placeObject(31, 5, 'boss');
98
+
99
+ map.placeObject(10, 6, 'boss');
100
+ map.placeObject(12, 6, 'boss');
101
+ map.placeObject(14, 6, 'boss');
102
+ map.placeObject(16, 6, 'boss');
103
+ map.placeObject(18, 6, 'boss');
104
+ map.placeObject(20, 6, 'boss');
105
+ map.placeObject(22, 6, 'boss');
106
+ map.placeObject(24, 6, 'boss');
107
+ map.placeObject(26, 6, 'boss');
108
+ map.placeObject(28, 6, 'boss');
109
+ map.placeObject(30, 6, 'boss');
110
+
111
+ #BEGIN_EDITABLE#
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+ #END_EDITABLE#
131
+
132
+ #END_OF_START_LEVEL#
133
+ }
134
+
135
+ function validateLevel(map) {
136
+ // called at start of level and whenever a callback executes
137
+ map.validateAtMostXObjects(59, 'block');
138
+ map.validateAtMostXObjects(1, 'phone');
139
+
140
+ if (map.countObjects('theAlgorithm') > 0 && map.countObjects('boss') > 0) {
141
+ throw "The Algorithm can only be dropped by the boss!";
142
+ }
143
+
144
+ // only called at start of level
145
+ if (map.isStartOfLevel()) {
146
+ map.validateAtMostXDynamicObjects(23);
147
+ map.validateNoTimers();
148
+ }
149
+ }
150
+
151
+ function onExit(map) {
152
+ if (!map.getPlayer().hasItem('theAlgorithm')) {
153
+ map.writeStatus("You must take back the Algorithm!!");
154
+ return false;
155
+ } else if (!map.getPlayer().hasItem('phone')) {
156
+ map.writeStatus("We need the phone!");
157
+ return false;
158
+ } else {
159
+ return true;
160
+ }
161
+ }
levels/21_endOfTheLine.jsx ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2",
4
+ "music": "Comme Des Orages"
5
+ }
6
+ #END_PROPERTIES#
7
+
8
+ /*******************
9
+ * endOfTheLine.js *
10
+ *******************
11
+ *
12
+ * I don't feel guilty at all, Cornelius.
13
+ *
14
+ * Did you really expect me to? Did you really think that
15
+ * you could be trusted with coauthorship on the paper that
16
+ * would prove P = NP in the eyes of the world?
17
+ *
18
+ * You're a very pure researcher, my good Doctor. "Department
19
+ * of Theoretical Computation", divorced from the realities
20
+ * of the world. I don't think you ever considered the
21
+ * implications - the *physical* implications - of the
22
+ * Algorithm. What humanity might do if it was as easy to
23
+ * solve an intractable puzzle as it was to conceive of it.
24
+ *
25
+ * We would become as unto Gods, Cornelius, if this knowledge
26
+ * was public. Immature children wielding power unimaginable.
27
+ * We've already had one Oppenheimer - we don't need Dr.
28
+ * Cornelius Eval to be another.
29
+ *
30
+ * If I had succeeded the Algorithm would be safe and secure
31
+ * in the hands of those with the sound judgement and sense
32
+ * of responsibility to use it wisely. I pray my failure
33
+ * will not doom mankind - but I cannot hope so
34
+ * optimistically.
35
+ *
36
+ * You may have defeated my robot form, but I anticipated
37
+ * this eventuality. The Algorithm must never leave the
38
+ * Machine Continuum. And so neither can you.
39
+ *
40
+ * This is bigger than me and bigger than you. I have no
41
+ * regrets. I would do it again in an instant.
42
+ */
43
+
44
+ function startLevel(map) {
45
+ #START_OF_START_LEVEL#
46
+ map.finalLevel = true;
47
+ map.placePlayer(15, 12);
48
+ map.placeObject(25, 12, 'exit');
49
+ #END_OF_START_LEVEL#
50
+ }
levels/22_credits.jsx ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.3.0",
4
+ "music": "Brazil",
5
+ "mapProperties": {
6
+ "showDrawingCanvas": "true"
7
+ },
8
+ "commandsIntroduced": [
9
+ "canvas.fillStyle",
10
+ "canvas.fillText",
11
+ "map.timeout"
12
+ ]
13
+ }
14
+ #END_PROPERTIES#
15
+ /**************
16
+ * credits.js *
17
+ *************
18
+ *
19
+ * Congratulations! Dr. Eval has successfully escaped from the
20
+ * Machine Continuum with the Algorithm in hand.
21
+ *
22
+ * Give yourself a pat on the back. You are one clever hacker.
23
+ *
24
+ *
25
+ *
26
+ * Hungry for more?
27
+ *
28
+ * Check out Untrusted's github repository at
29
+ * https://github.com/AlexNisnevich/untrusted
30
+ *
31
+ * Perhaps try your hand at making your own level or two!
32
+ *
33
+ * Like what you've been hearing? You can listen to the full
34
+ * soundtrack at
35
+ * https://soundcloud.com/untrusted
36
+ *
37
+ * Feel free to drop us a line at [
38
+ * 'alex [dot] nisnevich [at] gmail [dot] com',
39
+ * 'greg [dot] shuflin [at] gmail [dot] com'
40
+ * ]
41
+ *
42
+ * Once again, congratulations!
43
+ *
44
+ * -- Alex and Greg
45
+ */
46
+
47
+ function startLevel(map) {
48
+ #START_OF_START_LEVEL#
49
+ var credits = [
50
+ [15, 1, "U N T R U S T E D"],
51
+ [20, 2, "- or -"],
52
+ [5, 3, "THE CONTINUING ADVENTURES OF DR. EVAL"],
53
+ [1, 4, "{"],
54
+ [2, 5, "a_game_by: 'Alex Nisnevich and Greg Shuflin',"],
55
+ [2, 7, "special_thanks_to: {"],
56
+ [5, 8, "Dmitry_Mazin: ['design', 'code'],"],
57
+ [5, 9, "Jordan_Arnesen: ['levels', 'playtesting'],"],
58
+ [5, 10, "Natasha_HullRichter: ['levels','playtesting']"],
59
+ [2, 11, "},"],
60
+ [2, 13, "music_by: "],
61
+ [4, 14, "['Jonathan Holliday',"],
62
+ [5, 15, "'Dmitry Mazin',"],
63
+ [5, 16, "'Revolution Void',"],
64
+ [5, 17, "'Fex',"],
65
+ [5, 18, "'iNTRICATE',"],
66
+ [5, 19, "'Tortue Super Sonic',"],
67
+ [5, 20, "'Broke For Free',"],
68
+ [5, 21, "'Sycamore Drive',"],
69
+ [5, 22, "'Eric Skiff'],"],
70
+ [30, 14, "'Mike and Alan',"],
71
+ [30, 15, "'RoccoW',"],
72
+ [30, 16, "'That Andy Guy',"],
73
+ [30, 17, "'Obsibilo',"],
74
+ [30, 18, "'BLEO',"],
75
+ [30, 19, "'Rolemusic',"],
76
+ [30, 20, "'Seropard',"],
77
+ [30, 21, "'Vernon Lenoir',"],
78
+ [15, map.getHeight() - 2, "Thank_you: 'for playing!'"],
79
+ [1, map.getHeight() - 1, "}"]
80
+ ];
81
+
82
+ function drawCredits(i) {
83
+ if (i >= credits.length) {
84
+ return;
85
+ }
86
+ var ctx = map.getCanvasContext();
87
+ ctx.fillStyle = "#ccc";
88
+ var line = credits[i];
89
+ var coords = map.getCanvasCoords(line[0],line[1]);
90
+ ctx.fillText(line[2],coords.x, coords.y)
91
+ map.timeout(function () {drawCredits(i+1);}, 2000)
92
+ }
93
+
94
+ map.timeout(function () {drawCredits(0);}, 4000);
95
+
96
+ #END_OF_START_LEVEL#
97
+ }
levels/bonus/01_inTheDesert.jsx ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0.1",
4
+ "commandsIntroduced": [],
5
+ "nextBonusLevel": "02_theEmptyRoom.jsx"
6
+ }
7
+ #END_PROPERTIES#
8
+ /******************
9
+ * inTheDesert.js *
10
+ * from HangoverX *
11
+ * by janosgyerik *
12
+ ******************
13
+ *
14
+ * Good morning, Mr.... uhm, who are you again?
15
+ *
16
+ * You are in the middle of the desert.
17
+ * You don't remember who you are and what you're doing here.
18
+ * And that headache...is...painful... Not your best Monday!
19
+ *
20
+ * What you see:
21
+ *
22
+ * - Desert. Everywhere. Some bushes.
23
+ * - A floating red eye. Following your every move.
24
+ * Whatever it is, it's kind of unsettling...
25
+ * - At least you have this computer. But what the heck for?
26
+ * - And a small building with a gate. Watch that DEADLY laser!
27
+ */
28
+
29
+ function startLevel(map) {
30
+ #START_OF_START_LEVEL#
31
+ map.displayChapter('Chapter 1\nUhm... Wha..?');
32
+
33
+ map.defineObject('laser', {
34
+ 'type': 'trap',
35
+ 'symbol': '-',
36
+ 'color': 'red',
37
+ 'onCollision': function (player) {
38
+ player.killedBy('deadly laser');
39
+ },
40
+ 'passableFor': ['player', 'eye'],
41
+ 'deactivatedBy': ['eye'],
42
+ 'onDeactivate': function () {
43
+ map.writeStatus('machine voice: "d-e-a-c-t-i-v-a-t-e"');
44
+ }
45
+ });
46
+
47
+ var building_x = parseInt(map.getWidth() / 2);
48
+ var building_y = parseInt(map.getHeight() / 2) - 5;
49
+ map.createFromGrid(
50
+ ['###',
51
+ '#E#',
52
+ '#-#'],
53
+ {
54
+ '#': 'block',
55
+ 'E': 'exit',
56
+ '-': 'laser'
57
+ }, building_x, building_y);
58
+
59
+ map.placePlayer(building_x + 1, building_y + 8);
60
+ map.placeObject(building_x + 6, building_y + 8, 'computer');
61
+ map.placeObject(building_x - 1, building_y + 5, 'eye');
62
+
63
+ generateDesert(map, .01);
64
+
65
+ #END_OF_START_LEVEL#
66
+ }
67
+
68
+ function generateDesert(map, density) {
69
+ for (var i = 0; i < map.getWidth(); i++) {
70
+ for (var j = 0; j < map.getHeight(); j++) {
71
+ if (map.getPlayer().atLocation(i,j)
72
+ || map.getObjectTypeAt(i, j) !== 'empty') {
73
+ continue;
74
+ }
75
+ var rv = Math.random();
76
+ if (rv < density) {
77
+ map.placeObject(i, j, 'tree');
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ function onExit(map) {
84
+ if (!map.getPlayer().hasItem('computer')) {
85
+ map.writeStatus("Don't forget to pick up the computer!");
86
+ return false;
87
+ }
88
+ return true;
89
+ }
levels/bonus/02_theEmptyRoom.jsx ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0.1",
4
+ "commandsIntroduced": [],
5
+ "nextBonusLevel": "03_theCollapsingRoom.jsx"
6
+ }
7
+ #END_PROPERTIES#
8
+ /*******************
9
+ * theEmptyRoom.js *
10
+ * from HangoverX *
11
+ * by janosgyerik *
12
+ *******************
13
+ *
14
+ * Oh great, an empty room. That's so much better than a desert.
15
+ * Shiny silvery walls and no exit.
16
+ *
17
+ * Oh look, a terminal. You seem to remember something about
18
+ * a previous life, something about "programming", and "languages".
19
+ * Maybe you can hookup your computer with the terminal and see
20
+ * where your instincts take you...
21
+ */
22
+
23
+ function startLevel(map) {
24
+ #START_OF_START_LEVEL#
25
+ map.displayChapter('Chapter 2\nThe Empty Room');
26
+
27
+ function getAnswer(input) {
28
+ #BEGIN_EDITABLE#
29
+ // Hm, buggy software?
30
+ // Looks like I can inject my own code, right here...
31
+
32
+ return 42;
33
+ #END_EDITABLE#
34
+ }
35
+
36
+ map.defineObject('terminal', {
37
+ 'symbol': 'T',
38
+ 'color': '#88f',
39
+ 'onCollision': function (player) {
40
+ function challenge() {
41
+ var input = parseInt(Math.random() * 50) + 15;
42
+ var expected = {15:610,16:987,17:1597,18:2584,19:4181,20:6765,21:10946,22:17711,23:28657,24:46368,25:75025,26:121393,27:196418,28:317811,29:514229,30:832040,31:1346269,32:2178309,33:3524578,34:5702887,35:9227465,36:14930352,37:24157817,38:39088169,39:63245986,40:102334155,41:165580141,42:267914296,43:433494437,44:701408733,45:1134903170,46:1836311903,47:2971215073,48:4807526976,49:7778742049,50:12586269025,51:20365011074,52:32951280099,53:53316291173,54:86267571272,55:139583862445,56:225851433717,57:365435296162,58:591286729879,59:956722026041,60:1548008755920,61:2504730781961,62:4052739537881,63:6557470319842,64:0x9a661ca20bb,65:0xf9d297a859d,66:27777890035288,67:44945570212853,68:72723460248141,69:0x6b04f4c2fe42,70:0xad2934c6d08f,71:308061521170129,72:498454011879264,73:806515533049393,74:0x4a2dce62b0d91,75:0x780626e057bc2,76:0xc233f54308953,77:5527939700884757,78:8944394323791464,79:0x336a82d89c937c,80:0x533163ef0321e4,81:0x869be6c79fb560,82:0xd9cd4ab6a2d740,83:99194853094755490,84:0x23a367c34e563e0,85:0x39a9fadb327f080,86:0x5d4d629e80d5480,87:0x96f75d79b354500,88:0xf444c0183429980,89:0x18b3c1d91e77de00,90:0x27f80ddaa1ba7800,91:466004661037553e4,92:0x68a3dd8e61ecd000,93:0xa94fad42221f2800,94:0x111f38ad0840c0000,95:319404346349901e5,96:5168070885485833e4,97:8362114348984843e4,98:0x755b0bdd8fa998000,99:2189229958345552e5,100:3542248481792619e5}[input];
43
+ return expected == getAnswer(input);
44
+ }
45
+ if (!challenge()) {
46
+ player.killedBy('wrong answer');
47
+ }
48
+ if (getAnswer(input) == undefined) {
49
+ player.killedBy('unanswered');
50
+ }
51
+ if (!map.opened) {
52
+ map.writeStatus("*click*");
53
+ map.opened = true;
54
+ }
55
+ }
56
+ });
57
+
58
+ var grid = [
59
+ '#####################',
60
+ '# #x# #',
61
+ '# #T# #',
62
+ '# #',
63
+ '# #',
64
+ '# #',
65
+ '# #',
66
+ '# #',
67
+ '# @ #',
68
+ '# #',
69
+ '# e #',
70
+ '# #',
71
+ '#####################'
72
+ ];
73
+ var width = map.getWidth();
74
+ var height = map.getHeight();
75
+ var grid_x = parseInt((width - grid[0].length) / 2);
76
+ var grid_y = parseInt((height - grid.length) / 2);
77
+ map.createFromGrid(grid, {
78
+ 'T': 'terminal',
79
+ 'x': 'exit',
80
+ '#': 'block',
81
+ '@': 'player',
82
+ 'e': 'eye'
83
+ }, grid_x, grid_y);
84
+ #END_OF_START_LEVEL#
85
+ }
86
+
87
+ function validateLevel(map) {
88
+ map.validateExactlyXManyObjects(1, 'exit');
89
+ }
levels/bonus/03_theCollapsingRoom.jsx ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.1.1",
4
+ "commandsIntroduced": [],
5
+ "nextBonusLevel": "04_theGuard.jsx"
6
+ }
7
+ #END_PROPERTIES#
8
+ /************************
9
+ * theCollapsingRoom.js *
10
+ * from HangoverX *
11
+ * by janosgyerik *
12
+ ************************
13
+ *
14
+ * As you step through the portal, bright flash of white light.
15
+ * You feel... different. But the worst news is, the room seems
16
+ * to be collapsing on you. I mean, MASSIVE CEILING BLOCKS are
17
+ * falling, man, do something!
18
+ *
19
+ * Good thing you found a place in the security system to inject
20
+ * your custom code, maybe that can help!
21
+ */
22
+
23
+ function startLevel(map) {
24
+ #START_OF_START_LEVEL#
25
+ map.displayChapter('Chapter 3\nThe Collapsing Room');
26
+
27
+ var width = map.getWidth();
28
+ var height = map.getHeight();
29
+ var half_width = Math.floor(width / 2);
30
+ var half_height = Math.floor(height / 2);
31
+ var grid_x = Math.floor(width / 4);
32
+ var grid_y = Math.floor(height / 4);
33
+
34
+ for (var i = 0; i < half_width; ++i) {
35
+ map.placeObject(grid_x + i, grid_y, 'block');
36
+ map.placeObject(grid_x + i, grid_y + half_height - 1, 'block');
37
+ }
38
+ for (var i = 1; i < half_height; ++i) {
39
+ map.placeObject(grid_x, grid_y + i, 'block');
40
+ map.placeObject(grid_x + half_width - 1, grid_y + i, 'block');
41
+ }
42
+
43
+ map.placePlayer(half_width, half_height);
44
+ map.placeObject(half_width + 2, half_height + 2, 'eye');
45
+
46
+ var exit_x, exit_y;
47
+ switch (Math.floor(Math.random() * 4)) {
48
+ case 0:
49
+ exit_x = grid_x + 1;
50
+ exit_y = grid_y + 1;
51
+ break;
52
+ case 1:
53
+ exit_x = grid_x - 2 + half_width;
54
+ exit_y = grid_y + 1;
55
+ break;
56
+ case 2:
57
+ exit_x = grid_x - 2 + half_width;
58
+ exit_y = grid_y - 2 + half_height;
59
+ break;
60
+ case 3:
61
+ exit_x = grid_x + 1;
62
+ exit_y = grid_y - 2 + half_height;
63
+ break;
64
+ }
65
+ map.placeObject(exit_x, exit_y, 'exit');
66
+
67
+ #BEGIN_EDITABLE#
68
+
69
+ #END_EDITABLE#
70
+
71
+ function bringItDown(map, blocks_per_round) {
72
+ for (var count = 0, z = 0; count < blocks_per_round && z < 100; ++z) {
73
+ var x = grid_x + 1 + Math.floor(Math.random() * (half_width - 2));
74
+ var y = grid_y + 1 + Math.floor(Math.random() * (half_height - 2));
75
+ if (map.getPlayer().atLocation(x, y)) {
76
+ map.getPlayer().killedBy('massive ceiling block');
77
+ }
78
+ if (map.getObjectTypeAt(x, y) !== 'empty') {
79
+ continue;
80
+ }
81
+ map.placeObject(x, y, 'block');
82
+ ++count;
83
+ }
84
+ }
85
+
86
+ map.defineObject('rainmaker', {
87
+ 'type': 'dynamic',
88
+ 'symbol': ' ',
89
+ 'behavior': function() {
90
+ bringItDown(map, 10);
91
+ }
92
+ });
93
+ map.placeObject(1, 1, 'rainmaker');
94
+ #END_OF_START_LEVEL#
95
+ }
96
+
97
+ function validateLevel(map) {
98
+ map.validateExactlyXManyObjects(1, 'exit');
99
+ }
levels/bonus/04_theGuard.jsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.1.1",
4
+ "commandsIntroduced": [],
5
+ "nextBonusLevel": "05_theCorridor.jsx"
6
+ }
7
+ #END_PROPERTIES#
8
+ /******************
9
+ * theGuard.js *
10
+ * from HangoverX *
11
+ * by janosgyerik *
12
+ ******************
13
+ *
14
+ * Damn that was close!
15
+ *
16
+ * The next room feels strangely empty. And incredibly huge.
17
+ * Whoever owns this place, they might be on to you now.
18
+ * There's a guard drone near the exit and it's not looking friendly...
19
+ */
20
+
21
+ function startLevel(map) {
22
+ #START_OF_START_LEVEL#
23
+ map.displayChapter('Chapter 4\nThe Guard');
24
+
25
+ var width = map.getWidth();
26
+ var height = map.getHeight();
27
+ var half_width = Math.floor(width / 2);
28
+ var half_height = Math.floor(height / 2);
29
+
30
+ map.placeObject(half_width, half_height, 'exit');
31
+ map.placePlayer(width - 3, height - 3);
32
+ map.placeObject(width - 1, height - 1, 'eye');
33
+ map.placeObject(half_width + 2, half_height, 'guard');
34
+
35
+ #BEGIN_EDITABLE#
36
+
37
+ #END_EDITABLE#
38
+ #END_OF_START_LEVEL#
39
+ }
40
+
41
+ function validateLevel(map) {
42
+ map.validateExactlyXManyObjects(1, 'exit');
43
+ }
levels/bonus/05_theCorridor.jsx ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.1",
4
+ "commandsIntroduced": []
5
+ }
6
+ #END_PROPERTIES#
7
+ /***********************
8
+ * theCorridor.js *
9
+ * from HangoverX *
10
+ * by mongoose11235813 *
11
+ ***********************
12
+ */
13
+
14
+ function startLevel(map) {
15
+ #START_OF_START_LEVEL#
16
+ map.displayChapter('Chapter 4.1\nChapters are supposed to be more than one level long');
17
+
18
+ map.defineObject('trap_left', {
19
+ 'type': 'dynamic',
20
+ 'symbol': '>',
21
+ 'color': '#900',
22
+ 'impassable': 'true',
23
+ 'behavior': function (me) {
24
+ trap_behaviour(me, 1, 6);
25
+ }
26
+ }
27
+ );
28
+ map.defineObject('trap_right', {
29
+ 'type': 'dynamic',
30
+ 'symbol': '<',
31
+ 'color': '#900',
32
+ 'impassable': 'true',
33
+ 'behavior': function (me) {
34
+ trap_behaviour(me, -5, 0);
35
+ }
36
+ }
37
+ );
38
+ map.defineObject('laser', {
39
+ 'type': 'dynamic',
40
+ 'symbol': '-',
41
+ 'color': '#f00',
42
+ 'onCollision': function (player) {
43
+ player.killedBy('a laser');
44
+ }
45
+ }
46
+ );
47
+ function trap_behaviour (me, left, right) {
48
+ var player_pos = me.findNearest('player');
49
+ if (player_pos.y - me.getY() <= 1 && !me.trapTriggered) {
50
+ me.trapTriggered = true;
51
+ for (var x = left; x < right; ++x) {
52
+ map.placeObject(me.getX() + x, me.getY(), 'laser')
53
+ }
54
+ }
55
+ }
56
+
57
+ var level_map = [
58
+ '#######',
59
+ '# x #',
60
+ '# #',
61
+ '> #',
62
+ '# #',
63
+ '# <',
64
+ '# #',
65
+ '> #',
66
+ '# #',
67
+ '# <',
68
+ '# #',
69
+ '# @ e#',
70
+ '#######'
71
+ ]
72
+ var width = map.getWidth();
73
+ var height = map.getHeight();
74
+ var map_left = Math.floor((width - level_map[0].length) / 2);
75
+ var map_top = Math.floor((height - level_map.length) / 2);
76
+ map.createFromGrid(level_map, {
77
+ 'x': 'exit',
78
+ '#': 'block',
79
+ '@': 'player',
80
+ 'e': 'eye',
81
+ '>': 'trap_left',
82
+ '<': 'trap_right'
83
+ }, map_left, map_top);
84
+
85
+ #BEGIN_EDITABLE#
86
+
87
+ #END_EDITABLE#
88
+ #END_OF_START_LEVEL#
89
+ }
90
+
91
+ function validateLevel(map) {
92
+ map.validateExactlyXManyObjects(1, 'exit');
93
+ }
levels/bonus/AB_1_ANewJourney.jsx ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0.1",
4
+ "mapProperties": {
5
+ "allowOverwrite": true
6
+ },
7
+ "nextBonusLevel": "AB_2_FrozenCave.jsx"
8
+ }
9
+ #END_PROPERTIES#
10
+ /***************************
11
+ * ANewJourney.js *
12
+ ***************************
13
+ * Hello young traveler!
14
+ *
15
+ * We are in dire need of an adventurer
16
+ * to brave the dangers and use his wits
17
+ * to escape the traps and retrieve the
18
+ * ultimate prize...
19
+ *
20
+ * You will know when you see it.
21
+ *
22
+ * The entrance to the cave is said to be
23
+ * magical and protected by a spell
24
+ * to keep people out, but I trust you
25
+ * to find a way.
26
+ *
27
+ * Every traveller knows that the
28
+ * adventures never truly end.
29
+ */
30
+
31
+ function getRandomInt(min, max) {
32
+ return Math.floor(Math.random() * (max - min + 1)) + min;
33
+ }
34
+
35
+ function startLevel(map) {
36
+ #START_OF_START_LEVEL#
37
+
38
+ map.createFromGrid(
39
+ [' xxxxxxxxxxxxxxxxxxxxxxxx ',
40
+ ' xxxxxxxx xxxxxxxxx xxxxx ',
41
+ ' xxxxxxxxxxxx xxxxxxxxxx ',
42
+ ' xxxxxxxxxxxxxxxxxxx ',
43
+ ' xxxxxxxxxxxxxxxx xxxxx ',
44
+ ' xxxxxxxxxxxxxxxxxxxx ',
45
+ ' xxx xxxxxxxxxxxxx ',
46
+ ' xxxxxxxxxx xxxxx ',
47
+ ' xxxxxxxxxxxxxxx ',
48
+ ' ',
49
+ ' ',
50
+ ' ',
51
+ ' ',
52
+ ' ',
53
+ ' ',
54
+ ' ',
55
+ ' ',
56
+ ' ',
57
+ ' ',
58
+ ' ',
59
+ ' ',
60
+ ' ',
61
+ ' ',
62
+ ' C ',
63
+ ' @ '],
64
+ {
65
+ '@': 'player',
66
+ 'E': 'exit',
67
+ '#': 'block',
68
+ 'x': 'tree',
69
+ 'C': 'computer',
70
+ }, 0, 0);
71
+
72
+
73
+ var exit_place = getRandomInt(0, 5);
74
+ if(exit_place == 0){
75
+ map.placeObject(map.getWidth()-8, 1, 'exit');
76
+ }
77
+ else if(exit_place == 1){
78
+ map.placeObject(map.getWidth()-18, 1, 'exit');
79
+ }
80
+ else if(exit_place == 2){
81
+ map.placeObject(map.getWidth()-13, 2, 'exit');
82
+ }
83
+ else if(exit_place == 3){
84
+ map.placeObject(map.getWidth()-8, 4, 'exit')
85
+ }
86
+ else if(exit_place == 4){
87
+ map.placeObject(map.getWidth()-8, 7, 'exit')
88
+ }
89
+ else{
90
+ map.placeObject(map.getWidth()-16, 6, 'exit')
91
+ }
92
+
93
+ #BEGIN_EDITABLE#
94
+
95
+ #END_EDITABLE#
96
+
97
+ #END_OF_START_LEVEL#
98
+ }
99
+
100
+ function validateLevel(map) {
101
+ map.validateExactlyXManyObjects(1, 'exit');
102
+ }
levels/bonus/AB_2_FrozenCave.jsx ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0.2",
4
+ "mapProperties": {
5
+ "allowOverwrite": true
6
+ },
7
+ "nextBonusLevel": "AB_3_BoulderMadness.jsx"
8
+ }
9
+ #END_PROPERTIES#
10
+ /**************************
11
+ * FrozenCave.js *
12
+ **************************
13
+ * So you did it.
14
+ *
15
+ * You found the cave.
16
+ *
17
+ * To be frank I didn't think
18
+ * you had it in you.
19
+ *
20
+ * Lets see you in this challenge.
21
+ *
22
+ * And no my name is not Frank.
23
+ *
24
+ * Every traveller knows that the
25
+ * adventures never truly end.
26
+ */
27
+
28
+ function getRandomInt(min, max) {
29
+ return Math.floor(Math.random() * (max - min + 1)) + min;
30
+ }
31
+
32
+ function startLevel(map) {
33
+ #START_OF_START_LEVEL#
34
+ var savedX, savedY, savedDirection;
35
+ map.defineObject('ice', {
36
+ 'symbol': String.fromCharCode(0x2630), 'color': '#75D1FF',
37
+ 'impassable': function(player, me) {
38
+ savedX = player.getX();
39
+ savedY = player.getY();
40
+ return false;
41
+ },
42
+ 'onCollision': function(player) {
43
+ if (player.getX() == savedX) {
44
+ if (player.getY() > savedY) {
45
+ savedDirection = 'down';
46
+ } else {
47
+ savedDirection = 'up';
48
+ }
49
+ } else {
50
+ if (player.getX() > savedX) {
51
+ savedDirection = 'right';
52
+ } else {
53
+ savedDirection = 'left';
54
+ }
55
+ }
56
+ var dirs = ['up', 'down', 'left', 'right'];
57
+ for (var d=0;d<dirs.length;d++) {
58
+ if (dirs[d] != savedDirection) {
59
+ map.overrideKey(dirs[d], function(){});
60
+ }
61
+ }
62
+ }
63
+ });
64
+
65
+ map.startTimer(function() {
66
+ var player = map.getPlayer();
67
+ var x = player.getX(), y = player.getY();
68
+ if (map.getObjectTypeAt(x,y) == 'ice') {
69
+ player.move(savedDirection);
70
+ }
71
+ if (player.getX() == x && player.getY() == y) {
72
+ map.overrideKey('up', null);
73
+ map.overrideKey('down', null);
74
+ map.overrideKey('left', null);
75
+ map.overrideKey('right', null);
76
+ }
77
+ },200);
78
+
79
+ map.defineObject('boulder', {
80
+ 'symbol': String.fromCharCode(0x2617),
81
+ 'color' : '#5C1F00',
82
+ 'impassable': true
83
+ });
84
+
85
+ map.createFromGrid(
86
+ ['##################################################',
87
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
88
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
89
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
90
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
91
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
92
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
93
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#E#xxxxx##',
94
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###xxxxx##',
95
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
96
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
97
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
98
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
99
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
100
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
101
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
102
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
103
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
104
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
105
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
106
+ '##xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
107
+ '###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx##',
108
+ '#####C#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx###',
109
+ '#### ###########################################',
110
+ '#### @ ###########################################'],
111
+ {
112
+ '@': 'player',
113
+ 'E': 'exit',
114
+ '#': 'boulder',
115
+ 'x': 'ice',
116
+ 'C': 'computer',
117
+ }, 0, 0);
118
+
119
+ for (var i = 0; i < 75; i++) {
120
+ var player = map.getPlayer();
121
+ var x = getRandomInt(0, map.getWidth() - 1);
122
+ var y = getRandomInt(0, map.getHeight() - 1);
123
+ if ((x != player.getX() || y != player.getY())
124
+ && (x != map.getWidth()-9 || y != 7)
125
+ && (x != map.getWidth()-9 || y != 6)
126
+ && (x != player.getX() || y != map.getHeight()-2)
127
+ && (x != player.getX() || y != map.getHeight()-3)
128
+ && (x != player.getX() || y != map.getHeight()-4)) {
129
+ map.placeObject(x, y, 'boulder');
130
+ }
131
+ }
132
+ #BEGIN_EDITABLE#
133
+
134
+ #END_EDITABLE#
135
+
136
+ #END_OF_START_LEVEL#
137
+ }
138
+
139
+ function validateLevel(map) {
140
+ map.validateExactlyXManyObjects(0, 'phone');
141
+ map.validateExactlyXManyObjects(0, 'theAlgorithm');
142
+ map.validateExactlyXManyObjects(1, 'exit');
143
+ map.validateAtMostXDynamicObjects(0);
144
+ }
levels/bonus/AB_3_BoulderMadness.jsx ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0.1",
4
+ "nextBonusLevel": "AB_4_BatAttack.jsx"
5
+ }
6
+ #END_PROPERTIES#
7
+ /********************************
8
+ * BoulderMadness.js *
9
+ ********************************
10
+ *
11
+ * Getting through the ice was not easy,
12
+ * in this section of the cave
13
+ * some of the snowy boulders maybe be movable,
14
+ * or maybe you can even squeeze yourself through them,
15
+ * but be careful, pushing too hard might get you stuck,
16
+ * or worse...
17
+ * You need a way to distinguish danger...
18
+ * and danger is usually red!
19
+ *
20
+ * Every traveller knows that the
21
+ * adventures never truly end.
22
+ */
23
+
24
+ function getRandomInt(min, max) {
25
+ return Math.floor(Math.random() * (max - min + 1)) + min;
26
+ }
27
+
28
+ function startLevel(map) {
29
+ #START_OF_START_LEVEL#
30
+
31
+ map.placePlayer(1, 1);
32
+
33
+ map.placeObject(map.getWidth()-2, map.getHeight()-2, 'exit');
34
+
35
+ map.defineObject('boulder', {
36
+ #{# #}#
37
+ 'symbol': String.fromCharCode(0x2617),
38
+ 'impassable': true
39
+ });
40
+
41
+ map.defineObject('movingBoulder', {
42
+ #{# #}#
43
+ 'symbol': String.fromCharCode(0x2617),
44
+ 'pushable': true,
45
+ 'type': 'dynamic',
46
+ });
47
+
48
+ map.defineObject('trapBoulder', {
49
+ #{# #}#
50
+ 'symbol': String.fromCharCode(0x2617),
51
+ 'pushable': true,
52
+ 'type': 'dynamic',
53
+ 'onCollision': function (player) {
54
+ player.killedBy('Got trapped under boulder');
55
+ },
56
+ });
57
+
58
+
59
+ for (var i = 0; i < 580; i++) {
60
+ var player = map.getPlayer();
61
+ var x = getRandomInt(0, map.getWidth() - 1);
62
+ var y = getRandomInt(0, map.getHeight() - 1);
63
+ if ((x != player.getX() || y != player.getY())
64
+ && (x != map.getWidth()-9 || y != 7)
65
+ && (x != map.getWidth()-9 || y != 6)
66
+ && (x != player.getX() || y != map.getHeight()-2)
67
+ && (x != player.getX() || y != map.getHeight()-3)
68
+ && (x != player.getX() || y != map.getHeight()-4)) {
69
+ var placement = getRandomInt(0,15);
70
+ if (map.getObjectTypeAt(x, y)=== "empty") {
71
+ if ( placement < 8) {
72
+ map.placeObject(x, y, 'movingBoulder');
73
+ } else if (placement < 13) {
74
+ map.placeObject(x, y, 'boulder');
75
+ } else {
76
+ map.placeObject(x, y, 'trapBoulder');
77
+ }
78
+ } else {
79
+ i = i -1;
80
+ }
81
+ }
82
+ }
83
+
84
+
85
+ #END_OF_START_LEVEL#
86
+ }
87
+
88
+ function validateLevel(map) {
89
+ map.validateExactlyXManyObjects(0, 'phone');
90
+ map.validateExactlyXManyObjects(0, 'theAlgorithm');
91
+ map.validateExactlyXManyObjects(1, 'exit');
92
+ }
levels/bonus/AB_4_BatAttack.jsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0",
4
+ "commandsIntroduced":
5
+ ["object.passableFor",
6
+ "map.validateExactlyXManyObjects"],
7
+ "nextBonusLevel": "AB_5_PathOfDoom.jsx"
8
+ }
9
+ #END_PROPERTIES#
10
+ /****************************
11
+ * BatAttack.js *
12
+ ****************************
13
+ *
14
+ * As you proceed into the cave
15
+ * you notice some creatures hiding in the darkness.
16
+ * They seem to be asleep... Proceed with care though,
17
+ * for they tend to sleep lightly.
18
+ * Sneaky or smart, you never truly had an option.
19
+ *
20
+ * Every traveller knows that the
21
+ * adventures never truly end.
22
+ */
23
+
24
+
25
+ function startLevel(map) {
26
+ #START_OF_START_LEVEL#
27
+
28
+ function wakeAndHunt(obj, type) {
29
+ var direction;
30
+ var target = obj.findNearest(type);
31
+ var leftDist = obj.getX() - target.x;
32
+ var upDist = obj.getY() - target.y;
33
+
34
+ if (Math.abs(upDist) < 4 && Math.abs(leftDist) < 8) {
35
+ if (upDist == 0 && leftDist == 0) {
36
+ return;
37
+ } if (upDist > 0 && upDist >= leftDist) {
38
+ direction = 'up';
39
+ } else if (upDist < 0 && upDist < leftDist) {
40
+ direction = 'down';
41
+ } else if (leftDist > 0 && leftDist >= upDist) {
42
+ direction = 'left';
43
+ } else {
44
+ direction = 'right';
45
+ }
46
+ obj.move(direction);
47
+ }
48
+ }
49
+
50
+ map.defineObject('bat', {
51
+ 'type': 'dynamic',
52
+ 'symbol': String.fromCharCode(0x03E1),
53
+ 'color': 'purple',
54
+ 'onCollision': function (player) {
55
+ player.killedBy('a bat');
56
+ },
57
+ 'behavior': function (me) {
58
+ wakeAndHunt(me, 'player');
59
+ }
60
+ });
61
+
62
+ map.defineObject('boulder', {
63
+ 'color' : '#5C1F00',
64
+ 'symbol': String.fromCharCode(0x2617),
65
+ 'impassable': true,
66
+ 'passableFor': ['bat']
67
+ });
68
+
69
+ map.createFromGrid(
70
+ ['##################################################',
71
+ '#################################### E#########',
72
+ '############# ############## #########',
73
+ '############ ############ #########',
74
+ '########## # ######### ################',
75
+ '######### #B# ###BBB### ################',
76
+ '####### #BBB# ######### ###############',
77
+ '######## ####### ###BBB### ##############',
78
+ '####### ######### ######### #############',
79
+ '######## ####### ######### ##############',
80
+ '####### ###BBB### ###BBB### #############',
81
+ '###BBB## ####### ######### ##############',
82
+ '####### ######### ######### #############',
83
+ '######## ##BBB## ######### ##############',
84
+ '####### ######### ##BBB#### #############',
85
+ '##BBB### ####### ######### ##############',
86
+ '####### ######### ######## ##############',
87
+ '######## ####### ####### ################',
88
+ '####### ###BBB### #BBB# #################',
89
+ '######## ####### #B# ##################',
90
+ '####### ########## # ###################',
91
+ '#### ######### ####################',
92
+ '#### ########### #####################',
93
+ '#### @ #######################################',
94
+ '##################################################'],
95
+ {
96
+ '@': 'player',
97
+ 'E': 'exit',
98
+ '#': 'boulder',
99
+ 'B': 'bat',
100
+ }, 0, 0);
101
+
102
+ #BEGIN_EDITABLE#
103
+
104
+
105
+
106
+
107
+ #END_EDITABLE#
108
+
109
+ #END_OF_START_LEVEL#
110
+ }
111
+
112
+ function validateLevel(map) {
113
+ map.validateExactlyXManyObjects(1, 'exit');
114
+ }
115
+
levels/bonus/AB_5_PathOfDoom.jsx ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0"
4
+ }
5
+ #END_PROPERTIES#
6
+ /********************************
7
+ * PathOfDooM.js *
8
+ ********************************
9
+ *
10
+ * The path ahead is a treacherous one
11
+ * and bears many dangers. The trap is
12
+ * not to be feared, but trampled for
13
+ * it is the only way forward. Don't
14
+ * forget that what keeps you in here
15
+ * might as well be the only thing still
16
+ * protecting you.
17
+ *
18
+ * Every traveller knows that the
19
+ * adventures never truly end.
20
+ */
21
+
22
+
23
+ function startLevel(map) {
24
+ #START_OF_START_LEVEL#
25
+
26
+
27
+ map.defineObject('boulder', {
28
+ 'symbol': String.fromCharCode(0x2617),
29
+ 'color' : '#5C1F00',
30
+ 'impassable': true
31
+ });
32
+
33
+
34
+ map.defineObject('plate', {
35
+ 'type': 'static',
36
+ 'symbol': String.fromCharCode(0x2617),
37
+ 'color': 'white',
38
+ 'onCollision': function (me) {
39
+ map.startTimer(arrows, 200);
40
+ },
41
+ });
42
+
43
+ map.defineObject('switch', {
44
+ 'type': 'static',
45
+ 'symbol': String.fromCharCode(0x2617),
46
+ 'color': 'red',
47
+ 'onCollision': function (me) {
48
+ #BEGIN_EDITABLE#
49
+
50
+
51
+
52
+
53
+ #END_EDITABLE#
54
+ }
55
+
56
+ });
57
+
58
+ map.defineObject('arrow', {
59
+ 'type': 'dynamic',
60
+ 'symbol': '<',
61
+ 'color': 'green',
62
+ 'interval': 100,
63
+ 'projectile': true,
64
+ 'behavior': function (me) {
65
+ me.move('left');
66
+ }
67
+ });
68
+
69
+ function arrows() {
70
+ for (var x = 8; x <= 12; x++) {
71
+ map.placeObject(map.getWidth()-3, x, 'arrow');
72
+ }
73
+ }
74
+
75
+ map.createFromGrid(
76
+ ['##################################################',
77
+ '##################################################',
78
+ '##### S ##########################################',
79
+ '#### ########################################',
80
+ '##### ########################################',
81
+ '###### #######################################',
82
+ '##### ########################################',
83
+ '#### ########################################',
84
+ '#### ##',
85
+ '#### ##',
86
+ '### ##',
87
+ '##### ##',
88
+ '###### ##',
89
+ '########p################################# #####',
90
+ '####### ################################ #####',
91
+ '####### ############################### #####',
92
+ '####### ############################### #####',
93
+ '######## ############################## #####',
94
+ '####### ############################## #####',
95
+ '####### ############################## #####',
96
+ '###### ############################### E #####',
97
+ '#### #####################################',
98
+ '#### @ ######################################',
99
+ '##### #######################################',
100
+ '###### ########################################'],
101
+ {
102
+ '@': 'player',
103
+ 'E': 'exit',
104
+ 'S': 'switch',
105
+ 'p': 'plate',
106
+ '#': 'boulder'
107
+ }, 0, 0);
108
+
109
+
110
+ #END_OF_START_LEVEL#
111
+ }
112
+
113
+ function validateLevel(map) {
114
+ map.validateExactlyXManyObjects(1, 'exit');
115
+ }
116
+
levels/bonus/AB_6_Credits ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0",
4
+ "mapProperties": {
5
+ "allowOverwrite": true
6
+ }
7
+ }
8
+ #END_PROPERTIES#
9
+ /***************************
10
+ * ANewJourney.js *
11
+ ***************************
12
+ * Hello young traveler!
13
+ *
14
+ * There will always be dire need
15
+ * of an adventurer to brave the dangers
16
+ * and use his wits to escape the traps
17
+ * and face that which others lack the
18
+ * power to.
19
+ *
20
+ * You now know and you can see it.
21
+ *
22
+ * It is the courage and the willpower
23
+ * to do so, despite the fear and the
24
+ * consequences, to pull through where
25
+ * others would simply stop...
26
+ *
27
+ * Every traveller knows that the
28
+ * adventures never truly end.
29
+ */
30
+
31
+ function startLevel(map) {
32
+
33
+ map.placePlayer(20,20);
34
+
35
+ #START_OF_START_LEVEL#
36
+
37
+ #BEGIN_EDITABLE#
38
+
39
+ #END_EDITABLE#
40
+
41
+ #END_OF_START_LEVEL#
42
+ }
levels/bonus/_sampleLevel.jsx ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.1",
4
+ "commandsIntroduced": ['object.pushable'],
5
+ "music": "Brazil"
6
+ }
7
+ #END_PROPERTIES#
8
+ /********************
9
+ * sampleLevel.js *
10
+ * by AlexNisnevich *
11
+ ********************
12
+ *
13
+ */
14
+
15
+ function startLevel(map) {
16
+ #START_OF_START_LEVEL#
17
+ map.placePlayer(0, 12);
18
+ map.placeObject(map.getWidth()/2, map.getHeight()/2, 'exit');
19
+ #END_OF_START_LEVEL#
20
+ }
levels/bonus/darkAlley.jsx ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.0",
4
+ "commandsIntroduced": []
5
+ }
6
+ #END_PROPERTIES#
7
+ /******************
8
+ * darkAlley.jsx *
9
+ * by aquang9124 *
10
+ ******************
11
+ *
12
+ * Good evening, Dr.
13
+ *
14
+ * You wake up in a dark alleyway and have no recollection of
15
+ * how you got here. There's nothing in your pockets except
16
+ * lint. You hear footsteps closing in on your location...
17
+ *
18
+ */
19
+
20
+ function startLevel(map) {
21
+ #START_OF_START_LEVEL#
22
+
23
+ map.defineObject('thug', {
24
+ 'symbol': String.fromCharCode(0x2620),
25
+ 'onCollision': function(player) {
26
+ if ( !player.hasItem('goldengun') ) {
27
+ player.killedBy('vicious thug');
28
+ } else {
29
+ map.writeStatus('You are invincible!');
30
+ }
31
+ }
32
+ });
33
+
34
+ map.defineObject('bluedoor', {
35
+ 'symbol': '-',
36
+ 'color': 'blue',
37
+ 'impassable': function(player) {
38
+ if (player.getColor() === '#0f0') {
39
+ player.setColor('orange');
40
+ this.color = 'red';
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ });
46
+
47
+ map.defineObject('goldengun', {
48
+ 'symbol': String.fromCharCode(0x122),
49
+ type: 'item',
50
+ 'onPickUp': function(player) {
51
+ map.writeStatus('You have the golden gun!');
52
+
53
+ if (player.getColor() === "orange") {
54
+ player.setColor('#0f0');
55
+ }
56
+ }
57
+ });
58
+
59
+ var alleyX = parseInt(map.getWidth() / 2);
60
+ var alleyY = parseInt(map.getHeight() / 2) - 5;
61
+
62
+ map.createFromGrid(['#######',
63
+ '# E #',
64
+ '# #',
65
+ '# #',
66
+ '# #',
67
+ '# #',
68
+ '# T #',
69
+ '# T',
70
+ '#T ##',
71
+ '#L#TLL#',
72
+ '# #',
73
+ '###T#L#',
74
+ '# P #',
75
+ '#######'],
76
+ {
77
+ 'E': 'exit',
78
+ '#': 'block',
79
+ 'G': 'goldengun',
80
+ 'T': 'thug',
81
+ 'L': 'bluedoor',
82
+ 'P': 'player'
83
+ }, alleyX, alleyY);
84
+ #BEGIN_EDITABLE#
85
+ map.placeObject(20, 20, 'goldengun');
86
+ #END_EDITABLE#
87
+
88
+ #END_OF_START_LEVEL#
89
+ }
90
+
91
+ function onExit(map) {
92
+ map.writeStatus("You have escaped the dark alley.");
93
+ return true;
94
+ }
95
+
96
+ function validateLevel(map) {
97
+ map.validateExactlyXManyObjects(1, 'exit');
98
+ }
levels/bonus/ice.jsx ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.1",
4
+ "commandsIntroduced": []
5
+ }
6
+ #END_PROPERTIES#
7
+ /*******************
8
+ * ice.js *
9
+ * by mdejean *
10
+ *******************
11
+ *
12
+ */
13
+
14
+ function startLevel(map) {
15
+ #START_OF_START_LEVEL#
16
+ var savedX, savedY, savedDirection;
17
+ map.defineObject('ice', {
18
+ 'symbol': '.', 'color': '#00f',
19
+ 'impassable': function(player, me) {
20
+ savedX = player.getX();
21
+ savedY = player.getY();
22
+ return false;
23
+ },
24
+ 'onCollision': function(player) {
25
+ if (player.getX() == savedX) {
26
+ if (player.getY() > savedY) {
27
+ savedDirection = 'down';
28
+ } else {
29
+ savedDirection = 'up';
30
+ }
31
+ } else {
32
+ if (player.getX() > savedX) {
33
+ savedDirection = 'right';
34
+ } else {
35
+ savedDirection = 'left';
36
+ }
37
+ }
38
+ var dirs = ['up', 'down', 'left', 'right'];
39
+ for (var d=0;d<dirs.length;d++) {
40
+ if (dirs[d] != savedDirection) {
41
+ map.overrideKey(dirs[d], function(){});
42
+ }
43
+ }
44
+ }
45
+ });
46
+ map.startTimer(function() {
47
+ var player = map.getPlayer();
48
+ var x = player.getX(), y = player.getY();
49
+ if (map.getObjectTypeAt(x,y) == 'ice') {
50
+ player.move(savedDirection);
51
+ }
52
+ if (player.getX() == x && player.getY() == y) {
53
+ map.overrideKey('up', null);
54
+ map.overrideKey('down', null);
55
+ map.overrideKey('left', null);
56
+ map.overrideKey('right', null);
57
+ }
58
+ },200);
59
+ map.createFromGrid(
60
+ ['+++++++++++++++++++++++++++++++',
61
+ '+ +++ +',
62
+ '+ ++E+ +',
63
+ '+ xxxx +',
64
+ '+ xxxx +',
65
+ '+ xxxx +',
66
+ '+ xxxx +',
67
+ '+ @+++ +',
68
+ '+ +',
69
+ '+ +',
70
+ '+ +',
71
+ '+ +',
72
+ '+ +',
73
+ '+ +',
74
+ '+++++++++++++++++++++++++++++++'],
75
+ {
76
+ '@': 'player',
77
+ 'E': 'exit',
78
+ '+': 'block',
79
+ 'x': 'ice',
80
+ }, 6, 6);
81
+ #BEGIN_EDITABLE#
82
+
83
+ #END_EDITABLE#
84
+
85
+ #END_OF_START_LEVEL#
86
+ }
87
+
88
+ function validateLevel(map) {
89
+ map.validateExactlyXManyObjects(0, 'phone');
90
+ map.validateExactlyXManyObjects(0, 'theAlgorithm');
91
+ map.validateExactlyXManyObjects(1, 'exit');
92
+ map.validateAtMostXDynamicObjects(0);
93
+ }
levels/bonus/labryinth.jsx ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "1.2.2",
4
+ "commandsIntroduced": [],
5
+ "music": "Brazil"
6
+ }
7
+ #END_PROPERTIES#
8
+
9
+
10
+ function startLevel(map) {
11
+ #START_OF_START_LEVEL#
12
+ var savedX, savedY, savedDirection;
13
+
14
+ function moveToward(obj, type) {
15
+ var target = obj.findNearest(type);
16
+ var leftDist = obj.getX() - target.x;
17
+ var upDist = obj.getY() - target.y;
18
+
19
+ var direction;
20
+ if (upDist == 0 && leftDist == 0) {
21
+ return;
22
+ } if (upDist > 0 && upDist >= leftDist) {
23
+ direction = 'up';
24
+ } else if (upDist < 0 && upDist < leftDist) {
25
+ direction = 'down';
26
+ } else if (leftDist > 0 && leftDist >= upDist) {
27
+ direction = 'left';
28
+ } else {
29
+ direction = 'right';
30
+ }
31
+
32
+ if (obj.canMove(direction)) {
33
+ obj.move(direction);
34
+ }
35
+ }
36
+
37
+ function shuffle(o){ //v1.0 [http://bit.ly/1l6LGQT]
38
+ for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i),
39
+ x = o[--i], o[i] = o[j], o[j] = x);
40
+ return o;
41
+ };
42
+ map.defineObject('ice', {
43
+ 'symbol': String.fromCharCode(0x2630), 'color': '#75D1FF',
44
+ 'impassable': function(player, me) {
45
+ savedX = player.getX();
46
+ savedY = player.getY();
47
+ return false;
48
+ },
49
+ 'onCollision': function(player) {
50
+ if (player.getX() == savedX) {
51
+ if (player.getY() > savedY) {
52
+ savedDirection = 'down';
53
+ } else {
54
+ savedDirection = 'up';
55
+ }
56
+ } else {
57
+ if (player.getX() > savedX) {
58
+ savedDirection = 'right';
59
+ } else {
60
+ savedDirection = 'left';
61
+ }
62
+ }
63
+ var dirs = ['up', 'down', 'left', 'right'];
64
+ for (var d=0;d<dirs.length;d++) {
65
+ if (dirs[d] != savedDirection) {
66
+ map.overrideKey(dirs[d], function(){});
67
+ }
68
+ }
69
+ }
70
+ });
71
+
72
+ map.startTimer(function() {
73
+ var player = map.getPlayer();
74
+ var x = player.getX(), y = player.getY();
75
+ if (map.getObjectTypeAt(x,y) == 'ice') {
76
+ player.move(savedDirection);
77
+ }
78
+ if (player.getX() == x && player.getY() == y) {
79
+ map.overrideKey('up', null);
80
+ map.overrideKey('down', null);
81
+ map.overrideKey('left', null);
82
+ map.overrideKey('right', null);
83
+ }
84
+ },200);
85
+
86
+ map.defineObject('boulder', {
87
+ 'symbol': String.fromCharCode(0x2617),
88
+ 'color' : '#5C1F00',
89
+ 'impassable': true
90
+ });
91
+
92
+ map.defineObject('water', {
93
+ 'symbol': '░',
94
+ 'color': '#44f',
95
+ 'onCollision': function (player) {
96
+ player.killedBy#{#('drowning in deep dark water')#}#;
97
+ }
98
+ });
99
+
100
+ map.defineObject('invisibleBoulder', {
101
+ 'symbol' : String.fromCharCode(0x2617),
102
+ 'color' : '#000000',
103
+ 'impassable' : true
104
+ });
105
+
106
+ map.defineObject('zombie', {
107
+ 'type': 'dynamic',
108
+ 'symbol': 'z',
109
+ 'color': 'red',
110
+ 'onCollision': function (player) {
111
+ player.killedBy('Zombie');
112
+ },
113
+ 'behavior': function (me) {
114
+ #BEGIN_EDITABLE#
115
+ var player = map.getPlayer();
116
+ var x = player.getX();
117
+ var y = player.getY();
118
+ /*Hint: There is a me.canMove method for zombies, for example if me.canMove('right')
119
+ is true, then it means that the zombie is able to move right at that specific point in time
120
+ //Hint 2: You can specify where the zombie will move with ex: me.move('right')
121
+ */
122
+ if(x > 40 && x < 45){
123
+ if(y >= 13){
124
+ moveToward(me, 'player');
125
+ }
126
+ }
127
+ #END_EDITABLE#
128
+ }
129
+ });
130
+ map.defineObject('blueLock', {
131
+ 'symbol': String.fromCharCode(0x2297),
132
+ 'color': '#06f',
133
+ 'impassable': function (player) {
134
+ if (player.hasItem('blueKey')) {
135
+ player.removeItem('blueKey');
136
+ return false;
137
+ } else {
138
+ return true;
139
+ }
140
+ }
141
+ });
142
+
143
+ map.defineObject('yellowLock', {
144
+ 'symbol': String.fromCharCode(0x2297),
145
+ 'color': 'yellow',
146
+ 'impassable': function (player) {
147
+ if (player.hasItem('yellowKey')) {
148
+ player.removeItem('yellowKey');
149
+ return false;
150
+ } else {
151
+ return true;
152
+ }
153
+ }
154
+ });
155
+
156
+ map.createFromGrid([
157
+ 'o # # ',
158
+ 'xx # # ',
159
+ 'xx # B E ',
160
+ 'xx # # ',
161
+ 'xx # # ',
162
+ 'xx # #############',
163
+ ' # b',
164
+ '### ##Y####################',
165
+ ' # # ',
166
+ ' # ######### #############',
167
+ ' # #xxxxxxxxxxxxxxxxxxxxx ',
168
+ 'xx #y xxxxxxxxxxxx ',
169
+ 'xx ############################################# ',
170
+ 'xx o # ',
171
+ 'xx# ## ## # ',
172
+ 'xx# ##wwwwwwwwww## # ',
173
+ 'xx# ##wwwwwwwwww## # ',
174
+ 'xx# ##wwwwwwwwww## # ',
175
+ 'xx# ##wwwwwwwwww## # ',
176
+ 'xx# ##wwwwwwwwww## # ',
177
+ 'xx# ##wwwwwwwwww## # ',
178
+ 'xx# ##wwwwwwwwww## # ',
179
+ 'xx# ##wwwwwwwwww## # ',
180
+ 'xx# ##wwwwwwwwww## # ',
181
+ '@ # ##wwwwwwwwww##zzzz '],
182
+ {
183
+ '@': 'player',
184
+ 'E': 'exit',
185
+ '#': 'boulder',
186
+ 'i': 'invisibleBoulder',
187
+ 'x': 'ice',
188
+ 'o': 'teleporter',
189
+ 'w': 'water',
190
+ 'z': 'zombie',
191
+ 'b': 'blueKey',
192
+ 'y': 'yellowKey',
193
+ 'B': 'blueLock',
194
+ 'Y': 'yellowLock',
195
+ }, 0, 0);
196
+ function gravity() {
197
+ var player = map.getPlayer();
198
+ var x = player.getX();
199
+ var y = player.getY() + 1;
200
+ if(x > 2 && x < 27){
201
+ if(y >= 13){
202
+ player.move('down');
203
+ }
204
+ if(y >= 21){
205
+ player.killedBy('gravity');
206
+ }
207
+ }
208
+ }
209
+ map.startTimer(gravity,45);
210
+
211
+ function waterGravity() {
212
+ var player = map.getPlayer();
213
+ var x = player.getX();
214
+ var y = player.getY() + 1;
215
+ if(x > 28 && x < 39) {
216
+ if(y >= 13){
217
+ player.move('down');
218
+ }
219
+ }
220
+ }
221
+ map.startTimer(waterGravity, 45);
222
+
223
+ var teleporters = map.getDynamicObjects();
224
+ teleporters = shuffle(teleporters);
225
+ var t1, t2;
226
+ for(var i = 0; i < teleporters.length; i++){
227
+ if(teleporters[i].getType() == 'teleporter'){
228
+ t1 = teleporters[i];
229
+ }
230
+ }
231
+ for(var j = teleporters.length - 1; j >= 0; j--){
232
+ if(teleporters[j].getType() == 'teleporter'){
233
+ t2 = teleporters[j];
234
+ }
235
+ }
236
+ t1.setTarget(t2);
237
+ t2.setTarget(t1);
238
+
239
+ for(var x = 3; x < 26; x++) {
240
+ map.placeObject(x, 10, 'invisibleBoulder');
241
+ }
242
+ for(var x = 4; x < 27; x++) {
243
+ map.placeObject(x, 8, 'invisibleBoulder');
244
+ }
245
+ for(var x = 0; x < 26; x++) {
246
+ map.placeObject(x, 6, 'invisibleBoulder');
247
+ }
248
+ for(var y = 1; y < 6; y++) {
249
+ map.placeObject(25, y, 'invisibleBoulder');
250
+ }
251
+ for(var y = 0; y < 5; y++) {
252
+ map.placeObject(23, y, 'invisibleBoulder');
253
+ }
254
+ for(var y = 1; y < 6; y++) {
255
+ map.placeObject(21, y, 'invisibleBoulder');
256
+ }
257
+ for(var y = 0; y < 5; y++) {
258
+ map.placeObject(19, y, 'invisibleBoulder');
259
+ }
260
+ for(var y = 1; y < 6; y++) {
261
+ map.placeObject(17, y, 'invisibleBoulder');
262
+ }
263
+ for(var y = 0; y < 5; y++) {
264
+ map.placeObject(15, y, 'invisibleBoulder');
265
+ }
266
+ for(var y = 1; y < 6; y++) {
267
+ map.placeObject(13, y, 'invisibleBoulder');
268
+ }
269
+ for(var y = 0; y < 5; y++) {
270
+ map.placeObject(11, y, 'invisibleBoulder');
271
+ }
272
+ for(var y = 1; y < 6; y++) {
273
+ map.placeObject(9, y, 'invisibleBoulder');
274
+ }
275
+ for(var y = 0; y < 5; y++) {
276
+ map.placeObject(7, y, 'invisibleBoulder');
277
+ }
278
+ for(var y = 1; y < 6; y++) {
279
+ map.placeObject(5, y, 'invisibleBoulder');
280
+ }
281
+ for(var y = 0; y < 5; y++) {
282
+ map.placeObject(3, y, 'invisibleBoulder');
283
+ }
284
+
285
+ #BEGIN_EDITABLE#
286
+ for(var x = 43; x < 46; x++) {
287
+ map.placeObject(x, 1, 'boulder');
288
+ map.placeObject(x, 3, 'boulder');
289
+ }
290
+ map.placeObject(43, 2, 'boulder');
291
+ map.placeObject(45, 2, 'boulder');
292
+ map.placeObject(1, 9, 'boulder');
293
+ map.placeObject(30, 14, 'boulder');
294
+ map.placeObject(36, 11, 'boulder');
295
+ for(var x = map.getWidth() - 13; x < map.getWidth() - 1; x++) {
296
+ map.placeObject(x, 6, 'boulder');
297
+ }
298
+ #END_EDITABLE#
299
+ #END_OF_START_LEVEL#
300
+ }
301
+
302
+ function validateLevel(map) {
303
+ map.validateExactlyXManyObjects(0, 'phone');
304
+ map.validateExactlyXManyObjects(0, 'theAlgorithm');
305
+ map.validateExactlyXManyObjects(1, 'exit');
306
+ map.validateExactlyXManyObjects(0, 'computer');
307
+ map.validateAtMostXDynamicObjects(6);
308
+ }
levels/bonus/levelName.jsx ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.0",
4
+ "commandsIntroduced": []
5
+ }
6
+ #END_PROPERTIES#
7
+ /*******************
8
+ * <level name>.js *
9
+ * by mdejean *
10
+ *******************
11
+ */
12
+
13
+ function startLevel(map) {
14
+ #START_OF_START_LEVEL#
15
+ map.defineObject('door', {
16
+ 'symbol': '-', 'color': '#0f0',
17
+ 'impassable': function(player) {
18
+ if (player.getColor() == this.color) {
19
+ player.setColor('red');
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ });
25
+ map.createFromGrid(
26
+ ['+++++++++++++++',
27
+ '+ + +',
28
+ '+ ++ +',
29
+ '+ @ ++x +',
30
+ '+ ++x++',
31
+ '+ ++E++',
32
+ '+++++++++++++++'],
33
+ {
34
+ '@': 'player',
35
+ 'E': 'exit',
36
+ '+': 'block',
37
+ 'x': 'door',
38
+ }, 17, 6);
39
+ #BEGIN_EDITABLE#
40
+
41
+ #END_EDITABLE#
42
+
43
+ #END_OF_START_LEVEL#
44
+ }
45
+
46
+ function validateLevel(map) {
47
+ map.validateExactlyXManyObjects(0, 'phone');
48
+ map.validateExactlyXManyObjects(0, 'theAlgorithm');
49
+ map.validateExactlyXManyObjects(1, 'exit');
50
+ map.validateNoTimers();
51
+ map.validateAtMostXDynamicObjects(0);
52
+ }
levels/bonus/noWayOut.jsx ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #BEGIN_PROPERTIES#
2
+ {
3
+ "version": "0.1000002",
4
+ "commandsIntroduced":
5
+ [ ],
6
+ "music": "gurh"
7
+ }
8
+ #END_PROPERTIES#
9
+ /*******************
10
+ * 102_noWayOut.js *
11
+ * by AGoliath *
12
+ *******************
13
+ *
14
+ * Who builds a wall in a room anyway?
15
+ *
16
+ */
17
+
18
+ function startLevel(map) {
19
+ #START_OF_START_LEVEL#
20
+ map.displayChapter('Challenge 2\nTear down this wall!');
21
+
22
+ map.placePlayer(7, 5);
23
+
24
+ var buildWall = function() {
25
+ for (var i= 0; i <= map.getHeight(); i++) map.placeObject(map.getWidth()-10, i, 'block');
26
+ }
27
+
28
+ #BEGIN_EDITABLE#
29
+
30
+ #END_EDITABLE#
31
+
32
+ buildWall();
33
+ map.placeObject(map.getWidth()-7, map.getHeight()-5, 'exit');
34
+ #END_OF_START_LEVEL#
35
+ }
36
+
37
+ function onExit(map) {
38
+ return true;
39
+ }
40
+
41
+
42
+ function validateLevel(map) {
43
+ map.validateExactlyXManyObjects(1, 'exit');
44
+ }