├── favicon.ico
├── favicon.png
├── .hgignore
├── .gitignore
├── src
├── shaders
│ ├── clear.shader
│ ├── drawAll.shader
│ ├── vertexPoints.vshader
│ ├── cameraRendererFast.shader
│ ├── cameraRenderer.vshader
│ ├── primitivePalette.shader
│ ├── primitiveRenderer.shader
│ ├── scroller.shader
│ ├── stampPalette.shader
│ ├── cameraRendererSimple.shader
│ ├── drawCircle.shader
│ ├── drawRect.shader
│ ├── mixPalette.shader
│ ├── mixRect.shader
│ ├── cameraRendererTV.shader
│ ├── cameraRenderer.shader
│ ├── copyPasteRect.shader
│ ├── intersectSpawn.shader
│ └── shieldGenerator.shader
├── gl
│ ├── World.js
│ ├── Palette.js
│ ├── GLHelper.js
│ ├── Dish.js
│ ├── ParticleSystem.js
│ ├── Rule.js
│ └── Reactor.js
├── Tests.js
├── CellSpace.js
├── CellSpace
│ ├── State.js
│ ├── Utils.js
│ ├── Setup.js
│ ├── GameLoop.js
│ └── GUI.js
├── data
│ └── FileStore.js
├── Utils.js
├── story
│ └── StoryTeller.js
└── EvoCell.js
├── lighttpdconfig
├── test.config
└── test3001.config
├── README.md
├── TODO.txt
├── css
└── style.css
└── cellspace.html
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wizard23/evocell/HEAD/favicon.ico
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wizard23/evocell/HEAD/favicon.png
--------------------------------------------------------------------------------
/.hgignore:
--------------------------------------------------------------------------------
1 | syntax: glob
2 | ccc_rules/*
3 | patterns/*
4 | rules/*
5 | *.swp
6 | *~
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | rules/
2 | r.js
3 | .idea/
4 | cellspace.sublime-project
5 | cellspace.sublime-workspace
6 | *.js~
7 | *.html~
8 |
--------------------------------------------------------------------------------
/src/shaders/clear.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | varying vec2 vTexCoord;
6 |
7 | uniform vec4 color;
8 |
9 | void main(void) {
10 | gl_FragColor = color;
11 | }
12 |
--------------------------------------------------------------------------------
/src/shaders/drawAll.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 | uniform sampler2D texFrame;
5 | uniform float state;
6 |
7 | varying vec2 vTexCoord;
8 | void main(void) {
9 | gl_FragColor = vec4(0., 0., 0., state);
10 | }
11 |
--------------------------------------------------------------------------------
/src/shaders/vertexPoints.vshader:
--------------------------------------------------------------------------------
1 | attribute vec2 pointPos;
2 | uniform vec2 offset;
3 | //uniform float pointSize;
4 |
5 | void main() {
6 | gl_Position = vec4(pointPos + offset, 0.0, 1.0);
7 | //gl_Position = vec4(pointPos, 0.0, 1.0);
8 | gl_PointSize = 3.;
9 | }
10 |
--------------------------------------------------------------------------------
/lighttpdconfig/test.config:
--------------------------------------------------------------------------------
1 | server.document-root = ".."
2 |
3 | server.port = 3000
4 |
5 | mimetype.assign = (
6 | ".html" => "text/html",
7 | ".txt" => "text/plain",
8 | ".jpg" => "image/jpeg",
9 | ".png" => "image/png",
10 | ".css" => "text/css",
11 | ".js" => "text/javascript",
12 | )
13 |
--------------------------------------------------------------------------------
/lighttpdconfig/test3001.config:
--------------------------------------------------------------------------------
1 | server.document-root = ".."
2 |
3 | server.port = 3001
4 |
5 | mimetype.assign = (
6 | ".html" => "text/html",
7 | ".txt" => "text/plain",
8 | ".jpg" => "image/jpeg",
9 | ".png" => "image/png",
10 | ".css" => "text/css",
11 | ".js" => "application/javascript",
12 | )
13 |
--------------------------------------------------------------------------------
/src/shaders/cameraRendererFast.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texFrame2;
7 | uniform vec2 resolution;
8 |
9 | varying vec2 vTexCoord;
10 |
11 | const float borderW = 0.15;
12 |
13 | void main(void) {
14 | gl_FragColor = texture2D(texFrame, vTexCoord);
15 | }
--------------------------------------------------------------------------------
/src/shaders/cameraRenderer.vshader:
--------------------------------------------------------------------------------
1 | attribute vec3 aPos;
2 | attribute vec2 aTexCoord;
3 |
4 | varying vec2 vTexCoord;
5 |
6 | uniform mat4 projectionMatrix;
7 | uniform mat4 modelViewMatrix;
8 |
9 | void main(void) {
10 | gl_Position = vec4(aPos, 1.);
11 | gl_Position = projectionMatrix * modelViewMatrix * gl_Position;
12 | vTexCoord = aTexCoord;
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | evocell
2 | =======
3 |
4 | A HTML5/WebGL app to evolve & play around with ruletable based cellular automata
5 |
6 | Test the ALPHA version of the ca based space shooter: EvoCellSpace
7 | Very early draft of EvoCell Lab for evolving CAs: EvoCellLab
8 |
--------------------------------------------------------------------------------
/src/shaders/primitivePalette.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | varying vec2 vTexCoord;
7 | void main(void) {
8 | vec4 color = texture2D(texFrame, vTexCoord);
9 | if (color.a > 0.)
10 | gl_FragColor = vec4(0, color.a*2600.0, color.a*color.a*color.a*8000000.0, 1.);
11 | else
12 | gl_FragColor = vec4(0., 0., 0., 0.);
13 | }
14 |
--------------------------------------------------------------------------------
/src/shaders/primitiveRenderer.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform float scale;
7 | const float damp = 0.5;
8 |
9 | varying vec2 vTexCoord;
10 | void main(void) {
11 | vec4 color = texture2D(texFrame, vTexCoord);
12 | if (mod(gl_FragCoord.x, scale) < 1. || mod(gl_FragCoord.y, scale) < 1.)
13 | gl_FragColor = vec4(color.r*damp, color.g*damp, color.b*damp, 1.);
14 | else
15 | gl_FragColor = color;
16 | }
17 |
--------------------------------------------------------------------------------
/src/shaders/scroller.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 |
7 | uniform vec2 scroll;
8 |
9 | varying vec2 vTexCoord;
10 | void main(void) {
11 | vec2 pos = vTexCoord+scroll;
12 | //if (pos.x > 1.) pos.x -= 1.;
13 | //if (pos.x < 0.) pos.x += 1.;
14 |
15 | //if (pos.y > 1.) pos.y -= 1.;
16 | //if (pos.y < 0.) pos.y += 1.;
17 |
18 | //pos.x = mod(pos.x, 1.);
19 | //pos.y = mod(pos.y, 1.);
20 |
21 | vec4 color = texture2D(texFrame, pos);
22 | gl_FragColor = color;
23 | }
--------------------------------------------------------------------------------
/src/shaders/stampPalette.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texNew;
7 | uniform sampler2D texPalette;
8 |
9 | varying vec2 vTexCoord;
10 | void main(void) {
11 | vec4 color = texture2D(texNew, vTexCoord);
12 | vec4 mappedColor = texture2D(texPalette, vec2(color.a, 0.5));
13 | vec4 oldColor = texture2D(texFrame, vTexCoord);
14 |
15 | if (mappedColor.a > 0.)
16 | {
17 | gl_FragColor = mappedColor;
18 | }
19 | else
20 | {
21 | gl_FragColor = oldColor;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/shaders/cameraRendererSimple.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texFrame2;
7 | uniform vec2 resolution;
8 |
9 | varying vec2 vTexCoord;
10 |
11 | const float borderW = 0.15;
12 |
13 | void main(void) {
14 | vec4 color;
15 | color = texture2D(texFrame, vTexCoord);
16 | vec4 color2 = texture2D(texFrame2, vTexCoord);
17 | float damp = color2.a;
18 | damp = 3. * damp * damp;
19 |
20 | if (damp <= 0.) damp = 1.;
21 |
22 | gl_FragColor = vec4(damp * color.rgb, 1.);
23 | }
--------------------------------------------------------------------------------
/src/shaders/drawCircle.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 |
7 | uniform vec2 center;
8 | uniform float radius;
9 | uniform float state;
10 |
11 | varying vec2 vTexCoord;
12 | void main(void) {
13 | vec4 color = texture2D(texFrame, vTexCoord);
14 |
15 | float dx = center.x - gl_FragCoord.x;
16 | float dy = center.y - gl_FragCoord.y;
17 | float d = sqrt(dx*dx + dy*dy);
18 |
19 | if (d < radius)
20 | {
21 | gl_FragColor = vec4(0., 0., 0., state);
22 | }
23 | else
24 | {
25 | gl_FragColor = color;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/shaders/drawRect.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 |
7 | uniform vec2 rectPos;
8 | uniform vec2 rectSize;
9 | uniform float state;
10 |
11 | varying vec2 vTexCoord;
12 | void main(void) {
13 | vec4 color = texture2D(texFrame, vTexCoord);
14 |
15 | if (gl_FragCoord.x >= rectPos.x && gl_FragCoord.x <= rectPos.x + rectSize.x &&
16 | gl_FragCoord.y >= rectPos.y && gl_FragCoord.y <= rectPos.y + rectSize.y)
17 | {
18 | gl_FragColor = vec4(0., 0., 0., state);
19 | }
20 | else
21 | {
22 | gl_FragColor = color;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/shaders/mixPalette.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texNew;
7 | uniform sampler2D texPalette;
8 |
9 | varying vec2 vTexCoord;
10 | void main(void) {
11 | vec4 color = texture2D(texNew, vTexCoord);
12 | vec4 mappedColor = texture2D(texPalette, vec2(color.a, 0.5));
13 | vec4 oldColor = texture2D(texFrame, vTexCoord);
14 |
15 | if (mappedColor.a > 0.)
16 | {
17 | gl_FragColor = vec4((1.-mappedColor.a) * oldColor.rgb + mappedColor.a * mappedColor.rgb, 1.);
18 | }
19 | else
20 | {
21 | gl_FragColor = oldColor;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/shaders/mixRect.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 |
7 | uniform vec2 rectPos;
8 | uniform vec2 rectSize;
9 | uniform vec4 color;
10 |
11 | varying vec2 vTexCoord;
12 | void main(void) {
13 | vec4 oldColor = texture2D(texFrame, vTexCoord);
14 |
15 | if (gl_FragCoord.x >= rectPos.x && gl_FragCoord.x <= rectPos.x + rectSize.x &&
16 | gl_FragCoord.y >= rectPos.y && gl_FragCoord.y <= rectPos.y + rectSize.y)
17 | {
18 | gl_FragColor = color.a * color + (1. - color.a) * oldColor;
19 | }
20 | else
21 | {
22 | gl_FragColor = oldColor;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/gl/World.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | var World = function(gl) {
4 | this.gl = gl;
5 | this.dishes = {};
6 | this.shaders = {};
7 |
8 | this.setup()
9 | this.loader = function(callback) {
10 | //callback(data);
11 | }
12 |
13 | this.mouseDown = function() {
14 | }
15 | this.stepInteract= function() {
16 | }
17 | this.step = function() {
18 | }
19 | this.render = function() {
20 | }
21 |
22 | }
23 |
24 | var SimpleWorld = function() {
25 |
26 | }
27 |
28 | var CellSpaceWorld = new EC.World({
29 | dishes: {
30 | shipDish: {},
31 | largerEnemyDish: {width = 1024, height: 512}
32 |
33 | shaders:
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/shaders/cameraRendererTV.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texFrame2;
7 | uniform vec2 resolution;
8 |
9 | varying vec2 vTexCoord;
10 |
11 | const float borderW = 0.15;
12 |
13 | void main(void) {
14 | vec4 color;
15 | color = texture2D(texFrame, vTexCoord);
16 | vec4 color2 = texture2D(texFrame2, vTexCoord);
17 | float damp = color2.a;
18 | damp = 3. * damp * damp;
19 |
20 | if (damp <= 0.) damp = 1.;
21 |
22 | if (mod(gl_FragCoord.y, 6.) < 3.) {
23 | //damp *= 0.7;
24 | }
25 | else {
26 | damp = 1.;
27 | }
28 |
29 | gl_FragColor = vec4(damp * color.rgb, 1.);
30 | }
--------------------------------------------------------------------------------
/src/shaders/cameraRenderer.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D texFrame2;
7 | uniform vec2 resolution;
8 |
9 | varying vec2 vTexCoord;
10 |
11 | const float borderW = 0.15;
12 |
13 | void main(void) {
14 | vec4 color;
15 | color = texture2D(texFrame, vTexCoord);
16 |
17 | float modX = mod(vTexCoord.x*resolution.x, 1.);
18 | float modY = mod(vTexCoord.y*resolution.y, 1.);
19 | if ( modX < borderW || modX > (1.-borderW) || modY < borderW || modY > (1.-borderW))
20 | {
21 | vec4 color2 = texture2D(texFrame2, vTexCoord);
22 | float damp = color2.a;
23 | //damp /= 2.;
24 | //damp = 0.5;
25 | damp = 3. * damp * damp;
26 | gl_FragColor = vec4(color.r*damp, color.g*damp, color.b*damp, 1.);
27 | }
28 | else
29 | {
30 | //color = texture2D(texFrame, vTexCoord);
31 | gl_FragColor = color;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/shaders/copyPasteRect.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform vec2 destinationPos;
7 | uniform vec2 destinationSize;
8 |
9 | uniform sampler2D texSource;
10 | uniform vec2 sourcePos;
11 | uniform vec2 sourceRes;
12 |
13 | varying vec2 vTexCoord;
14 | void main(void) {
15 | vec4 oldColor = texture2D(texFrame, vTexCoord);
16 | //gl_FragColor = oldColor;
17 |
18 | //
19 |
20 | vec4 sourceColor = texture2D(texSource, (gl_FragCoord.xy - destinationPos + sourcePos)/sourceRes);
21 |
22 |
23 | if (gl_FragCoord.x >= destinationPos.x && gl_FragCoord.x <= destinationPos.x + destinationSize.x &&
24 | gl_FragCoord.y >= destinationPos.y && gl_FragCoord.y <= destinationPos.y + destinationSize.y)
25 | {
26 | gl_FragColor = sourceColor;
27 | }
28 | else
29 | {
30 | gl_FragColor = vec4(0., 0., 0., 0.); //oldColor;
31 | gl_FragColor = oldColor;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/shaders/intersectSpawn.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform sampler2D texFrame;
6 | uniform sampler2D tex1;
7 | uniform sampler2D tex2;
8 |
9 | //
10 | const float OP_REPLACE = 0.;
11 | const float OP_ADD = 1.;
12 |
13 | uniform float operation;
14 | uniform float state;
15 |
16 | //uniform float maximum;
17 |
18 | varying vec2 vTexCoord;
19 | void main(void) {
20 | vec4 color1 = texture2D(tex1, vTexCoord);
21 | vec4 color2 = texture2D(tex2, vTexCoord);
22 | vec4 oldColor = texture2D(texFrame, vTexCoord);
23 | float oldState = oldColor.w;
24 |
25 | if (color1.a > 0. && color2.a > 0.)
26 | {
27 | if (operation == OP_REPLACE)
28 | {
29 | gl_FragColor = vec4(0., 0., 0., state);
30 | }
31 | else if (operation == OP_ADD)
32 | {
33 | oldState += state;
34 | if (oldState < 0.) oldState = 0.;
35 | gl_FragColor = vec4(0., 0., 0., oldState);
36 | }
37 | }
38 | else
39 | {
40 | gl_FragColor = oldColor;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Tests.js:
--------------------------------------------------------------------------------
1 |
2 | // document.getElementById("saveGameState").addEventListener("click", function(evt) {
3 |
4 | // fileStore.addObject("gameStates", {id: "test23XXXX", state: {txt: "txt", nr: 123} },
5 | // function() { alert("works" + 23); },
6 | // function() { alert("NOT: " + 23); }
7 | // );
8 |
9 | // }, false);
10 |
11 | // var works = ["yo", 12];
12 | // var notworks = ["yo", 12];
13 |
14 | // document.getElementById("saveGameState").addEventListener("click", function(evt) {
15 | // works = [];
16 | // notworks = [];
17 | // _.each(gameState, function(value, key) {
18 | // var clonedState = {};
19 | // clonedState[key] = value;
20 |
21 | // try {
22 | // fileStore.addObject("gameStates", {id: "test23" + key, state: clonedState },
23 | // function() {
24 | // // alert("works" + key);
25 | // },
26 | // function() {
27 | // // alert("NOT: " + key);
28 | // }
29 | // );
30 |
31 | // works.push("\"" + key + "\"");
32 | // }
33 | // catch (ex) {
34 | // //alert("Except: " + key);
35 | // notworks.push("\"" + key + "\"");
36 | // }
37 |
38 | // });
39 | // alert(notworks);
40 | // }, false);
--------------------------------------------------------------------------------
/TODO.txt:
--------------------------------------------------------------------------------
1 | * State bekommt dishes und rules sub model
2 | ** paint generalize
3 | ** load rule (from db)
4 | ** import/export rule from db
5 | * Cleanup App by splitting CellSpace.js
6 | * variable initial probaility (optional param)
7 |
8 | useful git aliases (for ~/.bashrc or ~/.bash_profile):
9 | # Git shortcuts
10 | alias g='git'
11 | alias gs='git status | more'
12 | alias ga='git add '
13 | alias gb='git branch '
14 | alias gc='git commit'
15 | alias gcnv="git commit --no-verify"
16 | alias gd='git diff'
17 | alias go='git checkout '
18 | alias gl='git log'
19 | alias gh='git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short'
20 |
21 |
22 | OLD:
23 | ====
24 | gl context wie geht man damit um? -> Reactor
25 | Klasen identifizieren
26 |
27 | CA stuff unabhaengig von canvas -> nope aber im reactor
28 |
29 |
30 | Usecases:
31 |
32 | ca = loadCA("myRule.ec23")
33 | ca.setSize(1024, 768);
34 | ca.clear();
35 | ca.loadPattern("myPatern.ec23", 100, 100);
36 | ca.step();
37 | ca.steps(100);
38 |
39 |
40 | palette = new CAPalette();
41 | paltette.setColor(0, 255, 0, 0);
42 |
43 | caCanvas = new CACanvas(1500, 300);
44 | caCanvas.setPallette(palette);
45 | caCanvas.cellSize = 5;
46 | caCanvas.drawCA(ca, 100, 500);
47 |
48 |
--------------------------------------------------------------------------------
/src/shaders/shieldGenerator.shader:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 | uniform sampler2D texShield;
5 | uniform sampler2D texTarget;
6 | uniform float width;
7 | uniform float height;
8 |
9 | varying vec2 vTexCoord;
10 |
11 | float dx = 1./width, dy=1./height;
12 |
13 | void main(void) {
14 | float aMax = 0.;
15 | float aMin = 1.; // smallest val larger 0
16 | float a;
17 |
18 | float selfA = texture2D(texShield, vTexCoord).a;
19 | float targetA = texture2D(texTarget, vTexCoord).a;
20 |
21 | if (targetA > 0.)
22 | {
23 | if (selfA > 0.)
24 | {
25 | gl_FragColor = vec4(0, 0., 0., selfA);
26 | }
27 | else
28 | {
29 | a = texture2D(texShield, vTexCoord + vec2(0, 0)).a;
30 | if (a > 0. && a < aMin) aMin = a;
31 | a = texture2D(texShield, vTexCoord + vec2(dx, 0)).a;
32 | if (a > 0. && a < aMin) aMin = a;
33 | a = texture2D(texShield, vTexCoord + vec2(-dx, 0)).a;
34 | if (a > 0. && a < aMin) aMin = a;
35 | a = texture2D(texShield, vTexCoord + vec2(0, dy)).a;
36 | if (a > 0. && a < aMin) aMin = a;
37 | a = texture2D(texShield, vTexCoord + vec2(0, -dy)).a;
38 | if (a > 0. && a < aMin) aMin = a;
39 |
40 | if (aMin == 1.) aMin = 0.; // no min was found
41 |
42 | gl_FragColor = vec4(0, 0., 0., aMin);
43 | }
44 | }
45 | else
46 | {
47 | gl_FragColor = vec4(0, 0., 0., 0);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/gl/Palette.js:
--------------------------------------------------------------------------------
1 | define(["gl/GLHelper"], function(glhelper) {
2 | // RulesTexture and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt, RuleTexture ist auch ein shader
3 | // public interface
4 | var Palette = function(reactor, colors)
5 | {
6 | this.reactor = reactor;
7 | this.gl = reactor.gl;
8 |
9 |
10 | this.colors = colors || [];
11 | };
12 |
13 | Palette.prototype.setColor = function(index, color)
14 | {
15 | this.colors[index] = color;
16 | this.invalidateProgram();
17 | };
18 |
19 | Palette.prototype.generateColors = function(timeLine) {
20 |
21 | var oldIdx = -1;
22 | var oldCol = [];
23 |
24 | for (var colIdx in timeLine) {
25 | colIdx = parseInt(colIdx);
26 | var col = timeLine[colIdx];
27 |
28 | if (oldIdx !== -1) {
29 | var n = colIdx - oldIdx;
30 | for (var curIdx = oldIdx; curIdx <= colIdx; curIdx++)
31 | {
32 | var a = (curIdx-oldIdx)/n;
33 | var ia = 1 - a;
34 | this.setColor(curIdx, [ia*oldCol[0]+a*col[0], ia*oldCol[1]+a*col[1],
35 | ia*oldCol[2]+a*col[2], ia*oldCol[3]+a*col[3]]);
36 | }
37 | }
38 |
39 | oldIdx = colIdx;
40 | oldCol = col;
41 | }
42 | };
43 |
44 | Palette.prototype.getTexture = function()
45 | {
46 | if (!this.texture) {
47 | var pixels = [];
48 | for(var j = 0; j < 256; j++) // x axis
49 | {
50 | var color = this.colors[j];
51 | if (color)
52 | {
53 | pixels.push(color[0]);
54 | pixels.push(color[1]);
55 | pixels.push(color[2]);
56 | pixels.push(color[3]);
57 | }
58 | else
59 | pixels = pixels.concat([0,1,0,1]);
60 | }
61 |
62 | this.texture = glhelper.createRGBATexture(this. gl, 256, 1, new Uint8Array(pixels));
63 | }
64 | return this.texture;
65 | };
66 |
67 | Palette.prototype.invalidateProgram = function()
68 | {
69 | this.ruleTexture = null;
70 | };
71 |
72 | return Palette;
73 | });
74 |
--------------------------------------------------------------------------------
/src/gl/GLHelper.js:
--------------------------------------------------------------------------------
1 | define(function() {
2 | // used state frames
3 | // RGBA for now because ALPHA textures can not be rendered into
4 | function createRGBATexture(gl, width, height, pixels)
5 | {
6 | var texture = gl.createTexture();
7 | gl.bindTexture(gl.TEXTURE_2D, texture);
8 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
9 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
10 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
11 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
12 |
13 | var mode = (isPOT(width) && isPOT(height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
14 |
15 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);
16 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);
17 |
18 | return texture;
19 | }
20 |
21 | function isPOT(nr) {
22 | var p = 1;
23 | while (p < nr) {
24 | p *= 2;
25 | }
26 | return p == nr;
27 | }
28 |
29 | // used by the ruletable
30 | // ALPHA only to save memory
31 | function createAlphaTexture(gl, width, height, pixels)
32 | {
33 | var texture = gl.createTexture();
34 | gl.bindTexture(gl.TEXTURE_2D, texture);
35 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
36 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, pixels);
37 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
38 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
39 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
40 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
41 | return texture;
42 | }
43 |
44 | function getShader(gl, shaderType, sourceText)
45 | {
46 | var shader;
47 | shader = gl.createShader(shaderType);
48 | gl.shaderSource(shader, sourceText);
49 | gl.compileShader(shader);
50 | if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0)
51 | {
52 | alert(gl.getShaderInfoLog(shader));
53 | alert(sourceText);
54 | }
55 | return shader;
56 | }
57 |
58 | return {
59 | createRGBATexture: createRGBATexture,
60 | createAlphaTexture: createAlphaTexture,
61 | getShader: getShader,
62 | }
63 | });
64 |
--------------------------------------------------------------------------------
/src/CellSpace.js:
--------------------------------------------------------------------------------
1 | var perfStartedJSTime = performance.now();
2 |
3 | require.config({
4 | baseUrl: 'src',
5 | paths: {
6 | jquery: '../../res_evocell/src/libs/jquery-1.10.2',
7 | "jquery-ui": '../../res_evocell/src/libs/jquery-ui-1.10.4.custom',
8 | "underscore": "../../res_evocell/src/libs/underscore",
9 | backbone: "../../res_evocell/src/libs/backbone",
10 | knockback: "../../res_evocell/src/libs/knockback",
11 | knockout: "../../res_evocell/src/libs/knockout-3.0.0",
12 | meSpeak: "../../res_evocell/src/libs/mespeak/mespeak",
13 | three: "../../res_evocell/src/libs/three",
14 | "jquery-cycle": "../../res_evocell/src/libs/jquery.cycle.all",
15 | datgui: "../../res_evocell/src/libs/dat.gui.min",
16 | FileSaver: "../../res_evocell/src/libs/FileSaver",
17 | },
18 | shim: {
19 | datgui: {
20 | exports: "dat",
21 | },
22 | "jquery-ui": {
23 | exports: "$",
24 | deps: ['jquery', '../../res_evocell/src/libs/farbtastic']
25 | },
26 | "jquery-cycle": {
27 | deps: ["jquery-ui"]
28 | },
29 | underscore : {
30 | exports: "_",
31 | },
32 | backbone : {
33 | exports: "Backbone",
34 | deps: ['underscore'],
35 | },
36 | knockback: {
37 | exports: "kb",
38 | deps: ["backbone"],
39 | },
40 | knockout: {
41 | exports: "ko",
42 | },
43 | meSpeak: {
44 | exports: "meSpeak",
45 | },
46 | three: {
47 | exports: "THREE",
48 | },
49 | }
50 | });
51 |
52 |
53 | require([
54 | "jquery-ui", "Utils", "EvoCell", "story/StoryTeller", "underscore",
55 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui",
56 | "CellSpace/State", "CellSpace/Setup", "CellSpace/GameLoop", "CellSpace/GUI", "CellSpace/Utils"],
57 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat,
58 | gameState, csSetup, csGame, csUI, csUtils) {
59 | "use strict";
60 |
61 | gameState.perfRequireTime = performance.now();
62 | gameState.perfStartedJSTime = perfStartedJSTime;
63 |
64 | // MAIN LOOP (must be less than 20 LOC :)
65 |
66 | // TODO: extract this in CellSpace.App which bundles State, Setup, UI, ...
67 |
68 | // TODO: should we not put this in backbone ready function?
69 | // is jquery ready better?
70 | //$(window).load(function(e) {
71 | var canvas = document.getElementById('c');
72 |
73 | csSetup.setup(canvas, function () {
74 | csUI.setupGui();
75 |
76 | gameState.mainLoop = new utils.AnimationLoop(0, function() {
77 | csUI.pollUserInteraction();
78 | if (!gameState.pause || gameState.doOneStep) {
79 | csGame.step();
80 | gameState.doOneStep = false;
81 | }
82 | csGame.render();
83 | });
84 |
85 | gameState.perfFinishedJSTime = performance.now();
86 | csUtils.refreshGUI(["perfStartedJSTime","perfFinishedJSTime", "perfRequireTime"]);
87 |
88 | gameState.mainLoop.start();
89 | });
90 | //});
91 | });
92 |
--------------------------------------------------------------------------------
/src/CellSpace/State.js:
--------------------------------------------------------------------------------
1 | define(["backbone", "three", "datgui", "Utils"], function(Backbone, THREE, dat, utils) {
2 | "use strict";
3 |
4 |
5 | // // Define a Todo Model
6 | // var Layer = Backbone.Model.extend({
7 | // // Default todo attribute values
8 | // defaults: {
9 | // name: "no name given",
10 | // palette: null,
11 | // rule: null,
12 | // dish: null,
13 | // }
14 | // });
15 |
16 |
17 | //var Layers =
18 |
19 | // Instantiate the Todo Model with a title, with the completed attribute
20 | // defaulting to false
21 | // var myTodo = new Todo({
22 | // title: 'Check attributes property of the logged models in the console.'
23 | // });
24 |
25 | var resPath = "../res_evocell/";
26 | var libPath = "../" + resPath + "src/libs/";
27 |
28 |
29 | var gameState = {
30 | // APPSTATE ///////////
31 | canvas: null,
32 | reactor: null,
33 | gl: null,
34 | renderLoop: null,
35 | gui: new dat.GUI(),
36 | //fpsMonotor: null,
37 | keyboard: utils.keyboard,
38 |
39 | shaders: {},
40 | dishes: {},
41 | rules: {},
42 | colors: {},
43 | shots: null,
44 |
45 | pause: false,
46 | doOnestep: false,
47 |
48 | // GAMESTATE //////////
49 | // shots
50 | maxParticles: 1000,
51 | shotSpeed: 3.4,
52 | frontShots: 3,
53 | frontShotAngle: 0.2,
54 | shotN: 8,
55 | bombFired: 0,
56 | bAngle: 0, // direction of bomb fire
57 |
58 | autoFireCounter: 0,
59 | autoFireOn: 0,
60 | lastMouseNDC: new THREE.Vector2(),
61 |
62 | gameW: 256, gameH: 256,
63 | screenW: window.innerWidth ? window.innerWidth : 1000,
64 | screenH: window.innerHeight ? window.innerHeight :800,
65 |
66 | randomDensity: 0.001,
67 | enemySpeed: 0.5,
68 | weaponExplosionParam: 3,
69 |
70 | zoom: 1/3, // cell/pixel ratio
71 | rot: 0,
72 |
73 | cameraAngle: 60 * (Math.PI/180),
74 | viewMatrix: new THREE.Matrix4(),
75 | projectionMatrix: new THREE.Matrix4(),
76 |
77 | enableScrolling: 1,
78 | renderer: "Simple", // "Cell", "TV", "Simple", "Fast"
79 | shipX: 0, shipY: 0,
80 | shipRadius: 3,
81 | shipDir: 0,
82 | shipSpeed: 0,
83 | shipSpeedX: 0, shipSpeedY: 0, // derived values
84 |
85 | scrollX: 0, scrollY: 0,
86 | parallaxX: 0, parallaxY: 0,
87 | playerEnergy: 1000,
88 | stepSize: 1.5,
89 |
90 | mouseMode: "shoot",
91 | // HACK: find better init solution
92 | cnt: 100, // used for executing dishes.enemy only every nth tep
93 |
94 | sndInit: new Audio(resPath + "sound/Digital_SFX_Set/laser3.mp3"),
95 | snd: new Audio(resPath + "sound/Digital_SFX_Set/laser6.mp3"),
96 | sndBomb: new Audio(resPath + "sound/Digital_SFX_Set/laser4.mp3"),
97 | sndHit: new Audio(resPath + "sound/Digital_SFX_Set/laser9.mp3"),
98 | sndHit2: new Audio(resPath + "sound/Digital_SFX_Set/laser9.mp3"),
99 |
100 | // game model
101 | civX: 0.1,
102 | civY: 0.1,
103 | civZ: 0.1,
104 | civW: 0.1,
105 |
106 | clipX: 0.1,
107 | clipY: 0.1,
108 |
109 | drawModel: new Backbone.Model({
110 | availableRules: [],
111 | selectedRules: [],
112 |
113 | // TODO: should all be populated
114 | availableLayers: [], // gets populated
115 | availableStates: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
116 |
117 | drawSizeX: 30,
118 | drawSizeY: 30,
119 | selectedDrawShape : "circle",
120 | selectedStates : [3],
121 | selectedLayers : ["enemy"],
122 | }),
123 |
124 | selection : {
125 | active: false,
126 | size: [0, 0],
127 | pos:[0, 0],
128 | downPos: [0, 0],
129 | lastPos: [0, 0],
130 | },
131 |
132 | // Example: local resources in the same directory
133 | // resPath: "./"
134 | // resources in res_evocell repository
135 | resPath: resPath,
136 | libPath: libPath,
137 |
138 | // performance measures
139 | perfStartJSTime: -0.001,
140 | perfRequireTime: -0.001,
141 | perfFinishedJSTime: -0.001,
142 | showBuffer: false,
143 | showRule: false,
144 | };
145 |
146 | return gameState;
147 | });
--------------------------------------------------------------------------------
/src/gl/Dish.js:
--------------------------------------------------------------------------------
1 | define(["gl/GLHelper"], function(glhelper) {
2 | // Rules and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt
3 | Dish = function(reactor, w, h)
4 | {
5 | this.gl = reactor.gl;
6 | this.setSize(w, h);
7 | }
8 |
9 | Dish.prototype.flip = function()
10 | {
11 | this.frameFlip = -this.frameFlip;
12 | }
13 |
14 | Dish.prototype.setSize = function(w, h)
15 | {
16 | if (!this.texture1)
17 | {
18 | this.gl.deleteTexture(this.texture1);
19 | }
20 | if (!this.texture2)
21 | {
22 | this.gl.deleteTexture(this.texture2);
23 | }
24 |
25 | if (!this.fb1)
26 | {
27 | this.gl.deleteFramebuffer(this.fb1);
28 | }
29 | if (!this.fb2)
30 | {
31 | this.gl.deleteFramebuffer(this.fb2);
32 | }
33 |
34 | this.width = w;
35 | this.height = h;
36 |
37 | this.texture1 = createDishTexture(this.gl, w, h, 0);
38 | this.texture2 = createDishTexture(this.gl, w, h, 0);
39 |
40 | this.fb1 = this.gl.createFramebuffer();
41 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fb1);
42 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, this.texture1, 0);
43 |
44 | this.fb2 = this.gl.createFramebuffer();
45 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fb2);
46 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, this.texture2, 0);
47 |
48 | this.frameFlip = 1;
49 | }
50 |
51 | Dish.prototype.setAll = function(state) {
52 | this.randomize(1, 0); // HACK!!!! TODO: fix clearshader!
53 | }
54 |
55 | Dish.prototype.randomize = function(nrStates, density)
56 | {
57 | var texture = createDishTextureRandom(this.gl, this.width, this.height, nrStates, density);
58 | var fb;
59 |
60 | if (this.frameFlip > 0)
61 | {
62 | this.gl.deleteTexture(this.texture1);
63 | fb = this.fb1;
64 | this.texture1 = texture;
65 | }
66 | else
67 | {
68 | this.gl.deleteTexture(this.texture2);
69 | fb = this.fb2;
70 | this.texture2 = texture;
71 | }
72 |
73 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fb);
74 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0);
75 | }
76 |
77 | Dish.prototype.getCurrentTexture = function()
78 | {
79 | return (this.frameFlip > 0) ? this.texture1 : this.texture2;
80 | }
81 |
82 | Dish.prototype.getCurrentFramebuffer = function()
83 | {
84 | return (this.frameFlip > 0) ? this.fb1 : this.fb2;
85 | }
86 |
87 | Dish.prototype.getNextFramebuffer = function()
88 | {
89 | return (this.frameFlip > 0) ? this.fb2 : this.fb1;
90 | }
91 |
92 | function createDishTexture(gl, width, height, state)
93 | {
94 | var pixels = [];
95 | for(var i = 0; i < height; i++) // y axis
96 | {
97 | for(var j = 0; j < width; j++) // x axis
98 | {
99 | pixels.push(0);
100 | pixels.push(0);
101 | pixels.push(0);
102 | pixels.push(state);
103 | }
104 | }
105 | // must be RGBA otherwise we cant use it as color attachment of the framebuffer
106 | return glhelper.createRGBATexture(gl, width, height, new Uint8Array(pixels))
107 | }
108 |
109 | function createDishTextureRandom(gl, width, height, states, density)
110 | {
111 | var pixels = [];
112 | for(var i = 0; i < height; i++) // y axis
113 | {
114 | for(var j = 0; j < width; j++) // x axis
115 | {
116 | if (Math.random() < density)
117 | {
118 | pixels.push(0);
119 | pixels.push(0);
120 | pixels.push(0);
121 | pixels.push(Math.floor(Math.random()*states));
122 | }
123 | else
124 | {
125 | pixels.push(0);
126 | pixels.push(0);
127 | pixels.push(0);
128 | pixels.push(0);
129 | }
130 | }
131 | }
132 | // must be RGBA otherwise we cant use it as color attachment of the framebuffer
133 | return glhelper.createRGBATexture(gl, width, height, new Uint8Array(pixels))
134 | }
135 |
136 | /*
137 | function createFrameTextureFromPattern(gl, evoCellData, width, height)
138 | {
139 | var pixels = [];
140 | for(var i = 0; i < height; i++) // y axis
141 | {
142 | for(var j = 0; j < width; j++) // x axis
143 | {
144 | if (j < evoCellData.patternWidth && i < evoCellData.patternHeight)
145 | {
146 | pixels.push(evoCellData.patternData[i*evoCellData.patternWidth+j]);
147 | }
148 | else pixels.push(0);
149 | }
150 | }
151 | return createCATexture(gl, width, height, new Uint8Array(pixels));
152 | }
153 | */
154 | return Dish;
155 | });
156 |
157 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family:sans-serif;
3 | background:rgba(0,0,0,1);
4 | font-size: 0.7em;
5 | }
6 |
7 | #toolContainer {
8 | xtransition-property: all;
9 | xtransition-duration: 2.5s;
10 | xtransition-timing-function: ease;
11 | xtransition-delay: 0s;
12 |
13 | overflow:hidden;
14 | width:30em;
15 | position: fixed; top: 0px;left: 0px; z-index:10;
16 | color: white;
17 |
18 | z-index: 120;
19 | padding: 0;
20 | margin: 0;
21 | }
22 |
23 | .toolWindow {
24 | transition-property: max-height;
25 | transition-duration: 0.5s;
26 | transition-timing-function: ease;
27 | transition-delay: 0s;
28 | overflow:hidden;
29 | max-height:2.7em; /*(padding == 2*0.4 zaehlen nicht -0.8em) 2em + 2 * 2* 0.1 paddig + 2* 0.2 margin unten */
30 | color: white;
31 | background:rgba(255, 0, 204, .3);
32 | z-index: 125;
33 | margin: 0;
34 | padding: 0.4em;
35 | }
36 |
37 | /*
38 | #toolsMenu {
39 | padding: 0.2em;
40 | margin: 0.2em;
41 | border: 0px;
42 | position: fixed; bottom: 0px;right: 0px; z-index:2;
43 | color: white; width: 400px;
44 | background:rgba(0,0,0,1);
45 | }
46 |
47 | #toolsMenu a {
48 |
49 | }
50 | */
51 |
52 | .toolMenuContent {
53 | background:rgba(0,0,0,0.85);
54 | margin: 0;
55 | padding: 0.4em;
56 | }
57 |
58 |
59 | .toolWindow:hover {
60 | background:rgba(255, 0, 204, .6);
61 | }
62 |
63 | .activeWindow {
64 | max-height: 1200px;
65 | background:rgba(255, 0, 204, .6);
66 | }
67 | /* 2 + 0.4 oben + 0.2 marg + 0.1pad = 2.7*/
68 | .toolMenuHeader {
69 | background:rgba(0,0,0,0.8);
70 | margin: 0 0 0.2em 0;
71 | padding: 0.1em;
72 | cursor:pointer;
73 | font-size: 2em;
74 |
75 | background-image:url("../../res_evocell/images/icons/icons/plus.png");
76 | background-size: 1.4em;
77 | background-repeat:no-repeat;
78 | background-position:right top;
79 | }
80 |
81 | /*.activeWindow:nth-child(1) {*/
82 | .activeWindow .toolMenuHeader {
83 | background:rgba(0,0,0, .5);
84 | background-image:url("../../res_evocell/images/icons/icons/minus.png");
85 | background-size: 1.4em;
86 | background-repeat:no-repeat;
87 | background-position:right top;
88 | }
89 |
90 |
91 | /*#aboutWindow {*/
92 | /*transition-property: max-height;*/
93 | /*transition-duration: 0.3s;*/
94 | /*transition-timing-function: ease;*/
95 | /*transition-delay: 0s;*/
96 |
97 | /*overflow:hidden;*/
98 | /*width: 100%;*/
99 | /*max-height:4.2em;*/
100 | /*color: white;*/
101 | /*background:rgba(255,0,0,0.2);*/
102 |
103 | /*z-index: 120; */
104 | /*}*/
105 |
106 | #aboutWindow:hover {
107 | max-height: 1200px;
108 | }
109 |
110 |
111 | .buttons {
112 | color: white;
113 | }
114 |
115 | .buttons:hover{
116 | text-shadow: 0 0 1.0em yellow
117 | }
118 |
119 | .buttons:active{
120 | color: black;
121 | }
122 |
123 | .storyElement {
124 | kjkjjposition: relative;
125 | display: none;
126 | color: white;
127 | }
128 |
129 | .xxxstoryElement h2 {
130 | padding: 5px 5px 5px 5px;
131 | position: absolute;
132 | bottom: 30px;
133 | right: 200px;
134 | width: 400px;
135 | color: white;
136 | background:rgba(0,0,0,0.7);
137 |
138 | display: inline;
139 | float: none;
140 | }
141 |
142 | .storyElement img {
143 | height: 75%;
144 | }
145 |
146 |
147 | select {
148 | font-size:9pt;
149 | letter-spacing:0.07em;
150 | color: white;
151 | background:transparent;
152 | border: solid 1px #808080;
153 | padding:3px;
154 | cursor:pointer;
155 | }
156 | select option:checked {
157 | }
158 | select option:nth-child(even) { }
159 |
160 |
161 | #centeredContainer {
162 | position: absolute;
163 | top: 50%;
164 | left: 0;
165 | width: 100%;
166 | margin-top: -10px;
167 | line-height: 20px;
168 | text-align: center;
169 | vertical-align: center;
170 | z-index: 110;
171 | background:rgba(255,255,255, 0);
172 | display: none;
173 | }
174 |
175 | #centeredMessage {
176 | padding: 100px;
177 | width:600px;
178 | background:rgba(255,0, 0, 0.8);
179 | display: inline-block;
180 | font-size: 3em;
181 | line-height: 1em;
182 | }
183 |
184 | .fileUpload {
185 | position: relative;
186 | overflow: hidden;
187 | margin: 10px;
188 | padding: 10px;
189 | }
190 |
191 | .fileUpload input.upload {
192 | position: absolute;
193 | top: 0;
194 | right: 0;
195 | margin: 0;
196 | padding: 0;
197 | font-size: 20px;
198 | cursor: pointer;
199 | opacity: 0;
200 | filter: alpha(opacity=0);
201 | }
202 |
--------------------------------------------------------------------------------
/src/data/FileStore.js:
--------------------------------------------------------------------------------
1 | define(["underscore", "backbone"], function(_, bb) {
2 | var indexedDB = window.indexedDB;
3 |
4 | const dbName = "EvoCell";
5 | var db = null;
6 |
7 | const ruleStoreName = "rules";
8 | var ruleStore = null;
9 |
10 | var readyFns = [];
11 |
12 | var ready = function(fn) {
13 | if (db) fn();
14 | else {
15 | readyFns.push(fn);
16 | }
17 | };
18 |
19 | var request = indexedDB.open(dbName, 4);
20 | request.onerror = function(event) {
21 | alert("Oh no! Why didn't you allow my web app to use IndexedDB?! Cell is sad now!");
22 | };
23 | request.onsuccess = function(event) {
24 | db = event.target.result;
25 |
26 | _.each(readyFns, function(fn) { fn(); });
27 |
28 | db.onerror = function(event) {
29 | // Generic error handler for all errors targeted at this database's requests!
30 | alert("Database error: " + event.target.errorCode);
31 | };
32 | };
33 |
34 | request.onupgradeneeded = function(event) {
35 | //alert("upgrade from: " + event.oldVersion + " to: " + event.newVersion);
36 | var db = event.target.result;
37 |
38 | if(!db.objectStoreNames.contains(ruleStoreName)) {
39 | var objectStore = db.createObjectStore(ruleStoreName, { keyPath: "name"});
40 | }
41 | // else
42 | // {
43 | // db.deleteObjectStore(ruleStoreName);
44 | // }
45 |
46 | if(!db.objectStoreNames.contains("gameStates")) {
47 | db.createObjectStore("gameStates", { keyPath: "id"});
48 | }
49 | // Create an index to search customers by name. We may have duplicates
50 | // so we can't use a unique index.
51 | //objectStore.createIndex("name", "name", { unique: true });
52 | };
53 |
54 | var addObject = function(storeName, data, oncomplete, onerror) {
55 | var transaction = db.transaction([storeName], "readwrite");
56 | if (oncomplete) transaction.oncomplete = oncomplete;
57 | if (onerror) transaction.onerror = onerror;
58 | var objectStore = transaction.objectStore(storeName);
59 | var request = objectStore.add(data);
60 | request.onerror = function(event) {
61 | //alert("me so sorry, could not add: " + data + "\n" + storeName);
62 | };
63 | request.onsuccess = function(event) {
64 | // dont call callback here call it later when transaction is done!
65 | };
66 | };
67 |
68 | var deleteObject = function(storeName, id, callback) {
69 | var transaction = db.transaction([storeName], "readwrite");
70 | transaction.oncomplete = function(e) {
71 | if (callback) callback();
72 | };
73 | var request = transaction
74 | .objectStore(storeName)
75 | .delete(id);
76 | request.onerror = function(event) {
77 | alert("me so sorry, could not delte object id:" + id);
78 | };
79 | request.onsuccess = function(event) {
80 | // It's gone!
81 | };
82 | };
83 | var getObject = function(storeName, id, callback) {
84 | var objectStore = db.transaction([storeName]).objectStore(storeName);
85 | var request = objectStore.get(id);
86 | request.onerror = function(event) {
87 | alert("me so sorry, could not load object with id name:" + id);
88 | };
89 | request.onsuccess = function(event) {
90 | var result = event.target.result;
91 | if (!result)
92 | alert("Empty result for id: " + id);
93 | callback(result);
94 | };
95 | };
96 | var getAllObjects = function(storeName, callback, filter) {
97 | filter = filter || function(x) { return true; };
98 | var objects = [];
99 | var objectStore = db.transaction(ruleStoreName).objectStore(storeName);
100 |
101 | objectStore.openCursor().onsuccess = function(event) {
102 | var cursor = event.target.result;
103 | if (cursor) {
104 | if (filter(cursor.value))
105 | objects.push(cursor.value);
106 | cursor.continue();
107 | }
108 | else {
109 | callback(objects);
110 | }
111 | };
112 | };
113 |
114 | var storeRule = function(name, ruleData, callback) {
115 | addObject(ruleStoreName, {name: name, ruleData: ruleData}, callback);
116 | };
117 |
118 | var deleteRule = function(name, callback) {
119 | deleteObject(ruleStoreName, name, callback);
120 | };
121 |
122 | var loadRule = function(name, callback) {
123 | getObject(ruleStoreName, name, callback);
124 | };
125 |
126 | var loadAllRules = function(callback) {
127 | getAllObjects(ruleStoreName, callback);
128 | };
129 |
130 | var loadAllRuleNames = function(callback) {
131 | getAllObjects(ruleStoreName, function(rules) {
132 | callback(_.map(rules, function(rule) { return rule.name; }));
133 | });
134 | } ;
135 |
136 | return {
137 | addObject: addObject,
138 | deleteObject: deleteObject,
139 | getObject: getObject,
140 | getAllObjects: getAllObjects,
141 |
142 | // depricated "rule" functions use addObject, deleteObject, ... insted
143 | storeRule: storeRule,
144 | deleteRule: deleteRule,
145 | loadRule: loadRule,
146 | loadAllRules: loadAllRules,
147 | loadAllRuleNames: loadAllRuleNames,
148 |
149 | ready: ready,
150 | };
151 | });
152 |
--------------------------------------------------------------------------------
/src/gl/ParticleSystem.js:
--------------------------------------------------------------------------------
1 | define([], function() {
2 | var ParticleSystem = function(reactor, n, width, height) {
3 | this.reactor = reactor;
4 | var gl = reactor.gl;
5 | this.gl = gl;
6 | this.n = n;
7 | this.width = width;
8 | this.height = height;
9 |
10 | var pointAlive = new Uint8Array(n);
11 | var pointCoordinates = new Float32Array(2*n);
12 | var pointSpeeds = new Float32Array(2*n);
13 |
14 | this.pointCoordinates = pointCoordinates;
15 | this.pointSpeeds = pointSpeeds;
16 | //var pointAlive = new Array(n);
17 |
18 | for (var i = 0; i < n; i++)
19 | {
20 | pointAlive[i] = 1;
21 | pointSpeeds[2*i] = 0;
22 | pointSpeeds[2*i+1] = 0;
23 | pointCoordinates[2*i] = 10; // outside of bound
24 | pointCoordinates[2*i+1] = 0;
25 | }
26 |
27 | var pixelValues = new Uint8Array(width*height*4); // for colission detection
28 | this.pixelValues = pixelValues;
29 |
30 | var pointsBuffer = gl.createBuffer();
31 | this.pointsBuffer = pointsBuffer;
32 | gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);
33 | gl.bufferData(gl.ARRAY_BUFFER, pointCoordinates.byteLength, gl.STATIC_DRAW);
34 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, pointCoordinates);
35 | };
36 |
37 | ParticleSystem.prototype.draw = function(shader, dish, offsetx, offsety) {
38 | var gl = this.gl;
39 | gl.bindBuffer(gl.ARRAY_BUFFER, this.pointsBuffer);
40 | gl.bufferData(gl.ARRAY_BUFFER, this.pointCoordinates.byteLength, gl.STATIC_DRAW);
41 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.pointCoordinates);
42 |
43 | var psContext = this;
44 | this.reactor.applyShader(shader, dish.getCurrentFramebuffer(), false, function(gl, shader) {
45 | //gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);
46 | var pointPosLoc = gl.getAttribLocation(shader, "pointPos");
47 | gl.enableVertexAttribArray(pointPosLoc);
48 | gl.vertexAttribPointer(pointPosLoc, 2, gl.FLOAT, gl.FALSE, 0, 0);
49 |
50 | // var pointAliveLoc = gl.getAttribLocation(shader, "pointAlive");
51 | // gl.enableVertexAttribArray(pointAliveLoc);
52 | // gl.vertexAttribPointer(pointAliveLoc, 1, gl.UNSIGNED_BYTE, gl.FALSE, 0, 0);
53 |
54 | gl.uniform1f(gl.getUniformLocation(shader, "state"), 3/255);
55 |
56 | gl.uniform2f(gl.getUniformLocation(shader, "offset"), offsetx/psContext.width, offsety/psContext.height);
57 |
58 | gl.drawArrays(gl.POINTS,0, psContext.pointCoordinates.length/2);
59 | });
60 | };
61 |
62 | ParticleSystem.prototype.step = function() {
63 | // move
64 | for (var i = 0; i < this.n; i++)
65 | {
66 | this.pointCoordinates[2*i] += this.pointSpeeds[2*i];
67 | this.pointCoordinates[2*i+1] += this.pointSpeeds[2*i+1];
68 | }
69 | };
70 |
71 | ParticleSystem.prototype.collide = function (dish, offsetx, offsety, cb, timeShift) {
72 | timeShift = timeShift || 0;
73 |
74 | var gl = this.gl;
75 |
76 | gl.bindFramebuffer(gl.FRAMEBUFFER, dish.getCurrentFramebuffer());
77 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, dish.getCurrentTexture(), 0);
78 | gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, this.pixelValues);
79 |
80 |
81 | var soffsetx = offsetx / this.width;
82 | var soffsety = offsety / this.height;
83 |
84 | for (var i = 0; i < this.n; i++)
85 | {
86 | var pX = Math.round(offsetx + this.width*0.5*(this.pointCoordinates[2*i]+1 + timeShift * this.pointSpeeds[2*i]));
87 | var pY = Math.round(offsety + this.height*0.5*(this.pointCoordinates[2*i+1]+1 + timeShift * this.pointSpeeds[2*i+1]));
88 |
89 | if (pX >= 0 && pX < this.width && pY >= 0 && pY < this.height) {
90 | if (this.pixelValues[(pX+pY*this.width)*4 + 3] !== 0) {
91 | if (cb) cb([pX, pY]);
92 | this.pointCoordinates[2*i] = 10.0; // out of range
93 | this.pointCoordinates[2*i+1] = 10.0;
94 | this.pointSpeeds[2*i] = 0;
95 | this.pointSpeeds[2*i+1] = 0;
96 | }
97 | }
98 | }
99 |
100 | return this.pixelValues;
101 | };
102 |
103 | var allocIdx = 0;
104 | ParticleSystem.prototype.allocateParticle = function(x, y, xs, ys) {
105 | var xScale = 2/this.width;
106 | var yScale = 2/this.height;
107 |
108 | x = xScale * x - 1;
109 | y = yScale * y - 1;
110 | xs = xScale * xs;
111 | ys = yScale * ys;
112 |
113 | for (var j = 0; j < this.n; j++)
114 | {
115 | var i = (j+allocIdx)%this.n;
116 | var xx = this.pointCoordinates[2*i];
117 | var yy = this.pointCoordinates[2*i+1];
118 | if (xx < -1 || xx > 1 || yy < -1 || yy > 1)
119 | {
120 | this.pointCoordinates[2*i] = x;
121 | this.pointCoordinates[2*i + 1] = y;
122 |
123 | this.pointSpeeds[2*i] = xs;
124 | this.pointSpeeds[2*i + 1] = ys;
125 |
126 | allocIdx = i+1;
127 | break;
128 | }
129 | }
130 | };
131 |
132 | ParticleSystem.prototype.allocateSphere = function(n, x, y, s, angle, shipSpeedX, shipSpeedY) {
133 | this.allocateParticles(n, function(i, n) {
134 | return [x, y,
135 | s * Math.cos(angle+Math.PI*2*i/n) + shipSpeedX,
136 | s * Math.sin(angle+Math.PI*2*i/n) + shipSpeedY
137 | ];
138 | });
139 | };
140 |
141 | ParticleSystem.prototype.allocateParticles = function(n, generatorFn) {
142 | for (var i = 0; i < n; i++) {
143 | var params = generatorFn(i, n);
144 | this.allocateParticle.apply(this, params);
145 | }
146 | };
147 |
148 | return ParticleSystem;
149 | });
150 |
--------------------------------------------------------------------------------
/src/gl/Rule.js:
--------------------------------------------------------------------------------
1 | define(["gl/GLHelper"], function(glhelper) {
2 | // RulesTexture and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt, RuleTexture ist auch ein shader
3 | // public interface
4 | var Rule = function(reactor, ruleData, dish)
5 | {
6 | this.reactor = reactor;
7 | this.gl = reactor.gl;
8 |
9 | this.setRule(ruleData);
10 | // candy
11 | if (dish)
12 | this.setCompileSizeDish(dish);
13 | };
14 |
15 | Rule.prototype.setRule = function(ruleData)
16 | {
17 | this.ruleData = ruleData;
18 |
19 |
20 | if (ruleData.containsRule)
21 | {
22 | var xN = Math.ceil(ruleData.nrNeighbours/2);
23 | var yN = Math.floor(ruleData.nrNeighbours/2);
24 | var width = Math.pow(ruleData.nrStates, xN);
25 | var height = Math.pow(ruleData.nrStates, yN);
26 |
27 | this.nrStates = ruleData.nrStates;
28 | this.ruleTexture = glhelper.createAlphaTexture(this.gl, width, height, ruleData.ruleTable);
29 |
30 | this.width = width;
31 | this.height = height;
32 |
33 | this.invalidateProgram();
34 | }
35 | if (ruleData.containsPattern)
36 | {
37 | this.patternTexture = glhelper.createAlphaTexture(this.gl,
38 | ruleData.patternWidth, ruleData.patternHeight, ruleData.patternData);
39 | }
40 | };
41 |
42 | Rule.prototype.getPatternTexture = function() {
43 | return this.patternTexture;
44 | };
45 |
46 | // candy
47 | Rule.prototype.setCompileSizeDish = function(dish) {
48 | this.setCompileSize(dish.width, dish.height);
49 | };
50 |
51 | Rule.prototype.setCompileSize = function(w, h)
52 | {
53 | this.compileWidth = w;
54 | this.compileHeight = h;
55 |
56 | this.invalidateProgram();
57 | };
58 |
59 | Rule.prototype.getTexture = function()
60 | {
61 | return this.ruleTexture;
62 | };
63 |
64 | Rule.prototype.getProgram = function()
65 | {
66 | if (this.program == null)
67 | {
68 | if (this.compileWidth == null || this.compileHeight == null || this.ruleData == null)
69 | {
70 | throw "You have to set the width and height and ruledata of the Rule object to compile a shader";
71 | }
72 |
73 | this.program = this.reactor.compileShader(getFragmentShaderSourceFromEvoCellData(this.gl, this.ruleData, this.compileWidth, this.compileHeight));
74 | }
75 |
76 | return this.program;
77 | }
78 |
79 | // private
80 |
81 | Rule.prototype.invalidateProgram = function()
82 | {
83 | this.program = null;
84 | }
85 |
86 | //////////////////////////////////////////////////////////
87 | // Static functions
88 | //////////////////////////////////////////////////////////
89 |
90 | var EvoCellFragmentShaderTemplate =
91 | "#ifdef GL_ES\n" +
92 | "precision highp float;\n" +
93 | "#endif\n" +
94 | " uniform sampler2D texFrame;\n" +
95 | " uniform sampler2D texRule;\n" +
96 | " \n" +
97 | " varying vec2 vTexCoord;\n" +
98 | " const float dx = 1./%XRES%., dy=1./%YRES%.;\n" +
99 | " const float stateScale = 255.;\n" +
100 | " const float states = %STATES%.;\n" +
101 | " const float width = %WIDTH%, height = %HEIGHT%;\n" +
102 | " \n" +
103 | " \n" +
104 | "void main(void) {\n" +
105 | " float v; \n" +
106 | " float idx = 0.;\n" +
107 | " %XBLOCK% \n" +
108 | " \n" +
109 | " float idy = 0.;\n" +
110 | " %YBLOCK% \n" +
111 | " \n" +
112 | " vec2 vvvv=vec2(0.5/width + idx*stateScale/width, 0.5/height + idy*stateScale/height); \n" +
113 | " vec4 lookup = texture2D(texRule, vvvv);\n" +
114 | " gl_FragColor = vec4(0, 0., 0., lookup.a);\n" +
115 | //" gl_FragColor = vec4(0, 0., vTexCoord.x, vTexCoord.y);\n" +
116 | "}";
117 |
118 | var getFragmentShaderSourceFromEvoCellData = function(gl, evoCellData, xres, yres)
119 | {
120 | var xblock = "";
121 | var yblock = "";
122 | var nInXBlock = Math.ceil(evoCellData.nrNeighbours/2);
123 | var vget = "";
124 | var multiplier = "";
125 | var widthExpr = "";
126 | var heightExpr = "";
127 |
128 | for (var nIndex = 0; nIndex < evoCellData.neighbourhood.length; nIndex++)
129 | {
130 | var neighbour = evoCellData.neighbourhood[nIndex];
131 |
132 | vget = "v = texture2D(texFrame, vTexCoord + vec2(" + neighbour[0] + ".*dx, " + neighbour[1] + ".*dy)).a;\n";
133 |
134 |
135 | if (nIndex < nInXBlock)
136 | {
137 | xblock += vget;
138 | xblock += "idx += " + multiplier + "v;\n";
139 |
140 | if (widthExpr != "")
141 | widthExpr += "*";
142 | widthExpr += "states";
143 | }
144 | else
145 | {
146 | yblock += vget;
147 | yblock += "idy += " + multiplier + "v;\n";
148 |
149 | if (heightExpr != "")
150 | heightExpr += "*";
151 | heightExpr += "states";
152 | }
153 |
154 | if (nIndex == nInXBlock-1)
155 | multiplier = "";
156 | else
157 | multiplier += "states*";
158 | }
159 |
160 | var shaderSource = EvoCellFragmentShaderTemplate;
161 | shaderSource = shaderSource.replace("%STATES%", evoCellData.nrStates);
162 | shaderSource = shaderSource.replace("%XRES%", xres); // width of the state texture
163 | shaderSource = shaderSource.replace("%YRES%", yres); // height of the state texture
164 | shaderSource = shaderSource.replace("%WIDTH%", widthExpr); // width of the rule texture
165 | shaderSource = shaderSource.replace("%HEIGHT%", heightExpr); // height of the rule texture
166 | shaderSource = shaderSource.replace("%XBLOCK%", xblock);
167 | shaderSource = shaderSource.replace("%YBLOCK%", yblock);
168 |
169 | //alert(shaderSource);
170 | return shaderSource;
171 | }
172 |
173 |
174 |
175 | return Rule;
176 | });
177 |
--------------------------------------------------------------------------------
/src/Utils.js:
--------------------------------------------------------------------------------
1 | define(["jquery", "FileSaver"], function($, saveAs) {
2 | function relMouseCoords(event){
3 | var totalOffsetX = 0;
4 | var totalOffsetY = 0;
5 | var canvasX = 0;
6 | var canvasY = 0;
7 | var currentElement = this;
8 |
9 | do{
10 | totalOffsetX += currentElement.offsetLeft;
11 | totalOffsetY += currentElement.offsetTop;
12 | }
13 | while(currentElement = currentElement.offsetParent)
14 |
15 | canvasX = event.pageX - totalOffsetX;
16 | canvasY = event.pageY - totalOffsetY;
17 |
18 | return {x:canvasX, y:canvasY}
19 | }
20 | HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
21 |
22 | var getNDCFromMouseEvent = function(canvas, evt, screenW, screenH) {
23 | var coords = canvas.relMouseCoords(evt);
24 | return new THREE.Vector2(coords.x/screenW, (screenH - coords.y)/screenH);
25 | };
26 |
27 |
28 | // static
29 | var playSound = function(snd) {
30 | try {
31 | snd.currentTime=0;
32 | snd.play();
33 | } catch(ex) {}
34 | };
35 |
36 | // TODO: gamestate access, otherwise static
37 | // gets NDC (0 to 1) of clicked postion
38 | // itersects line form eye (0, 0, 0) to cliked position of a viewMatrix transformed plane in x/y plane
39 | // returns computed object coordinates (-1 to 1 for x and y, 0 for z)
40 | var intersectClick = function(clickedNDC, viewMatrix, camAH) {
41 | var invMV = new THREE.Matrix4().getInverse(viewMatrix);
42 |
43 | var planeNormal = new THREE.Vector4(0, 0, -1, 0);
44 | var planePoint = new THREE.Vector4(0, 0, 0, 1);
45 |
46 | // here the projection matrix is used or at least the cameraAngle
47 | var sf = Math.sin(camAH)/Math.cos(camAH);
48 | var lineDir = new THREE.Vector4(sf*(2*clickedNDC.x - 1), sf*(2*clickedNDC.y - 1), -1, 0);
49 | var linePoint = new THREE.Vector4();
50 |
51 | planeNormal.applyMatrix4(viewMatrix);
52 | planePoint.applyMatrix4(viewMatrix);
53 |
54 | var a = new THREE.Vector4().subVectors(planePoint, linePoint).dot(planeNormal);
55 | var b = lineDir.dot(planeNormal);
56 |
57 | var pointPos = a / b;
58 |
59 | var point = new THREE.Vector4().addVectors(linePoint, lineDir.clone().multiplyScalar(pointPos));
60 | var deltaPoint = point.clone().applyMatrix4(invMV);
61 |
62 | return deltaPoint;
63 | };
64 |
65 | function getFromURL(url, responseType, cb) {
66 | var r = new XMLHttpRequest();
67 | r.open("GET", url, true);
68 | // "arraybuffer", "blob", "document", "json", and "text".
69 | r.responseType = responseType;
70 | r.onload = function() { // XHR2
71 | if (cb) cb(r.response); // XHR2
72 | };
73 | r.send();
74 | }
75 |
76 | var requestAnimFrame = (function(){
77 | return window.webkitRequestAnimationFrame ||
78 | window.mozRequestAnimationFrame ||
79 | function(callback, element){ setTimeout(callback, 1000 / 60); };
80 | })();
81 |
82 | // if ms == 0 use AbimationLoop otherwise fixed timing
83 | var AnimationLoop = function(ms, callback) {
84 | this.ms = ms;
85 | this.callback = callback;
86 | this.pauseRequested = true;
87 | };
88 |
89 | AnimationLoop.prototype.start = function() {
90 | var loopContext = this;
91 | this.pauseRequested = false;
92 | var myFn = function() {
93 | // requestnext frame aspa according to: https://developer.mozilla.org/en-US/docs/Games/Anatomy
94 | if (!loopContext.pauseRequested) {
95 | if (loopContext.ms)
96 | setTimeout(myFn, loopContext.ms);
97 | else
98 | requestAnimFrame(myFn);
99 |
100 | loopContext.callback();
101 | }
102 | };
103 | myFn();
104 | };
105 |
106 | AnimationLoop.prototype.step = function() {
107 | this.callback();
108 | }
109 |
110 | AnimationLoop.prototype.stop = function() {
111 | this.pauseRequested = true;
112 | }
113 |
114 | AnimationLoop.prototype.toggle = function() {
115 | if (this.pauseRequested) {
116 | this.start();
117 | }
118 | else {
119 | this.stop();
120 | }
121 | }
122 |
123 | var FPSMonitor = function(elementID) {
124 | this.frames = 0;
125 |
126 | var delayMs = 1000;
127 | var time;
128 | var monitorContext = this;
129 |
130 | function fr(){
131 | var ti = new Date().getTime();
132 | var fps = Math.round(1000*monitorContext.frames/(ti - time));
133 | document.getElementById(elementID).innerHTML = fps.toString();
134 | monitorContext.frames = 0;
135 | time = ti;
136 | }
137 | var timer = setInterval(fr, delayMs);
138 | time = new Date().getTime();
139 | };
140 | FPSMonitor.prototype.frameIncrease = function() {
141 | this.frames++;
142 | };
143 |
144 | var ExtractFilename = function(url) {}
145 |
146 | var Keyboard = {
147 | _pressed: {},
148 |
149 | LEFT: 37,
150 | UP: 38,
151 | RIGHT: 39,
152 | DOWN: 40,
153 |
154 | isPressed: function(keyCode) {
155 | return this._pressed[keyCode];
156 | },
157 |
158 | onKeydown: function(event) {
159 | this._pressed[event.keyCode] = true;
160 | },
161 |
162 | onKeyup: function(event) {
163 | delete this._pressed[event.keyCode];
164 | }
165 | };
166 | function setupKeyboard()
167 | {
168 | window.addEventListener('keyup', function(event) { Keyboard.onKeyup(event); }, false);
169 | window.addEventListener('keydown', function(event) { Keyboard.onKeydown(event); }, false);
170 | }
171 | setupKeyboard();
172 |
173 |
174 | return {
175 | AnimationLoop : AnimationLoop,
176 | FPSMonitor : FPSMonitor,
177 |
178 | getFromURL: getFromURL,
179 | saveAs: saveAs,
180 |
181 | playSound: playSound,
182 | intersectClick: intersectClick,
183 | getNDCFromMouseEvent: getNDCFromMouseEvent,
184 |
185 | keyboard : Keyboard
186 | };
187 | });
188 |
--------------------------------------------------------------------------------
/src/CellSpace/Utils.js:
--------------------------------------------------------------------------------
1 | define([
2 | "jquery-ui", "Utils", "EvoCell", "story/StoryTeller", "underscore",
3 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui",
4 | "CellSpace/State"],
5 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat,
6 | gameState) {
7 | "use strict";
8 |
9 | /////// PUBLIC ///////////
10 | // access to gamestate
11 | var pollAutoFire = function() {
12 | if (gameState.autoFireOn) {
13 | if (gameState.autoFireCounter === 0) {
14 | var clickedPoint = intersectClick(gameState.lastMouseNDC);
15 | fireShotAt(gameState.gameW*(clickedPoint.x+1)/2, gameState.gameH*(clickedPoint.y+1)/2);
16 |
17 | gameState.autoFireCounter = 5;
18 | }
19 | else {
20 | gameState.autoFireCounter--;
21 | }
22 | }
23 | };
24 |
25 | // TODO: gameState access
26 | var fireShotAt = function(tx, ty) {
27 | // spawn shot
28 | var dX = tx-gameState.shipX;
29 | var dY = ty-gameState.shipY;
30 | var dL = Math.sqrt(dX*dX+dY*dY);
31 |
32 | var sX = gameState.shotSpeed * dX/dL;
33 | var sY = gameState.shotSpeed * dY/dL;
34 |
35 | sX += gameState.shipSpeedX;
36 | sY += gameState.shipSpeedY;
37 |
38 | var aa = gameState.frontShots > 1 ? -gameState.frontShotAngle/2 : 0;
39 |
40 | for (var i = 0; i < gameState.frontShots; i++) {
41 | gameState.shots.allocateParticle(gameState.shipX-1*gameState.scrollX, gameState.shipY-1*gameState.scrollY,
42 | Math.cos(aa)*sX + Math.sin(aa)*sY, -Math.sin(aa)*sX + Math.cos(aa)*sY);
43 |
44 | if (gameState.frontShots > 1)
45 | aa += gameState.frontShotAngle/(gameState.frontShots-1);
46 | }
47 |
48 | utils.playSound(gameState.snd);
49 | };
50 |
51 | // TODO: could be done in backbone via a conceptual model ;)
52 | var updateButtons = function() {
53 | if (gameState.pause) {
54 | document.getElementById("playPause").children[0].className = "fa fa-play fa-2x";
55 | }
56 | else {
57 | document.getElementById("playPause").children[0].className = "fa fa-pause fa-2x";
58 | }
59 | };
60 |
61 | var refreshGUI = function(names) {
62 | names = names ? (names.length ? names : [names]) : [];
63 |
64 | var refresh = function(controller) {
65 | if (_.contains(names, controller.property)) {
66 | controller.updateDisplay();
67 | }
68 | };
69 |
70 | _.each(gameState.gui.__controllers, refresh);
71 | _.each(gameState.gui.__folders, function(folder) {
72 | _.each(folder.__controllers, refresh);
73 | });
74 | };
75 |
76 | var resetGame = function() {
77 | gameState.cnt = 0;
78 | gameState.dishes.enemy.randomize(gameState.rules.enemy.nrStates, gameState.randomDensity);
79 | gameState.dishes.background.randomize(gameState.rules.enemy.nrStates, 0.01);
80 | if (gameState.shipX < 0 || gameState.shipX > gameState.gameW ||
81 | gameState.shipY < 0 || gameState.shipY > gameState.gameH) {
82 | gameState.shipX = gameState.gameW/2;
83 | gameState.shipY = gameState.gameH/2;
84 | }
85 | utils.playSound(gameState.sndInit);
86 | };
87 |
88 | var gameStep = function() {
89 | gameState.pause = true;
90 | gameState.doOneStep = true;
91 | updateButtons();
92 | };
93 |
94 | var gamePlayPause = function() {
95 | gameState.pause = !gameState.pause;
96 | updateButtons();
97 | };
98 |
99 | var onScreenSizeChanged = function() {
100 | gameState.reactor.setRenderSize(gameState.screenW, gameState.screenH);
101 | refreshGUI(["screenW", "screenH"]);
102 | };
103 |
104 | var onGameSizeChanged = function() {
105 | var reactor = gameState.reactor;
106 |
107 | reactor.setDefaultDishSize(gameState.gameW, gameState.gameH);
108 |
109 | // hack reinit shots
110 | gameState.shots = new EC.ParticleSystem(reactor, gameState.maxParticles, gameState.gameW, gameState.gameH);
111 |
112 | // reinit instead of resize (we lose state but who cares?)
113 | var dishes = gameState.dishes;
114 | dishes.enemy = reactor.compileDish();
115 | dishes.enemyShield = reactor.compileDish();
116 | dishes.background = reactor.compileDish();
117 | dishes.ship = reactor.compileDish();
118 | dishes.shipExplosion = reactor.compileDish();
119 | dishes.weapon = reactor.compileDish();
120 | dishes.weaponExplosion = reactor.compileDish();
121 | dishes.copy = reactor.compileDish();
122 | dishes.buffer = reactor.compileDish();
123 | dishes.render = reactor.compileDish();
124 | dishes.render2 = reactor.compileDish();
125 | dishes.colliding = reactor.compileDish();
126 |
127 | try
128 | {
129 | var rules = gameState.rules;
130 | rules.enemy.setCompileSizeDish(gameState.dishes.enemy);
131 | rules.background.setCompileSizeDish(gameState.dishes.background);
132 | rules.ship.setCompileSizeDish(gameState.dishes.ship);
133 | rules.weapon.setCompileSizeDish(gameState.dishes.enemy);
134 | rules.shipExplosion.setCompileSizeDish(gameState.dishes.background);
135 | rules.weaponExplosion.setCompileSizeDish(gameState.dishes.background);
136 |
137 | resetGame();
138 | }
139 | catch (ex) {
140 | // FIXME: catch all is dirty hack store rule and compiled rule seperately (dishes, rules and ecfiles)
141 | }
142 | };
143 |
144 | var refreshAvailableRules = function() {
145 | fileStore.loadAllRuleNames(function(names) {
146 | gameState.drawModel.set("availableRules", names);
147 | //alert(names);
148 | });
149 | };
150 |
151 | var refreshAvailableDishes = function() {
152 | gameState.drawModel.set("availableLayers", Object.keys(gameState.dishes));
153 | };
154 |
155 | // dir
156 | var zoom = function(dir) {
157 | var zoomSpeed = 1.005;
158 | gameState.zoom *= Math.pow(zoomSpeed, dir);
159 | refreshGUI(["zoom"]);
160 | };
161 |
162 | ////// PTIVATE //////////////
163 |
164 | return {
165 | pollAutoFire: pollAutoFire,
166 | fireShotAt: fireShotAt,
167 | zoom: zoom,
168 | updateButtons: updateButtons,
169 | refreshGUI: refreshGUI,
170 | resetGame: resetGame,
171 | gameStep: gameStep,
172 | gamePlayPause: gamePlayPause,
173 |
174 | onScreenSizeChanged: onScreenSizeChanged,
175 | onGameSizeChanged: onGameSizeChanged,
176 |
177 | refreshAvailableRules: refreshAvailableRules,
178 | refreshAvailableDishes: refreshAvailableDishes,
179 | };
180 | });
--------------------------------------------------------------------------------
/cellspace.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CellSpace (an EvoCell spaceshooter)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
33 |
34 |
41 |
42 |
43 |
44 |
45 |
46 | message text
47 |
48 |
49 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | fps: n/a
119 |
120 |
121 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/src/gl/Reactor.js:
--------------------------------------------------------------------------------
1 | define(["gl/GLHelper", "gl/Dish", "gl/Rule"], function(glhelper, Dish, Rule) {
2 | var Reactor = function(canvas, w, h)
3 | {
4 | var gl = getGL(canvas);
5 |
6 | var posBuffer = gl.createBuffer();
7 | gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
8 | var vertices = new Float32Array([-1,-1,0, 1,-1,0, -1,1,0, 1,1,0]);
9 | var texCoords = new Float32Array([0,0, 1,0, 0,1, 1,1]);
10 | var texCoordOffset = vertices.byteLength;
11 | gl.bufferData(gl.ARRAY_BUFFER, texCoordOffset + texCoords.byteLength, gl.STATIC_DRAW);
12 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);
13 | gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);
14 |
15 | this.setDefaultDishSize(w, h);
16 | this.canvas = canvas;
17 | this.renderWidth = canvas.width;
18 | this.renderHeight = canvas.height;
19 | this.gl = gl;
20 |
21 | this.rules = {};
22 | this.dishes = {};
23 | this.palettes = {};
24 |
25 | this.posBuffer = posBuffer;
26 | this.texCoordOffset = texCoordOffset;
27 | };
28 |
29 | Reactor.prototype.setRenderSize = function(w, h)
30 | {
31 | this.canvas.width = w;
32 | this.canvas.height = h;
33 | this.renderWidth = w;
34 | this.renderHeight = h;
35 | };
36 | Reactor.prototype.setDefaultDishSize = function(w, h)
37 | {
38 | this.defaultDishSize = {width: w, height : h};
39 | };
40 |
41 | Reactor.prototype.paintDish = function(paintShader, dish, dish2, callback)
42 | {
43 | this.gl.viewport(0,0, this.renderWidth, this.renderHeight);
44 | var bindCallback = function(gl, progCA)
45 | {
46 | gl.uniform1i(gl.getUniformLocation(progCA, "texFrame"), 0);
47 | gl.activeTexture(gl.TEXTURE0);
48 | gl.bindTexture(gl.TEXTURE_2D, dish.getCurrentTexture());
49 |
50 | gl.uniform1i(gl.getUniformLocation(progCA, "texFrame2"), 1);
51 | gl.activeTexture(gl.TEXTURE1);
52 | gl.bindTexture(gl.TEXTURE_2D, dish2.getCurrentTexture());
53 |
54 | if (callback)
55 | callback(gl, progCA);
56 | };
57 |
58 | this.applyShader(paintShader, null, bindCallback);
59 | };
60 |
61 | // main function to call
62 | Reactor.prototype.mixDish = function(mixShader, mainDish, parameters, callback)
63 | {
64 | this.gl.viewport(0,0, mainDish.width, mainDish.height);
65 | var framebuffer = mainDish.getNextFramebuffer();
66 | var bindCallback = function(gl, progCA)
67 | {
68 | // set param for shader to TEXTURE0
69 | gl.uniform1i(gl.getUniformLocation(progCA, "texFrame"), 0);
70 | // set glcontext TEXTURE0 to current frame of mainDish
71 | gl.activeTexture(gl.TEXTURE0);
72 | gl.bindTexture(gl.TEXTURE_2D, mainDish.getCurrentTexture());
73 |
74 | // TODO: if arrays (not dictionaries) then name tex1, tex2, tex3, ...
75 | var textureCount = 1;
76 | for (var paramName in parameters) {
77 | var param = parameters[paramName];
78 | // type conversion
79 | if (param instanceof Dish) {
80 | param = param.getCurrentTexture();
81 | }
82 |
83 | // distinguish types
84 | if (param instanceof WebGLTexture) {
85 | gl.uniform1i(gl.getUniformLocation(progCA, paramName), textureCount);
86 | gl.activeTexture(gl.TEXTURE0 + textureCount);
87 | gl.bindTexture(gl.TEXTURE_2D, param);
88 | textureCount++;
89 | }
90 | else {
91 | //if (param is int) // TODO: int
92 | if (typeof param === "number") {
93 | gl.uniform1f(gl.getUniformLocation(progCA, paramName), param);
94 | }
95 | else {
96 | var l = param.length;
97 | if (l === 2) {
98 | gl.uniform2f(gl.getUniformLocation(progCA, paramName), param[0], param[1]);
99 | }
100 | else if (l === 4) {
101 | gl.uniform4f(gl.getUniformLocation(progCA, paramName), param[0], param[1], param[2], param[3]);
102 | }
103 | }
104 | }
105 | }
106 |
107 | if (callback) {
108 | callback(gl, progCA);
109 | }
110 | };
111 | this.applyShader(mixShader, framebuffer, bindCallback);
112 | mainDish.flip();
113 | };
114 |
115 | Reactor.prototype.step = function(rule, dish)
116 | {
117 | var callback = function(gl, progCA)
118 | {
119 | gl.uniform1i(gl.getUniformLocation(progCA, "texRule"), 1);
120 | gl.activeTexture(gl.TEXTURE1);
121 | gl.bindTexture(gl.TEXTURE_2D, rule.getTexture());
122 | }
123 | this.applyShaderOnDish(rule.getProgram(), dish, callback);
124 | }
125 |
126 | Reactor.prototype.applyShaderOnDish = function(shader, dish, bindCallbackUser)
127 | {
128 | this.gl.viewport(0,0, dish.width, dish.height);
129 |
130 | var framebuffer = dish.getNextFramebuffer();
131 | var bindCallback = function(gl, progCA)
132 | {
133 | gl.uniform1i(gl.getUniformLocation(progCA, "texFrame"), 0);
134 | gl.activeTexture(gl.TEXTURE0);
135 | gl.bindTexture(gl.TEXTURE_2D, dish.getCurrentTexture());
136 |
137 | if (bindCallbackUser)
138 | bindCallbackUser(gl, shader);
139 | }
140 |
141 | this.applyShader(shader, framebuffer, bindCallback)
142 |
143 | dish.flip();
144 | }
145 |
146 | Reactor.prototype.compileShader = function(vertexSrc, fragSrc)
147 | {
148 | if (!fragSrc)
149 | {
150 | fragSrc = vertexSrc;
151 | vertexSrc = "attribute vec3 aPos;" +
152 | "attribute vec2 aTexCoord;" +
153 | "varying vec2 vTexCoord;" +
154 | "void main(void) {" +
155 | " gl_Position = vec4(aPos, 1.);" +
156 | " vTexCoord = aTexCoord;" +
157 | " gl_PointSize = 2.0;" +
158 | "}";
159 | }
160 |
161 | var gl = this.gl;
162 | var shader = gl.createProgram();
163 | gl.attachShader(shader, glhelper.getShader(gl, gl.VERTEX_SHADER, vertexSrc));
164 | gl.attachShader(shader, glhelper.getShader(gl, gl.FRAGMENT_SHADER, fragSrc));
165 | gl.linkProgram(shader);
166 |
167 | return shader;
168 | };
169 |
170 | Reactor.prototype.compileDish = function(w, h)
171 | {
172 | var dish = new Dish(this, w || this.defaultDishSize.width, h || this.defaultDishSize.height);
173 | return dish;
174 | };
175 |
176 | Reactor.prototype.compileRule = function(ruleData, dish)
177 | {
178 | var rule = new Rule(this, ruleData, dish);
179 | return rule;
180 | };
181 |
182 | Reactor.prototype.compilePalette = function(paletteId, colors)
183 | {
184 | // does this solve palette troules
185 | };
186 |
187 | // GL level
188 |
189 |
190 | Reactor.prototype.applyShader = function(shader, framebuffer, bindCallback, renderCallbac)
191 | {
192 | var gl = this.gl;
193 |
194 | gl.useProgram(shader);
195 |
196 | if (framebuffer)
197 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
198 | else
199 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
200 |
201 | // other arguments
202 | if (bindCallback) bindCallback(gl, shader);
203 |
204 | if (!renderCallbac) {
205 | // default arguments for all 3D shader (convention)
206 | gl.bindBuffer(gl.ARRAY_BUFFER, this.posBuffer);
207 | var aPosLoc = gl.getAttribLocation(shader, "aPos");
208 | gl.enableVertexAttribArray( aPosLoc );
209 | var aTexLoc = gl.getAttribLocation(shader, "aTexCoord");
210 | gl.enableVertexAttribArray( aTexLoc );
211 | gl.vertexAttribPointer(aPosLoc, 3, gl.FLOAT, gl.FALSE, 0, 0);
212 | gl.vertexAttribPointer(aTexLoc, 2, gl.FLOAT, gl.FALSE, 0, this.texCoordOffset);
213 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
214 | }
215 | else
216 | renderCallbac(gl, shader);
217 | }
218 |
219 | // helpers
220 | function getGL(c) {
221 | var gl = null;
222 |
223 | try
224 | {
225 | gl = c.getContext("experimental-webgl", {depth : false });
226 | }
227 | catch(e) {}
228 | if (!gl)
229 | {
230 | alert("Your browser does not support WebGL");
231 | return null;
232 | }
233 | return gl;
234 | }
235 |
236 | return Reactor;
237 | });
238 |
239 |
240 |
241 |
--------------------------------------------------------------------------------
/src/story/StoryTeller.js:
--------------------------------------------------------------------------------
1 | define([
2 | "jquery-ui", "Utils", "EvoCell", "underscore", "backbone", "knockback",
3 | "knockout", "meSpeak", "CellSpace/State", "jquery-cycle"],
4 | function($, utils, EC, _, Backbone, kb, ko, meSpeak, gameState) {
5 |
6 | function getSpeechURL(text, language) {
7 | if (!language) language = "en";
8 | var x = "https://translate.google.com/translate_tts?ie=UTF-8&q=" + text +"&tl=" + language;
9 | //x = "http://translate.google.com/translate_tts?ie=UTF-8&q=" + text +"&tl=en&total=1&idx=0&textlen=23&prev=input";
10 | return x.replace(/ /g, "%20");
11 | }
12 |
13 | meSpeak.loadConfig(gameState.resPath + "src/libs/mespeak/mespeak_config.json");
14 | meSpeak.loadVoice(gameState.resPath + 'src/libs/mespeak/voices/en/en-us.json');
15 |
16 | //meSpeak.speak("hello", {}, function() {alert();});
17 |
18 | var templateSrc = $('#part-template').html();
19 |
20 | var htmlTemplate = templateSrc ? _.template(templateSrc) : function() { return "TODO: ADD TEMPLATE"};
21 |
22 | var story = {
23 | parts: ["Alerted by <%= reason %>...",
24 | "...<%= group%>...",
25 | "...used <%= explanation %>...",
26 | "...to shrink <%= device%> down to quantum scale.",
27 | "What started as <%= description %>...",
28 | "...turned out to be <%= finale %>!"],
29 |
30 | reason: [
31 | {text: "an urgent national security concern of highly classified nature", image: "Classified.gif"},
32 | {text: "the ongoing loss of traditional values", image:"traditional-family.jpg" },
33 | {text: "unexplained oscilations in the social web",
34 | image:["social-media1.jpg"]},
35 | //{text: "strange quantum events at a macroscopic scale", image:"entangled.jpg"},
36 | {text: "a sudden increase of global warming",
37 | image:["polarbear.jpg", "Fight-Global-Warming-global-warming-prevention-725103_1280_1024.jpg"]},
38 | {text: "GNU violation reports", image:"GNU-Linux.png"},
39 | ],
40 |
41 | group: [
42 | {text: "a highly skilled team of Metalab members",
43 | image:["metalab.jpeg", "Metalab06LR.jpg", "600px-Metalab_wappen_neu.png",
44 | "800px-Lounge_idealzustand.JPG"]},
45 | {text: "a team of alien scientists", image: "socialistspaceWorkers.jpg",
46 | url:"http://historiesofthingstocome.blogspot.co.at/2010/08/retro-futurism-4-russians-in-space.html"},
47 | {text:"a paranoid superintelligent bonobo who escaped from a secret CIA prison",
48 | image:["bonobo-portrait.jpg", "bonobo.jpg"]},
49 | {text: "an adventurous giraffe",
50 | image:["giraffe.jpg", "giraffe2.jpeg", "giraffe3.jpeg", "giraffe4.jpeg"],
51 | url:"http://www.giraffes.org/giraffelink.html"},
52 | {text:"the US goverment in accordance with their handlers",
53 | image:["usalien.png", "illuminati.jpg", "usalien.png"] },
54 | //{text:"Boxxy", image:["boxxy.jpg", "boxxy2.png"]},
55 | ],
56 |
57 | explanation: [
58 | {text: "an arduino hack", image: ["arduinohack.jpg", "Arduino-Powerglove.jpg"]},
59 | {text: "lean management principles", image: "lean.jpg"},
60 | {text: "a classic McGyver technique", image: ["macgyver.jpg", "macgyver2.jpg", "macgyver3.jpg"]},
61 | {text: "innovative js libraries", image:"JavaScript_Tools_Library_Frameworks.jpg"},
62 | {text: "a digital matter manipulator", image: "complexdevice.jpg"},
63 | {text: "a directed explosion aproach not unlike some of the concepts of project Orion", image: "orion.gif"},
64 | ],
65 |
66 | device: [
67 | {text: "a retrofited space shuttle", image:"spaceShuttle.jpg",
68 | url: "http://www.wired.com/images_blogs/wiredscience/2012/04/space-shuttle-discovery-jurvetson-flickr.jpg"},
69 | {text: "a russian nuclear submarine with a critical core", image:"submarine.jpg"},
70 | {text:"a timemachine", image:"timemachine.jpg"},
71 | {text:"a DIY spaceship", image: "diy-spacecraft-1.jpg"},
72 | {text:"a spaceship from a parallel timeline", image: "rocketpreparations.jpg"},
73 | {text:"a huge robot", image:["landwalker.jpeg", "robotbog.jpg", "kidswalkerrobot.jpg"]},
74 | ],
75 |
76 |
77 |
78 | description: [
79 | {text: "a quest to kill space nazis", image:"spacenazi.jpg"},
80 | {text: "the search for the holy grail of quantum mechanics", image: "quantum.jpg"},
81 | {text: "an harmless adventure between consenting adults", image: "consenting.jpg"},
82 | {text: "a quest to enlighten mankind", image: "enlightenment.jpeg"},
83 | //{text: "as a simple preparation step", image: "xkcdsuccess.png"},
84 | //{text: "as a public backup solution for classified power point presentations",
85 | // image:["publicBackup.jpg", "big-prism-snowden-greenwald.jpg"]},
86 | ],
87 |
88 | finale: [
89 | {text: "an adventure in a cellular world unseen by anyone before", image: "blue.jpg"},
90 | {text: "a universe of cellular automata hidden in every atom", image: "cellcity.jpg"},
91 | {text: "ALL YOUR BASE ARE BELONG TO US", image: "allYourBase.jpg"},
92 | ],
93 | }
94 |
95 | var probabilities = {};
96 |
97 | function choose(key) {
98 | var alternatives = story[key];
99 | //var l = alternatives.length;
100 |
101 | if (!probabilities[key]) {
102 | var prob = [];
103 | for (var idx in alternatives) {
104 | prob[idx] = 1;
105 | }
106 | probabilities[key] = prob;
107 | }
108 | var prob = probabilities[key];
109 | var sum = _.reduce(prob, function(a, b) {return a+b;});
110 |
111 | var selectedIdx = -1;
112 | _.reduce(prob, function(a, b, idx) {
113 | var v = a-b;
114 | if (v <= 0 && a > 0)
115 | selectedIdx = idx;
116 | return v;
117 | }, sum*Math.random());
118 |
119 | var aIdx = selectedIdx;
120 | prob[aIdx] /= 2;
121 | //var aIdx = sum*Math.random();
122 |
123 | var alt = alternatives[aIdx];
124 | return alt;
125 | }
126 |
127 | var RunIntro = function() {
128 | $("#container").html("");
129 | $("#container").fadeIn();
130 |
131 | //var audios = {};
132 | var texts = [];
133 |
134 | for (var partIndex in story.parts)
135 | {
136 | var part = story.parts[partIndex];
137 | var partTemplate = _.template(part);
138 | var paramsRegex=new RegExp(/<%=(.*)%>/g);
139 |
140 | var r = paramsRegex.exec(part);
141 | var key = r[1].trim();
142 |
143 | //var alternatives = story[key];
144 | //var l = alternatives.length;
145 | //var aIdx = Math.floor(l*Math.random());
146 | //var alt = alternatives[aIdx];
147 | var alt = choose(key);
148 |
149 |
150 | var image = alt.image;
151 | if (Array.isArray(image)) {
152 | image = image[Math.floor(image.length*Math.random())];
153 | }
154 |
155 | if (!alt.text)
156 | alt = {text: alt};
157 |
158 | var params = {};
159 | params[key] = alt.text;
160 | var text = partTemplate(params);
161 | texts.push(text);
162 |
163 | var id = "sc" + partIndex;
164 | $("#container").append(htmlTemplate({text:text, image: gameState.resPath + "images/" + image, id:id}));
165 |
166 | //var url = getSpeechURL(text);
167 | //var a = new Audio(url);
168 | //audios[id] = a;
169 | }
170 |
171 | /*_.delay(function(text) {
172 | meSpeak.speak(text);
173 | }, 8000*partIndex, text);
174 | */
175 |
176 | $('#container').cycle({
177 | timeout: 0,
178 | fx: 'fade' // choose your transition type, ex: fade, scrollUp, shuffle, etc...
179 | });
180 |
181 | var callMe = function () {
182 | $('#container').fadeOut()
183 | };
184 |
185 |
186 | for (var i = story.parts.length - 1; i >= 0; i--) {
187 | callMe = (function (i, callMe) {
188 | id = "sc" + i;
189 | var cont = $("#"+id);
190 | //var a = audios[id];
191 | var canceled = false;
192 |
193 | var next = _.once(function() {
194 | if (i != story.parts.length - 1)
195 | $('#container').cycle("next");
196 | _.delay(callMe, 700);
197 | });
198 |
199 | /*
200 | a.addEventListener("ended", function() {
201 | if (!canceled) {
202 | next();
203 | }
204 | }, false);
205 | */
206 |
207 | return function() {
208 | meSpeak.speak(texts[i], {}, next);
209 | //a.play();
210 | cont.click(function() {
211 | meSpeak.stop();
212 | next();
213 | canceled = true;
214 | //a.pause();
215 | //next();
216 | });
217 | };
218 | })(i, callMe);
219 | }
220 |
221 | callMe();
222 | };
223 |
224 | var RunDeath = function(callback) {
225 | //alert("You died!");
226 | //RunIntro();
227 | //MessageBox("You died!
Avoid the enemy cells!
", "rgb(255, 0, 0)", 4000, callback);
228 | MessageBox("You died! Avoid the enemy cells!", "rgb(255, 0, 0)", 4000, callback);
229 | };
230 |
231 | var MessageBox = function(html, color, delay, callback) {
232 | $("#centeredMessage").html(html);
233 | $("#centeredContainer").fadeIn(200, function() {
234 | var fadeOut = _.once(function() {
235 | $("#centeredContainer").fadeOut(200, callback);
236 | });
237 | $("#centeredContainer").click(fadeOut);
238 | _.delay(fadeOut, delay);
239 | });
240 | };
241 |
242 | return {
243 | RunIntro: RunIntro,
244 | RunDeath: RunDeath
245 | }
246 | });
247 |
--------------------------------------------------------------------------------
/src/CellSpace/Setup.js:
--------------------------------------------------------------------------------
1 | define(["Utils", "data/FileStore", "EvoCell", "CellSpace/State", "CellSpace/Utils"],
2 | function(utils, fileStore, EC, gameState, csUtils) {
3 | "use strict";
4 |
5 | var initDB = function() {
6 | // hackinit
7 | var resPath = gameState.resPath;
8 | gameState.resFiles = [
9 | resPath + "rules/enemy_ludwigBuildships",
10 | resPath + "rules/moore5-coolspaceships",
11 | resPath + "rules/EvoloopN9",
12 | resPath + "rules/GameOfLife",
13 | resPath + "patterns/evoloop30.ecpattern",
14 | ];
15 |
16 |
17 |
18 | var loader = new EC.ResLoader();
19 | _.each(gameState.resFiles, function(fileURL, fileKey) {
20 | loader.load("predefinded_" + fileKey, fileURL, "ecfile");
21 | });
22 | // read from state rules and patterns to load
23 |
24 | loader.start(true, function(data) {
25 | // do nothing since we just use the cache
26 | csUtils.refreshAvailableRules();
27 | });
28 |
29 |
30 | };
31 |
32 | // static
33 | var setup = function(canvas, callback) {
34 |
35 | var resPath = gameState.resPath;
36 |
37 | var loader = new EC.ResLoader();
38 |
39 | loader.load("rules.enemy", resPath + "rules/enemy_ludwigBuildships", "ecfile");
40 | //loader.load("rules.enemy", resPath + "rules/enemy_ludwigBuildships_lessActive", "ecfile");
41 | //loader.load("rules.enemy", resPath + "rules/enemy_lounge23", "ecfile");
42 | //loader.load("rules.enemy", resPath + "rules/enemy_evil", "ecfile");
43 | //loader.load("rules.enemy", resPath + "rules/22C3_mirrorsymetric_gliders-randomwaver", "ecfile");
44 | //loader.load("rules.enemy", "rules/enemy_d54_awesomeships", "ecfile");
45 | //loader.load("rules.enemy", resPath + "rules/enemy_d52_replicator", "ecfile");
46 | //loader.load("rules.enemy", resPath + "rules/enemy_holeshooter", "ecfile");
47 | //loader.load("rules.enemy", "rules/enemy_holeshooter", "ecfile");
48 | //loader.load("rules.enemy", "rules/gridworld6", "ecfile");
49 | //loader.load("rules.enemy", "rules/enemy_quaderwelt_moreactive", "ecfile");
50 | //loader.load("rules.enemy", "rules/enemy_d29", "ecfile");
51 | //loader.load("rules.enemy", "rules/enemy_linesLounge_moreactive", "ecfile");
52 | //loader.load("rules.enemy", "rules/enemy_linesLounge_moreactive-mutA_mut", "ecfile");
53 |
54 | loader.load("rules.background", resPath + "rules/enemy_linebuilder", "ecfile");
55 | loader.load("rules.weapon", resPath + "rules/ship_avg4_nice", "ecfile");
56 |
57 | loader.load("rules.weaponExplosion", resPath + "rules/cross4-wave-spaceshipshoot", "ecfile");
58 | loader.load("rules.weaponExplosion", resPath + "rules/moore5-coolspaceships", "ecfile");
59 | //loader.load("rules.weaponExplosion", resPath + "rules/explosion_avg2-far2", "ecfile");
60 | //loader.load("rules.weaponExplosion", resPath + "rules/ship_avg4_nice", "ecfile");
61 |
62 |
63 | loader.load("rules.shipExplosion", resPath + "rules/cross4-wave-spaceshipshoot", "ecfile");
64 | loader.load("rules.shipExplosion", resPath + "rules/moore5-coolspaceships", "ecfile");
65 |
66 | loader.load("rules.ship", resPath + "rules/ship_avg4_nice", "ecfile");
67 | loader.load("rules.ship", resPath + "rules/22C3_mirrorsymetric_gliders-randomwaver", "ecfile");
68 |
69 |
70 | // rules/ship_avg4_nice rules/ship_avg4_schweif
71 |
72 | loader.load("vertexPoints", "src/shaders/vertexPoints.vshader", "text");
73 | loader.load("drawAll", "src/shaders/drawAll.shader", "text");
74 |
75 | loader.load("clear", "src/shaders/clear.shader", "text");
76 | loader.load("mixPalette", "src/shaders/mixPalette.shader", "text");
77 | loader.load("mixRect", "src/shaders/mixRect.shader", "text");
78 | loader.load("stampPalette", "src/shaders/stampPalette.shader", "text");
79 |
80 | loader.load("drawRect", "src/shaders/drawRect.shader", "text");
81 | loader.load("drawCircle", "src/shaders/drawCircle.shader", "text");
82 |
83 | loader.load("scroller", "src/shaders/scroller.shader", "text");
84 |
85 | loader.load("rendererVertex", "src/shaders/cameraRenderer.vshader", "text");
86 | loader.load("rendererFragmentCell", "src/shaders/cameraRenderer.shader", "text");
87 | loader.load("rendererFragmentTV", "src/shaders/cameraRendererTV.shader", "text");
88 | loader.load("rendererFragmentSimple", "src/shaders/cameraRendererSimple.shader", "text");
89 | loader.load("rendererFragmentFast", "src/shaders/cameraRendererFast.shader", "text");
90 |
91 | loader.load("intersectSpawn", "src/shaders/intersectSpawn.shader", "text");
92 | loader.load("shieldSpawn", "src/shaders/shieldGenerator.shader", "text");
93 |
94 | loader.load("copyPaste", "src/shaders/copyPasteRect.shader", "text");
95 |
96 | var setupFn = function (data) {
97 | // Setup core
98 | var reactor = new EC.Reactor(canvas, gameState.gameW, gameState.gameH);
99 | gameState.reactor = reactor;
100 | gameState.gl = reactor.gl;
101 | gameState.canvas = reactor.canvas;
102 |
103 | csUtils.onScreenSizeChanged();
104 | csUtils.onGameSizeChanged();
105 |
106 | gameState.shaders.drawPoints = reactor.compileShader(data.vertexPoints, data.drawAll);
107 |
108 | gameState.shaders.clear = reactor.compileShader(data.clear);
109 |
110 | gameState.shaders.cameraRendererCell = reactor.compileShader(data.rendererVertex, data.rendererFragmentCell);
111 | gameState.shaders.cameraRendererTV = reactor.compileShader(data.rendererVertex, data.rendererFragmentTV);
112 | gameState.shaders.cameraRendererSimple = reactor.compileShader(data.rendererVertex, data.rendererFragmentSimple);
113 | gameState.shaders.cameraRendererFast = reactor.compileShader(data.rendererVertex, data.rendererFragmentFast);
114 |
115 | gameState.shaders.drawRect = reactor.compileShader(data.drawRect);
116 | gameState.shaders.drawCircle = reactor.compileShader(data.drawCircle);
117 |
118 | gameState.shaders.mix = reactor.compileShader(data.mixPalette);
119 | gameState.shaders.mixRect = reactor.compileShader(data.mixRect);
120 | gameState.shaders.stamp = reactor.compileShader(data.stampPalette);
121 |
122 | gameState.shaders.intersectSpawn = reactor.compileShader(data.intersectSpawn);
123 | gameState.shaders.shieldSpawn = reactor.compileShader(data.shieldSpawn);
124 | gameState.shaders.copy = reactor.compileShader(data.copyPaste);
125 |
126 | gameState.shaders.scroll = reactor.compileShader(data.scroller);
127 |
128 | csUtils.refreshAvailableRules();
129 | csUtils.refreshAvailableDishes();
130 |
131 | //fileStore.storeRule(data.rules.background);
132 | //fileStore.loadRule("starwars", function(loadedRule) {
133 | // rules.enemy = reactor.compileRule(loadedRule, dishes.enemy);
134 | //})
135 |
136 | //data.rules.enemy.MakeStarWarsRule()
137 | //fileStore.storeRule("starwars", data.rules.enemy);
138 |
139 | var rules = gameState.rules;
140 | rules.enemy = reactor.compileRule(data["rules.enemy"], gameState.dishes.enemy);
141 | rules.background = reactor.compileRule(data["rules.background"], gameState.dishes.background);
142 | rules.ship = reactor.compileRule(data["rules.ship"], gameState.dishes.ship);
143 | rules.weapon = reactor.compileRule(data["rules.weapon"], gameState.dishes.enemy);
144 | rules.shipExplosion = reactor.compileRule(data["rules.shipExplosion"], gameState.dishes.background);
145 | rules.weaponExplosion = reactor.compileRule(data["rules.weaponExplosion"], gameState.dishes.background);
146 |
147 | gameState.colors.enemy = new EC.Palette(reactor, [
148 | [0, 0, 0, 0],
149 | [140, 10, 140, 255],
150 | [255, 255, 255, 255],
151 | [255, 30, 255, 255],
152 | [255, 110, 255, 255]
153 | ]);
154 |
155 | gameState.colors.enemy.generateColors({
156 | 0: [0,0,0,0],
157 | 1:[140, 0, 140, 255],
158 | //2:[255,255,255,255],
159 | 3:[255,30, 255, 255],
160 | 5:[0,255,0,255],
161 | 7:[0,255,255,255],
162 | 9:[255,255,0,255],
163 | });
164 |
165 | /*gameState.colors.enemy.generateColors({
166 | 0: [0,0,0,0],
167 | 1:[255,0,0,255],
168 | 4:[255,255,0,255],
169 | 7:[0,255,255,255],
170 | 10:[255,255,255,255],
171 | });
172 | */
173 |
174 | var weaponV = 100;
175 | gameState.colors.weapon = new EC.Palette(reactor, [
176 | [0, 0, 0, 0],
177 | [0, 120, 0, weaponV],
178 | [0, 255, 0, weaponV],
179 | [120, 255, 0, weaponV],
180 | [200, 255, 0, weaponV],
181 | ]);
182 |
183 | var shipExplosionV = 210;
184 | gameState.colors.shipExplosion = new EC.Palette(reactor, [
185 | [60, 60, 90, 0],
186 | [23, 108, 126, shipExplosionV],
187 | [18, 164, 195, shipExplosionV],
188 | [0, 210, 255, shipExplosionV],
189 | [150, 210, 255, shipExplosionV],
190 | [255, 255, 255, shipExplosionV]
191 | ]);
192 |
193 | var weaponExplosionV = 210;
194 | gameState.colors.weaponExplosion = new EC.Palette(reactor, [
195 | [0, 0, 0, 0],
196 | [255, 0, 0, weaponExplosionV],
197 | [255, 160, 0, weaponExplosionV],
198 | [255, 255, 0, weaponExplosionV]
199 | ]);
200 |
201 | //gameState.colors.shipExplosion = gameState.colors.weaponExplosion;
202 |
203 | //gameState.colors.weaponExplosion = gameState.colors.shipExplosion;
204 |
205 |
206 | var bs = 0.12;
207 | gameState.colors.background = new EC.Palette(reactor, [
208 | [0, 0, 0, 0],
209 | [bs*10, bs*80, bs*80, 255],
210 | [bs*20, bs*170, bs*170, 255],
211 | [bs*30, bs*255, bs*255, 255]
212 | ]);
213 |
214 | gameState.colors.ship = new EC.Palette(reactor, [
215 | [0, 0, 0, 0],
216 | [0, 0, 255, 100],
217 | [0, 80, 255, 200],
218 | [0, 190, 255, 255],
219 | [0, 225, 255, 255],
220 | [0, 255, 255, 255],
221 | ]);
222 |
223 | gameState.colors.copy = new EC.Palette(reactor, [
224 | [0, 0, 0, 0],
225 | [0, 130, 0, 255],
226 | [0, 190, 0, 255],
227 | [0, 255, 0, 255]
228 | ]);
229 |
230 | gameState.colors.enemyShield = new EC.Palette(reactor);
231 |
232 | var a = 100;
233 | gameState.colors.enemyShield.generateColors({
234 | 0: [0,0,0,0],
235 | 6:[255,0,0,a],
236 | 12:[0,255,0,a],
237 | 18:[0,0,255,a],
238 | 24:[255,255,255,a]
239 | });
240 |
241 | // alpha only shield
242 | gameState.colors.enemyShield.generateColors({
243 | 0: [0,0,0,0],
244 | 1:[255,255,0,100],
245 | 24:[255,255,0,255]
246 | });
247 |
248 | gameState.dishes.enemy.randomize(gameState.rules.enemy.nrStates, 0.001);
249 | gameState.dishes.background.randomize(gameState.rules.enemy.nrStates, 0.01);
250 | gameState.shipX = gameState.gameW/2;
251 | gameState.shipY = gameState.gameH/2;
252 |
253 | callback();
254 | };
255 |
256 | // USE this if you need the database (we do we refresh the list of rules in setup)
257 | var oldSetupFn = setupFn;
258 | setupFn = function(data) {
259 | fileStore.ready(function() {
260 | oldSetupFn(data);
261 | });
262 | };
263 |
264 | loader.start(true, setupFn);
265 | };
266 |
267 | return {
268 | setup: setup,
269 | initDB: initDB,
270 | };
271 | });
--------------------------------------------------------------------------------
/src/EvoCell.js:
--------------------------------------------------------------------------------
1 | define(["Utils", "data/FileStore", "gl/Reactor", "gl/Dish", "gl/Rule", "gl/Palette", "gl/ParticleSystem"],
2 | function(utils, FileStore, Reactor, Dish, Rule, Palette, ParticleSystem) {
3 |
4 | var ResLoader = function() {
5 | this.queue = {};
6 | this.types = {};
7 | this.factories = {
8 | "ecfile": function(arraybuffer) { return new ECFile(arraybuffer); },
9 | };
10 | this.objs = {};
11 | };
12 |
13 | ResLoader.prototype.load = function(id, url, type) {
14 | this.queue[id] = url;
15 | this.types[id] = type;
16 | var o = {};
17 | this.objs[id] = o;
18 | return o;
19 | };
20 |
21 | ResLoader.prototype.start = function(useDB, cb) {
22 | var item;
23 | var data = {};
24 | var loaderCtx = this;
25 | var itemsToLoad = Object.keys(this.queue).length;
26 | var loadedCount = 0;
27 |
28 | var doParsing = function(result, givenType) {
29 | if (loaderCtx.factories[givenType])
30 | result = loaderCtx.factories[givenType](result);
31 | return result;
32 | };
33 |
34 | var createBla = function(key) {
35 | return function(result) {
36 | data[key] = result;
37 | loadedCount++;
38 | loaderCtx.objs[key].value = result;
39 | if (loadedCount == itemsToLoad) {
40 | cb(data);
41 | }
42 | };
43 | };
44 | var ctx = this;
45 | var iterateAll = function(cachedNames) {
46 | _.each(ctx.queue, function(url, key) {
47 | var givenType = ctx.types[key];
48 | var factory = loaderCtx.factories[givenType];
49 | var type = factory ? "arraybuffer" : givenType || "arraybuffer";
50 |
51 | // used cache version if availab
52 | if (useDB && _.contains(cachedNames, url))
53 | {
54 | FileStore.loadRule(url, function(rule) {
55 | var result = rule.ruleData;
56 | createBla(key)(result);
57 | });
58 | }
59 | else
60 | {
61 | utils.getFromURL(ctx.queue[key], type, function(result) {
62 | result = doParsing(result, givenType);
63 |
64 | if (useDB)
65 | FileStore.storeRule(url, result);
66 |
67 | createBla(key)(result);
68 | });
69 | }
70 | });
71 | };
72 |
73 | if (useDB) {
74 | var oldIterateAll = iterateAll;
75 | iterateAll = function() {
76 | FileStore.ready(function() {
77 | FileStore.loadAllRuleNames(function(cachedNames) {
78 | oldIterateAll(cachedNames);
79 | });
80 | });
81 | };
82 | }
83 |
84 | iterateAll();
85 | };
86 |
87 | var ECFile = function(arrayBuffer) {
88 | if (arrayBuffer)
89 | this.loadFromArrayBuffer(arrayBuffer);
90 | };
91 |
92 | ECFile.prototype.saveToBlob = function() {
93 | var evoCellData = this;
94 | var rawData = this.saveToArrayBuffer();
95 | var blob = new Blob([rawData], {type: 'application/octet-stream'});
96 | return blob;
97 | };
98 |
99 | ECFile.prototype.saveToDataURL = function() {
100 | var evoCellData = this;
101 | var rawData = this.saveToArrayBuffer();
102 | var base64Encoded = arrayBufferToBase64(rawData);
103 | return 'data:application/octet-stream;base64,' + base64Encoded;
104 | };
105 |
106 | ECFile.prototype.saveToArrayBuffer = function() {
107 | var evoCellData = this;
108 | //calculate target size
109 | var bufferSize = 20; //magic + contains flags + reserved
110 |
111 | if (evoCellData.containsRule) {
112 | bufferSize += 12; //magic + nrStates + nrNeighbours
113 | bufferSize += evoCellData.ruleTableSize;
114 | }
115 |
116 | if (evoCellData.containsNeighbourhood) {
117 | bufferSize += 12; //magic + nrNeighbours + nrDimensions
118 | bufferSize += 8 * evoCellData.neighbourhood.length;
119 | }
120 |
121 | if (evoCellData.containsPattern) {
122 | bufferSize += 16; //magic + nrDimensions + width + height
123 | bufferSize += evoCellData.patternWidth * evoCellData.patternHeight;
124 | }
125 |
126 | var arrayBuffer = new ArrayBuffer(bufferSize);
127 | var dv = new DataView(arrayBuffer);
128 | var index = 0;
129 |
130 | //general part
131 | dv.setUint32(index, 0x0000002A); index += 4;
132 | dv.setUint32(index, evoCellData.containsRule ? 1 : 0); index += 4;
133 | dv.setUint32(index, evoCellData.containsNeighbourhood ? 1 : 0); index += 4;
134 | dv.setUint32(index, evoCellData.containsPattern ? 1 : 0); index += 4;
135 | dv.setUint32(index, 0); index += 4;
136 |
137 | if (evoCellData.containsRule) {
138 | dv.setUint32(index, 0x00000913); index += 4;
139 | dv.setUint32(index, evoCellData.nrStates); index += 4;
140 | dv.setUint32(index, evoCellData.nrNeighbours); index += 4;
141 | var ruleTableBuffer = new Uint8Array(arrayBuffer, index, evoCellData.ruleTableSize);
142 | ruleTableBuffer.set(evoCellData.ruleTable, 0);
143 | index += Math.pow(evoCellData.nrStates, evoCellData.nrNeighbours);
144 | }
145 |
146 | if (evoCellData.containsNeighbourhood) {
147 | dv.setUint32(index, 0x0000004E31); index += 4;
148 | dv.setUint32(index, evoCellData.nrNeighbours); index += 4;
149 | dv.setUint32(index, 2); index += 4;
150 | for (var i = 0; i < evoCellData.nrNeighbours; i++) {
151 | dv.setUint32(index, evoCellData.neighbourhood[i][0]); index += 4;
152 | dv.setUint32(index, evoCellData.neighbourhood[i][1]); index += 4;
153 | }
154 | }
155 |
156 | if (evoCellData.containsPattern) {
157 | dv.setUint32(index, 0x00005ABF); index += 4;
158 | dv.setUint32(index, 2); index += 4;
159 | dv.setUint32(index, evoCellData.patternWidth); index += 4;
160 | dv.setUint32(index, evoCellData.patternHeight); index += 4;
161 | var patternDataAlias = new Uint8Array(arrayBuffer, index, evoCellData.patternWidth * evoCellData.patternHeight);
162 | patterDataAlias.set(evoCellData.patternData, 0);
163 | }
164 |
165 | return arrayBuffer;
166 | }
167 |
168 | // returns an object with up to 3 fileds: neighbourhood, ruletable, pattern
169 | // or null if a fileformat error is detected
170 | ECFile.prototype.loadFromArrayBuffer = function(arrayBuffer) {
171 | var evoCellData = this;
172 | var nrNeighbours, nrStates, nrDimensions;
173 | var magic, containsRules, containsNeighbourhood, containsPattern, neighbourCount;
174 |
175 | var dv = new DataView(arrayBuffer);
176 | var index = 0;
177 |
178 | magic = dv.getUint32(index); index += 4;
179 | // all evocellfiles must start with 0x002A
180 | if (magic != 42) return null;
181 |
182 | containsRules = dv.getUint32(index); index += 4;
183 | containsNeighbourhood = dv.getUint32(index); index += 4;
184 | containsPattern = dv.getUint32(index); index += 4;
185 | index += 4; // ignore reserved but unused value
186 |
187 | if (containsRules) {
188 | var rulesMagic = dv.getUint32(index); index += 4;
189 | // valid rules must have this magic value here
190 | if (rulesMagic != 2323) return null;
191 | nrStates = dv.getUint32(index); index += 4;
192 | nrNeighbours = dv.getUint32(index); index += 4;
193 |
194 | var ruleTableSize = Math.pow(nrStates, nrNeighbours);
195 | var ruleTable = new Uint8Array(arrayBuffer, index, ruleTableSize);
196 | //var ruleTableView = new Uint8Array(ruleTableSize);
197 | //var ruleTable = ruleTableView.buffer;
198 | //alert(ruleTableView.subarray);
199 | index += ruleTableSize;
200 |
201 | evoCellData.containsRule = true;
202 | evoCellData.nrStates = nrStates;
203 | evoCellData.nrNeighbours = nrNeighbours;
204 | evoCellData.ruleTable = ruleTable;
205 | evoCellData.ruleTableSize = ruleTableSize; // convinient to have but redundant
206 | }
207 | else
208 | evoCellData.containsRule = false;
209 |
210 | if (containsNeighbourhood) {
211 | var neighbourhoodMagic = dv.getUint32(index); index += 4;
212 | // valid rules must have this magic value here
213 | if (neighbourhoodMagic != 0x4E31) return null;
214 | var nrNeighboursRedundant = dv.getUint32(index); index += 4;
215 | // nr of neighbours has to match
216 | if (nrNeighbours != nrNeighboursRedundant) return null;
217 | nrDimensions = dv.getUint32(index); index += 4;
218 | if (nrDimensions != 2) return null;
219 |
220 | var neighbourhood = [];
221 | for (n = 0; n < nrNeighbours; n++)
222 | {
223 | var x = dv.getInt32(index); index += 4;
224 | var y = dv.getInt32(index); index += 4;
225 | neighbourhood.push([x, y]);
226 | }
227 |
228 | evoCellData.containsNeighbourhood = true;
229 | evoCellData.nrDimensions = nrDimensions;
230 | evoCellData.neighbourhood = neighbourhood;
231 | evoCellData.symmetries = calculateSymmetries(evoCellData);
232 | }
233 | else
234 | evoCellData.containsNeighbourhood = false;
235 |
236 | if (containsPattern) {
237 | var patternMagic = dv.getUint32(index); index += 4;
238 | // valid rules must have this magic value here
239 | if (patternMagic != 23231) return null;
240 | var nrDimensions = dv.getUint32(index); index += 4;
241 | if (nrDimensions != 2) return null;
242 |
243 | var sizeX = dv.getInt32(index); index += 4;
244 | var sizeY = dv.getInt32(index); index += 4;
245 |
246 | var pattern = new Uint8Array(arrayBuffer, index, sizeX*sizeY);
247 |
248 | evoCellData.containsPattern = true;
249 | evoCellData.patternWidth = sizeX;
250 | evoCellData.patternHeight = sizeY;
251 | evoCellData.patternData = pattern;
252 | }
253 | else
254 | evoCellData.containsPattern = false;
255 |
256 | return evoCellData;
257 | }
258 |
259 | function calculateSymmetries(evoCellData)
260 | {
261 | symmetryPermutations = [];
262 |
263 | for (var rot = 0; rot < 4; rot++)
264 | {
265 | var rotVals = [];
266 | for (var i = 0; i < evoCellData.nrNeighbours; i++)
267 | {
268 | var roted = evoCellData.neighbourhood[i];
269 | for (var r = 0; r < rot; r++)
270 | roted = rot90(roted);
271 |
272 | for (var s = 0; s < evoCellData.nrNeighbours; s++)
273 | {
274 | if (evoCellData.neighbourhood[s][0] == roted[0] && evoCellData.neighbourhood[s][1] == roted[1])
275 | {
276 | rotVals.push(s);
277 | break;
278 | }
279 | }
280 | }
281 | // check if rotation was successful
282 | if (rotVals.length == evoCellData.neighbourhood.length)
283 | {
284 | symmetryPermutations.push(rotVals);
285 | }
286 | }
287 | return symmetryPermutations;
288 | }
289 |
290 | // dirty does not allocate assumes 4 states, moore
291 | ECFile.prototype.MakeStarWarsRule = function() {
292 | var rt = this.ruleTable;
293 | var states = 4;
294 |
295 | var d = [0,0,0, 0,0,0, 0,0,0];
296 |
297 | var idx = 0;
298 | for (d[8] = 0; d[8] < states; d[8]++) {
299 | for (d[7] = 0; d[7] < states; d[7]++) {
300 | for (d[6] = 0; d[6] < states; d[6]++) {
301 | for (d[5] = 0; d[5] < states; d[5]++) {
302 | for (d[4] = 0; d[4] < states; d[4]++) {
303 | for (d[3] = 0; d[3] < states; d[3]++) {
304 | for (d[2] = 0; d[2] < states; d[2]++) {
305 | for (d[1] = 0; d[1] < states; d[1]++) {
306 | for (d[0] = 0; d[0] < states; d[0]++) {
307 |
308 | var res = 0;
309 |
310 | var sum = 0;
311 | for (var i = 1; i < 9; i++) {
312 | if (d[i] == 3) {
313 | sum++;
314 | }
315 | }
316 |
317 | if (d[0] == 0) {
318 | if (sum ==2) res = 3;
319 | else res = 0;
320 | }
321 | else if (d[0] == 3) {
322 | if (sum == 3 || sum == 4 || sum == 5) res = 3;
323 | else res = d[0]-1;
324 | }
325 | else {
326 | res = d[0]-1;
327 | }
328 |
329 | rt[idx] = res;
330 | idx++;
331 | }
332 | }
333 | }
334 | }
335 | }
336 | }
337 | }
338 | }
339 | }
340 | }
341 |
342 | function rot90(xy)
343 | {
344 | return [-xy[1], xy[0]];
345 | }
346 |
347 |
348 | return {
349 | ECFile : ECFile,
350 | Reactor: Reactor,
351 | Dish: Dish,
352 | Rule: Rule,
353 | Palette : Palette,
354 | ParticleSystem: ParticleSystem,
355 | ResLoader: ResLoader
356 | }
357 | });
358 |
--------------------------------------------------------------------------------
/src/CellSpace/GameLoop.js:
--------------------------------------------------------------------------------
1 | define([
2 | "jquery-ui", "Utils", "EvoCell", "story/StoryTeller", "underscore",
3 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui",
4 | "CellSpace/State", "CellSpace/Setup", "CellSpace/Utils", "CellSpace/GUI"],
5 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat,
6 | gameState, csSetup, csUtils, csUI) {
7 |
8 | "use strict";
9 |
10 | var OP_REPLACE = 0;
11 | var OP_ADD = 1;
12 |
13 | var enemyStepSum = 0;
14 |
15 | var step = function() {
16 | var reactor = gameState.reactor;
17 |
18 | // ENEMIES //////////////////////////////////////
19 | enemyStepSum += gameState.enemySpeed;
20 | while (enemyStepSum > 1) {
21 | reactor.step(gameState.rules.enemy, gameState.dishes.enemy);
22 | enemyStepSum--;
23 | }
24 |
25 | if (gameState.cnt % 6 === 0)
26 | reactor.step(gameState.rules.background, gameState.dishes.background);
27 |
28 | // SHIP ///////////////////////////////////////////
29 | reactor.step(gameState.rules.weaponExplosion, gameState.dishes.weaponExplosion);
30 | reactor.step(gameState.rules.weapon, gameState.dishes.weapon);
31 | reactor.step(gameState.rules.shipExplosion, gameState.dishes.shipExplosion);
32 | reactor.step(gameState.rules.ship, gameState.dishes.ship);
33 |
34 |
35 | var shipR = gameState.shipRadius;
36 | // "DRAW" SHIP
37 | reactor.mixDish(gameState.shaders.drawCircle, gameState.dishes.ship,
38 | {center: [gameState.shipX, gameState.shipY], radius: shipR, state: (gameState.rules.ship.nrStates-1)/255});
39 |
40 | var cb = function(pos) {
41 | try
42 | {
43 | gameState.sndHit.playbackRate = 2.0;
44 | gameState.sndHit.volume = 0.5;
45 | utils.playSound(gameState.sndHit);
46 | } catch(ex) {}
47 | };
48 |
49 | // too costly
50 | //gameState.shots.collide(gameState.dishes.enemy, cb);
51 | gameState.shots.step();
52 |
53 |
54 |
55 | reactor.mixDish(gameState.shaders.clear, gameState.dishes.colliding, {color: [0,0,0,0]});
56 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.colliding,
57 | {texNew: gameState.dishes.enemy, texPalette: gameState.colors.background.getTexture()});
58 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.colliding,
59 | {texNew: gameState.dishes.weaponExplosion, texPalette: gameState.colors.enemy.getTexture()});
60 |
61 |
62 | var enemyPixel = gameState.shots.collide(gameState.dishes.colliding, gameState.scrollX, gameState.scrollY, cb, -1);
63 | //var enemyPixel = gameState.shots.collide(gameState.dishes.weaponExplosion, gameState.scrollX, gameState.scrollY, cb, -1);
64 | //var enemyPixel = gameState.shots.collide(gameState.dishes.enemy,
65 | // 0, 0, cb);
66 |
67 | gameState.shots.draw(gameState.shaders.drawPoints, gameState.dishes.weapon,
68 | 2*gameState.scrollX, 2*gameState.scrollY);
69 | //gameState.shots.draw(gameState.shaders.drawPoints, gameState.dishes.weapon, 0, 0);
70 |
71 |
72 |
73 | // collide ship
74 | if (gameState.cnt > 40) {
75 | var pX, pY;
76 |
77 | var oldEnergy = gameState.playerEnergy;
78 |
79 | for (pX = -shipR; pX <= shipR; pX++) {
80 | for (pY = -shipR; pY <= shipR; pY++) {
81 |
82 | var xxx = Math.round(gameState.shipX + pX);
83 | var yyy = Math.round(gameState.shipY + pY);
84 |
85 | if (enemyPixel[(xxx+yyy*gameState.gameW)*4 + 3] !== 0) {
86 | gameState.playerEnergy -= 1;
87 |
88 | // reactor.mixDish(gameState.shaders.drawCircle, gameState.dishes.weapon,
89 | // {center: [gameState.shipX + pX, gameState.shipY + pY], radius: 1.5, state: (gameState.rules.ship.nrStates-1)/255});
90 |
91 | }
92 | }
93 | }
94 |
95 | if (oldEnergy !== gameState.playerEnergy) {
96 | csUtils.refreshGUI(["playerEnergy"]);
97 | }
98 |
99 | // did we just die?
100 | if (gameState.playerEnergy < 0) {
101 | var oldPause = gameState.pause;
102 | gameState.pause = 1;
103 |
104 | storyTeller.RunDeath(function() {
105 | gameState.playerEnergy = 1000;
106 | csUtils.refreshGUI(["playerEnergy"]);
107 |
108 | csUtils.resetGame();
109 | gameState.pause = oldPause;
110 | });
111 |
112 | }
113 | }
114 |
115 |
116 | // Dish INTERACTION ///////////////////////////////////
117 |
118 |
119 |
120 |
121 | // weapon + enemy -> weaponExplosion
122 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.weaponExplosion, {
123 | tex1: gameState.dishes.weapon, tex2: gameState.dishes.enemy,
124 | state: (gameState.rules.weaponExplosion.nrStates-1)/255,
125 | operation: OP_REPLACE
126 | });
127 |
128 | // enemy spawn even more explosion when explodion
129 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.weaponExplosion,
130 | {tex1: gameState.dishes.enemy, tex2: gameState.dishes.weaponExplosion, state: 3/255, operation: OP_REPLACE});
131 |
132 |
133 | //enemy shields generate
134 | reactor.mixDish(gameState.shaders.shieldSpawn, gameState.dishes.enemyShield, {
135 | texShield: gameState.dishes.enemyShield, texTarget: gameState.dishes.enemy,
136 | width: gameState.gameW, height: gameState.gameH
137 | });
138 |
139 | // shield gets erroded by explosions
140 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemyShield, {
141 | tex1: gameState.dishes.enemyShield, tex2: gameState.dishes.weapon,
142 | state: -1/255,
143 | operation: OP_ADD
144 | });
145 |
146 | // weapon killed by shield
147 | /*
148 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.weapon, {
149 | tex1: gameState.dishes.enemyShield, tex2: gameState.dishes.weapon,
150 | state: 0/255,
151 | operation: OP_REPLACE
152 | });
153 | */
154 |
155 | // explosions get killed by shield
156 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.weaponExplosion, {
157 | tex1: gameState.dishes.enemyShield, tex2: gameState.dishes.weaponExplosion,
158 | state: 0/255,
159 | operation: OP_REPLACE
160 | });
161 |
162 |
163 |
164 | // // enemy dies from explosion
165 | // reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemy,
166 | // {tex1: gameState.dishes.enemy, tex2: gameState.dishes.weaponExplosion, state: -1/255, operation: OP_ADD});
167 | //must have faster c thanship; TODO: use bigger neighbourhoods
168 | if (gameState.cnt % gameState.weaponExplosionParam === 0) {
169 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemy,
170 | {tex1: gameState.dishes.enemy, tex2: gameState.dishes.weaponExplosion, state: 0/255, operation: OP_REPLACE});
171 | }
172 |
173 |
174 | // strange infectious weapon from expoloshion
175 | // reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.weapon,
176 | // {tex1: gameState.dishes.weapon, tex2: gameState.dishes.weaponExplosion, state: 3/255, operation: OP_REPLACE});
177 |
178 |
179 | // ship to enemy colissions spawn shipExplosions
180 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.shipExplosion,
181 | {tex1: gameState.dishes.ship, tex2: gameState.dishes.enemy,
182 | state: (gameState.rules.shipExplosion.nrStates-1)/255,
183 | operation: OP_REPLACE});
184 |
185 | // shipExplosions reinforced by enemys
186 | // state 3 actually makes it passive
187 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.shipExplosion,
188 | {tex1: gameState.dishes.enemy, tex2: gameState.dishes.shipExplosion, state: 3/255, operation: OP_REPLACE});
189 |
190 | // dishes.enemy gets slowly killed by shipExplosions
191 | //if (gameState.cnt % 2 === 1) {
192 | // reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemy,
193 | // {tex1: gameState.dishes.enemy, tex2: gameState.dishes.shipExplosion, state: -1/255, operation: OP_ADD});
194 | // reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemy,
195 | // {tex1: gameState.dishes.enemy, tex2: gameState.dishes.shipExplosion, state: 0, operation: OP_REPLACE});
196 | //}
197 |
198 | if (gameState.cnt % 1 === 0) {
199 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.enemy,
200 | {tex1: gameState.dishes.enemy, tex2: gameState.dishes.shipExplosion, state: -1/255, operation: OP_ADD});
201 | }
202 |
203 | // ship gets killed by shipExplosions
204 | reactor.mixDish(gameState.shaders.intersectSpawn, gameState.dishes.ship,
205 | {tex1: gameState.dishes.ship, tex2: gameState.dishes.shipExplosion, state: 0/255, operation: OP_REPLACE});
206 |
207 |
208 | // DAT.gui workaround (stores ints as strings)
209 | if (gameState.enableScrolling === 1 || gameState.enableScrolling === "1") {
210 | var deltaX = Math.round(gameState.gameW/2 - gameState.shipX);
211 | var deltaY = Math.round(gameState.gameH/2 - gameState.shipY);
212 | if (deltaX || deltaY) {
213 | gameState.shipX += deltaX;
214 | gameState.shipY += deltaY;
215 |
216 | gameState.scrollX += deltaX;
217 | gameState.scrollY += deltaY;
218 |
219 |
220 | gameState.parallaxX += deltaX;
221 | gameState.parallaxY += deltaY;
222 |
223 |
224 | var parallaxDist = 3;
225 | var pScrollX = Math.round(gameState.parallaxX/parallaxDist);
226 | var pScrollY = Math.round(gameState.parallaxY/parallaxDist);
227 |
228 | if (pScrollX || pScrollY) {
229 | var dX = -pScrollX/gameState.gameW;
230 | var dY = -pScrollY/gameState.gameH;
231 |
232 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.background,
233 | {scroll: [dX, dY]});
234 |
235 | gameState.parallaxX -= pScrollX*parallaxDist;
236 | gameState.parallaxY -= pScrollY*parallaxDist;
237 | }
238 |
239 | csUtils.refreshGUI(["shipX", "shipY", "scrollX", "scrollY", "parallaxX", "parallaxY"]);
240 |
241 | var dX = -deltaX/gameState.gameW;
242 | var dY = -deltaY/gameState.gameH;
243 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.ship,
244 | {scroll: [dX, dY]});
245 |
246 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.shipExplosion,
247 | {scroll: [dX, dY]});
248 |
249 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.enemy,
250 | {scroll: [dX, dY]});
251 |
252 | //reactor.mixDish(gameState.shaders.scroll, gameState.dishes.background,
253 | // {scroll: [dX, dY]});
254 |
255 |
256 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.enemyShield,
257 | {scroll: [dX, dY]});
258 |
259 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.weapon,
260 | {scroll: [dX, dY]});
261 |
262 | reactor.mixDish(gameState.shaders.scroll, gameState.dishes.weaponExplosion,
263 | {scroll: [dX, dY]});
264 | }
265 | }
266 |
267 | // move ship
268 | gameState.shipX += gameState.shipSpeedX;
269 | gameState.shipY += gameState.shipSpeedY;
270 |
271 | gameState.cnt++;
272 | };
273 |
274 | var render = function() {
275 | var reactor = gameState.reactor;
276 |
277 | var camera = new THREE.PerspectiveCamera( 180*gameState.cameraAngle/Math.PI, 1, 0.01, 1000 );
278 | //camera.lookAt(new THREE.Vector3( 0, 0, -1 )); // what does it do?
279 | gameState.projectionMatrix = camera.projectionMatrix;
280 |
281 | //viewMatrix = new THREE.Matrix4();
282 | var quaternion = new THREE.Quaternion();
283 | quaternion.setFromAxisAngle( new THREE.Vector3( 0, 0.7, 1 ).normalize(), gameState.rot );
284 |
285 | var shipClipX = 2*(gameState.shipX-gameState.dishes.enemy.width/2)/gameState.dishes.enemy.width;
286 | var shipClipY = 2*(gameState.shipY-gameState.dishes.enemy.height/2)/gameState.dishes.enemy.height;
287 |
288 | var scaleX = gameState.gameW / gameState.screenW;
289 | var scaleY = gameState.gameH / gameState.screenH;
290 |
291 | var transMatrix = new THREE.Matrix4().compose(new THREE.Vector3(
292 | -shipClipX, -shipClipY, 0),
293 | new THREE.Quaternion(),
294 | new THREE.Vector3(scaleX,scaleY,1)
295 | );
296 | var rotMatrix = new THREE.Matrix4().compose(new THREE.Vector3(0,0,0),
297 | quaternion,
298 | new THREE.Vector3(1,1,1)
299 | );
300 |
301 | // correct order is firts translate then rotate
302 | gameState.viewMatrix = new THREE.Matrix4().multiplyMatrices(rotMatrix, transMatrix);
303 |
304 | // subtract mapped ship position to center player shi
305 | var shipPos = new THREE.Vector4(shipClipX, shipClipY, 0, 1);
306 | shipPos.applyMatrix4(gameState.viewMatrix);
307 | shipPos.multiplyScalar(-1);
308 | shipPos.add(new THREE.Vector3(0,0, -gameState.zoom)); // move to negative x
309 | //posPlayer = new THREE.Vector3(0,0, -pixel); // do this to not track ship
310 | var shipCenterMatrix = new THREE.Matrix4().compose(shipPos,
311 | new THREE.Quaternion(),
312 | new THREE.Vector3(1,1,1)
313 | );
314 | // now Subtract mapped shipPos
315 | gameState.viewMatrix = new THREE.Matrix4().multiplyMatrices(shipCenterMatrix, gameState.viewMatrix);
316 |
317 | // COMPOSE ////////////////////////////////////////////
318 | reactor.mixDish(gameState.shaders.clear, gameState.dishes.render, {color: [0,0,0,255]});
319 |
320 |
321 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
322 | {texNew: gameState.dishes.background, texPalette: gameState.colors.background.getTexture()});
323 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
324 | {texNew: gameState.dishes.enemy, texPalette: gameState.colors.enemy.getTexture()});
325 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
326 | {texNew: gameState.dishes.weapon, texPalette: gameState.colors.weapon.getTexture()});
327 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
328 | {texNew: gameState.dishes.ship, texPalette: gameState.colors.ship.getTexture()});
329 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
330 | {texNew: gameState.dishes.weaponExplosion, texPalette: gameState.colors.weaponExplosion.getTexture()});
331 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
332 | {texNew: gameState.dishes.shipExplosion, texPalette: gameState.colors.shipExplosion.getTexture()});
333 |
334 | if (gameState.showBuffer) {
335 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
336 | {texNew: gameState.dishes.buffer, texPalette: gameState.colors.weapon.getTexture()});
337 | }
338 |
339 | if (gameState.selection.active) {
340 | reactor.mixDish(gameState.shaders.mixRect, gameState.dishes.render, {
341 | rectPos: gameState.selection.pos,
342 | rectSize: gameState.selection.size,
343 | color: [1,1,1,0.3],
344 | });
345 | }
346 |
347 | if (gameState.showRule) {
348 | // TODO: fixup this dirty hack for visualizing ruletable
349 | var rule = gameState.rules[csUI.getActiveDishName()];
350 | var colors = gameState.colors[csUI.getActiveDishName()];
351 |
352 | if (rule && colors) {
353 | reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
354 | {texNew: rule.getTexture(), texPalette: colors.getTexture()}
355 | );
356 | }
357 | }
358 |
359 | // render shields in render2
360 | reactor.mixDish(gameState.shaders.clear, gameState.dishes.render2, {color: [0,0,0,0]});
361 | reactor.mixDish(gameState.shaders.stamp, gameState.dishes.render2,
362 | {texNew: gameState.dishes.enemyShield, texPalette: gameState.colors.enemyShield.getTexture()});
363 |
364 | //reactor.mixDish(gameState.shaders.mix, gameState.dishes.render,
365 | // {texNew: gameState.dishes.enemyShield, texPalette: gameState.colors.enemyShield.getTexture()});
366 |
367 |
368 | reactor.paintDish(gameState.shaders["cameraRenderer" + gameState.renderer], gameState.dishes.render, gameState.dishes.render2, function(gl, shader) {
369 | gl.uniform2f(gl.getUniformLocation(shader, "resolution"), gameState.gameW, gameState.gameH);
370 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, "projectionMatrix"), false, gameState.projectionMatrix.elements);
371 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, "modelViewMatrix"), false, gameState.viewMatrix.elements);
372 | });
373 |
374 | gameState.fpsMonotor.frameIncrease();
375 |
376 | };
377 |
378 | return {
379 | step: step,
380 | render: render,
381 | };
382 | });
--------------------------------------------------------------------------------
/src/CellSpace/GUI.js:
--------------------------------------------------------------------------------
1 | define([
2 | "jquery", "Utils", "EvoCell", "story/StoryTeller", "underscore",
3 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui",
4 | "CellSpace/State", "CellSpace/Setup", "CellSpace/GameLoop", "CellSpace/Utils"],
5 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat,
6 | gameState, csSetup, gameLoop, csUtils) {
7 | "use strict";
8 |
9 | // used for breaking to 0 and then reversse
10 | var allowReturn = 0;
11 | var oldPauseState = false;
12 | var buttonWasDown = 0;
13 | var bounceEsc = 0;
14 |
15 | var getActiveDishName = function() {
16 | return gameState.drawModel.attributes.selectedLayers[0] || "enemy";
17 | }
18 |
19 | var getActiveDish = function() {
20 | return gameState.dishes[getActiveDishName()] || gameState.dishes.enemy;
21 | };
22 |
23 | var updateSelection = function(evt) {
24 | if (evt.button === 2) buttonWasDown = true;
25 |
26 | if (gameState.selection.active || evt.button === 2)
27 | {
28 | var coords = gameState.canvas.relMouseCoords(evt);
29 | var x = coords.x;
30 | var y = gameState.screenH - coords.y;
31 | var clickedNDC = utils.getNDCFromMouseEvent(gameState.canvas, evt, gameState.screenW, gameState.screenH);
32 | var clickedPoint = utils.intersectClick(clickedNDC, gameState.viewMatrix, gameState.cameraAngle/2);
33 |
34 | var cx = Math.round(gameState.gameW*(clickedPoint.x+1)/2);
35 | var cy = Math.round(gameState.gameH*(clickedPoint.y+1)/2);
36 |
37 | // start new
38 | if (!gameState.selection.active)
39 | {
40 | oldPauseState = gameState.pause;
41 | gameState.pause = true;
42 | gameState.selection.downPos = [cx, cy];
43 | gameState.selection.active = 2;
44 | }
45 |
46 | var dx = gameState.selection.lastPos[0] - cx;
47 | var dy = gameState.selection.lastPos[1] - cy;
48 |
49 | if (evt.ctrlKey)
50 | {
51 | gameState.selection.downPos[0] += dx;
52 | gameState.selection.downPos[1] += dy;
53 | }
54 | if (evt.shiftKey)
55 | {
56 | gameState.selection.downPos[0] -= dx;
57 | gameState.selection.downPos[1] -= dy;
58 | }
59 |
60 | var ox = gameState.selection.downPos[0];
61 | var oy = gameState.selection.downPos[1];
62 |
63 | var minx = Math.min(cx, ox);
64 | var miny = Math.min(cy, oy);
65 |
66 | gameState.selection.lastPos = [cx, cy];
67 | gameState.selection.pos = [minx, miny];
68 | gameState.selection.size = [Math.abs(cx-ox), Math.abs(cy-oy)];
69 | }
70 | if (evt.button != 2) { // not pressed
71 | if (gameState.selection.active && buttonWasDown) {
72 | gameState.selection.active--;
73 |
74 | if (!gameState.selection.active)
75 | {
76 | var dish = getActiveDish();
77 |
78 | gameState.reactor.mixDish(gameState.shaders.copy, gameState.dishes.buffer, {
79 | destinationPos: [0, 0],
80 | destinationSize: gameState.selection.size,
81 | texSource: dish,
82 | sourcePos: gameState.selection.pos,
83 | sourceRes: [gameState.gameW, gameState.gameH],
84 | });
85 |
86 | gameState.pause = oldPauseState;
87 | }
88 | }
89 | buttonWasDown = false;
90 | }
91 | };
92 |
93 | var setupGui = function() {
94 | window.addEventListener("resize", function () {
95 | gameState.screenW = window.innerWidth;
96 | gameState.screenH = window.innerHeight;
97 | csUtils.onScreenSizeChanged();
98 | }, false);
99 |
100 | document.getElementById("assignLayerRule").addEventListener('click', function(evt) {
101 | var ruleName = gameState.drawModel.get("selectedRules")[0];
102 | var layerName = gameState.drawModel.get("selectedLayers")[0];
103 | var dish = gameState.dishes[layerName];
104 |
105 | fileStore.loadRule(ruleName, function(loadedRule) {
106 | gameState.rules[layerName] = gameState.reactor.compileRule(loadedRule.ruleData, dish);
107 | });
108 |
109 | }, false);
110 |
111 |
112 | document.getElementById("loadPattern").addEventListener('click', function(evt) {
113 | var ruleName = gameState.drawModel.get("selectedRules")[0];
114 |
115 | fileStore.loadRule(ruleName, function(loadedRule) {
116 | if (loadedRule.ruleData.containsPattern) {
117 | var rule = gameState.reactor.compileRule(loadedRule.ruleData);
118 |
119 | gameState.selection.size = [loadedRule.ruleData.patternWidth, loadedRule.ruleData.patternHeight];
120 |
121 | gameState.reactor.mixDish(gameState.shaders.copy, gameState.dishes.buffer, {
122 | destinationPos:[0, 0],
123 | destinationSize: gameState.selection.size,
124 | texSource: rule.getPatternTexture(),
125 | sourcePos: [0, 0],
126 | sourceRes: gameState.selection.size,
127 | });
128 | }
129 | });
130 | }, false);
131 |
132 |
133 | document.getElementById("savePattern").addEventListener('click', function(evt) {
134 | var ruleName = prompt('Pattern can haz name?','pattern_');
135 |
136 | if (ruleName)
137 | {
138 | // capture pixel from buffer
139 | var dish = gameState.dishes.buffer;
140 | var w = gameState.selection.size[0];
141 | var h = gameState.selection.size[1];
142 | var patternDataRGBA = new Uint8Array(w*h*4);
143 | var gl = gameState.reactor.gl;
144 |
145 | gl.bindFramebuffer(gl.FRAMEBUFFER, dish.getCurrentFramebuffer());
146 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, dish.getCurrentTexture(), 0);
147 | gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, patternDataRGBA);
148 |
149 | var patternData = new Uint8Array(w*h);
150 | for (var i = w*h-1; i >= 0; i--) {
151 | patternData[i] = patternDataRGBA[4*i +3];
152 | }
153 |
154 | // construct ECFile
155 | var ruleData = {
156 | containsPattern: true,
157 | patternWidth: gameState.selection.size[0],
158 | patternHeight: gameState.selection.size[1],
159 | patternData: patternData,
160 | };
161 |
162 | fileStore.storeRule(ruleName, ruleData, function() {
163 | csUtils.refreshAvailableRules();
164 | });
165 | }
166 | }, false);
167 |
168 |
169 | $('#importRule').change(function(evt) {
170 | var files = evt.target.files; // FileList object
171 |
172 | _.each(files, function(file) {
173 | var reader = new FileReader();
174 | reader.onload = function(evt) {
175 | var arrayBufferData = evt.target.result;
176 | var evoCellData = new EC.ECFile(arrayBufferData);
177 | //alert(evoCellData);
178 | //alert(file.name);
179 | fileStore.storeRule(file.name, evoCellData, function() {
180 | csUtils.refreshAvailableRules();
181 | });
182 | };
183 | reader.readAsArrayBuffer(file); // start async operation
184 | });
185 | });
186 |
187 | document.getElementById("deleteRule").addEventListener('click', function(evt) {
188 | var selectedRules = gameState.drawModel.get("selectedRules");
189 |
190 | var refresh = _.after(selectedRules.length, function() {
191 | //alert("refresh");
192 | csUtils.refreshAvailableRules();
193 | });
194 |
195 | _.each(selectedRules, function(ruleName) {
196 | fileStore.loadRule(ruleName, function(rule) {
197 | if (!rule ) {
198 | alert("cant delete nonextent: " + ruleName);
199 | }
200 | fileStore.deleteRule(ruleName, function() {
201 | //alert("deleted:" + ruleName);
202 | refresh();
203 | });
204 | });
205 | });
206 | }, false);
207 |
208 | document.getElementById("stepLink").addEventListener('click', function(evt) {
209 | csUtils.gameStep();
210 | }, false);
211 |
212 | document.getElementById("playPause").addEventListener('click', function(evt) {
213 | csUtils.gamePlayPause();
214 | }, false);
215 |
216 | document.getElementById("showIntroLink").addEventListener('click', function(evt) {
217 | storyTeller.RunIntro();
218 | }, false);
219 |
220 | document.getElementById("initializeDB").addEventListener("click", function(evt) {
221 | csSetup.initDB();
222 | }, false);
223 |
224 |
225 |
226 | var nonPersistables = [
227 | "canvas","reactor","gl","gui","keyboard","shaders","dishes","rules","colors","shots",
228 | "sndInit","snd","sndBomb","sndHit","sndHit2","drawModel","mainLoop"
229 | ];
230 |
231 | document.getElementById("saveGameState").addEventListener("click", function(evt) {
232 | var clonedState = {};
233 | _.each(gameState, function(value, key) {
234 | if (!_.contains(nonPersistables, key)) {
235 | clonedState[key] = value;
236 | }
237 | });
238 | fileStore.addObject("gameStates", {id: "test23", state: clonedState });
239 | }, false);
240 |
241 | document.getElementById("loadGameState").addEventListener("click", function(evt) {
242 | fileStore.getObject("gameStates", "test23", function(loadedState) {
243 | _.each(loadedState.state, function(value, key) {
244 | gameState[key] = value;
245 | });
246 | });
247 | }, false);
248 |
249 |
250 | var gui = gameState.gui;
251 |
252 | gui.add(gameState, 'playerEnergy');
253 |
254 | var folder = gui.addFolder('App');
255 | folder.add(gameState, 'zoom', 0.05, 2).step(0.01);
256 |
257 | // ugly hack to make ro a two decimal nr in datgui
258 | var oldRot = gameState.rot;
259 | gameState.rot = 0.01;
260 | // hack continues below
261 | var ctrl = folder.add(gameState, 'rot', 0, Math.PI*2).step(0.01);
262 | //ctrl.__precision = 3; // does not help
263 | //ctrl.__impliedStep = 0.001; // does not help
264 | // hack!
265 | gameState.rot = oldRot;
266 | csUtils.refreshGUI(["rot"]);
267 | // end of ugly hack
268 |
269 | folder.add(gameState, 'frontShots', 1, 12).step(1);
270 | folder.add(gameState, 'frontShotAngle', 0, 2*Math.PI);
271 | folder.add(gameState, 'shipRadius', 1, 20);
272 | folder.add(gameState, 'randomDensity', 0, 1);
273 |
274 | folder.add(gameState, "enemySpeed", 0, 12);
275 | folder.add(gameState, "weaponExplosionParam");
276 |
277 | folder.add(gameState, 'enableScrolling', {yes: 1, no: 0});
278 |
279 | folder.add(gameState, 'shipX');
280 | folder.add(gameState, 'shipY');
281 |
282 | folder.add(gameState, 'scrollX');
283 | folder.add(gameState, 'scrollY');
284 |
285 | folder.add(gameState, 'gameW').onFinishChange(csUtils.onGameSizeChanged);
286 | folder.add(gameState, 'gameH').onFinishChange(csUtils.onGameSizeChanged);
287 |
288 | folder = gui.addFolder('Core');
289 | var screenWCtrl = folder.add(gameState, 'screenW');
290 | var screenHCtrl = folder.add(gameState, 'screenH');
291 | folder.add(gameState, 'renderer', {Fast: "Fast", Simple:"Simple", TV:"TV", Cell:"Cell"});
292 |
293 | folder = gui.addFolder('Debug');
294 | folder.add(gameState, 'perfStartJSTime');
295 | folder.add(gameState, 'perfRequireTime');
296 | folder.add(gameState, 'perfFinishedJSTime');
297 | folder.add(gameState, "showBuffer");
298 | folder.add(gameState, "showRule");
299 |
300 | var onResized = function(value) {
301 | gameState.reactor.setRenderSize(gameState.screenW, gameState.screenH);
302 | };
303 | screenWCtrl.onFinishChange(onResized);
304 | screenHCtrl.onFinishChange(onResized);
305 |
306 | var view_model = kb.viewModel(gameState.drawModel);
307 | //view_model.full_name = ko.computed((->return "#{@first_name()} #{@last_name()}"), view_model)
308 | ko.applyBindings(view_model, document.getElementById("drawTool"));
309 |
310 |
311 | var getActiveWindowId = function() {
312 | var window = null;
313 | var windows = document.getElementsByClassName("activeWindow");
314 | if (windows.length > 0) return windows[0].id;
315 | return null;
316 | };
317 |
318 | String.prototype.replaceAll = function(search, replace) {
319 | if (replace === undefined) {
320 | return this.toString();
321 | }
322 | return this.split(search).join(replace);
323 | }
324 |
325 | var toggleClass = function(element, className) {
326 | var classes = element.getAttribute("class");
327 | if (classes.indexOf(className) >= 0) {
328 | classes = classes.replace(className, "")
329 | }
330 | else {
331 | classes = classes + " " + className;
332 | }
333 | element.setAttribute('class', classes);
334 | }
335 |
336 | _.each(document.getElementsByClassName("toolMenuHeader"), function(toolWindow) {
337 | toolWindow.addEventListener("click", function(evt) {
338 |
339 | var element = toolWindow.parentElement;
340 | //var element = toolWindow;
341 |
342 | // deactivate old active Windows except self
343 | _.each(document.getElementsByClassName("activeWindow"), function(activeW) {
344 | if (activeW !== element) {
345 | toggleClass(activeW, 'activeWindow');
346 | }
347 | });
348 |
349 | toggleClass(element, 'activeWindow');
350 | }, false);
351 | });
352 |
353 |
354 | // TODO: implement palette
355 | // $('#colorpicker1').farbtastic('#color1');
356 |
357 |
358 | gameState.fpsMonotor = new utils.FPSMonitor("fpsMonitor");
359 |
360 | function handleCanvasMouseDown(evt) {
361 | var coords = gameState.canvas.relMouseCoords(evt);
362 | var x = coords.x;
363 | var y = gameState.screenH - coords.y;
364 |
365 | var activeTool = getActiveWindowId();
366 |
367 | var clickedNDC = utils.getNDCFromMouseEvent(gameState.canvas, evt, gameState.screenW, gameState.screenH);
368 | var clickedPoint = utils.intersectClick(clickedNDC, gameState.viewMatrix, gameState.cameraAngle/2);
369 |
370 | var dish = getActiveDish();
371 |
372 | if (activeTool === "ToolWindow" && evt.button === 0) {
373 | var state = 0;
374 | var firstSel = gameState.drawModel.attributes.selectedStates[0];
375 | if (firstSel) state = firstSel;
376 |
377 | if (dish) {
378 | if (gameState.drawModel.attributes.selectedDrawShape == "circle") {
379 | gameState.reactor.mixDish(gameState.shaders.drawCircle, dish, {
380 | center: [gameState.gameW*(clickedPoint.x+1)/2, gameState.gameH*(clickedPoint.y+1)/2],
381 | radius: gameState.drawModel.attributes.drawSizeX/2, state: state/255
382 | });
383 | }
384 | else {
385 | gameState.reactor.mixDish(gameState.shaders.drawRect, dish, {
386 | rectPos: [gameState.gameW*(clickedPoint.x+1)/2, gameState.gameH*(clickedPoint.y+1)/2],
387 | rectSize: [gameState.drawModel.attributes.drawSizeX, gameState.drawModel.attributes.drawSizeY],
388 | state: state/255
389 | });
390 | }
391 | }
392 |
393 | }
394 | else if (evt.button === 0) { // || mouseMode == "shoot") {
395 | csUtils.fireShotAt(gameState.gameW*(clickedPoint.x+1)/2, gameState.gameH*(clickedPoint.y+1)/2);
396 | // no autofire for now
397 | //gameState.autoFireOn = 1 - gameState.autoFireOn;
398 | }
399 | // copy
400 | else if (evt.button === 2) {
401 | /*
402 | gameState.reactor.mixDish(gameState.shaders.copy, gameState.dishes.buffer, {
403 | destinationPos: [0, 0],
404 | destinationSize: [gameState.dishes.buffer.width, gameState.dishes.buffer.height],
405 | texSource: dish,
406 | sourcePos: [gameState.gameW*(clickedPoint.x+1)/2-gameState.dishes.buffer.width/2,
407 | gameState.gameH*(clickedPoint.y+1)/2-gameState.dishes.buffer.height/2],
408 | sourceRes: [gameState.gameW, gameState.gameH],
409 | });
410 | */
411 |
412 | updateSelection(evt);
413 | }
414 | // paste
415 | else if (evt.button === 1) {
416 | var tx = gameState.gameW*(clickedPoint.x+1)/2-gameState.selection.size[0]/2;
417 | var ty = gameState.gameH*(clickedPoint.y+1)/2-gameState.selection.size[1]/2;
418 |
419 | gameState.reactor.mixDish(gameState.shaders.copy, dish, {
420 | destinationPos:[tx, ty],
421 | destinationSize: gameState.selection.size,
422 | texSource: gameState.dishes.buffer,
423 | sourcePos: [0, 0],
424 | sourceRes: [gameState.dishes.buffer.width, gameState.dishes.buffer.height],
425 | });
426 | // gameState.reactor.mixDish(gameState.shaders.copy, dish, {
427 | // destinationPos:[gameState.gameW*(clickedPoint.x+1)/2-gameState.dishes.buffer.width/2,
428 | // gameState.gameH*(clickedPoint.y+1)/2-gameState.dishes.buffer.height/2],
429 | // destinationSize: [gameState.dishes.buffer.width, gameState.dishes.buffer.height],
430 | // texSource: gameState.dishes.buffer,
431 | // sourcePos: [0, 0],
432 | // sourceRes: [gameState.dishes.buffer.width, gameState.dishes.buffer.height],
433 | // });
434 | }
435 |
436 | evt.preventDefault();
437 | evt.stopPropagation();
438 | }
439 | gameState.canvas.addEventListener('mousedown', handleCanvasMouseDown, false);
440 |
441 | var blockContextMenu = function (evt) {
442 | evt.preventDefault();
443 | };
444 | gameState.canvas.addEventListener('contextmenu', blockContextMenu, false);
445 |
446 | var handleCanvasMouseMove = function(evt)
447 | {
448 | updateSelection(evt);
449 | gameState.lastMouseNDC = utils.getNDCFromMouseEvent(gameState.canvas, evt, gameState.screenW, gameState.screenH);
450 | };
451 | gameState.canvas.addEventListener('mousemove', handleCanvasMouseMove, false);
452 |
453 |
454 | var handleMouseWheel = function(e) {
455 | var maxZoom = 25;
456 | var delta = Math.max(-maxZoom, Math.min(maxZoom, (e.wheelDelta || -e.detail)));
457 | delta *=4;
458 | //delta /= 1000;
459 | csUtils.zoom(delta);
460 | return false;
461 | };
462 | gameState.canvas.addEventListener("mousewheel", handleMouseWheel, false);
463 | gameState.canvas.addEventListener("DOMMouseScroll", handleMouseWheel, false);
464 | };
465 |
466 | var once = 1;
467 | var pollUserInteraction = function() {
468 | var keyboard = gameState.keyboard;
469 |
470 | csUtils.pollAutoFire();
471 | // USER INPUT Poll Keyboard //////////////////////////////////////////////////
472 |
473 |
474 | // TODO: move this to animaion independed poll function
475 | if (keyboard.isPressed("Z".charCodeAt()))
476 | {
477 | if (once) {
478 | once=0;
479 | csUtils.gameStep();
480 | }
481 | }
482 | else if (keyboard.isPressed("Q".charCodeAt()))
483 | {
484 | if (once) {
485 | once=0;
486 | csUtils.gamePlayPause();
487 | }
488 | }
489 | else
490 | once = 1;
491 |
492 | // shoot straight ahead
493 | if (keyboard.isPressed("X".charCodeAt()))
494 | {
495 | var tx = gameState.shipX + Math.cos(gameState.shipDir);
496 | var ty = gameState.shipY + Math.sin(gameState.shipDir);
497 | csUtils.fireShotAt(tx, ty);
498 | }
499 |
500 |
501 | if (keyboard.isPressed("O".charCodeAt()))
502 | {
503 | gameState.zoom -= 0.03;
504 | csUtils.refreshGUI(["zoom"]);
505 | }
506 |
507 | if (keyboard.isPressed("L".charCodeAt()))
508 | {
509 | gameState.zoom += 0.03;
510 | csUtils.refreshGUI(["zoom"]);
511 | }
512 |
513 | if (keyboard.isPressed("N".charCodeAt()))
514 | {
515 | gameState.rot += 0.05;
516 | csUtils.refreshGUI(["zoom"]);
517 | }
518 |
519 | if (keyboard.isPressed("M".charCodeAt()))
520 | {
521 | gameState.rot -= 0.05;
522 | csUtils.refreshGUI(["zoom"]);
523 | }
524 |
525 |
526 | // direction/speed based controll of ship
527 | var rotSpeed = 0.15;
528 | var accel = 0.1;
529 | var minSpeed = -1;
530 | var maxSpeed = 3;
531 |
532 | if (keyboard.isPressed(keyboard.UP)) {
533 | gameState.shipSpeed += accel;
534 | if (gameState.shipSpeed > maxSpeed)
535 | gameState.shipSpeed = maxSpeed;
536 | }
537 | if (keyboard.isPressed(keyboard.DOWN)) {
538 | if (gameState.shipSpeed > 0) {
539 | gameState.shipSpeed -= accel;
540 | if (gameState.shipSpeed < 0) {
541 | gameState.shipSpeed = 0;
542 | allowReturn = 0;
543 | }
544 | }
545 | else if (allowReturn) {
546 | gameState.shipSpeed -= accel;
547 | if (gameState.shipSpeed < minSpeed)
548 | gameState.shipSpeed = minSpeed;
549 | }
550 | }
551 | else {
552 | allowReturn = 1;
553 | }
554 |
555 | // direction and speed of layer ship
556 | if (keyboard.isPressed(keyboard.LEFT)) gameState.shipDir += rotSpeed;
557 | if (keyboard.isPressed(keyboard.RIGHT)) gameState.shipDir -= rotSpeed;
558 | gameState.shipSpeedX = gameState.shipSpeed * Math.cos(gameState.shipDir);
559 | gameState.shipSpeedY = gameState.shipSpeed * Math.sin(gameState.shipDir);
560 |
561 | // space
562 | if (keyboard.isPressed(32)) {
563 | csUtils.resetGame();
564 |
565 | //gameState.dishes.enemyShield.randomize(, gameState.randomDensity);
566 | }
567 |
568 | if (keyboard.isPressed("S".charCodeAt())) {
569 | gameState.reactor.mixDish(gameState.shaders.clear, gameState.dishes.enemyShield,
570 | {color: [0,0,0,24/255]});
571 | }
572 |
573 | // escape
574 | if (keyboard.isPressed(27))
575 | {
576 | if (!bounceEsc) {
577 | bounceEsc = 1;
578 |
579 | // reset selection
580 | if (gameState.selection.active) {
581 | gameState.selection.active = 0;
582 | }
583 | else {
584 |
585 | var dishes = gameState.dishes;
586 | var blockedDishes = [dishes.buffer, dishes.background];
587 |
588 | var saveDishes = _.filter(dishes, function (d) {
589 | return !_.contains(blockedDishes, d);
590 | });
591 | _.each(saveDishes, function (dish) {
592 | dish.setAll(0);
593 | });
594 | }
595 | }
596 | }
597 | else {
598 | bounceEsc = 0;
599 | }
600 |
601 | if (keyboard.isPressed("I".charCodeAt())) {
602 | gameState.shotN++;
603 | }
604 |
605 | if (keyboard.isPressed("K".charCodeAt())) {
606 | gameState.shotN--;
607 | if (gameState.shotN <= 0) gameState.shotN = 1;
608 | }
609 |
610 | if (keyboard.isPressed("B".charCodeAt())) {
611 | if (!gameState.bombFired) {
612 | //gameState.bombFired = 1; // allow permanent fire for now
613 | for (var i = 0; i < gameState.shotN; i++)
614 | {
615 | gameState.bAngle += Math.PI * 2 / 1.61803398875;
616 | gameState.shots.allocateSphere(1,
617 | gameState.shipX -1*gameState.scrollX, gameState.shipY -1*gameState.scrollY,
618 | gameState.shotSpeed, gameState.bAngle,
619 | gameState.shipSpeedX, gameState.shipSpeedY);
620 | }
621 |
622 | utils.playSound(gameState.sndBomb);
623 | }
624 | }
625 | else {
626 | gameState.bombFired = 0;
627 | }
628 |
629 | if (keyboard.isPressed("1".charCodeAt()))
630 | {
631 | gameState.mouseMode = "shoot";
632 | }
633 | if (keyboard.isPressed("P".charCodeAt()))
634 | {
635 | gameState.mouseMode = "paste";
636 | }
637 | if (keyboard.isPressed("C".charCodeAt()))
638 | {
639 | gameState.mouseMode = "copy";
640 | }
641 |
642 | var sDX = 0, sDY = 0;
643 | if (keyboard.isPressed("D".charCodeAt()))
644 | {
645 | sDX = 1;
646 | }
647 | if (keyboard.isPressed("A".charCodeAt()))
648 | {
649 | sDX = -1;
650 | }
651 | if (keyboard.isPressed("W".charCodeAt()))
652 | {
653 | sDY = 1;
654 | }
655 | if (keyboard.isPressed("S".charCodeAt()))
656 | {
657 | sDY = -1;
658 | }
659 | if (sDX || sDY) {
660 | if (!gameState.shotDelay)
661 | {
662 | gameState.shotDelay = 3;
663 | /*var px = (gameState.shipX/gameState.gameW)*2 - 1;
664 | var py = (gameState.shipY/gameState.gameH)*2 - 1;
665 | var sX = sDX * gameState.shotSpeed;
666 | var sY = sDY*gameState.shotSpeed;
667 |
668 | gameState.shots.allocateParticle(gameState.shipX, gameState.shipY, sX, sY);
669 | */
670 | csUtils.fireShotAt(gameState.shipX + sDX, gameState.shipY + sDY);
671 | }
672 | else
673 | gameState.shotDelay--;
674 | }
675 | else
676 | gameState.shotDelay = 0;
677 | };
678 |
679 | return {
680 | setupGui: setupGui,
681 | pollUserInteraction: pollUserInteraction,
682 | getActiveDishName: getActiveDishName,
683 | };
684 | });
--------------------------------------------------------------------------------