├── .gitignore ├── LICENSE.md ├── README.md ├── build.sh ├── build └── dummy.txt ├── index-debug.html ├── m ├── l1.png ├── l2.png ├── l3.png └── q2.png ├── package.json ├── shrinkit.js └── source ├── audio.js ├── entity-cpu.js ├── entity-explosion.js ├── entity-health.js ├── entity-particle.js ├── entity-plasma.js ├── entity-player.js ├── entity-sentry.js ├── entity-spider.js ├── entity.js ├── game.js ├── html-template.html ├── main.js ├── music-dark-meat-beat.js ├── random.js ├── renderer.js ├── sonantx-reduced.js ├── sound-effects.js └── terminal.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | work 3 | build -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dominic Szablewski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UNDERRUN - A JS13k GAME 2 | 3 | My entry for the 2018 [js13k](https://js13kgames.com/) competition. 4 | 5 | Play here: https://phoboslab.org/underrun/ 6 | 7 | MIT Licensed 8 | 9 | Please be aware that this projects makes use of the Sonant-X library (albeit heavily modified) which is published under the zlib license. -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | cat \ 2 | source/game.js \ 3 | source/random.js \ 4 | source/renderer.js \ 5 | source/entity.js \ 6 | source/entity-player.js \ 7 | source/entity-cpu.js \ 8 | source/entity-plasma.js \ 9 | source/entity-spider.js \ 10 | source/entity-sentry.js \ 11 | source/entity-particle.js \ 12 | source/entity-health.js \ 13 | source/entity-explosion.js \ 14 | source/sonantx-reduced.js \ 15 | source/music-dark-meat-beat.js \ 16 | source/sound-effects.js \ 17 | source/audio.js \ 18 | source/terminal.js \ 19 | source/main.js \ 20 | > build/underrun.js 21 | 22 | node shrinkit.js build/underrun.js > build/underrun.compact.js 23 | 24 | ./node_modules/uglify-es/bin/uglifyjs build/underrun.compact.js \ 25 | --compress --screw-ie8 --mangle toplevel -c --beautify --mangle-props regex='/^_/;' \ 26 | -o build/underrun.min.beauty.js 27 | 28 | ./node_modules/uglify-es/bin/uglifyjs build/underrun.compact.js \ 29 | --compress --screw-ie8 --mangle toplevel --mangle-props regex='/^_/;' \ 30 | -o build/underrun.min.js 31 | 32 | 33 | rm build/underrun.zip 34 | 35 | sed -e '/GAME_SOURCE/{r build/underrun.min.js' -e 'd}' source/html-template.html > underrun.html 36 | zip -9 build/underrun.zip m/q2.png m/l1.png m/l2.png m/l3.png underrun.html 37 | ls -la build/ 38 | mv underrun.html build/underrun.html 39 | -------------------------------------------------------------------------------- /build/dummy.txt: -------------------------------------------------------------------------------- 1 | dummy -------------------------------------------------------------------------------- /index-debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/m/l1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/underrun/f933e29152d7fc1ca61d4b3eaa8b29551d7d7a62/m/l1.png
--------------------------------------------------------------------------------
/m/l2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/underrun/f933e29152d7fc1ca61d4b3eaa8b29551d7d7a62/m/l2.png
--------------------------------------------------------------------------------
/m/l3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/underrun/f933e29152d7fc1ca61d4b3eaa8b29551d7d7a62/m/l3.png
--------------------------------------------------------------------------------
/m/q2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/underrun/f933e29152d7fc1ca61d4b3eaa8b29551d7d7a62/m/q2.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "undderun",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "underrun.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "uglify-es": "^3.3.9"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/shrinkit.js:
--------------------------------------------------------------------------------
1 | const webgl_replace = {
2 | ACTIVE_ATTRIBUTES: 35721,
3 | ACTIVE_ATTRIBUTE_MAX_LENGTH: 35722,
4 | ACTIVE_TEXTURE: 34016,
5 | ACTIVE_UNIFORMS: 35718,
6 | ACTIVE_UNIFORM_MAX_LENGTH: 35719,
7 | ALIASED_LINE_WIDTH_RANGE: 33902,
8 | ALIASED_POINT_SIZE_RANGE: 33901,
9 | ALPHA: 6406,
10 | ALPHA_BITS: 3413,
11 | ALWAYS: 519,
12 | ARRAY_BUFFER: 34962,
13 | ARRAY_BUFFER_BINDING: 34964,
14 | ATTACHED_SHADERS: 35717,
15 | BACK: 1029,
16 | BLEND: 3042,
17 | BLEND_COLOR: 32773,
18 | BLEND_DST_ALPHA: 32970,
19 | BLEND_DST_RGB: 32968,
20 | BLEND_EQUATION: 32777,
21 | BLEND_EQUATION_ALPHA: 34877,
22 | BLEND_EQUATION_RGB: 32777,
23 | BLEND_SRC_ALPHA: 32971,
24 | BLEND_SRC_RGB: 32969,
25 | BLUE_BITS: 3412,
26 | BOOL: 35670,
27 | BOOL_VEC2: 35671,
28 | BOOL_VEC3: 35672,
29 | BOOL_VEC4: 35673,
30 | BROWSER_DEFAULT_WEBGL: 37444,
31 | BUFFER_SIZE: 34660,
32 | BUFFER_USAGE: 34661,
33 | BYTE: 5120,
34 | CCW: 2305,
35 | CLAMP_TO_EDGE: 33071,
36 | COLOR_ATTACHMENT0: 36064,
37 | COLOR_BUFFER_BIT: 16384,
38 | COLOR_CLEAR_VALUE: 3106,
39 | COLOR_WRITEMASK: 3107,
40 | COMPILE_STATUS: 35713,
41 | COMPRESSED_TEXTURE_FORMATS: 34467,
42 | CONSTANT_ALPHA: 32771,
43 | CONSTANT_COLOR: 32769,
44 | CONTEXT_LOST_WEBGL: 37442,
45 | CULL_FACE: 2884,
46 | CULL_FACE_MODE: 2885,
47 | CURRENT_PROGRAM: 35725,
48 | CURRENT_VERTEX_ATTRIB: 34342,
49 | CW: 2304,
50 | DECR: 7683,
51 | DECR_WRAP: 34056,
52 | DELETE_STATUS: 35712,
53 | DEPTH_ATTACHMENT: 36096,
54 | DEPTH_BITS: 3414,
55 | DEPTH_BUFFER_BIT: 256,
56 | DEPTH_CLEAR_VALUE: 2931,
57 | DEPTH_COMPONENT: 6402,
58 | DEPTH_COMPONENT16: 33189,
59 | DEPTH_FUNC: 2932,
60 | DEPTH_RANGE: 2928,
61 | DEPTH_STENCIL: 34041,
62 | DEPTH_STENCIL_ATTACHMENT: 33306,
63 | DEPTH_TEST: 2929,
64 | DEPTH_WRITEMASK: 2930,
65 | DITHER: 3024,
66 | DONT_CARE: 4352,
67 | DST_ALPHA: 772,
68 | DST_COLOR: 774,
69 | DYNAMIC_DRAW: 35048,
70 | ELEMENT_ARRAY_BUFFER: 34963,
71 | ELEMENT_ARRAY_BUFFER_BINDING: 34965,
72 | EQUAL: 514,
73 | FASTEST: 4353,
74 | FLOAT: 5126,
75 | FLOAT_MAT2: 35674,
76 | FLOAT_MAT3: 35675,
77 | FLOAT_MAT4: 35676,
78 | FLOAT_VEC2: 35664,
79 | FLOAT_VEC3: 35665,
80 | FLOAT_VEC4: 35666,
81 | FRAGMENT_SHADER: 35632,
82 | FRAMEBUFFER: 36160,
83 | FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 36049,
84 | FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 36048,
85 | FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 36051,
86 | FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 36050,
87 | FRAMEBUFFER_BINDING: 36006,
88 | FRAMEBUFFER_COMPLETE: 36053,
89 | FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 36054,
90 | FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 36057,
91 | FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 36055,
92 | FRAMEBUFFER_UNSUPPORTED: 36061,
93 | FRONT: 1028,
94 | FRONT_AND_BACK: 1032,
95 | FRONT_FACE: 2886,
96 | FUNC_ADD: 32774,
97 | FUNC_REVERSE_SUBTRACT: 32779,
98 | FUNC_SUBTRACT: 32778,
99 | GENERATE_MIPMAP_HINT: 33170,
100 | GEQUAL: 518,
101 | GREATER: 516,
102 | GREEN_BITS: 3411,
103 | HIGH_FLOAT: 36338,
104 | HIGH_INT: 36341,
105 | INCR: 7682,
106 | INCR_WRAP: 34055,
107 | INFO_LOG_LENGTH: 35716,
108 | INT: 5124,
109 | INT_VEC2: 35667,
110 | INT_VEC3: 35668,
111 | INT_VEC4: 35669,
112 | INVALID_ENUM: 1280,
113 | INVALID_FRAMEBUFFER_OPERATION: 1286,
114 | INVALID_OPERATION: 1282,
115 | INVALID_VALUE: 1281,
116 | INVERT: 5386,
117 | KEEP: 7680,
118 | LEQUAL: 515,
119 | LESS: 513,
120 | LINEAR: 9729,
121 | LINEAR_MIPMAP_LINEAR: 9987,
122 | LINEAR_MIPMAP_NEAREST: 9985,
123 | LINES: 1,
124 | LINE_LOOP: 2,
125 | LINE_STRIP: 3,
126 | LINE_WIDTH: 2849,
127 | LINK_STATUS: 35714,
128 | LOW_FLOAT: 36336,
129 | LOW_INT: 36339,
130 | LUMINANCE: 6409,
131 | LUMINANCE_ALPHA: 6410,
132 | MAX_COMBINED_TEXTURE_IMAGE_UNITS: 35661,
133 | MAX_CUBE_MAP_TEXTURE_SIZE: 34076,
134 | MAX_FRAGMENT_UNIFORM_VECTORS: 36349,
135 | MAX_RENDERBUFFER_SIZE: 34024,
136 | MAX_TEXTURE_IMAGE_UNITS: 34930,
137 | MAX_TEXTURE_SIZE: 3379,
138 | MAX_VARYING_VECTORS: 36348,
139 | MAX_VERTEX_ATTRIBS: 34921,
140 | MAX_VERTEX_TEXTURE_IMAGE_UNITS: 35660,
141 | MAX_VERTEX_UNIFORM_VECTORS: 36347,
142 | MAX_VIEWPORT_DIMS: 3386,
143 | MEDIUM_FLOAT: 36337,
144 | MEDIUM_INT: 36340,
145 | MIRRORED_REPEAT: 33648,
146 | NEAREST: 9728,
147 | NEAREST_MIPMAP_LINEAR: 9986,
148 | NEAREST_MIPMAP_NEAREST: 9984,
149 | NEVER: 512,
150 | NICEST: 4354,
151 | NONE: 0,
152 | NOTEQUAL: 517,
153 | NO_ERROR: 0,
154 | NUM_COMPRESSED_TEXTURE_FORMATS: 34466,
155 | ONE: 1,
156 | ONE_MINUS_CONSTANT_ALPHA: 32772,
157 | ONE_MINUS_CONSTANT_COLOR: 32770,
158 | ONE_MINUS_DST_ALPHA: 773,
159 | ONE_MINUS_DST_COLOR: 775,
160 | ONE_MINUS_SRC_ALPHA: 771,
161 | ONE_MINUS_SRC_COLOR: 769,
162 | OUT_OF_MEMORY: 1285,
163 | PACK_ALIGNMENT: 3333,
164 | POINTS: 0,
165 | POLYGON_OFFSET_FACTOR: 32824,
166 | POLYGON_OFFSET_FILL: 32823,
167 | POLYGON_OFFSET_UNITS: 10752,
168 | RED_BITS: 3410,
169 | RENDERBUFFER: 36161,
170 | RENDERBUFFER_ALPHA_SIZE: 36179,
171 | RENDERBUFFER_BINDING: 36007,
172 | RENDERBUFFER_BLUE_SIZE: 36178,
173 | RENDERBUFFER_DEPTH_SIZE: 36180,
174 | RENDERBUFFER_GREEN_SIZE: 36177,
175 | RENDERBUFFER_HEIGHT: 36163,
176 | RENDERBUFFER_INTERNAL_FORMAT: 36164,
177 | RENDERBUFFER_RED_SIZE: 36176,
178 | RENDERBUFFER_STENCIL_SIZE: 36181,
179 | RENDERBUFFER_WIDTH: 36162,
180 | RENDERER: 7937,
181 | REPEAT: 10497,
182 | REPLACE: 7681,
183 | RGB: 6407,
184 | RGB5_A1: 32855,
185 | RGB565: 36194,
186 | RGBA: 6408,
187 | RGBA4: 32854,
188 | SAMPLER_2D: 35678,
189 | SAMPLER_CUBE: 35680,
190 | SAMPLES: 32937,
191 | SAMPLE_ALPHA_TO_COVERAGE: 32926,
192 | SAMPLE_BUFFERS: 32936,
193 | SAMPLE_COVERAGE: 32928,
194 | SAMPLE_COVERAGE_INVERT: 32939,
195 | SAMPLE_COVERAGE_VALUE: 32938,
196 | SCISSOR_BOX: 3088,
197 | SCISSOR_TEST: 3089,
198 | SHADER_COMPILER: 36346,
199 | SHADER_SOURCE_LENGTH: 35720,
200 | SHADER_TYPE: 35663,
201 | SHADING_LANGUAGE_VERSION: 35724,
202 | SHORT: 5122,
203 | SRC_ALPHA: 770,
204 | SRC_ALPHA_SATURATE: 776,
205 | SRC_COLOR: 768,
206 | STATIC_DRAW: 35044,
207 | STENCIL_ATTACHMENT: 36128,
208 | STENCIL_BACK_FAIL: 34817,
209 | STENCIL_BACK_FUNC: 34816,
210 | STENCIL_BACK_PASS_DEPTH_FAIL: 34818,
211 | STENCIL_BACK_PASS_DEPTH_PASS: 34819,
212 | STENCIL_BACK_REF: 36003,
213 | STENCIL_BACK_VALUE_MASK: 36004,
214 | STENCIL_BACK_WRITEMASK: 36005,
215 | STENCIL_BITS: 3415,
216 | STENCIL_BUFFER_BIT: 1024,
217 | STENCIL_CLEAR_VALUE: 2961,
218 | STENCIL_FAIL: 2964,
219 | STENCIL_FUNC: 2962,
220 | STENCIL_INDEX: 6401,
221 | STENCIL_INDEX8: 36168,
222 | STENCIL_PASS_DEPTH_FAIL: 2965,
223 | STENCIL_PASS_DEPTH_PASS: 2966,
224 | STENCIL_REF: 2967,
225 | STENCIL_TEST: 2960,
226 | STENCIL_VALUE_MASK: 2963,
227 | STENCIL_WRITEMASK: 2968,
228 | STREAM_DRAW: 35040,
229 | SUBPIXEL_BITS: 3408,
230 | TEXTURE: 5890,
231 | TEXTURE0: 33984,
232 | TEXTURE1: 33985,
233 | TEXTURE2: 33986,
234 | TEXTURE3: 33987,
235 | TEXTURE4: 33988,
236 | TEXTURE5: 33989,
237 | TEXTURE6: 33990,
238 | TEXTURE7: 33991,
239 | TEXTURE8: 33992,
240 | TEXTURE9: 33993,
241 | TEXTURE10: 33994,
242 | TEXTURE11: 33995,
243 | TEXTURE12: 33996,
244 | TEXTURE13: 33997,
245 | TEXTURE14: 33998,
246 | TEXTURE15: 33999,
247 | TEXTURE16: 34000,
248 | TEXTURE17: 34001,
249 | TEXTURE18: 34002,
250 | TEXTURE19: 34003,
251 | TEXTURE20: 34004,
252 | TEXTURE21: 34005,
253 | TEXTURE22: 34006,
254 | TEXTURE23: 34007,
255 | TEXTURE24: 34008,
256 | TEXTURE25: 34009,
257 | TEXTURE26: 34010,
258 | TEXTURE27: 34011,
259 | TEXTURE28: 34012,
260 | TEXTURE29: 34013,
261 | TEXTURE30: 34014,
262 | TEXTURE31: 34015,
263 | TEXTURE_2D: 3553,
264 | TEXTURE_BINDING_2D: 32873,
265 | TEXTURE_BINDING_CUBE_MAP: 34068,
266 | TEXTURE_CUBE_MAP: 34067,
267 | TEXTURE_CUBE_MAP_NEGATIVE_X: 34070,
268 | TEXTURE_CUBE_MAP_NEGATIVE_Y: 34072,
269 | TEXTURE_CUBE_MAP_NEGATIVE_Z: 34074,
270 | TEXTURE_CUBE_MAP_POSITIVE_X: 34069,
271 | TEXTURE_CUBE_MAP_POSITIVE_Y: 34071,
272 | TEXTURE_CUBE_MAP_POSITIVE_Z: 34073,
273 | TEXTURE_MAG_FILTER: 10240,
274 | TEXTURE_MIN_FILTER: 10241,
275 | TEXTURE_WRAP_S: 10242,
276 | TEXTURE_WRAP_T: 10243,
277 | TRIANGLES: 4,
278 | TRIANGLE_FAN: 6,
279 | TRIANGLE_STRIP: 5,
280 | UNPACK_ALIGNMENT: 3317,
281 | UNPACK_COLORSPACE_CONVERSION_WEBGL: 37443,
282 | UNPACK_FLIP_Y_WEBGL: 37440,
283 | UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441,
284 | UNSIGNED_BYTE: 5121,
285 | UNSIGNED_INT: 5125,
286 | UNSIGNED_SHORT: 5123,
287 | UNSIGNED_SHORT_4_4_4_4: 32819,
288 | UNSIGNED_SHORT_5_5_5_1: 32820,
289 | UNSIGNED_SHORT_5_6_5: 33635,
290 | VALIDATE_STATUS: 35715,
291 | VENDOR: 7936,
292 | VERSION: 7938,
293 | VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 34975,
294 | VERTEX_ATTRIB_ARRAY_ENABLED: 34338,
295 | VERTEX_ATTRIB_ARRAY_NORMALIZED: 34922,
296 | VERTEX_ATTRIB_ARRAY_POINTER: 34373,
297 | VERTEX_ATTRIB_ARRAY_SIZE: 34339,
298 | VERTEX_ATTRIB_ARRAY_STRIDE: 34340,
299 | VERTEX_ATTRIB_ARRAY_TYPE: 34341,
300 | VERTEX_SHADER: 35633,
301 | VIEWPORT: 2978,
302 | ZERO: 0,
303 |
304 | activeTexture: "gl.acT",
305 | attachShader: "gl.atS",
306 | bindAttribLocation: "gl.biAL",
307 | bindBuffer: "gl.biB",
308 | bindFramebuffer: "gl.biF",
309 | bindRenderbuffer: "gl.biR",
310 | bindTexture: "gl.biT",
311 | blendColor: "gl.blC",
312 | blendEquation: "gl.blE",
313 | blendEquationSeparate: "gl.blES",
314 | blendFunc: "gl.blF",
315 | blendFuncSeparate: "gl.blFS",
316 | bufferData: "gl.buD",
317 | bufferSubData: "gl.buSD",
318 | checkFramebufferStatus: "gl.chFS",
319 | clear: "gl.cl",
320 | clearColor: "gl.clC",
321 | clearDepth: "gl.clD",
322 | clearStencil: "gl.clS",
323 | colorMask: "gl.coM",
324 | compileShader: "gl.coS",
325 | compressedTexImage2D: "gl.coTI2D",
326 | compressedTexSubImage2D: "gl.coTSI2D",
327 | copyTexImage2D: "gl.coTI2D",
328 | copyTexSubImage2D: "gl.coTSI2D",
329 | createBuffer: "gl.crB",
330 | createFramebuffer: "gl.crF",
331 | createProgram: "gl.crP",
332 | createRenderbuffer: "gl.crR",
333 | createShader: "gl.crS",
334 | createTexture: "gl.crT",
335 | cullFace: "gl.cuF",
336 | deleteBuffer: "gl.deB",
337 | deleteFramebuffer: "gl.deF",
338 | deleteProgram: "gl.deP",
339 | deleteRenderbuffer: "gl.deR",
340 | deleteShader: "gl.deS",
341 | deleteTexture: "gl.deT",
342 | depthFunc: "gl.deF",
343 | depthMask: "gl.deM",
344 | depthRange: "gl.deR",
345 | detachShader: "gl.deS",
346 | disable: "gl.di",
347 | disableVertexAttribArray: "gl.diVAA",
348 | drawArrays: "gl.drA",
349 | drawElements: "gl.drE",
350 | enable: "gl.en",
351 | enableVertexAttribArray: "gl.enVAA",
352 | finish: "gl.fi",
353 | flush: "gl.fl",
354 | framebufferRenderbuffer: "gl.frR",
355 | framebufferTexture2D: "gl.frT2D",
356 | frontFace: "gl.frF",
357 | generateMipmap: "gl.geM",
358 | getActiveAttrib: "gl.geAA",
359 | getActiveUniform: "gl.geAU",
360 | getAttachedShaders: "gl.geAS",
361 | getAttribLocation: "gl.geAL",
362 | getBufferParameter: "gl.geBP",
363 | getContextAttributes: "gl.geCA",
364 | getError: "gl.geE",
365 | getExtension: "gl.geE",
366 | getFramebufferAttachmentParameter: "gl.geFAP",
367 | getParameter: "gl.geP",
368 | getProgramParameter: "gl.gePP",
369 | getProgramInfoLog: "gl.gePIL",
370 | getRenderbufferParameter: "gl.geRP",
371 | getShaderParameter: "gl.geSP",
372 | getShaderInfoLog: "gl.geSIL",
373 | getShaderPrecisionFormat: "gl.geSPF",
374 | getShaderSource: "gl.geSS",
375 | getSupportedExtensions: "gl.geSE",
376 | getTexParameter: "gl.geTP",
377 | getUniform: "gl.geU",
378 | getUniformLocation: "gl.geUL",
379 | getVertexAttrib: "gl.geVA",
380 | getVertexAttribOffset: "gl.geVAO",
381 | hint: "gl.hi",
382 | isBuffer: "gl.isB",
383 | isContextLost: "gl.isCL",
384 | isEnabled: "gl.isE",
385 | isFramebuffer: "gl.isF",
386 | isProgram: "gl.isP",
387 | isRenderbuffer: "gl.isR",
388 | isShader: "gl.isS",
389 | isTexture: "gl.isT",
390 | lineWidth: "gl.liW",
391 | linkProgram: "gl.liP",
392 | pixelStorei: "gl.piS",
393 | polygonOffset: "gl.poO",
394 | readPixels: "gl.reP",
395 | renderbufferStorage: "gl.reS",
396 | sampleCoverage: "gl.saC",
397 | scissor: "gl.sc",
398 | shaderSource: "gl.shS",
399 | stencilFunc: "gl.stF",
400 | stencilFuncSeparate: "gl.stFS",
401 | stencilMask: "gl.stM",
402 | stencilMaskSeparate: "gl.stMS",
403 | stencilOp: "gl.stO",
404 | stencilOpSeparate: "gl.stOS",
405 | texParameterf: "gl.teP",
406 | texParameteri: "gl.teP",
407 | texImage2D: "gl.teI2D",
408 | texSubImage2D: "gl.teSI2D",
409 | uniform1f: "gl.un1f",
410 | uniform1fv: "gl.un1fv",
411 | uniform1i: "gl.un1i",
412 | uniform1iv: "gl.un1iv",
413 | uniform2f: "gl.un2f",
414 | uniform2fv: "gl.un2fv",
415 | uniform2i: "gl.un2i",
416 | uniform2iv: "gl.un2iv",
417 | uniform3f: "gl.un3f",
418 | uniform3fv: "gl.un3fv",
419 | uniform3i: "gl.un3i",
420 | uniform3iv: "gl.un3iv",
421 | uniform4f: "gl.un4f",
422 | uniform4fv: "gl.un4fv",
423 | uniform4i: "gl.un4i",
424 | uniform4iv: "gl.un4iv",
425 | uniformMatrix2fv: "gl.unM2fv",
426 | uniformMatrix3fv: "gl.unM3fv",
427 | uniformMatrix4fv: "gl.unM4fv",
428 | useProgram: "gl.usP",
429 | validateProgram: "gl.vaP",
430 | vertexAttrib1f: "gl.veA1f",
431 | vertexAttrib1fv: "gl.veA1fv",
432 | vertexAttrib2f: "gl.veA2f",
433 | vertexAttrib2fv: "gl.veA2fv",
434 | vertexAttrib3f: "gl.veA3f",
435 | vertexAttrib3fv: "gl.veA3fv",
436 | vertexAttrib4f: "gl.veA4f",
437 | vertexAttrib4fv: "gl.veA4fv",
438 | vertexAttribPointer: "gl.veAP",
439 | viewport: "gl.vi"
440 | };
441 |
442 | const sonantxr_replace = {
443 | rowLen: '_rl',
444 | endPattern: '_ep',
445 | songData: '_sd',
446 | osc1_oct: '_1o',
447 | osc1_det: '_1d',
448 | osc1_detune: '_1t',
449 | osc1_xenv: '_1x',
450 | osc1_vol: '_1v',
451 | osc1_waveform: '_1w',
452 | osc2_oct: '_2o',
453 | osc2_det: '_2d',
454 | osc2_detune: '_2t',
455 | osc2_xenv: '_2x',
456 | osc2_vol: '_2v',
457 | osc2_waveform: '_2w',
458 | noise_fader: '_nf',
459 | env_attack: '_ea',
460 | env_sustain: '_es',
461 | env_release: '_er',
462 | env_master: '_em',
463 | fx_filter: '_ff',
464 | fx_freq: '_fq',
465 | fx_resonance: '_fr',
466 | fx_delay_time: '_ft',
467 | fx_delay_amt: '_fa',
468 | fx_pan_freq: '_fp',
469 | fx_pan_amt: '_fm',
470 | lfo_osc1_freq: '_lf',
471 | lfo_fx_freq: '_lx',
472 | lfo_freq: '_lq',
473 | lfo_amt: '_la',
474 | lfo_waveform: '_lw'
475 | };
476 |
477 | const fs = require('fs');
478 |
479 | fs.readFile(process.argv[2], 'utf8', (err, data) => {
480 | for (var name in webgl_replace) {
481 | var d = webgl_replace[name];
482 | data = data.replace(new RegExp('\\bgl\\.'+name+'\\b', 'g'), d);
483 | }
484 | for (var name in sonantxr_replace) {
485 | var d = sonantxr_replace[name];
486 | data = data.replace(new RegExp('\\b'+name+'\\b', 'g'), d);
487 | }
488 | console.log(data);
489 | process.exit(0);
490 | });
--------------------------------------------------------------------------------
/source/audio.js:
--------------------------------------------------------------------------------
1 | var audio_ctx = new (window.webkitAudioContext||window.AudioContext)(),
2 | audio_sfx_shoot,
3 | audio_sfx_hit,
4 | audio_sfx_hurt,
5 | audio_sfx_beep,
6 | audio_sfx_pickup,
7 | audio_sfx_terminal,
8 | audio_sfx_explode;
9 |
10 | function audio_init(callback) {
11 | sonantxr_generate_song(audio_ctx, music_dark_meat_beat, function(buffer){
12 | audio_play(buffer, true);
13 | callback();
14 | });
15 | sonantxr_generate_sound(audio_ctx, sound_shoot, 140, function(buffer){
16 | audio_sfx_shoot = buffer;
17 | });
18 | sonantxr_generate_sound(audio_ctx, sound_hit, 134, function(buffer){
19 | audio_sfx_hit = buffer;
20 | });
21 | sonantxr_generate_sound(audio_ctx, sound_beep, 173, function(buffer){
22 | audio_sfx_beep = buffer;
23 | });
24 | sonantxr_generate_sound(audio_ctx, sound_hurt, 144, function(buffer){
25 | audio_sfx_hurt = buffer;
26 | });
27 | sonantxr_generate_sound(audio_ctx, sound_pickup, 156, function(buffer){
28 | audio_sfx_pickup = buffer;
29 | });
30 | sonantxr_generate_sound(audio_ctx, sound_terminal, 156, function(buffer){
31 | audio_sfx_terminal = buffer;
32 | });
33 | sonantxr_generate_sound(audio_ctx, sound_explode, 114, function(buffer){
34 | audio_sfx_explode = buffer;
35 | });
36 | };
37 |
38 | function audio_play(buffer, loop) {
39 | var source = audio_ctx.createBufferSource();
40 | source.buffer = buffer;
41 | source.loop = loop;
42 | source.connect(audio_ctx.destination);
43 | source.start();
44 | };
--------------------------------------------------------------------------------
/source/entity-cpu.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_cpu_t extends entity_t {
3 | _init() {
4 | this._animation_time = 0;
5 | }
6 |
7 | _render() {
8 | this._animation_time += time_elapsed;
9 |
10 | push_block(this.x, this.z, 4, 17);
11 | var intensity = this.h == 5
12 | ? 0.02 + _math.sin(this._animation_time*10+_math.random()*2) * 0.01
13 | : 0.01;
14 | push_light(this.x + 4, 4, this.z + 12, 0.2, 0.4, 1.0, intensity);
15 | }
16 |
17 | _check(other) {
18 |
19 | if (this.h == 5 && other instanceof(entity_player_t)) {
20 | this.h = 10;
21 | cpus_rebooted++;
22 |
23 | var reboot_message =
24 | '\n\n\nREBOOTING..._' +
25 | 'SUCCESS\n';
26 |
27 | if (cpus_total-cpus_rebooted > 0) {
28 | terminal_show_notice(
29 | reboot_message +
30 | (cpus_total-cpus_rebooted)+' SYSTEM(S) STILL OFFLINE'
31 | );
32 | }
33 | else {
34 | if (current_level != 3) {
35 | terminal_show_notice(
36 | reboot_message +
37 | 'ALL SYSTEMS ONLINE\n' +
38 | 'TRIANGULATING POSITION FOR NEXT HOP...___' +
39 | 'TARGET ACQUIRED\n' +
40 | 'JUMPING...',
41 | next_level
42 | );
43 | }
44 | else {
45 | terminal_show_notice(
46 | reboot_message +
47 | 'ALL SYSTEMS ONLINE',
48 | next_level
49 | );
50 | }
51 | }
52 | audio_play(audio_sfx_beep);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/source/entity-explosion.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_explosion_t extends entity_t {
3 | _init() {
4 | this._lifetime = 1;
5 | }
6 |
7 | _update() {
8 | super._update();
9 | this._lifetime -= time_elapsed;
10 | if (this._lifetime < 0) {
11 | this._kill();
12 | }
13 | }
14 |
15 | _render() {
16 | push_light(this.x, 4, this.z + 6, 1,0.7,0.3, 0.08*(1-this._lifetime));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/source/entity-health.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_health_t extends entity_t {
3 | _check(other) {
4 | if (other instanceof(entity_player_t)) {
5 | this._kill();
6 | other.h += other.h < 5 ? 1 : 0;
7 | audio_play(audio_sfx_pickup);
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/source/entity-particle.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_particle_t extends entity_t {
3 | _init() {
4 | this._lifetime = 3;
5 | }
6 |
7 | _update() {
8 | this.ay = -320;
9 |
10 | if (this.y < 0) {
11 | this.y = 0;
12 | this.vy = -this.vy * 0.96;
13 | }
14 | super._update();
15 | this._lifetime -= time_elapsed;
16 | if (this._lifetime < 0) {
17 | this._kill();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/source/entity-plasma.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_plasma_t extends entity_t {
3 | _init(angle) {
4 | var speed = 96;
5 | this.vx = _math.cos(angle) * speed;
6 | this.vz = _math.sin(angle) * speed;
7 | }
8 |
9 | _render() {
10 | super._render();
11 | push_light(this.x, 4, this.z + 6, 0.9, 0.2, 0.1, 0.04);
12 | }
13 |
14 | _did_collide() {
15 | this._kill();
16 | }
17 |
18 | _check(other) {
19 | if (other instanceof(entity_spider_t) || other instanceof(entity_sentry_t)) {
20 | audio_play(audio_sfx_hit);
21 | other._receive_damage(this, 1);
22 | this._kill();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/source/entity-player.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_player_t extends entity_t {
3 | _init() {
4 | this._bob = this._last_shot = this._last_damage = this._frame = 0;
5 | }
6 |
7 | _update() {
8 | var t = this,
9 | speed = 128;
10 |
11 | // movement
12 | t.ax = keys[key_left] ? -speed : keys[key_right] ? speed : 0;
13 | t.az = keys[key_up] ? -speed : keys[key_down] ? speed : 0;
14 |
15 | // rotation - select appropriate sprite
16 | var angle = _math.atan2(
17 | mouse_y - (-34 + c.height * 0.8),
18 | mouse_x - (t.x + 6 + camera_x + c.width * 0.5)
19 | );
20 | t.s = 18 + ((angle / _math.PI * 4 + 10.5) % 8)|0;
21 |
22 | // bobbing
23 | t._bob += time_elapsed * 1.75 * (_math.abs(t.vx) + _math.abs(t.vz));
24 | t.y = _math.sin(t._bob) * 0.25;
25 |
26 | t._last_damage -= time_elapsed;
27 | t._last_shot -= time_elapsed;
28 |
29 | if (keys[key_shoot] && t._last_shot < 0) {
30 | audio_play(audio_sfx_shoot);
31 | new entity_plasma_t(t.x, 0, t.z, 0, 26, angle + _math.random() * 0.2 - 0.11);
32 | t._last_shot = 0.1;
33 | }
34 |
35 | super._update();
36 | }
37 |
38 | _render() {
39 | this._frame++;
40 | if (this._last_damage < 0 || this._frame % 6 < 4) {
41 | super._render();
42 | }
43 | push_light(this.x, 4, this.z + 6, 1,0.5,0, 0.04);
44 | }
45 |
46 | _kill() {
47 | super._kill();
48 | this.y = 10;
49 | this.z += 5;
50 | terminal_show_notice(
51 | 'DEPLOYMENT FAILED\n' +
52 | 'RESTORING BACKUP...'
53 | );
54 | setTimeout(reload_level, 3000);
55 | }
56 |
57 | _receive_damage(from, amount) {
58 | if (this._last_damage < 0) {
59 | audio_play(audio_sfx_hurt);
60 | super._receive_damage(from, amount);
61 | this._last_damage = 2;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/source/entity-sentry.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_sentry_t extends entity_t {
3 | _init() {
4 | this._select_target_counter = 0;
5 | this._target_x = this.x;
6 | this._target_z = this.z;
7 | this.h = 20;
8 | }
9 |
10 | _update() {
11 | var t = this,
12 | txd = t.x - t._target_x,
13 | tzd = t.z - t._target_z,
14 | xd = t.x - entity_player.x,
15 | zd = t.z - entity_player.z,
16 | dist = _math.sqrt(xd * xd + zd * zd);
17 |
18 | t._select_target_counter -= time_elapsed;
19 |
20 | // select new target after a while
21 | if (t._select_target_counter < 0) {
22 | if (dist < 64) {
23 | t._select_target_counter = _math.random() * 0.5 + 0.3;
24 | t._target_x = entity_player.x;
25 | t._target_z = entity_player.z;
26 | }
27 | if (dist < 48) {
28 | var angle = _math.atan2(
29 | entity_player.z - this.z,
30 | entity_player.x - this.x
31 | );
32 | new entity_sentry_plasma_t(t.x, 0, t.z, 0, 26, angle + _math.random() * 0.2 - 0.11);
33 | }
34 | }
35 |
36 | // set velocity towards target
37 | if (dist > 24) {
38 | t.ax = _math.abs(txd) > 2 ? (txd > 0 ? -48 : 48) : 0;
39 | t.az = _math.abs(tzd) > 2 ? (tzd > 0 ? -48 : 48) : 0;
40 | } else {
41 | t.ax = t.az = 0;
42 | }
43 |
44 | super._update();
45 | }
46 |
47 | _receive_damage(from, amount) {
48 | super._receive_damage(from, amount);
49 | this.vx = from.vx * 0.1;
50 | this.vz = from.vz * 0.1;
51 | this._spawn_particles(3);
52 | }
53 |
54 | _kill() {
55 | super._kill();
56 | new entity_explosion_t(this.x, 0, this.z, 0, 26);
57 | camera_shake = 3;
58 | audio_play(audio_sfx_explode);
59 | }
60 | }
61 |
62 | class entity_sentry_plasma_t extends entity_t {
63 | _init(angle) {
64 | var speed = 64;
65 | this.vx = _math.cos(angle) * speed;
66 | this.vz = _math.sin(angle) * speed;
67 | }
68 |
69 | _render() {
70 | super._render();
71 | push_light(this.x, 4, this.z + 6, 1.5, 0.2, 0.1, 0.04);
72 | }
73 |
74 | _did_collide() {
75 | this._kill();
76 | }
77 |
78 | _check(other) {
79 | if (other instanceof(entity_player_t)) {
80 | other._receive_damage(this, 1);
81 | this._kill();
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/source/entity-spider.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_spider_t extends entity_t {
3 | _init() {
4 | this._animation_time = 0;
5 | this._select_target_counter = 0;
6 | this._target_x = this.x;
7 | this._target_z = this.z;
8 | }
9 |
10 | _update() {
11 | var t = this,
12 | txd = t.x - t._target_x,
13 | tzd = t.z - t._target_z,
14 | xd = t.x - entity_player.x,
15 | zd = t.z - entity_player.z,
16 | dist = _math.sqrt(xd * xd + zd * zd);
17 |
18 | t._select_target_counter -= time_elapsed;
19 |
20 | // select new target after a while
21 | if (t._select_target_counter < 0 && dist < 64) {
22 | t._select_target_counter = _math.random() * 0.5 + 0.3;
23 | t._target_x = entity_player.x;
24 | t._target_z = entity_player.z;
25 | }
26 |
27 | // set velocity towards target
28 | t.ax = _math.abs(txd) > 2 ? (txd > 0 ? -160 : 160) : 0;
29 | t.az = _math.abs(tzd) > 2 ? (tzd > 0 ? -160 : 160) : 0;
30 |
31 | super._update();
32 | this._animation_time += time_elapsed;
33 | this.s = 27 + ((this._animation_time*15)|0)%3;
34 | }
35 |
36 | _receive_damage(from, amount) {
37 | super._receive_damage(from, amount);
38 | this.vx = from.vx;
39 | this.vz = from.vz;
40 | this._spawn_particles(5);
41 | }
42 |
43 | _check(other) {
44 | // slightly bounce off from other spiders to separate them
45 | if (other instanceof entity_spider_t) {
46 | var
47 | axis = (_math.abs(other.x - this.x) > _math.abs(other.z - this.z)
48 | ? 'x'
49 | : 'z'),
50 | amount = this[axis] > other[axis] ? 0.6 : -0.6;
51 |
52 | this['v'+axis] += amount;
53 | other['v'+axis] -= amount;
54 | }
55 |
56 | // hurt player
57 | else if (other instanceof entity_player_t) {
58 | this.vx *= -1.5;
59 | this.vz *= -1.5;
60 | other._receive_damage(this, 1);
61 | }
62 | }
63 |
64 | _kill() {
65 | super._kill();
66 | new entity_explosion_t(this.x, 0, this.z, 0, 26);
67 | camera_shake = 1;
68 | audio_play(audio_sfx_explode);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/source/entity.js:
--------------------------------------------------------------------------------
1 |
2 | class entity_t {
3 | constructor(x, y, z, friction, sprite, init_param) {
4 | var t = this;
5 | t.x = x; t.y = y; t.z = z;
6 | t.vx = t.vy = t.vz = t.ax = t.ay = t.az = 0;
7 | t.f = friction;
8 | t.s = sprite;
9 | t.h = 5;
10 |
11 | t._init(init_param);
12 | entities.push(t);
13 | }
14 |
15 | // separate _init() method, because "constructor" cannot be uglyfied
16 | _init(init_param) {}
17 |
18 | _update() {
19 | var t = this,
20 | last_x = t.x, last_z = t.z;
21 |
22 | // velocity
23 | t.vx += t.ax * time_elapsed - t.vx * _math.min(t.f * time_elapsed, 1);
24 | t.vy += t.ay * time_elapsed - t.vy * _math.min(t.f * time_elapsed, 1);
25 | t.vz += t.az * time_elapsed - t.vz * _math.min(t.f * time_elapsed, 1);
26 |
27 | // position
28 | t.x += t.vx * time_elapsed;
29 | t.y += t.vy * time_elapsed;
30 | t.z += t.vz * time_elapsed;
31 |
32 | // check wall collissions, horizontal
33 | if (t._collides(t.x, last_z)) {
34 | t._did_collide(t.x, t.y);
35 | t.x = last_x;
36 | t.vx = 0;
37 | }
38 |
39 | // check wall collissions, vertical
40 | if (t._collides(t.x, t.z)) {
41 | t._did_collide(t.x, t.y);
42 | t.z = last_z;
43 | t.vz = 0;
44 | }
45 | }
46 |
47 | _collides(x, z) {
48 | return level_data[(x >> 3) + (z >> 3) * level_width] > 7 || // top left
49 | level_data[((x + 6) >> 3) + (z >> 3) * level_width] > 7 || // top right
50 | level_data[((x + 6) >> 3) + ((z+4) >> 3) * level_width] > 7 || // bottom right
51 | level_data[(x >> 3) + ((z+4) >> 3) * level_width] > 7; // bottom left
52 | }
53 |
54 | _spawn_particles(amount) {
55 | for (var i = 0; i < amount; i++) {
56 | var particle = new entity_particle_t(this.x, 0, this.z, 1, 30);
57 | particle.vx = (_math.random() - 0.5) * 128;
58 | particle.vy = _math.random() * 96;
59 | particle.vz = (_math.random() - 0.5) * 128;
60 | }
61 | }
62 |
63 | // collision against static walls
64 | _did_collide() {}
65 |
66 | // collision against other entities
67 | _check(other) {}
68 |
69 | _receive_damage(from, amount) {
70 | this.h -= amount;
71 | if (this.h <= 0) {
72 | this._kill();
73 | }
74 | }
75 |
76 | _kill() {
77 | if (!this._dead) {
78 | this._dead = true;
79 | entities_to_kill.push(this);
80 | }
81 | }
82 |
83 | _render() { // render
84 | var t = this;
85 | push_sprite(t.x-1, t.y, t.z, t.s);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/source/game.js:
--------------------------------------------------------------------------------
1 |
2 | var udef, // global undefined
3 | _math = Math,
4 | _document = document,
5 | _temp,
6 |
7 | keys = {37: 0, 38: 0, 39: 0, 40: 0},
8 | key_up = 38, key_down = 40, key_left = 37, key_right = 39, key_shoot = 512,
9 | key_convert = {65: 37, 87: 38, 68: 39, 83: 40}, // convert AWDS to left up down right
10 | mouse_x = 0, mouse_y = 0,
11 |
12 | time_elapsed,
13 | time_last = performance.now(),
14 |
15 | level_width = 64,
16 | level_height = 64,
17 | level_data = new Uint8Array(level_width * level_height),
18 |
19 | cpus_total = 0,
20 | cpus_rebooted = 0,
21 |
22 | current_level = 0,
23 | entity_player,
24 | entities = [],
25 | entities_to_kill = [];
26 |
27 | function load_image(name, callback) {
28 | _temp = new Image();
29 | _temp.src = 'm/'+name+'.png';
30 | _temp.onload = callback;
31 | }
32 |
33 | function next_level(callback) {
34 | if (current_level == 3) {
35 | entities_to_kill.push(entity_player);
36 | terminal_run_outro();
37 | }
38 | else {
39 | current_level++;
40 | load_level(current_level, callback);
41 |
42 | }
43 | }
44 |
45 | function load_level(id, callback) {
46 | random_seed(0xBADC0DE1 + id);
47 | load_image('l'+id, function(){
48 | entities = [];
49 | num_verts = 0;
50 | num_lights = 0;
51 |
52 | cpus_total = 0;
53 | cpus_rebooted = 0;
54 |
55 | _temp = _document.createElement('canvas');
56 | _temp.width = _temp.height = level_width; // assume square levels
57 | _temp = _temp.getContext('2d')
58 | _temp.drawImage(this, 0, 0);
59 | _temp =_temp.getImageData(0, 0, level_width, level_height).data;
60 |
61 | for (var y = 0, index = 0; y < level_height; y++) {
62 | for (var x = 0; x < level_width; x++, index++) {
63 |
64 | // reduce to 12 bit color to accurately match
65 | var color_key =
66 | ((_temp[index*4]>>4) << 8) +
67 | ((_temp[index*4+1]>>4) << 4) +
68 | (_temp[index*4+2]>>4);
69 |
70 | if (color_key !== 0) {
71 | var tile = level_data[index] =
72 | color_key === 0x888 // wall
73 | ? random_int(0,5) < 4 ? 8 : random_int(8, 17)
74 | : array_rand([1,1,1,1,1,3,3,2,5,5,5,5,5,5,7,7,6]); // floor
75 |
76 |
77 | if (tile > 7) { // walls
78 | push_block(x * 8, y * 8, 4, tile-1);
79 | }
80 | else if (tile > 0) { // floor
81 | push_floor(x * 8, y * 8, tile-1);
82 |
83 | // enemies and items
84 | if (random_int(0, 16 - (id * 2)) == 0) {
85 | new entity_spider_t(x*8, 0, y*8, 5, 27);
86 | }
87 | else if (random_int(0, 100) == 0) {
88 | new entity_health_t(x*8, 0, y*8, 5, 31);
89 | }
90 | }
91 |
92 | // cpu
93 | if (color_key === 0x00f) {
94 | level_data[index] = 8;
95 | new entity_cpu_t(x*8, 0, y*8, 0, 18);
96 | cpus_total++;
97 | }
98 |
99 | // sentry
100 | if (color_key === 0xf00) {
101 | new entity_sentry_t(x*8, 0, y*8, 5, 32);
102 | }
103 |
104 | // player start position (blue)
105 | if (color_key === 0x0f0) {
106 | entity_player = new entity_player_t(x*8, 0, y*8, 5, 18);
107 | }
108 | }
109 | }
110 | }
111 |
112 | // Remove all spiders that spawned close to the player start
113 | for (var i = 0; i < entities.length; i++) {
114 | var e = entities[i];
115 | if (
116 | e instanceof(entity_spider_t) &&
117 | _math.abs(e.x - entity_player.x) < 64 &&
118 | _math.abs(e.z - entity_player.z) < 64
119 | ) {
120 | entities_to_kill.push(e);
121 | }
122 | }
123 |
124 | camera_x = -entity_player.x;
125 | camera_y = -300;
126 | camera_z = -entity_player.z - 100;
127 |
128 | level_num_verts = num_verts;
129 |
130 | terminal_show_notice(
131 | 'SCANNING FOR OFFLINE SYSTEMS...___' +
132 | (cpus_total)+' SYSTEMS FOUND'
133 | );
134 | callback && callback();
135 | });
136 | }
137 |
138 | function reload_level() {
139 | load_level(current_level);
140 | }
141 |
142 | function preventDefault(ev) {
143 | ev.preventDefault();
144 | }
145 |
146 | _document.onkeydown = function(ev){
147 | _temp = ev.keyCode;
148 | _temp = key_convert[_temp] || _temp;
149 | if (keys[_temp] !== udef) {
150 | keys[_temp] = 1;
151 | preventDefault(ev);
152 | }
153 | }
154 |
155 | _document.onkeyup = function(ev) {
156 | _temp = ev.keyCode;
157 | _temp = key_convert[_temp] || _temp;
158 | if (keys[_temp] !== udef) {
159 | keys[_temp] = 0;
160 | preventDefault(ev);
161 | }
162 | }
163 |
164 | _document.onmousemove = function(ev) {
165 | mouse_x = (ev.clientX / c.clientWidth) * c.width;
166 | mouse_y = (ev.clientY / c.clientHeight) * c.height;
167 | }
168 |
169 | _document.onmousedown = function(ev) {
170 | keys[key_shoot] = 1;
171 | preventDefault(ev);
172 | }
173 |
174 | _document.onmouseup = function(ev) {
175 | keys[key_shoot] = 0;
176 | preventDefault(ev);
177 | }
178 |
179 | function game_tick() {
180 | var time_now = performance.now();
181 | time_elapsed = (time_now - time_last)/1000;
182 | time_last = time_now;
183 |
184 | renderer_prepare_frame();
185 |
186 | // update and render entities
187 | for (var i = 0, e1, e2; i < entities.length; i++) {
188 | e1 = entities[i];
189 | if (e1._dead) { continue; }
190 | e1._update();
191 |
192 | // check for collisions between entities - it's quadratic and nobody cares \o/
193 | for (var j = i+1; j < entities.length; j++) {
194 | e2 = entities[j];
195 | if(!(
196 | e1.x >= e2.x + 9 ||
197 | e1.x + 9 <= e2.x ||
198 | e1.z >= e2.z + 9 ||
199 | e1.z + 9 <= e2.z
200 | )) {
201 | e1._check(e2);
202 | e2._check(e1);
203 | }
204 | }
205 |
206 | e1._render();
207 | }
208 |
209 | // center camera on player, apply damping
210 | camera_x = camera_x * 0.92 - entity_player.x * 0.08;
211 | camera_y = camera_y * 0.92 - entity_player.y * 0.08;
212 | camera_z = camera_z * 0.92 - entity_player.z * 0.08;
213 |
214 | // add camera shake
215 | camera_shake *= 0.9;
216 | camera_x += camera_shake * (_math.random()-0.5);
217 | camera_z += camera_shake * (_math.random()-0.5);
218 |
219 | // health bar, render with plasma sprite
220 | for (var i = 0; i < entity_player.h; i++) {
221 | push_sprite(-camera_x - 50 + i * 4, 29-camera_y, -camera_z-30, 26);
222 | }
223 |
224 | renderer_end_frame();
225 |
226 |
227 | // remove dead entities
228 | entities = entities.filter(function(entity) {
229 | return entities_to_kill.indexOf(entity) === -1;
230 | });
231 | entities_to_kill = [];
232 |
233 | requestAnimationFrame(game_tick);
234 | }
235 |
--------------------------------------------------------------------------------
/source/html-template.html:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/source/main.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | terminal_write_line('INITIATING...');
4 |
5 | audio_init(function(){
6 | _document.onclick = function() {
7 | _document.onclick = null;
8 | terminal_cancel();
9 | terminal_write_line('INITIATING...', function(){
10 | renderer_init();
11 |
12 | load_image('q2', function() {
13 | terminal_hide();
14 | renderer_bind_image(this);
15 | next_level(game_tick);
16 | });
17 |
18 | });
19 | };
20 |
21 | terminal_run_intro();
22 | });
23 |
24 |
25 |
--------------------------------------------------------------------------------
/source/music-dark-meat-beat.js:
--------------------------------------------------------------------------------
1 | var music_dark_meat_beat = {
2 | rowLen: 5513,
3 | endPattern: 25,
4 | songData: [
5 | {
6 | osc1_oct: 7,
7 | osc1_det: 0,
8 | osc1_detune: 0,
9 | osc1_xenv: 0,
10 | osc1_vol: 255,
11 | osc1_waveform: 2,
12 | osc2_oct: 8,
13 | osc2_det: 0,
14 | osc2_detune: 18,
15 | osc2_xenv: 0,
16 | osc2_vol: 255,
17 | osc2_waveform: 3,
18 | noise_fader: 0,
19 | env_attack: 21074,
20 | env_sustain: 56363,
21 | env_release: 100000,
22 | env_master: 199,
23 | fx_filter: 2,
24 | fx_freq: 948,
25 | fx_resonance: 92,
26 | fx_delay_time: 7,
27 | fx_delay_amt: 60,
28 | fx_pan_freq: 3,
29 | fx_pan_amt: 100,
30 | lfo_osc1_freq: 0,
31 | lfo_fx_freq: 1,
32 | lfo_freq: 7,
33 | lfo_amt: 138,
34 | lfo_waveform: 3,
35 | p: [2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5],
36 | c: [
37 | {
38 | n: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
39 | },
40 | {
41 | n: [122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
42 | },
43 | {
44 | n: [114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
45 | },
46 | {
47 | n: [119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
48 | },
49 | {
50 | n: [114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0]
51 | }
52 | ]
53 | },
54 | {
55 | osc1_oct: 7,
56 | osc1_det: 0,
57 | osc1_detune: 0,
58 | osc1_xenv: 0,
59 | osc1_vol: 192,
60 | osc1_waveform: 3,
61 | osc2_oct: 4,
62 | osc2_det: 0,
63 | osc2_detune: 0,
64 | osc2_xenv: 0,
65 | osc2_vol: 57,
66 | osc2_waveform: 0,
67 | noise_fader: 0,
68 | env_attack: 100,
69 | env_sustain: 150,
70 | env_release: 13636,
71 | env_master: 191,
72 | fx_filter: 2,
73 | fx_freq: 5839,
74 | fx_resonance: 254,
75 | fx_delay_time: 4,
76 | fx_delay_amt: 121,
77 | fx_pan_freq: 6,
78 | fx_pan_amt: 147,
79 | lfo_osc1_freq: 0,
80 | lfo_fx_freq: 0,
81 | lfo_freq: 6,
82 | lfo_amt: 195,
83 | lfo_waveform: 0,
84 | p: [2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2],
85 | c: [
86 | {
87 | n: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
88 | },
89 | {
90 | n: [131,0,131,0,131,0,0,0,133,0,134,0,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
91 | }
92 | ]
93 | },
94 | {
95 | osc1_oct: 7,
96 | osc1_det: 2,
97 | osc1_detune: 0,
98 | osc1_xenv: 1,
99 | osc1_vol: 196,
100 | osc1_waveform: 0,
101 | osc2_oct: 7,
102 | osc2_det: 0,
103 | osc2_detune: 0,
104 | osc2_xenv: 1,
105 | osc2_vol: 255,
106 | osc2_waveform: 0,
107 | noise_fader: 0,
108 | env_attack: 100,
109 | env_sustain: 0,
110 | env_release: 3636,
111 | env_master: 254,
112 | fx_filter: 2,
113 | fx_freq: 612,
114 | fx_resonance: 254,
115 | fx_delay_time: 6,
116 | fx_delay_amt: 27,
117 | fx_pan_freq: 0,
118 | fx_pan_amt: 0,
119 | lfo_osc1_freq: 0,
120 | lfo_fx_freq: 0,
121 | lfo_freq: 0,
122 | lfo_amt: 0,
123 | lfo_waveform: 0,
124 | p: [1,1,1,1,1,1,1,1,1,1,1,1],
125 | c: [
126 | {
127 | n: [140,0,0,0,0,0,0,0,140,0,0,0,0,0,0,0,140,0,0,0,0,0,0,0,140,0,0,0,0,0,0,0]
128 | }
129 | ]
130 | },
131 | {
132 | osc1_oct: 7,
133 | osc1_det: 0,
134 | osc1_detune: 0,
135 | osc1_xenv: 0,
136 | osc1_vol: 77,
137 | osc1_waveform: 1,
138 | osc2_oct: 2,
139 | osc2_det: 0,
140 | osc2_detune: 188,
141 | osc2_xenv: 0,
142 | osc2_vol: 7,
143 | osc2_waveform: 0,
144 | noise_fader: 21,
145 | env_attack: 53732,
146 | env_sustain: 0,
147 | env_release: 14545,
148 | env_master: 13,
149 | fx_filter: 0,
150 | fx_freq: 0,
151 | fx_resonance: 240,
152 | fx_delay_time: 2,
153 | fx_delay_amt: 222,
154 | fx_pan_freq: 3,
155 | fx_pan_amt: 47,
156 | lfo_osc1_freq: 0,
157 | lfo_fx_freq: 0,
158 | lfo_freq: 0,
159 | lfo_amt: 0,
160 | lfo_waveform: 0,
161 | p: [0,0,0,0,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3],
162 | c: [
163 | {
164 | n: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
165 | },
166 | {
167 | n: [131,0,131,0,131,0,0,0,133,0,134,0,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
168 | },
169 | {
170 | n: [131,0,131,0,131,0,0,0,136,0,134,0,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
171 | },
172 | {
173 | n: [131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
174 | }
175 | ]
176 | },
177 | {
178 | osc1_oct: 5,
179 | osc1_det: 0,
180 | osc1_detune: 0,
181 | osc1_xenv: 1,
182 | osc1_vol: 20,
183 | osc1_waveform: 0,
184 | osc2_oct: 7,
185 | osc2_det: 0,
186 | osc2_detune: 0,
187 | osc2_xenv: 1,
188 | osc2_vol: 7,
189 | osc2_waveform: 0,
190 | noise_fader: 178,
191 | env_attack: 0,
192 | env_sustain: 6338,
193 | env_release: 15454,
194 | env_master: 100,
195 | fx_filter: 3,
196 | fx_freq: 4352,
197 | fx_resonance: 240,
198 | fx_delay_time: 4,
199 | fx_delay_amt: 99,
200 | fx_pan_freq: 0,
201 | fx_pan_amt: 20,
202 | lfo_osc1_freq: 0,
203 | lfo_fx_freq: 1,
204 | lfo_freq: 7,
205 | lfo_amt: 64,
206 | lfo_waveform: 0,
207 | p: [0,0,0,0,1,1,1,1,1,1,1,1],
208 | c: [
209 | {
210 | n: [0,0,0,0,0,0,0,0,137,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,137,0,0,0,0,0,0,0]
211 | }
212 | ]
213 | },
214 | {
215 | osc1_oct: 8,
216 | osc1_det: 0,
217 | osc1_detune: 0,
218 | osc1_xenv: 1,
219 | osc1_vol: 82,
220 | osc1_waveform: 2,
221 | osc2_oct: 8,
222 | osc2_det: 0,
223 | osc2_detune: 0,
224 | osc2_xenv: 0,
225 | osc2_vol: 0,
226 | osc2_waveform: 0,
227 | noise_fader: 125,
228 | env_attack: 100,
229 | env_sustain: 0,
230 | env_release: 9090,
231 | env_master: 232,
232 | fx_filter: 3,
233 | fx_freq: 5200,
234 | fx_resonance: 63,
235 | fx_delay_time: 4,
236 | fx_delay_amt: 131,
237 | fx_pan_freq: 0,
238 | fx_pan_amt: 0,
239 | lfo_osc1_freq: 0,
240 | lfo_fx_freq: 0,
241 | lfo_freq: 0,
242 | lfo_amt: 0,
243 | lfo_waveform: 0,
244 | p: [0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1],
245 | c: [
246 | {
247 | n: [141,141,141,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
248 | }
249 | ]
250 | }
251 | ],
252 | songLen: 101
253 | };
--------------------------------------------------------------------------------
/source/random.js:
--------------------------------------------------------------------------------
1 | var rand_high, rand_low;
2 |
3 | function random_int(min, max) {
4 | rand_high = ((rand_high << 16) + (rand_high >> 16) + rand_low) & 0xffffffff;
5 | rand_low = (rand_low + rand_high) & 0xffffffff;
6 | var n = (rand_high >>> 0) / 0xffffffff;
7 | return (min + n * (max-min+1))|0;
8 | }
9 |
10 | function random_seed(seed) {
11 | rand_high = seed || 0xBADC0FFE;
12 | rand_low = seed ^ 0x49616E42;
13 | }
14 |
15 | function array_rand(array) {
16 | return array[random_int(0, array.length-1)];
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/source/renderer.js:
--------------------------------------------------------------------------------
1 | var
2 | gl = c.getContext('webgl') || c.getContext('experimental-webgl'),
3 | vertex_buffer,
4 | shader_program,
5 |
6 | texture_size = 1024,
7 | tile_size = 16,
8 | tile_fraction = tile_size / texture_size,
9 | px_nudge = 0.5 / texture_size,
10 |
11 | max_verts = 1024 * 64,
12 | num_verts = 0,
13 | level_num_verts,
14 | buffer_data = new Float32Array(max_verts*8), // allow 64k verts, 8 properties per vert
15 |
16 | light_uniform,
17 | max_lights = 16,
18 | num_lights = 0,
19 | light_data = new Float32Array(max_lights*7), // 32 lights, 7 properties per light
20 |
21 |
22 | camera_x = 0, camera_y = 0, camera_z = 0, camera_shake = 0,
23 | camera_uniform,
24 |
25 | shader_attribute_vec = 'attribute vec',
26 | shader_varying =
27 | 'precision highp float;' +
28 | 'varying vec3 vl;' +
29 | 'varying vec2 vuv;',
30 | shader_uniform = 'uniform ',
31 | shader_const_mat4 = "const mat4 ",
32 |
33 | vertex_shader =
34 | shader_varying +
35 | shader_attribute_vec + "3 p;" +
36 | shader_attribute_vec + "2 uv;" +
37 | shader_attribute_vec + "3 n;" +
38 | shader_uniform + "vec3 cam;" +
39 | shader_uniform + "float l[7*"+max_lights+"];" +
40 | shader_const_mat4 + "v=mat4(1,0,0,0,0,.707,.707,0,0,-.707,.707,0,0,-22.627,-22.627,1);" + // view
41 | shader_const_mat4 + "r=mat4(.977,0,0,0,0,1.303,0,0,0,0,-1,-1,0,0,-2,0);"+ // projection
42 | "void main(void){" +
43 | "vl=vec3(0.3,0.3,0.6);" + // ambient color
44 | "for(int i=0; i<"+max_lights+"; i++) {"+
45 | "vec3 lp=vec3(l[i*7],l[i*7+1],l[i*7+2]);" + // light position
46 | "vl+=vec3(l[i*7+3],l[i*7+4],l[i*7+5])" + // light color *
47 | "*max(dot(n,normalize(lp-p)),0.)" + // diffuse *
48 | "*(1./(l[i*7+6]*(" + // attentuation *
49 | "length(lp-p)" + // distance
50 | ")));" +
51 | "}" +
52 | "vuv=uv;" +
53 | "gl_Position=r*v*(vec4(p+cam,1.));" +
54 | "}",
55 |
56 | fragment_shader =
57 | shader_varying +
58 | shader_uniform + "sampler2D s;" +
59 | "void main(void){" +
60 | "vec4 t=texture2D(s,vuv);" +
61 | "if(t.a<.8)" + // 1) discard alpha
62 | "discard;" +
63 | "if(t.r>0.95&&t.g>0.25&&t.b==0.0)" + // 2) red glowing spider eyes
64 | "gl_FragColor=t;" +
65 | "else{" + // 3) calculate color with lights and fog
66 | "gl_FragColor=t*vec4(vl,1.);" +
67 | "gl_FragColor.rgb*=smoothstep(" +
68 | "112.,16.," + // fog far, near
69 | "gl_FragCoord.z/gl_FragCoord.w" + // fog depth
70 | ");" +
71 | "}" +
72 | "gl_FragColor.rgb=floor(gl_FragColor.rgb*6.35)/6.35;" + // reduce colors to ~256
73 | "}";
74 |
75 |
76 | function renderer_init() {
77 |
78 | // Create shorthand WebGL function names
79 | // var webglShortFunctionNames = {};
80 | for (var name in gl) {
81 | if (gl[name].length != udef) {
82 | gl[name.match(/(^..|[A-Z]|\d.|v$)/g).join('')] = gl[name];
83 | // webglShortFunctionNames[name] = 'gl.'+name.match(/(^..|[A-Z]|\d.|v$)/g).join('');
84 | }
85 | }
86 | // console.log(JSON.stringify(webglShortFunctionNames, null, '\t'));
87 |
88 | vertex_buffer = gl.createBuffer();
89 | gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
90 | gl.bufferData(gl.ARRAY_BUFFER, buffer_data, gl.DYNAMIC_DRAW);
91 |
92 | shader_program = gl.createProgram();
93 | gl.attachShader(shader_program, compile_shader(gl.VERTEX_SHADER, vertex_shader));
94 | gl.attachShader(shader_program, compile_shader(gl.FRAGMENT_SHADER, fragment_shader));
95 | gl.linkProgram(shader_program);
96 | gl.useProgram(shader_program);
97 |
98 | camera_uniform = gl.getUniformLocation(shader_program, "cam");
99 | light_uniform = gl.getUniformLocation(shader_program, "l");
100 |
101 | gl.enable(gl.DEPTH_TEST);
102 | gl.enable(gl.BLEND);
103 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
104 | gl.viewport(0,0,c.width,c.height);
105 |
106 | enable_vertex_attrib('p', 3, 8, 0);
107 | enable_vertex_attrib('uv', 2, 8, 3);
108 | enable_vertex_attrib('n', 3, 8, 5);
109 | }
110 |
111 | function renderer_bind_image(image) {
112 | var texture_2d = gl.TEXTURE_2D;
113 | gl.bindTexture(texture_2d, gl.createTexture());
114 | gl.texImage2D(texture_2d, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
115 | gl.texParameteri(texture_2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
116 | gl.texParameteri(texture_2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
117 | gl.texParameteri(texture_2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
118 | gl.texParameteri(texture_2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
119 | }
120 |
121 | function renderer_prepare_frame() {
122 | num_verts = level_num_verts;
123 | num_lights = 0;
124 |
125 | // reset all lights
126 | light_data.fill(1);
127 | }
128 |
129 | function renderer_end_frame() {
130 | gl.uniform3f(camera_uniform, camera_x, camera_y - 10, camera_z-30);
131 | gl.uniform1fv(light_uniform, light_data);
132 |
133 | gl.clearColor(0,0,0,1);
134 | gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
135 |
136 | gl.bufferData(gl.ARRAY_BUFFER, buffer_data, gl.DYNAMIC_DRAW);
137 | gl.drawArrays(gl.TRIANGLES, 0, num_verts);
138 | };
139 |
140 | function push_quad(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, nx, ny, nz, tile) {
141 | var u = tile * tile_fraction + px_nudge;
142 | buffer_data.set([
143 | x1, y1, z1, u, 0, nx, ny, nz,
144 | x2, y2, z2, u + tile_fraction - px_nudge, 0, nx, ny, nz,
145 | x3, y3, z3, u, 1, nx, ny, nz,
146 | x2, y2, z2, u + tile_fraction - px_nudge, 0, nx, ny, nz,
147 | x3, y3, z3, u, 1, nx, ny, nz,
148 | x4, y4, z4, u + tile_fraction - px_nudge, 1, nx, ny, nz
149 | ], num_verts * 8);
150 | num_verts += 6;
151 | };
152 |
153 | function push_sprite(x, y, z, tile) {
154 | // Only push sprites near to the camera
155 | if (
156 | _math.abs(-x - camera_x) < 128 &&
157 | _math.abs(-z - camera_z) < 128
158 | ) {
159 | var tilt = 3+(camera_z + z)/12; // tilt sprite when closer to camera
160 | push_quad(x, y + 6, z, x + 6, y + 6, z, x, y, z + tilt, x + 6, y, z + tilt, 0, 0, 1, tile);
161 | }
162 | }
163 |
164 | function push_floor(x, z, tile) {
165 | push_quad(x, 0, z, x + 8, 0, z, x, 0, z + 8, x + 8, 0, z + 8, 0,1,0, tile);
166 | };
167 |
168 | function push_block(x, z, tile_top, tile_sites) {
169 | // tall blocks for certain tiles
170 | var y = ~[8, 9, 17].indexOf(tile_sites) ? 16 : 8;
171 |
172 | push_quad(x, y, z, x + 8, y, z, x, y, z + 8, x + 8, y, z + 8, 0, 1, 0, tile_top); // top
173 | push_quad(x + 8, y, z, x + 8, y, z + 8, x + 8, 0, z, x + 8, 0, z + 8, 1, 0, 0, tile_sites); // right
174 | push_quad(x, y, z + 8, x + 8, y, z + 8, x, 0, z + 8, x + 8, 0, z + 8, 0, 0, 1, tile_sites); // front
175 | push_quad(x, y, z, x, y, z + 8, x, 0, z, x, 0, z + 8, -1, 0, 0, tile_sites); // left
176 | };
177 |
178 | function push_light(x, y, z, r, g, b, falloff) {
179 | // Only push lights near to the camera
180 | var max_light_distance = (128 + 1/falloff); // cheap ass approximation
181 | if (
182 | num_lights < max_lights &&
183 | _math.abs(-x - camera_x) < max_light_distance &&
184 | _math.abs(-z - camera_z) < max_light_distance
185 | ) {
186 | light_data.set([x, y, z, r, g, b, falloff], num_lights*7);
187 | num_lights++;
188 | }
189 | }
190 |
191 | function compile_shader(shader_type, shader_source) {
192 | var shader = gl.createShader(shader_type);
193 | gl.shaderSource(shader, shader_source);
194 | gl.compileShader(shader);
195 | // console.log(gl.getShaderInfoLog(shader));
196 | return shader;
197 | };
198 |
199 | function enable_vertex_attrib(attrib_name, count, vertex_size, offset) {
200 | var location = gl.getAttribLocation(shader_program, attrib_name);
201 | gl.enableVertexAttribArray(location);
202 | gl.vertexAttribPointer(location, count, gl.FLOAT, false, vertex_size * 4, offset * 4);
203 | };
204 |
--------------------------------------------------------------------------------
/source/sonantx-reduced.js:
--------------------------------------------------------------------------------
1 | // Gutted for js13k and modified to use Float32 buffers directly
2 | // ~ Dominic Szablewski, phoboslab.org, Sep 2018
3 |
4 | //
5 | // Sonant-X
6 | //
7 | // Copyright (c) 2014 Nicolas Vanhoren
8 | //
9 | // Sonant-X is a fork of js-sonant by Marcus Geelnard and Jake Taylor. It is
10 | // still published using the same license (zlib license, see below).
11 | //
12 | // Copyright (c) 2011 Marcus Geelnard
13 | // Copyright (c) 2008-2009 Jake Taylor
14 | //
15 | // This software is provided 'as-is', without any express or implied
16 | // warranty. In no event will the authors be held liable for any damages
17 | // arising from the use of this software.
18 | //
19 | // Permission is granted to anyone to use this software for any purpose,
20 | // including commercial applications, and to alter it and redistribute it
21 | // freely, subject to the following restrictions:
22 | //
23 | // 1. The origin of this software must not be misrepresented; you must not
24 | // claim that you wrote the original software. If you use this software
25 | // in a product, an acknowledgment in the product documentation would be
26 | // appreciated but is not required.
27 | //
28 | // 2. Altered source versions must be plainly marked as such, and must not be
29 | // misrepresented as being the original software.
30 | //
31 | // 3. This notice may not be removed or altered from any source
32 | // distribution.
33 |
34 | var sonantxr_generate_song, sonantxr_generate_sound;
35 |
36 | (function() {
37 | var WAVE_SPS = 44100; // Samples per second
38 | var MAX_TIME = 33; // maximum time, in millis, that the generator can use consecutively
39 |
40 | var audioCtx = null;
41 |
42 | // Oscillators
43 | function osc_sin(value)
44 | {
45 | return _math.sin(value * 6.283184);
46 | }
47 |
48 | function osc_square(value)
49 | {
50 | return osc_sin(value) < 0 ? -1 : 1;
51 | }
52 |
53 | function osc_saw(value)
54 | {
55 | return (value % 1) - 0.5;
56 | }
57 |
58 | function osc_tri(value)
59 | {
60 | var v2 = (value % 1) * 4;
61 | return v2 < 2 ? v2 - 1 : 3 - v2;
62 | }
63 |
64 | // Array of oscillator functions
65 | var oscillators =
66 | [
67 | osc_sin,
68 | osc_square,
69 | osc_saw,
70 | osc_tri
71 | ];
72 |
73 | function getnotefreq(n)
74 | {
75 | return 0.00390625 * _math.pow(1.059463094, n - 128);
76 | }
77 |
78 | function generateBuffer(samples) {
79 | return {
80 | left: new Float32Array(samples),
81 | right: new Float32Array(samples)
82 | };
83 | }
84 |
85 | function applyDelay(chnBuf, waveSamples, instr, rowLen) {
86 | var p1 = (instr.fx_delay_time * rowLen) >> 1;
87 | var t1 = instr.fx_delay_amt / 255;
88 |
89 | var n1 = 0;
90 | while(n1 < waveSamples - p1) {
91 | var b1 = n1;
92 | var l = (n1 + p1);
93 | chnBuf.left[l] += chnBuf.right[b1] * t1;
94 | chnBuf.right[l] += chnBuf.left[b1] * t1;
95 | n1++;
96 | }
97 | }
98 |
99 |
100 | function getAudioBuffer(ctx, mixBuf) {
101 | var buffer = ctx.createBuffer(2, mixBuf.left.length, WAVE_SPS); // Create Mono Source Buffer from Raw Binary
102 | buffer.getChannelData(0).set(mixBuf.left);
103 | buffer.getChannelData(1).set(mixBuf.right);
104 | return buffer;
105 | }
106 |
107 | var SoundGenerator = function(ctx, instr, rowLen) {
108 | this.ctx = ctx;
109 | this.instr = instr;
110 | this.rowLen = rowLen || 5605;
111 |
112 | this.osc_lfo = oscillators[instr.lfo_waveform];
113 | this.osc1 = oscillators[instr.osc1_waveform];
114 | this.osc2 = oscillators[instr.osc2_waveform];
115 | this.attack = instr.env_attack;
116 | this.sustain = instr.env_sustain;
117 | this.release = instr.env_release;
118 | this.panFreq = _math.pow(2, instr.fx_pan_freq - 8) / this.rowLen;
119 | this.lfoFreq = _math.pow(2, instr.lfo_freq - 8) / this.rowLen;
120 | };
121 |
122 | SoundGenerator.prototype._genSound = function(n, chnBuf, currentpos) {
123 | var c1 = 0;
124 | var c2 = 0;
125 |
126 | // Precalculate frequencues
127 | var o1t = getnotefreq(n + (this.instr.osc1_oct - 8) * 12 + this.instr.osc1_det) * (1 + 0.0008 * this.instr.osc1_detune);
128 | var o2t = getnotefreq(n + (this.instr.osc2_oct - 8) * 12 + this.instr.osc2_det) * (1 + 0.0008 * this.instr.osc2_detune);
129 |
130 | // State variable init
131 | var q = this.instr.fx_resonance / 255;
132 | var low = 0;
133 | var band = 0;
134 |
135 | var chnbufLength = chnBuf.left.length;
136 | var numSamples = this.attack + this.sustain + this.release - 1;
137 |
138 | for (var j = numSamples; j >= 0; --j) {
139 | var k = j + currentpos;
140 |
141 | // LFO
142 | var lfor = this.osc_lfo(k * this.lfoFreq) * this.instr.lfo_amt / 512 + 0.5;
143 |
144 | // Envelope
145 | var e = 1;
146 | if (j < this.attack) {
147 | e = j / this.attack;
148 | }
149 | else if (j >= this.attack + this.sustain) {
150 | e -= (j - this.attack - this.sustain) / this.release;
151 | }
152 |
153 | // Oscillator 1
154 | var t = o1t;
155 | if (this.instr.lfo_osc1_freq) {
156 | t += lfor;
157 | }
158 | if (this.instr.osc1_xenv) {
159 | t *= e * e
160 | }
161 | c1 += t;
162 | var rsample = this.osc1(c1) * this.instr.osc1_vol;
163 |
164 | // Oscillator 2
165 | t = o2t;
166 | if (this.instr.osc2_xenv) {
167 | t *= e * e;
168 | };
169 | c2 += t;
170 | rsample += this.osc2(c2) * this.instr.osc2_vol;
171 |
172 | // Noise oscillator
173 | if (this.instr.noise_fader) {
174 | rsample += (2*_math.random()-1) * this.instr.noise_fader * e;
175 | }
176 |
177 | rsample *= e / 255;
178 |
179 | // State variable filter
180 | var f = this.instr.fx_freq;
181 | if (this.instr.lfo_fx_freq) {
182 | f *= lfor;
183 | }
184 | f = 1.5 * _math.sin(f * 3.141592 / WAVE_SPS);
185 | low += f * band;
186 | var high = q * (rsample - band) - low;
187 | band += f * high;
188 | switch (this.instr.fx_filter) {
189 | case 1: // Hipass
190 | rsample = high;
191 | break;
192 | case 2: // Lopass
193 | rsample = low;
194 | break;
195 | case 3: // Bandpass
196 | rsample = band;
197 | break;
198 | case 4: // Notch
199 | rsample = low + high;
200 | break;
201 | default:
202 | }
203 |
204 | // Panning & master volume
205 | t = osc_sin(k * this.panFreq) * this.instr.fx_pan_amt / 512 + 0.5;
206 | rsample *= 0.00476 * this.instr.env_master; // 39 / 8192 = 0.00476
207 |
208 | // Add to 16-bit channel buffer
209 | // k = k * 2;
210 | if (k < chnbufLength) {
211 | chnBuf.left[k] += rsample * (1-t) ;
212 | chnBuf.right[k] += rsample * t;
213 | }
214 | }
215 | };
216 |
217 | SoundGenerator.prototype._createAudioBuffer = function(n, callBack) {
218 | var bufferSize = (this.attack + this.sustain + this.release - 1) + (32 * this.rowLen);
219 | var buffer = generateBuffer(bufferSize);
220 | this._genSound(n, buffer, 0);
221 | applyDelay(buffer, bufferSize, this.instr, this.rowLen);
222 |
223 | callBack(getAudioBuffer(this.ctx, buffer));
224 | };
225 |
226 |
227 |
228 |
229 | var MusicGenerator = function(ctx, song) {
230 | this.ctx = ctx;
231 | this.song = song;
232 | // Wave data configuration
233 | this.waveSize = WAVE_SPS * song.songLen; // Total song size (in samples)
234 | };
235 |
236 | MusicGenerator.prototype._generateTrack = function (instr, mixBuf, callBack) {
237 | var self = this;
238 | var chnBuf = generateBuffer(this.waveSize);
239 | // Preload/precalc some properties/expressions (for improved performance)
240 | var waveSamples = self.waveSize,
241 | rowLen = self.song.rowLen,
242 | endPattern = self.song.endPattern,
243 | soundGen = new SoundGenerator(self.ctx, instr, rowLen);
244 |
245 | var currentpos = 0;
246 | var p = 0;
247 | var row = 0;
248 | var recordSounds = function() {
249 | var beginning = Date.now();
250 | while (true) {
251 | if (row === 32) {
252 | row = 0;
253 | p += 1;
254 | continue;
255 | }
256 | if (p === endPattern - 1) {
257 | return finalize();
258 | }
259 | var cp = instr.p[p];
260 | if (cp) {
261 | var n = instr.c[cp - 1].n[row];
262 | if (n) {
263 | soundGen._genSound(n, chnBuf, currentpos);
264 | }
265 | }
266 | currentpos += rowLen;
267 | row += 1;
268 | if (Date.now() - beginning > MAX_TIME) {
269 | setTimeout(recordSounds, 0);
270 | return;
271 | }
272 | }
273 | };
274 |
275 | var finalize = function() {
276 | applyDelay(chnBuf, waveSamples, instr, rowLen);
277 | for (var b2 = 0; b2 < waveSamples; b2++) {
278 | mixBuf.left[b2] += chnBuf.left[b2];
279 | }
280 | for (var b2 = 0; b2 < waveSamples; b2++) {
281 | mixBuf.right[b2] += chnBuf.right[b2];
282 | }
283 | callBack();
284 | };
285 |
286 | recordSounds();
287 | };
288 |
289 | MusicGenerator.prototype._createAudioBuffer = function(callBack) {
290 | var self = this;
291 | var mixBuf = generateBuffer(this.waveSize);
292 | var track = 0;
293 |
294 | var nextTrack = function() {
295 | if (track < self.song.songData.length) {
296 | track += 1;
297 | self._generateTrack(self.song.songData[track - 1], mixBuf, nextTrack);
298 | }
299 | else {
300 | callBack(getAudioBuffer(self.ctx, mixBuf));
301 | }
302 | };
303 | nextTrack();
304 | };
305 |
306 |
307 | sonantxr_generate_song = function(audio_ctx, song_data, callback) {
308 | var music_generator = new MusicGenerator(audio_ctx, song_data);
309 | music_generator._createAudioBuffer(callback);
310 | };
311 |
312 | sonantxr_generate_sound = function(audio_ctx, instrument, note, callback) {
313 | var sound_generator = new SoundGenerator(audio_ctx, instrument);
314 | sound_generator._createAudioBuffer(note, callback);
315 | };
316 |
317 | })();
318 |
319 |
--------------------------------------------------------------------------------
/source/sound-effects.js:
--------------------------------------------------------------------------------
1 | var
2 | sound_terminal = {
3 | osc1_oct: 6,
4 | osc1_det: 0,
5 | osc1_detune: 0,
6 | osc1_xenv: 0,
7 | osc1_vol: 0,
8 | osc1_waveform: 0,
9 | osc2_oct: 10,
10 | osc2_det: 0,
11 | osc2_detune: 0,
12 | osc2_xenv: 0,
13 | osc2_vol: 168,
14 | osc2_waveform: 3,
15 | noise_fader: 0,
16 | env_attack: 351,
17 | env_sustain: 0,
18 | env_release: 444,
19 | env_master: 192,
20 | fx_filter: 2,
21 | fx_freq: 7355,
22 | fx_resonance: 130,
23 | fx_delay_time: 3,
24 | fx_delay_amt: 36,
25 | fx_pan_freq: 0,
26 | fx_pan_amt: 0,
27 | lfo_osc1_freq: 0,
28 | lfo_fx_freq: 0,
29 | lfo_freq: 0,
30 | lfo_amt: 0,
31 | lfo_waveform: 0
32 | },
33 |
34 | sound_shoot = {
35 | osc1_oct: 7,
36 | osc1_det: 0,
37 | osc1_detune: 0,
38 | osc1_xenv: 0,
39 | osc1_vol: 192,
40 | osc1_waveform: 0,
41 | osc2_oct: 2,
42 | osc2_det: 0,
43 | osc2_detune: 0,
44 | osc2_xenv: 0,
45 | osc2_vol: 192,
46 | osc2_waveform: 0,
47 | noise_fader: 28,
48 | env_attack: 269,
49 | env_sustain: 0,
50 | env_release: 444,
51 | env_master: 255,
52 | fx_filter: 0,
53 | fx_freq: 272,
54 | fx_resonance: 25,
55 | fx_delay_time: 5,
56 | fx_delay_amt: 29,
57 | fx_pan_freq: 0,
58 | fx_pan_amt: 47,
59 | lfo_osc1_freq: 0,
60 | lfo_fx_freq: 0,
61 | lfo_freq: 0,
62 | lfo_amt: 0,
63 | lfo_waveform: 0
64 | },
65 |
66 | sound_hit = {
67 | osc1_oct: 8,
68 | osc1_det: 0,
69 | osc1_detune: 0,
70 | osc1_xenv: 1,
71 | osc1_vol: 160,
72 | osc1_waveform: 3,
73 | osc2_oct: 8,
74 | osc2_det: 0,
75 | osc2_detune: 0,
76 | osc2_xenv: 1,
77 | osc2_vol: 99,
78 | osc2_waveform: 2,
79 | noise_fader: 60,
80 | env_attack: 50,
81 | env_sustain: 200,
82 | env_release: 6800,
83 | env_master: 125,
84 | fx_filter: 4,
85 | fx_freq: 11025,
86 | fx_resonance: 254,
87 | fx_delay_time: 0,
88 | fx_delay_amt: 13,
89 | fx_pan_freq: 5,
90 | fx_pan_amt: 0,
91 | lfo_osc1_freq: 0,
92 | lfo_fx_freq: 1,
93 | lfo_freq: 4,
94 | lfo_amt: 60,
95 | lfo_waveform: 0
96 | },
97 |
98 | sound_beep = {
99 | osc1_oct: 10,
100 | osc1_det: 0,
101 | osc1_detune: 0,
102 | osc1_xenv: 0,
103 | osc1_vol: 192,
104 | osc1_waveform: 2,
105 | osc2_oct: 6,
106 | osc2_det: 0,
107 | osc2_detune: 9,
108 | osc2_xenv: 0,
109 | osc2_vol: 192,
110 | osc2_waveform: 1,
111 | noise_fader: 0,
112 | env_attack: 137,
113 | env_sustain: 2000,
114 | env_release: 4611,
115 | env_master: 140,
116 | fx_filter: 1,
117 | fx_freq: 982,
118 | fx_resonance: 89,
119 | fx_delay_time: 6,
120 | fx_delay_amt: 25,
121 | fx_pan_freq: 6,
122 | fx_pan_amt: 77,
123 | lfo_osc1_freq: 0,
124 | lfo_fx_freq: 1,
125 | lfo_freq: 3,
126 | lfo_amt: 69,
127 | lfo_waveform: 0
128 | },
129 |
130 | sound_hurt = {
131 | osc1_oct: 7,
132 | osc1_det: 3,
133 | osc1_detune: 140,
134 | osc1_xenv: 1,
135 | osc1_vol: 232,
136 | osc1_waveform: 3,
137 | osc2_oct: 6,
138 | osc2_det: 0,
139 | osc2_detune: 9,
140 | osc2_xenv: 0,
141 | osc2_vol: 30,
142 | osc2_waveform: 1,
143 | noise_fader: 17,
144 | env_attack: 4611,
145 | env_sustain: 1403,
146 | env_release: 34215,
147 | env_master: 256,
148 | fx_filter: 4,
149 | fx_freq: 948,
150 | fx_resonance: 196,
151 | fx_delay_time: 0,
152 | fx_delay_amt: 0,
153 | fx_pan_freq: 0,
154 | fx_pan_amt: 1,
155 | lfo_osc1_freq: 0,
156 | lfo_fx_freq: 1,
157 | lfo_freq: 13,
158 | lfo_amt: 255,
159 | lfo_waveform: 2
160 | },
161 |
162 | sound_pickup = {
163 | osc1_oct: 5,
164 | osc1_det: 0,
165 | osc1_detune: 0,
166 | osc1_xenv: 1,
167 | osc1_vol: 97,
168 | osc1_waveform: 0,
169 | osc2_oct: 8,
170 | osc2_det: 0,
171 | osc2_detune: 0,
172 | osc2_xenv: 1,
173 | osc2_vol: 204,
174 | osc2_waveform: 0,
175 | noise_fader: 0,
176 | env_attack: 4298,
177 | env_sustain: 927,
178 | env_release: 1403,
179 | env_master: 255,
180 | fx_filter: 2,
181 | fx_freq: 484,
182 | fx_resonance: 134,
183 | fx_delay_time: 3,
184 | fx_delay_amt: 35,
185 | fx_pan_freq: 4,
186 | fx_pan_amt: 72,
187 | lfo_osc1_freq: 0,
188 | lfo_fx_freq: 1,
189 | lfo_freq: 6,
190 | lfo_amt: 231,
191 | lfo_waveform: 0
192 | },
193 |
194 | sound_explode = {
195 | osc1_oct: 8,
196 | osc1_det: 0,
197 | osc1_detune: 0,
198 | osc1_xenv: 1,
199 | osc1_vol: 147,
200 | osc1_waveform: 1,
201 | osc2_oct: 6,
202 | osc2_det: 0,
203 | osc2_detune: 0,
204 | osc2_xenv: 1,
205 | osc2_vol: 159,
206 | osc2_waveform: 1,
207 | noise_fader: 255,
208 | env_attack: 197,
209 | env_sustain: 1234,
210 | env_release: 21759,
211 | env_master: 232,
212 | fx_filter: 2,
213 | fx_freq: 1052,
214 | fx_resonance: 255,
215 | fx_delay_time: 4,
216 | fx_delay_amt: 73,
217 | fx_pan_freq: 3,
218 | fx_pan_amt: 25,
219 | lfo_osc1_freq: 0,
220 | lfo_fx_freq: 0,
221 | lfo_freq: 0,
222 | lfo_amt: 0,
223 | lfo_waveform: 0
224 | };
--------------------------------------------------------------------------------
/source/terminal.js:
--------------------------------------------------------------------------------
1 |
2 | var terminal_text_ident = '> ';
3 | var terminal_text_title = '' +
4 | 'UNDERRUN\n' +
5 | '__ \n' +
6 | 'CONCEPT, GRAPHICS & PROGRAMMING:\n' +
7 | 'DOMINIC SZABLEWSKI // PHOBOSLAB.ORG\n' +
8 | '__ \n' +
9 | 'MUSIC:\n' +
10 | 'ANDREAS LÖSCH // NO-FATE.NET\n' +
11 | '___ \n' +
12 | 'SYSTEM VERSION: 13.20.18\n' +
13 | 'CPU: PL(R) Q-COATL 7240 @ 12.6 THZ\n' +
14 | 'MEMORY: 108086391056891900 BYTES\n' +
15 | ' \n' +
16 | 'CONNECTING...';
17 |
18 | var terminal_text_garbage =
19 | '´A1e{∏éI9·NQ≥ÀΩ¸94CîyîR›kÈ¡˙ßT-;ûÅf^˛,¬›A∫S〫ÕÕ' +
20 | '1f@çX8ÎRjßf•ò√ã0êÃcÄ]Î≤moDÇ’ñ‰\\ˇ≠n=(s7É;';
21 |
22 | var terminal_text_story =
23 | 'DATE: SEP. 13, 2718 - 13:32\n' +
24 | 'CRITICAL SOFTWARE FAILURE DETECTED\n' +
25 | 'ANALYZING...\n' +
26 | '____\n \n' +
27 | 'ERROR CODE: JS13K2018\n' +
28 | 'STATUS: SYSTEMS OFFLINE\n' +
29 | 'DESCRIPTION: BUFFER UNDERRUN DUE TO SATCOM R.U.D.\n' +
30 | 'AFFECTED SYSTEM: FACILITY AUTOMATION\n' +
31 | 'AFFECTED SUBSYSTEMS: AI, RADIATION SHIELDS, POWER MANAGEMENT\n' +
32 | ' \n' +
33 | 'INITIATING RESCUE SYSTEM...\n' +
34 | '___' +
35 | 'FAILED\n \n' +
36 | 'ATTEMPTING AUTOMATED REBOOT...\n' +
37 | '___' +
38 | 'FAILED\n' +
39 | '_ \n \n' +
40 | 'MANUAL REBOOT OF ALL SYSTEMS REQUIRED\n' +
41 | '_ \n' +
42 | 'USE WASD OR CURSOR KEYS TO MOVE, MOUSE TO SHOOT\n' +
43 | 'CLICK TO INITIATE YOUR DEPLOYMENT\n ';
44 |
45 | var terminal_text_outro =
46 | 'ALL SATELLITE LINKS ONLINE\n' +
47 | 'CONNECTING...___' +
48 | 'CONNECTION ESTABLISHED\n' +
49 | 'RECEIVING TRANSMISSION...___ \n' +
50 |
51 | 'SENT: SEP. 13, 2018\n' +
52 | 'RCVD: SEP. 13, 2718\n \n' +
53 |
54 | 'THANKS FOR PLAYING ❤_ \n' +
55 | 'I HAVE PREVIOUSLY BEEN A PROUD SPONSOR OF THE JS13K\n' +
56 | 'COMPETITION SINCE THE VERY FIRST ONE BACK IN 2012.\n' +
57 | 'HOWEVER, THIS YEAR\'S COMPETITION WAS MY FIRST ONE\n' +
58 | 'AS A PARTICIPANT AND IT HAS BEEN TREMENDOUS FUN!\n \n' +
59 |
60 | 'I WANT TO THANK MY DEAR FRIEND ANDREAS LÖSCH OF\n' +
61 | 'NO-FATE.NET FOR COMPOSING SOME AWESOME MUSIC ON\n' +
62 | 'SUCH SHORT NOTICE.\n \n' +
63 |
64 | 'FURTHER THANKS GO OUT TO THE JS13K STAFF, THE\n' +
65 | 'SONANT-X DEVELOPERS AND ALL OTHER PARTICIPANTS\n' +
66 | 'IN THIS YEAR\'S JS13K. SEE YOU NEXT YEAR!\n \n' +
67 | 'DOMINIC__' +
68 | 'END OF TRANSMISSION';
69 |
70 | var terminal_text_buffer = [],
71 | terminal_state = 0,
72 | terminal_current_line,
73 | terminal_line_wait = 100,
74 | terminal_print_ident = true,
75 | terminal_timeout_id = 0,
76 | terminal_hide_timeout = 0;
77 |
78 | terminal_text_garbage += terminal_text_garbage + terminal_text_garbage;
79 |
80 | function terminal_show() {
81 | clearTimeout(terminal_hide_timeout);
82 | a.style.opacity = 1;
83 | a.style.display = 'block';
84 | }
85 |
86 | function terminal_hide() {
87 | a.style.opacity = 0;
88 | terminal_hide_timeout = setTimeout(function(){a.style.display = 'none'}, 1000);
89 | }
90 |
91 | function terminal_cancel() {
92 | clearTimeout(terminal_timeout_id);
93 | }
94 |
95 | function terminal_prepare_text(text) {
96 | return text.replace(/_/g, '\n'.repeat(10)).split('\n');
97 | }
98 |
99 | function terminal_write_text(lines, callback) {
100 | if (lines.length) {
101 | terminal_write_line(lines.shift(), terminal_write_text.bind(this, lines, callback));
102 | }
103 | else {
104 | callback && callback();
105 | }
106 | }
107 |
108 | function terminal_write_line(line, callback) {
109 | if (terminal_text_buffer.length > 20) {
110 | terminal_text_buffer.shift();
111 | }
112 | if (line) {
113 | audio_play(audio_sfx_terminal);
114 | terminal_text_buffer.push((terminal_print_ident ? terminal_text_ident : '') + line);
115 | a.innerHTML = '