├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── data
│ └── waves.js
├── fonts
│ ├── mozillavr.fnt
│ └── mozillavr.png
├── images
│ ├── damage.png
│ ├── decal0.png
│ ├── decal1.png
│ ├── decal2.png
│ ├── enemy_diff.jpg
│ ├── enemy_emissive0.jpg
│ ├── enemy_emissive1.jpg
│ ├── enemy_emissive2.jpg
│ ├── enemy_emissive3.jpg
│ ├── enemy_emissive_template.jpg
│ ├── enemy_spec.jpg
│ ├── face_guide.png
│ ├── font.png
│ ├── fx1.png
│ ├── fx2.png
│ ├── fx3.png
│ ├── fx4.png
│ ├── fx5.png
│ ├── fx6.png
│ ├── fx7.png
│ ├── fx8.png
│ ├── grad.png
│ ├── gun_diff.jpg
│ ├── gun_normal.png
│ ├── gun_spec.jpg
│ ├── icon-128.png
│ ├── icon-256.png
│ ├── icon.png
│ ├── menu.png
│ ├── set_diff.jpg
│ └── sky.jpg
├── models
│ ├── border.json
│ ├── enemy-bullet.json
│ ├── enemy0.json
│ ├── enemy1.json
│ ├── enemy2.json
│ ├── enemy3.json
│ ├── gameover.json
│ ├── gun.json
│ ├── logo.json
│ ├── player-bullet.json
│ ├── restart.json
│ ├── ring.json
│ └── welldone.json
├── rawfiles
│ ├── images
│ │ ├── decals.psd
│ │ ├── enemy.psd
│ │ ├── env.psd
│ │ ├── face.psd
│ │ ├── favicon.ico
│ │ ├── font.kra
│ │ ├── font.psd
│ │ ├── fx.psd
│ │ ├── icon-128.png
│ │ ├── icon-256.png
│ │ ├── icon.png
│ │ ├── menu.psd
│ │ └── sky.psd
│ ├── models
│ │ ├── background.blend
│ │ ├── border.blend
│ │ ├── border_lighting.blend
│ │ ├── bullet.blend
│ │ ├── deco.blend
│ │ ├── decos.blend
│ │ ├── enemies.blend
│ │ ├── enemy0.blend
│ │ ├── enemy1.blend
│ │ ├── enemy2.blend
│ │ ├── enemy3.blend
│ │ ├── exportWaves.py
│ │ ├── gameover.blend
│ │ ├── gun.blend
│ │ ├── gun_anim.blend
│ │ ├── logo.blend
│ │ ├── platform.blend
│ │ ├── player-bullet.blend
│ │ ├── restart.blend
│ │ ├── testsphere.blend
│ │ ├── waves.blend
│ │ └── welldone.blend
│ └── sounds
│ │ └── sfx
│ │ ├── .bitwig-project
│ │ ├── enemy0shoot.bwproject
│ │ ├── explosion0.bwproject
│ │ ├── explosion1.bwproject
│ │ ├── explosion2.bwproject
│ │ ├── explosion3.bwproject
│ │ ├── hitbullet.bwproject
│ │ ├── hitwall.bwproject
│ │ ├── hurt.bwproject
│ │ ├── playershoot.bwproject
│ │ ├── sfx.bwproject
│ │ └── sounds.aup
├── readme
│ ├── a-blast-10s.gif
│ ├── a-blast-1s.gif
│ ├── a-blast-22s.gif
│ ├── a-blast-3s.gif
│ ├── gun.png
│ ├── mainmenu.png
│ └── mainmenu2.png
└── sounds
│ ├── enemy0shoot.ogg
│ ├── enemy1shoot.ogg
│ ├── enemy2shoot.ogg
│ ├── enemy3shoot.ogg
│ ├── explosion0.ogg
│ ├── explosion1.ogg
│ ├── explosion2.ogg
│ ├── explosion3.ogg
│ ├── gun.ogg
│ ├── hitbullet.ogg
│ ├── hitwall.ogg
│ ├── hurt.ogg
│ ├── intro.ogg
│ └── music.ogg
├── build
└── build.js
├── css
└── main.css
├── favicon.ico
├── index.html
├── manifest.webmanifest
├── package-lock.json
├── package.json
├── src
├── a-asset-image.js
├── bullets
│ ├── enemy-fast.js
│ ├── enemy-fat.js
│ ├── enemy-medium.js
│ ├── enemy-slow.js
│ └── player.js
├── components
│ ├── animate-message.js
│ ├── bullet.js
│ ├── collision-helper.js
│ ├── countdown.js
│ ├── curve-movement.js
│ ├── decals.js
│ ├── enemy.js
│ ├── explosion.js
│ ├── gamestate-debug.js
│ ├── gamestate-visuals.js
│ ├── gamestate.js
│ ├── gun.js
│ ├── headset.js
│ ├── highscores.js
│ ├── json-model.js
│ ├── lifes-counter.js
│ ├── points-counter.js
│ ├── proxy_event.js
│ ├── restrict-position.js
│ ├── shoot-controls.js
│ ├── sound-fade.js
│ ├── spline-line.js
│ ├── timer-counter.js
│ ├── vr-analytics.js
│ └── weapon.js
├── enemies
│ ├── enemy0.js
│ ├── enemy1.js
│ ├── enemy2.js
│ ├── enemy3.js
│ └── enemy_start.js
├── index.js
├── lib
│ ├── letterpanel.js
│ ├── poolhelper.js
│ └── utils.js
└── systems
│ ├── bullet.js
│ ├── enemy.js
│ └── explosion.js
├── vendor
├── aframe-bmfont-component.js
├── aframe-bmfont-component.min.js
├── aframe-bmfont-text-component.min.js
├── aframe-master.min.js
└── aframe-master.min.js.map
└── webpack.config.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # FIX CRLF always when developer has not set
2 | # Linux/Mac: git config --global core.autocrlf input
3 | # Windows: git config --global core.autocrlf true
4 | # Auto detect text files and perform LF normalization
5 | * text=auto
6 | * eol=lf
7 |
8 | *.js text
9 | *.html text
10 | *.npmignore text
11 | *.md text
12 | .ackrc text
13 | .gitattributes text
14 | .gitignore text
15 | .jshintrc text
16 | .nojekyll text
17 | .travis.yml text
18 | LICENSE text
19 |
20 | # Avoid creation of unnecessary big commit objects
21 | # For these files we do not want to see text diff for
22 |
23 | *.min.* binary minified
24 | *js.map* binary
25 | *.svg binary
26 | *.mtl binary
27 | *.obj binary
28 | *.jpg binary
29 | *.jpeg binary
30 | *.png binary
31 | *.wav binary
32 | *.ogg binary
33 | *.gif binary
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .cache
3 | gh-pages/
4 | node_modules
5 | npm-debug.log*
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright © 2015-2016 A-Frame authors.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A-Blast
2 |
3 | A WebVR FPS mini-game demo using [A-Frame](http://aframe.io) by [Mozilla VR](http://mozvr.com).
4 |
5 | [Read the introductory blog post.](https://blog.mozvr.com/a-blast/)
6 |
7 | [](https://aframe.io/a-blast/)
8 |
9 | ## Usage
10 |
11 | - Grab a [WebVR-enabled browser](https://webvr.info/).
12 | - Head to [https://aframe.io/a-blast/](https://aframe.io/a-blast/) and start shooting. See the [blog post](https://blog.mozvr.com/a-blast/) for more information.
13 | - You can also play in desktop using the spacebar and the mouse, and on your mobile phone too (with or without Cardboard).
14 |
15 |
16 |
17 |
18 |
19 | ## Local Development
20 |
21 | ```bash
22 | git clone git@github.com:aframevr/a-blast && cd a-blast
23 | npm install
24 | npm start
25 | ```
26 |
27 | Then, load [`http://localhost:8080`](http://localhost:8080) in your browser.
28 |
29 | We are opened to new ideas and contributions, feel free to send a pull request with your A-Blast improvements.
30 |
31 |
32 |
33 |
34 |
35 |
36 | Soundtrack by José Manuel Pérez Paredes AKA [JosSs](https://soundcloud.com/josss-1/tracks)
37 |
--------------------------------------------------------------------------------
/assets/data/waves.js:
--------------------------------------------------------------------------------
1 | var WAVES = [
2 | {
3 | name: "WAVE 1",
4 | sequences: [
5 | {
6 | subwave: "2",
7 | enemies:[
8 | {
9 | type: "enemy0",
10 | points: [[-2.779,-3.466,-8.405],[-2.717,3.011,-7.228]],
11 | movement: "single",
12 | },
13 | {
14 | type: "enemy0",
15 | points: [[1.874,-7.677,-8.328],[3.490,6.398,-7.157]],
16 | movement: "single",
17 | }
18 | ]
19 | },
20 | {
21 | subwave: "3",
22 | enemies:[
23 | {
24 | type: "enemy0",
25 | points: [[-0.000,-8.839,-10.754],[-0.000,6.865,-9.169]],
26 | movement: "single",
27 | },
28 | {
29 | type: "enemy0",
30 | points: [[-4.720,-11.803,-9.161],[-6.315,5.616,-7.575]],
31 | movement: "single",
32 | },
33 | {
34 | type: "enemy0",
35 | points: [[4.324,-14.076,-9.161],[6.315,5.616,-7.575]],
36 | movement: "single",
37 | },
38 | {
39 | type: "enemy1",
40 | points: [[-0.000,-4.957,-9.048],[0.000,2.312,-7.866]],
41 | movement: "single",
42 | }
43 | ]
44 | },
45 | {
46 | subwave: "4",
47 | enemies:[
48 | {
49 | type: "enemy0",
50 | points: [[2.532,-10.411,-9.161],[3.697,6.535,-7.575]],
51 | movement: "single",
52 | },
53 | {
54 | type: "enemy0",
55 | points: [[-2.532,-10.095,-9.161],[-3.697,6.535,-7.575]],
56 | movement: "single",
57 | },
58 | {
59 | type: "enemy0",
60 | points: [[-0.000,-4.957,-9.048],[0.000,2.312,-7.866]],
61 | movement: "single",
62 | },
63 | {
64 | type: "enemy1",
65 | points: [[5.802,-13.645,-6.388],[4.858,0.887,-5.830],[2.253,3.765,-4.991],[-2.570,3.796,-4.924],[-4.115,2.613,-5.326],[-5.146,1.339,-5.770],[-5.333,1.050,-5.870]],
66 | movement: "pingpong",
67 | },
68 | {
69 | type: "enemy1",
70 | points: [[-5.802,-21.383,-7.624],[-4.858,0.593,-7.067],[-2.253,3.471,-6.228],[2.595,3.502,-6.161],[4.080,2.453,-6.569],[5.178,1.071,-6.960],[5.333,0.757,-7.107]],
71 | movement: "pingpong",
72 | }
73 | ]
74 | },
75 | {
76 | subwave: "5",
77 | enemies:[
78 | {
79 | type: "enemy0",
80 | points: [[6.423,-4.604,-11.022],[5.502,2.845,-8.485]],
81 | movement: "single",
82 | },
83 | {
84 | type: "enemy0",
85 | points: [[-6.423,-4.604,-11.022],[-5.502,2.845,-8.485]],
86 | movement: "single",
87 | },
88 | {
89 | type: "enemy1",
90 | points: [[-3.143,-16.484,-7.948],[-2.804,1.664,-6.817],[-2.624,4.543,-6.542]],
91 | movement: "pingpong",
92 | },
93 | {
94 | type: "enemy0",
95 | points: [[0.102,-12.302,-8.235],[0.140,0.816,-9.515],[0.159,6.137,-10.021]],
96 | movement: "pingpong",
97 | },
98 | {
99 | type: "enemy1",
100 | points: [[3.143,-16.484,-7.948],[2.804,1.664,-6.817],[2.624,4.543,-6.542]],
101 | movement: "pingpong",
102 | }
103 | ]
104 | },
105 | {
106 | subwave: "6",
107 | enemies:[
108 | {
109 | type: "enemy0",
110 | points: [[7.419,-2.617,-4.583],[6.251,1.269,-5.546],[3.603,3.048,-6.824],[-4.661,2.923,-6.788],[-6.749,5.301,-5.183]],
111 | movement: "pingpong",
112 | },
113 | {
114 | type: "enemy0",
115 | points: [[-7.419,-6.477,-4.873],[-6.251,1.269,-6.389],[-3.603,3.048,-8.657],[4.661,2.923,-8.159],[6.749,5.301,-5.473]],
116 | movement: "pingpong",
117 | },
118 | {
119 | type: ["enemy1","enemy0","enemy1"],
120 | points: [[-3.570,-10.350,-9.308],[-1.735,1.652,-10.408],[2.206,2.076,-10.369],[2.617,5.974,-10.166],[-2.128,5.852,-10.231]],
121 | movement: "loop",
122 | enemyTimeOffset: -1000,
123 | }
124 | ]
125 | },
126 | {
127 | subwave: "7",
128 | enemies:[
129 | {
130 | type: "enemy1",
131 | points: [[-0.117,-2.922,-9.989],[-6.728,3.366,-7.252],[5.960,3.435,-7.280]],
132 | movement: "pingpong",
133 | },
134 | {
135 | type: "enemy1",
136 | points: [[0.051,-3.250,-10.318],[4.455,2.252,-7.790],[-3.950,2.320,-7.818]],
137 | movement: "pingpong",
138 | },
139 | {
140 | type: "enemy1",
141 | points: [[0.058,-2.013,-9.194],[4.455,4.694,-6.410],[-3.950,4.763,-6.438]],
142 | movement: "pingpong",
143 | },
144 | {
145 | type: "enemy1",
146 | points: [[-0.019,-1.414,-8.712],[-2.206,5.633,-5.545],[1.956,5.702,-5.573]],
147 | movement: "pingpong",
148 | },
149 | {
150 | type: "enemy0",
151 | points: [[-5.886,-1.106,-5.230],[-4.331,4.539,-4.951],[0.000,6.983,-4.318]],
152 | movement: "single",
153 | },
154 | {
155 | type: "enemy0",
156 | points: [[4.955,-3.852,-5.815],[3.565,0.447,-6.539],[-0.237,1.357,-7.004]],
157 | movement: "single",
158 | }
159 | ]
160 | },
161 | {
162 | subwave: "8",
163 | enemies:[
164 | {
165 | type: "enemy1",
166 | points: [[-5.898,-3.573,-4.946],[-5.920,1.011,-4.932],[-5.901,3.772,-4.958]],
167 | movement: "pingpong",
168 | },
169 | {
170 | type: "enemy1",
171 | points: [[-4.267,-4.889,-6.345],[-4.289,1.815,-6.331],[-4.270,4.556,-6.357]],
172 | movement: "pingpong",
173 | },
174 | {
175 | type: "enemy1",
176 | points: [[-2.281,-6.165,-7.163],[-2.304,2.565,-7.150],[-2.284,5.285,-7.176]],
177 | movement: "pingpong",
178 | },
179 | {
180 | type: "enemy1",
181 | points: [[-0.043,-7.572,-7.469],[-0.066,1.799,-7.455],[-0.046,4.560,-7.481]],
182 | movement: "pingpong",
183 | },
184 | {
185 | type: "enemy1",
186 | points: [[2.119,-9.295,-7.249],[2.097,1.367,-7.236],[2.116,4.108,-7.262]],
187 | movement: "pingpong",
188 | },
189 | {
190 | type: "enemy1",
191 | points: [[4.068,-11.333,-6.409],[4.046,0.575,-6.395],[4.065,3.295,-6.422]],
192 | movement: "pingpong",
193 | },
194 | {
195 | type: "enemy1",
196 | points: [[5.715,-14.491,-5.093],[5.693,1.654,-5.079],[5.712,4.500,-5.105]],
197 | movement: "pingpong",
198 | }
199 | ]
200 | },
201 | {
202 | subwave: "9",
203 | enemies:[
204 | {
205 | type: ["enemy1","enemy1","enemy1","enemy1"],
206 | points: [[-8.397,-5.214,-8.919],[-5.099,5.574,-7.011],[-0.154,2.375,-7.229],[4.780,-0.703,-7.721],[7.075,2.325,-8.069],[5.146,5.540,-8.403],[0.530,2.524,-9.188],[-4.186,-0.706,-9.842],[-6.878,2.511,-8.356]],
207 | movement: "loop",
208 | enemyTimeOffset: -800,
209 | },
210 | {
211 | type: ["enemy1","enemy1","enemy1"],
212 | points: [[9.242,-9.088,-7.545],[5.098,5.615,-5.763],[0.153,2.416,-5.982],[-4.781,-0.661,-6.473],[-7.076,2.366,-6.821],[-5.147,5.581,-7.156],[-0.531,2.565,-7.941],[4.185,-0.665,-8.594],[6.877,2.552,-7.108]],
213 | movement: "loop",
214 | enemyTimeOffset: -800,
215 | }
216 | ]
217 | },
218 | {
219 | subwave: "10",
220 | enemies:[
221 | {
222 | type: "enemy0",
223 | points: [[3.269,-4.923,-7.350],[5.260,7.431,-5.765]],
224 | movement: "single",
225 | },
226 | {
227 | type: "enemy0",
228 | points: [[-3.277,-4.923,-7.350],[-5.267,7.431,-5.765]],
229 | movement: "single",
230 | },
231 | {
232 | type: "enemy0",
233 | points: [[-1.633,-3.221,-7.997],[-2.162,5.305,-6.815]],
234 | movement: "single",
235 | },
236 | {
237 | type: "enemy0",
238 | points: [[1.532,-3.221,-7.997],[2.185,5.305,-6.815]],
239 | movement: "single",
240 | },
241 | {
242 | type: ["enemy1","enemy1","enemy1","enemy1"],
243 | points: [[4.303,-1.904,-5.736],[-0.275,-2.036,-6.847],[-4.825,-1.601,-5.221],[-4.558,1.129,-5.118],[-3.569,3.022,-5.324],[-1.982,0.774,-5.739],[-0.064,2.984,-6.016],[1.848,0.764,-5.803],[3.476,2.997,-5.457],[4.700,0.738,-5.096],[5.241,-10.892,-5.080]],
244 | movement: "loop",
245 | enemyTimeOffset: -1500,
246 | }
247 | ]
248 | },
249 | {
250 | subwave: "11",
251 | enemies:[
252 | {
253 | type: "enemy3",
254 | points: [[0.238,-9.234,-9.310],[5.180,3.034,-9.861],[-0.018,2.720,-7.559],[-5.041,3.034,-9.861]],
255 | movement: "pingpong",
256 | }
257 | ]
258 | }
259 | ]
260 | },
261 | {
262 | name: "WAVE 2",
263 | sequences: [
264 | {
265 | subwave: "2001",
266 | enemies:[
267 | {
268 | type: ["enemy1","enemy1","enemy1","enemy0"],
269 | points: [[0.102,-11.982,-9.037],[0.142,3.765,-13.133],[-1.412,2.089,-8.481],[0.152,1.404,-2.714],[0.164,5.907,-2.462],[1.545,6.252,-9.056]],
270 | movement: "loop",
271 | enemyTimeOffset: -1585,
272 | }
273 | ]
274 | },
275 | {
276 | subwave: "2002",
277 | enemies:[
278 | {
279 | type: ["enemy0","enemy1","enemy0"],
280 | points: [[0.102,-20.812,-7.970],[0.142,3.765,-10.804],[-1.941,1.304,-7.540],[-1.808,1.404,-3.581],[-1.796,5.907,-1.843],[-1.697,6.252,-6.858]],
281 | movement: "loop",
282 | enemyTimeOffset: -1300,
283 | loopStart: 2,
284 | },
285 | {
286 | type: ["enemy0","enemy1","enemy0"],
287 | points: [[0.102,-11.982,-9.037],[0.142,3.765,-10.804],[1.859,1.304,-7.540],[1.992,1.404,-3.581],[2.003,5.907,-1.843],[2.102,6.252,-6.858]],
288 | movement: "loop",
289 | enemyTimeOffset: -1300,
290 | loopStart: 2,
291 | }
292 | ]
293 | },
294 | {
295 | subwave: "2003",
296 | enemies:[
297 | {
298 | type: ["enemy0","enemy1","enemy0"],
299 | points: [[-3.635,-12.072,-5.277],[-3.807,3.628,-8.542],[-2.358,1.958,-6.590],[-1.562,1.404,-2.714],[-3.219,4.870,-2.166],[-4.926,5.662,-7.516]],
300 | movement: "loop",
301 | enemyTimeOffset: -1585,
302 | },
303 | {
304 | type: "enemy2",
305 | points: [[0.373,-11.356,-50.516],[0.142,3.079,-17.640],[0.142,2.955,-12.288],[0.142,2.785,-8.047],[0.142,2.458,-6.873],[0.142,2.326,-6.423]],
306 | movement: "single",
307 | },
308 | {
309 | type: ["enemy1","enemy0","enemy1"],
310 | points: [[3.635,-12.072,-5.277],[3.807,3.628,-8.542],[2.358,1.958,-6.590],[1.562,1.404,-2.714],[3.219,4.870,-2.166],[4.926,5.662,-7.516]],
311 | movement: "loop",
312 | enemyTimeOffset: -1585,
313 | }
314 | ]
315 | },
316 | {
317 | subwave: "2004",
318 | enemies:[
319 | {
320 | type: "enemy2",
321 | points: [[-2.878,-7.579,-5.481],[-3.763,1.667,-4.806],[1.462,2.373,-3.978],[2.949,3.076,-1.987]],
322 | movement: "pingpong",
323 | },
324 | {
325 | type: "enemy2",
326 | points: [[2.348,-11.066,-5.481],[3.763,1.667,-4.806],[-1.462,2.373,-3.978],[-2.949,3.076,-1.987]],
327 | movement: "pingpong",
328 | },
329 | {
330 | type: "enemy2",
331 | points: [[4.017,-15.603,-32.074],[3.047,-0.480,-23.429],[1.549,1.924,-7.464]],
332 | movement: "single",
333 | },
334 | {
335 | type: "enemy2",
336 | points: [[-2.487,-14.442,-19.974],[-3.672,-0.480,-16.871],[-3.028,3.002,-7.177]],
337 | movement: "single",
338 | },
339 | {
340 | type: "enemy2",
341 | points: [[-1.403,-9.224,-24.527],[-1.992,0.174,-26.051],[0.580,3.837,-5.928]],
342 | movement: "single",
343 | },
344 | {
345 | type: "enemy2",
346 | points: [[0.583,-12.435,-27.199],[0.294,-0.480,-21.448],[-1.835,3.868,-8.518]],
347 | movement: "single",
348 | }
349 | ]
350 | },
351 | {
352 | subwave: "2005",
353 | enemies:[
354 | {
355 | type: ["enemy2","enemy2","enemy2","enemy2"],
356 | points: [[4.101,-5.177,-5.577],[5.205,1.154,-4.972],[3.834,5.993,-1.522],[0.169,4.682,-3.751],[-3.488,3.536,-6.089],[-5.189,5.007,-4.362],[-3.759,6.553,-2.504],[-0.338,5.556,-5.004],[3.158,4.411,-7.553],[5.153,5.207,-4.439]],
357 | movement: "loop",
358 | enemyTimeOffset: -800,
359 | loopStart: 2,
360 | },
361 | {
362 | type: "enemy0",
363 | points: [[-0.063,-6.742,-7.931],[-5.342,2.732,-7.897],[4.737,2.818,-7.933]],
364 | movement: "pingpong",
365 | },
366 | {
367 | type: "enemy0",
368 | points: [[-0.525,-7.183,-9.356],[5.342,1.205,-9.389],[-4.737,1.291,-9.424]],
369 | movement: "pingpong",
370 | }
371 | ]
372 | },
373 | {
374 | subwave: "2006",
375 | enemies:[
376 | {
377 | type: ["enemy0","enemy0"],
378 | points: [[-4.493,-1.832,-6.394],[6.629,-1.590,-5.740],[6.279,1.103,-4.304],[4.222,3.393,-3.757],[0.087,4.573,-4.070],[-4.130,3.392,-3.916],[-6.475,0.219,-4.915],[-6.310,-16.689,-5.513]],
379 | movement: "loop",
380 | enemyTimeOffset: -1100,
381 | },
382 | {
383 | type: ["enemy0","enemy1","enemy2"],
384 | points: [[4.541,-1.901,-6.427],[-6.699,-1.616,-5.719],[-6.636,1.504,-2.983],[-4.450,4.425,-1.980],[-0.088,5.989,-2.373],[4.322,4.430,-2.141],[6.786,0.236,-3.210],[6.295,-5.078,-5.031]],
385 | movement: "loop",
386 | enemyTimeOffset: -1100,
387 | },
388 | {
389 | type: ["enemy1","enemy1"],
390 | points: [[4.193,-1.096,-6.432],[-6.186,-0.905,-5.777],[-5.680,1.241,-4.993],[-3.541,2.773,-5.370],[-0.081,3.559,-6.116],[3.422,2.766,-5.528],[5.716,0.546,-5.292],[6.081,-10.693,-5.641]],
391 | movement: "loop",
392 | enemyTimeOffset: -1100,
393 | },
394 | {
395 | type: ["enemy0","enemy1","enemy2"],
396 | points: [[-3.823,-1.762,-6.705],[4.949,-1.243,-5.718],[3.229,1.704,-6.223],[0.074,2.425,-7.033],[-3.120,1.684,-6.379],[-5.116,-0.508,-5.958],[-5.312,-7.883,-5.939]],
397 | movement: "loop",
398 | enemyTimeOffset: -1100,
399 | }
400 | ]
401 | },
402 | {
403 | subwave: "2007",
404 | enemies:[
405 | {
406 | type: ["enemy3","enemy3"],
407 | points: [[0.000,-11.755,-24.508],[-0.154,0.110,-19.769],[-7.344,0.112,-3.727],[-7.629,-5.338,-2.696],[-4.647,-6.852,-19.380],[-3.776,4.304,-7.531],[-1.544,5.107,-3.990]],
408 | movement: "loop",
409 | enemyTimeOffset: -3000,
410 | loopStart: 2,
411 | },
412 | {
413 | type: ["enemy3","enemy3"],
414 | points: [[-0.000,-11.993,-25.733],[0.154,0.110,-23.081],[7.344,0.112,-3.727],[7.629,-5.338,-2.696],[4.647,-6.852,-19.287],[3.776,4.304,-7.531],[1.544,5.107,-3.990]],
415 | movement: "loop",
416 | enemyTimeOffset: -3000,
417 | loopStart: 2,
418 | }
419 | ]
420 | }
421 | ]
422 | }
423 | ];
424 |
--------------------------------------------------------------------------------
/assets/fonts/mozillavr.fnt:
--------------------------------------------------------------------------------
1 | info face="Y:\mozvr\work\ashooter\font\font" size=32 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
2 | common lineHeight=90 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0
3 | page id=0 file="Y:\mozvr\work\ashooter\font\font.png"
4 | chars count=86
5 | char id=97 x=376 y=51 width=42 height=54 xoffset=0 yoffset=38 xadvance=47 page=0 chnl=0 letter="a"
6 | char id=98 x=376 y=106 width=42 height=71 xoffset=0 yoffset=18 xadvance=47 page=0 chnl=0 letter="b"
7 | char id=99 x=419 y=0 width=39 height=52 xoffset=0 yoffset=38 xadvance=44 page=0 chnl=0 letter="c"
8 | char id=100 x=289 y=0 width=45 height=73 xoffset=0 yoffset=16 xadvance=50 page=0 chnl=0 letter="d"
9 | char id=101 x=376 y=302 width=42 height=52 xoffset=0 yoffset=37 xadvance=47 page=0 chnl=0 letter="e"
10 | char id=102 x=419 y=53 width=38 height=72 xoffset=0 yoffset=16 xadvance=43 page=0 chnl=0 letter="f"
11 | char id=103 x=126 y=278 width=54 height=72 xoffset=0 yoffset=35 xadvance=59 page=0 chnl=0 letter="g"
12 | char id=104 x=376 y=178 width=42 height=71 xoffset=0 yoffset=17 xadvance=47 page=0 chnl=0 letter="h"
13 | char id=105 x=510 y=175 width=18 height=70 xoffset=0 yoffset=18 xadvance=23 page=0 chnl=0 letter="i"
14 | char id=106 x=487 y=266 width=27 height=92 xoffset=0 yoffset=18 xadvance=32 page=0 chnl=0 letter="j"
15 | char id=107 x=285 y=182 width=45 height=76 xoffset=0 yoffset=14 xadvance=50 page=0 chnl=0 letter="k"
16 | char id=108 x=459 y=435 width=22 height=75 xoffset=0 yoffset=15 xadvance=27 page=0 chnl=0 letter="l"
17 | char id=109 x=66 y=333 width=59 height=51 xoffset=0 yoffset=36 xadvance=64 page=0 chnl=0 letter="m"
18 | char id=110 x=335 y=0 width=43 height=50 xoffset=0 yoffset=38 xadvance=48 page=0 chnl=0 letter="n"
19 | char id=111 x=332 y=237 width=43 height=52 xoffset=0 yoffset=38 xadvance=48 page=0 chnl=0 letter="o"
20 | char id=112 x=331 y=401 width=44 height=73 xoffset=0 yoffset=39 xadvance=49 page=0 chnl=0 letter="p"
21 | char id=113 x=332 y=74 width=43 height=71 xoffset=0 yoffset=39 xadvance=48 page=0 chnl=0 letter="q"
22 | char id=114 x=379 y=0 width=30 height=50 xoffset=0 yoffset=39 xadvance=35 page=0 chnl=0 letter="r"
23 | char id=115 x=376 y=250 width=42 height=51 xoffset=0 yoffset=39 xadvance=47 page=0 chnl=0 letter="s"
24 | char id=116 x=457 y=126 width=32 height=65 xoffset=0 yoffset=25 xadvance=37 page=0 chnl=0 letter="t"
25 | char id=117 x=376 y=422 width=41 height=53 xoffset=0 yoffset=38 xadvance=46 page=0 chnl=0 letter="u"
26 | char id=118 x=69 y=280 width=47 height=51 xoffset=0 yoffset=38 xadvance=52 page=0 chnl=0 letter="v"
27 | char id=119 x=0 y=280 width=68 height=52 xoffset=0 yoffset=38 xadvance=73 page=0 chnl=0 letter="w"
28 | char id=120 x=125 y=449 width=53 height=55 xoffset=0 yoffset=37 xadvance=58 page=0 chnl=0 letter="x"
29 | char id=121 x=234 y=204 width=50 height=74 xoffset=0 yoffset=37 xadvance=55 page=0 chnl=0 letter="y"
30 | char id=122 x=418 y=422 width=40 height=51 xoffset=0 yoffset=38 xadvance=45 page=0 chnl=0 letter="z"
31 | char id=48 x=286 y=74 width=45 height=51 xoffset=0 yoffset=40 xadvance=50 page=0 chnl=0 letter="0"
32 | char id=49 x=453 y=288 width=33 height=51 xoffset=0 yoffset=39 xadvance=38 page=0 chnl=0 letter="1"
33 | char id=50 x=285 y=130 width=46 height=51 xoffset=0 yoffset=39 xadvance=51 page=0 chnl=0 letter="2"
34 | char id=51 x=183 y=206 width=47 height=55 xoffset=0 yoffset=37 xadvance=52 page=0 chnl=0 letter="3"
35 | char id=52 x=284 y=447 width=46 height=55 xoffset=0 yoffset=35 xadvance=51 page=0 chnl=0 letter="4"
36 | char id=53 x=331 y=182 width=44 height=54 xoffset=0 yoffset=35 xadvance=49 page=0 chnl=0 letter="5"
37 | char id=54 x=284 y=335 width=46 height=56 xoffset=0 yoffset=33 xadvance=51 page=0 chnl=0 letter="6"
38 | char id=55 x=241 y=0 width=47 height=59 xoffset=0 yoffset=36 xadvance=52 page=0 chnl=0 letter="7"
39 | char id=56 x=284 y=392 width=46 height=54 xoffset=0 yoffset=34 xadvance=51 page=0 chnl=0 letter="8"
40 | char id=57 x=284 y=279 width=47 height=55 xoffset=0 yoffset=35 xadvance=52 page=0 chnl=0 letter="9"
41 | char id=65 x=71 y=141 width=57 height=64 xoffset=0 yoffset=26 xadvance=62 page=0 chnl=0 letter="A"
42 | char id=66 x=191 y=0 width=49 height=63 xoffset=0 yoffset=26 xadvance=54 page=0 chnl=0 letter="B"
43 | char id=67 x=234 y=438 width=49 height=66 xoffset=0 yoffset=24 xadvance=54 page=0 chnl=0 letter="C"
44 | char id=68 x=138 y=0 width=52 height=63 xoffset=0 yoffset=26 xadvance=57 page=0 chnl=0 letter="D"
45 | char id=69 x=331 y=335 width=44 height=65 xoffset=0 yoffset=25 xadvance=49 page=0 chnl=0 letter="E"
46 | char id=70 x=418 y=355 width=40 height=66 xoffset=0 yoffset=25 xadvance=45 page=0 chnl=0 letter="F"
47 | char id=71 x=132 y=70 width=52 height=65 xoffset=0 yoffset=25 xadvance=57 page=0 chnl=0 letter="G"
48 | char id=72 x=181 y=267 width=52 height=67 xoffset=0 yoffset=23 xadvance=57 page=0 chnl=0 letter="H"
49 | char id=73 x=490 y=175 width=19 height=65 xoffset=0 yoffset=24 xadvance=24 page=0 chnl=0 letter="I"
50 | char id=74 x=459 y=0 width=30 height=79 xoffset=0 yoffset=24 xadvance=35 page=0 chnl=0 letter="J"
51 | char id=75 x=74 y=70 width=57 height=65 xoffset=0 yoffset=25 xadvance=62 page=0 chnl=0 letter="K"
52 | char id=76 x=376 y=355 width=41 height=66 xoffset=0 yoffset=24 xadvance=46 page=0 chnl=0 letter="L"
53 | char id=77 x=0 y=211 width=69 height=68 xoffset=0 yoffset=24 xadvance=74 page=0 chnl=0 letter="M"
54 | char id=78 x=185 y=64 width=51 height=67 xoffset=0 yoffset=24 xadvance=56 page=0 chnl=0 letter="N"
55 | char id=79 x=125 y=385 width=56 height=63 xoffset=0 yoffset=26 xadvance=61 page=0 chnl=0 letter="O"
56 | char id=80 x=236 y=132 width=48 height=65 xoffset=0 yoffset=26 xadvance=53 page=0 chnl=0 letter="P"
57 | char id=81 x=63 y=400 width=61 height=75 xoffset=0 yoffset=25 xadvance=66 page=0 chnl=0 letter="Q"
58 | char id=82 x=182 y=404 width=51 height=65 xoffset=0 yoffset=26 xadvance=56 page=0 chnl=0 letter="R"
59 | char id=83 x=70 y=211 width=58 height=66 xoffset=0 yoffset=24 xadvance=63 page=0 chnl=0 letter="S"
60 | char id=84 x=182 y=335 width=51 height=68 xoffset=0 yoffset=23 xadvance=56 page=0 chnl=0 letter="T"
61 | char id=85 x=184 y=136 width=51 height=67 xoffset=0 yoffset=23 xadvance=56 page=0 chnl=0 letter="U"
62 | char id=86 x=80 y=0 width=57 height=69 xoffset=0 yoffset=22 xadvance=62 page=0 chnl=0 letter="V"
63 | char id=87 x=0 y=0 width=79 height=69 xoffset=0 yoffset=26 xadvance=84 page=0 chnl=0 letter="W"
64 | char id=88 x=0 y=400 width=62 height=67 xoffset=0 yoffset=26 xadvance=67 page=0 chnl=0 letter="X"
65 | char id=89 x=129 y=136 width=54 height=69 xoffset=0 yoffset=26 xadvance=59 page=0 chnl=0 letter="Y"
66 | char id=90 x=237 y=64 width=48 height=65 xoffset=0 yoffset=28 xadvance=53 page=0 chnl=0 letter="Z"
67 | char id=46 x=94 y=476 width=16 height=19 xoffset=0 yoffset=71 xadvance=21 page=0 chnl=0 letter="."
68 | char id=44 x=75 y=476 width=18 height=29 xoffset=0 yoffset=72 xadvance=23 page=0 chnl=0 letter=","
69 | char id=58 x=482 y=454 width=20 height=46 xoffset=0 yoffset=44 xadvance=25 page=0 chnl=0 letter=":"
70 | char id=64 x=0 y=141 width=70 height=69 xoffset=0 yoffset=19 xadvance=75 page=0 chnl=0 letter="@"
71 | char id=35 x=234 y=279 width=49 height=70 xoffset=0 yoffset=20 xadvance=54 page=0 chnl=0 letter="#"
72 | char id=60 x=419 y=288 width=33 height=65 xoffset=0 yoffset=27 xadvance=38 page=0 chnl=0 letter="<"
73 | char id=62 x=456 y=198 width=33 height=67 xoffset=0 yoffset=25 xadvance=38 page=0 chnl=0 letter=">"
74 | char id=40 x=487 y=359 width=26 height=94 xoffset=0 yoffset=13 xadvance=31 page=0 chnl=0 letter="("
75 | char id=41 x=459 y=340 width=27 height=94 xoffset=0 yoffset=12 xadvance=32 page=0 chnl=0 letter=")"
76 | char id=91 x=490 y=89 width=23 height=85 xoffset=0 yoffset=17 xadvance=28 page=0 chnl=0 letter="["
77 | char id=93 x=490 y=0 width=23 height=88 xoffset=0 yoffset=16 xadvance=28 page=0 chnl=0 letter="]"
78 | char id=63 x=419 y=126 width=37 height=71 xoffset=0 yoffset=20 xadvance=42 page=0 chnl=0 letter="?"
79 | char id=33 x=514 y=0 width=17 height=75 xoffset=0 yoffset=15 xadvance=22 page=0 chnl=0 letter="!"
80 | char id=45 x=47 y=480 width=27 height=18 xoffset=0 yoffset=53 xadvance=32 page=0 chnl=0 letter="-"
81 | char id=95 x=0 y=468 width=53 height=11 xoffset=0 yoffset=90 xadvance=58 page=0 chnl=0 letter="_"
82 | char id=126 x=0 y=480 width=46 height=24 xoffset=0 yoffset=46 xadvance=51 page=0 chnl=0 letter="~"
83 | char id=36 x=234 y=350 width=49 height=87 xoffset=0 yoffset=12 xadvance=54 page=0 chnl=0 letter="$"
84 | char id=37 x=0 y=70 width=73 height=70 xoffset=0 yoffset=20 xadvance=78 page=0 chnl=0 letter="%"
85 | char id=38 x=0 y=333 width=65 height=66 xoffset=0 yoffset=26 xadvance=70 page=0 chnl=0 letter="&"
86 | char id=47 x=419 y=198 width=36 height=89 xoffset=0 yoffset=14 xadvance=41 page=0 chnl=0 letter="/"
87 | char id=42 x=332 y=290 width=41 height=43 xoffset=0 yoffset=20 xadvance=46 page=0 chnl=0 letter="*"
88 | char id=43 x=129 y=206 width=53 height=60 xoffset=0 yoffset=29 xadvance=58 page=0 chnl=0 letter="+"
89 | char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=30 page=0 chnl=0 letter=" "
90 | char id=9 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=240 page=0 chnl=0 letter=" "
91 |
92 | kernings count=0
93 |
--------------------------------------------------------------------------------
/assets/fonts/mozillavr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/fonts/mozillavr.png
--------------------------------------------------------------------------------
/assets/images/damage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/damage.png
--------------------------------------------------------------------------------
/assets/images/decal0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/decal0.png
--------------------------------------------------------------------------------
/assets/images/decal1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/decal1.png
--------------------------------------------------------------------------------
/assets/images/decal2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/decal2.png
--------------------------------------------------------------------------------
/assets/images/enemy_diff.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_diff.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_emissive0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_emissive0.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_emissive1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_emissive1.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_emissive2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_emissive2.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_emissive3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_emissive3.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_emissive_template.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_emissive_template.jpg
--------------------------------------------------------------------------------
/assets/images/enemy_spec.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/enemy_spec.jpg
--------------------------------------------------------------------------------
/assets/images/face_guide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/face_guide.png
--------------------------------------------------------------------------------
/assets/images/font.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/font.png
--------------------------------------------------------------------------------
/assets/images/fx1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx1.png
--------------------------------------------------------------------------------
/assets/images/fx2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx2.png
--------------------------------------------------------------------------------
/assets/images/fx3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx3.png
--------------------------------------------------------------------------------
/assets/images/fx4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx4.png
--------------------------------------------------------------------------------
/assets/images/fx5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx5.png
--------------------------------------------------------------------------------
/assets/images/fx6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx6.png
--------------------------------------------------------------------------------
/assets/images/fx7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx7.png
--------------------------------------------------------------------------------
/assets/images/fx8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/fx8.png
--------------------------------------------------------------------------------
/assets/images/grad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/grad.png
--------------------------------------------------------------------------------
/assets/images/gun_diff.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/gun_diff.jpg
--------------------------------------------------------------------------------
/assets/images/gun_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/gun_normal.png
--------------------------------------------------------------------------------
/assets/images/gun_spec.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/gun_spec.jpg
--------------------------------------------------------------------------------
/assets/images/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/icon-128.png
--------------------------------------------------------------------------------
/assets/images/icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/icon-256.png
--------------------------------------------------------------------------------
/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/icon.png
--------------------------------------------------------------------------------
/assets/images/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/menu.png
--------------------------------------------------------------------------------
/assets/images/set_diff.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/set_diff.jpg
--------------------------------------------------------------------------------
/assets/images/sky.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/images/sky.jpg
--------------------------------------------------------------------------------
/assets/models/restart.json:
--------------------------------------------------------------------------------
1 | {
2 | "geometries":[{
3 | "data":{
4 | "name":"bgGeometry",
5 | "vertices":[-0.7479,-0.0557,-0.2918,-0.8309,-0.0557,0.3222,-0.6788,0.0557,-0.2648,-0.7542,0.0557,0.2925,0.7783,-0.0557,-0.2697,0.8005,-0.0557,0.2392,0.7064,0.0557,-0.2447,0.7265,0.0557,0.2171,-0.7479,-0.0557,-0.2918,-0.8309,-0.0557,0.3222,-0.7542,0.0557,0.2925,-0.7542,0.0557,0.2925,-0.6788,0.0557,-0.2648,-0.6788,0.0557,-0.2648,0.7265,0.0557,0.2171,0.7265,0.0557,0.2171,0.7064,0.0557,-0.2447,0.7064,0.0557,-0.2447,0.8005,-0.0557,0.2392,0.7783,-0.0557,-0.2697],
6 | "normals":[-0.8321,-0.1125,-0.543,0,-0,-1,0.836,-0.0364,-0.5475,0.0141,-0.9738,-0.2268,0.0495,0.9729,-0.2258],
7 | "metadata":{
8 | "generator":"io_three",
9 | "vertices":20,
10 | "version":3,
11 | "normals":5,
12 | "uvs":0,
13 | "faces":5
14 | },
15 | "uvs":[],
16 | "faces":[33,9,10,12,8,0,0,0,0,33,11,14,16,13,1,1,1,1,33,15,18,19,17,2,2,2,2,33,0,2,6,4,3,3,3,3,33,5,7,3,1,4,4,4,4]
17 | },
18 | "type":"Geometry",
19 | "uuid":"3C994563-C5DA-4D55-820D-E01948F9AA1C"
20 | },{
21 | "data":{
22 | "name":"restart.001Geometry",
23 | "vertices":[0.412,0.1127,0.05,0.5907,0.1127,0.138,0.5956,0.1127,-0.1394,0.5477,0.1127,-0.1653,0.4745,0.1127,-0.0017,0.3612,0.1127,-0.1209,0.41,0.1127,-0.1336,0.5009,0.1127,-0.0203,0.5526,0.1127,-0.0613,0.2186,0.1127,-0.1028,0.2557,0.1127,-0.1316,0.3641,0.1127,-0.0789,0.371,0.1127,0.0833,0.2293,0.1127,0.1516,0.2401,0.1127,0.1047,0.328,0.1127,0.0686,0.327,0.1127,0.0159,0.2733,0.1127,0.0334,0.285,0.1127,-0.0105,0.3251,0.1127,-0.0203,0.3231,0.1127,-0.0613,0.0657,0.1127,0.135,0.0731,0.1127,0.0881,0.1522,0.1127,0.051,0.032,0.1127,-0.0896,0.2157,0.1127,-0.0847,0.2362,0.1127,-0.0447,0.1053,0.1127,-0.0574,0.1961,0.1127,0.0588,0.0496,0.1127,0.072,0.0154,0.1127,0.05,-0.0305,0.1127,0.0647,-0.0315,0.1127,-0.1258,-0.0676,0.1127,-0.0974,-0.0627,0.1127,0.0725,-0.1428,0.1127,0.0979,-0.1672,0.1127,0.1575,-0.1228,0.1127,-0.1355,-0.091,0.1127,-0.1072,-0.1662,0.1127,0.0764,-0.2024,0.1127,0.0667,-0.2903,0.1127,-0.158,-0.1408,0.1127,-0.0916,-0.3259,0.1127,-0.0823,-0.2825,0.1127,-0.0574,-0.2932,0.1127,0.1653,-0.4438,0.1127,0.0842,-0.3821,0.1127,0.0393,-0.4505,0.1127,-0.0926,-0.3919,0.1127,-0.0799,-0.3518,0.1127,0.0207,-0.3245,0.1127,0.0002,-0.4475,0.1127,0.0544,-0.4739,0.1127,0.0315,-0.5179,0.1127,0.052,-0.5169,0.1127,-0.117,-0.5599,0.1127,-0.1433,-0.5481,0.1127,0.0618,-0.6185,0.1127,0.093,-0.6312,0.1127,0.1516,-0.1901,0.1127,0.0345,0.5547,0.1127,0.0727,0.5528,0.1127,-0.0177,0.4535,0.1127,0.0474,-0.152,0.1127,-0.0649,-0.2245,0.1127,-0.0885,-0.3962,0.1127,0.0808,-0.3238,0.1127,0.0377,-0.3254,0.1127,0.1199],
24 | "normals":[0,-0,-1,0,0,-1,-0,0,-1,-0,-0,-1,0,0,-1,-0,-0,-1,0,0,-1,0,-0.0001,-1,-0,0,-1,0,-0,-1,-0,-0,-1,-0,-0,-1,-0,-0,-1,0,0,-1,0,0,-1,-0,0,-1],
25 | "metadata":{
26 | "generator":"io_three",
27 | "vertices":69,
28 | "version":3,
29 | "normals":16,
30 | "uvs":0,
31 | "faces":61
32 | },
33 | "uvs":[],
34 | "faces":[32,0,1,61,0,0,0,32,19,16,11,1,1,1,32,27,24,23,2,2,2,32,36,29,34,3,3,3,32,42,65,64,4,4,4,32,47,66,67,5,5,5,32,59,52,57,3,3,3,32,64,39,38,1,1,1,32,4,63,62,6,6,6,32,68,46,45,2,2,2,32,63,0,61,7,7,7,32,39,60,40,2,2,2,32,40,65,41,1,1,1,32,19,18,16,1,1,1,32,10,9,20,1,1,1,32,11,10,20,2,2,2,32,18,17,16,1,1,1,32,11,20,19,1,1,1,32,14,13,15,4,4,4,32,15,13,12,1,1,1,32,12,11,16,8,8,8,32,16,15,12,1,1,1,32,22,21,23,1,1,1,32,27,26,25,0,0,0,32,23,21,28,1,1,1,32,27,25,24,0,0,0,32,23,28,27,0,0,0,32,30,31,29,1,1,1,32,36,34,35,3,3,3,32,34,31,33,9,9,9,32,31,34,29,4,4,4,32,33,31,32,0,0,0,32,38,37,42,2,2,2,32,42,41,65,2,2,2,32,64,38,42,3,3,3,32,44,43,51,8,8,8,32,50,49,47,2,2,2,32,67,44,51,10,10,10,32,49,48,47,8,8,8,32,67,51,50,11,11,11,32,46,66,47,1,1,1,32,50,47,67,12,12,12,32,53,54,52,1,1,1,32,59,57,58,3,3,3,32,57,55,56,0,0,0,32,54,57,52,1,1,1,32,57,54,55,4,4,4,32,64,60,39,1,1,1,32,61,1,62,0,0,0,32,2,3,8,8,8,8,32,7,6,4,2,2,2,32,1,2,62,3,3,3,32,6,5,4,1,1,1,32,62,2,8,3,3,3,32,0,63,4,13,13,13,32,7,4,62,1,1,1,32,62,8,7,2,2,2,32,45,44,67,14,14,14,32,68,66,46,2,2,2,32,45,67,68,15,15,15,32,40,60,65,9,9,9]
35 | },
36 | "type":"Geometry",
37 | "uuid":"7FA3E1DB-BBED-44D3-ACBA-7FE2D6D58928"
38 | }],
39 | "materials":[{
40 | "name":"mat-restart",
41 | "color":16777215,
42 | "uuid":"6CF285F3-D567-4695-8019-F97C0A4B5A9C",
43 | "depthTest":true,
44 | "blending":"NormalBlending",
45 | "type":"MeshBasicMaterial",
46 | "depthWrite":true
47 | },{
48 | "name":"mat-restart-bg",
49 | "color":10616832,
50 | "uuid":"415B601E-B130-4152-9FBA-33A7E6B59476",
51 | "vertexColors":0,
52 | "depthTest":true,
53 | "emissive":6356992,
54 | "blending":"NormalBlending",
55 | "type":"MeshLambertMaterial",
56 | "depthWrite":true
57 | }],
58 | "textures":[],
59 | "images":[],
60 | "metadata":{
61 | "type":"Object",
62 | "generator":"io_three",
63 | "sourceFile":"restart.blend",
64 | "version":4.4
65 | },
66 | "object":{
67 | "type":"Scene",
68 | "uuid":"3E5C22DE-2FF1-49AE-93EB-6C0E7F20C273",
69 | "children":[{
70 | "name":"bg",
71 | "uuid":"95058BEF-8418-4044-A5AD-8A5EFD52155C",
72 | "matrix":[-1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1],
73 | "visible":true,
74 | "type":"Mesh",
75 | "material":"415B601E-B130-4152-9FBA-33A7E6B59476",
76 | "castShadow":true,
77 | "receiveShadow":true,
78 | "geometry":"3C994563-C5DA-4D55-820D-E01948F9AA1C"
79 | },{
80 | "name":"restart",
81 | "uuid":"B7595896-149A-4985-ABE9-D4282F0E9A23",
82 | "matrix":[-1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1],
83 | "visible":true,
84 | "type":"Mesh",
85 | "material":"6CF285F3-D567-4695-8019-F97C0A4B5A9C",
86 | "castShadow":true,
87 | "receiveShadow":true,
88 | "geometry":"7FA3E1DB-BBED-44D3-ACBA-7FE2D6D58928"
89 | }],
90 | "matrix":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]
91 | },
92 | "animations":[{
93 | "name":"default",
94 | "fps":24,
95 | "tracks":[]
96 | }]
97 | }
--------------------------------------------------------------------------------
/assets/models/ring.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata":{
3 | "type":"Geometry",
4 | "materials":1,
5 | "generator":"io_three",
6 | "normals":91,
7 | "vertices":92,
8 | "faces":46,
9 | "version":3
10 | },
11 | "materials":[{
12 | "wireframe":false,
13 | "colorEmissive":[0.04,0.05,0.06],
14 | "depthWrite":true,
15 | "DbgColor":15658734,
16 | "blending":"NormalBlending",
17 | "shading":"lambert",
18 | "transparent":false,
19 | "colorDiffuse":[0.02,0.03,0.04],
20 | "depthTest":true,
21 | "visible":true,
22 | "DbgIndex":0,
23 | "opacity":1,
24 | "DbgName":"mat-background"
25 | }],
26 | "normals":[0.906919,0.421277,-0,0.977508,0.210761,-0,0.974914,0.22251,-0,0.89996,0.435926,-0,0.989227,-0.146214,-0,0.989196,-0.146519,-0,0.965575,-0.260079,-0,0.964873,-0.262642,-0,0.617908,0.786218,-0,0.780572,0.625019,-0,0.771355,0.636372,-0,0.608783,0.793329,-0,0.999817,0.017701,-0,0.999695,0.024323,-0,0.275216,0.961364,-0,0.445631,0.895199,-0,0.439436,0.898251,-0,0.271676,0.962371,-0,-0.149846,0.988678,-0,0.086612,0.996216,-0,0.086337,0.996246,-0,-0.145054,0.98941,-0,-0.612354,0.790551,-0,-0.411786,0.911252,-0,-0.402875,0.91522,-0,-0.605243,0.796014,-0,-0.719352,0.694601,-0,-0.708914,0.705283,-0,-0.706442,0.707755,-0,-0.720695,0.693228,-0,-0.604236,0.796777,-0,-0.68099,0.732261,-0,-0.684469,0.729026,-0,-0.610309,0.792138,-0,-0.383251,0.923612,-0,-0.498825,0.866695,-0,-0.504227,0.863552,-0,-0.386456,0.9223,-0,-0.457045,0.889431,-0,-0.3408,0.940123,-0,-0.462722,0.886471,-0,-0.833338,0.552751,-0,-0.65685,0.75399,-0,-0.667562,0.74453,-0,-0.843074,0.537736,-0,-0.990143,0.139958,-0,-0.942015,0.335551,-0,-0.946959,0.321268,-0,-0.992126,0.125034,-0,-0.964934,-0.262429,-0,-0.998535,-0.053591,-0,-0.997589,-0.069185,-0,-0.961058,-0.276254,-0,-0.745201,-0.666829,-0,-0.878964,-0.476852,-0,-0.875027,-0.484024,-0,-0.74514,-0.66689,-0,-0.398755,-0.91702,-0,-0.585284,-0.810785,-0,-0.590838,-0.806757,-0,-0.412793,-0.910794,-0,-0.049318,-0.998779,-0,-0.202673,-0.979217,-0,-0.219642,-0.975555,-0,-0.061525,-0.998077,-0,0.387829,-0.92172,-0,0.133946,-0.990966,-0,0.116123,-0.993225,-0,0.376293,-0.926481,-0,0.72927,-0.684194,-0,0.601825,-0.798608,-0,0.601947,-0.798517,-0,0.733085,-0.680105,-0,0.711173,-0.702994,-0,0.771844,-0.635792,-0,0.771111,-0.636677,-0,0.705313,-0.708884,-0,0.381909,-0.924161,-0,0.567309,-0.823481,-0,0.563707,-0.825953,-0,0.38319,-0.923643,-0,0.361034,-0.932524,-0,0.273507,-0.961852,-0,0.273934,-0.96173,-0,0.353252,-0.935514,-0,0.779534,-0.626331,-0,0.562365,-0.826868,-0,0.550707,-0.834681,-0,0.766839,-0.641804,-0,0.917142,-0.398541,-0,0.912534,-0.408948,-0],
27 | "vertices":[-49.1278,5.9901,-8.19676,-49.1278,-3.23673,-8.19676,-48.8282,-3.92736,-20.5838,-48.8282,5.29948,-20.5838,-43.2105,3.92764,-32.4147,-43.2105,-5.29919,-32.4147,-32.184,2.78468,-40.9277,-32.184,-6.44215,-40.9277,-18.7384,2.3275,-44.7869,-18.7384,-6.89934,-44.7869,-7.57594,-6.59455,-43.2456,-7.57594,2.63229,-43.2456,0.761053,3.39426,-36.7947,0.761053,-5.83258,-36.7947,9.61601,4.61282,-27.6203,9.61601,-4.61402,-27.6203,20.2215,-3.31942,-19.4946,20.2215,5.90742,-19.4946,32.0263,6.44064,-14.5686,32.0263,-2.78619,-14.5686,42.6689,5.90742,-9.0906,42.6689,-3.31942,-9.0906,49.3771,4.61342,1.37066,49.3771,-4.61342,1.37066,51.2282,-6.36321,15.3264,51.2282,2.86363,15.3264,47.5109,1.46384,28.7035,47.5109,-7.76299,28.7035,38.1583,1.46384,39.1111,38.1583,-7.76299,39.1111,26.2204,2.86363,44.4987,26.2204,-6.3632,44.4987,15.1521,-4.68584,45.1399,15.1521,4.54099,45.1399,5.25695,5.23177,40.9787,5.25695,-3.99506,40.9787,-3.66566,4.39127,31.3522,-3.66566,-4.83557,31.3522,-13.3944,3.3201,21.5639,-13.3944,-5.90674,21.5639,-26.4148,-5.75458,16.2221,-26.4148,3.47225,16.2221,-39.0371,4.38745,11.473,-39.0371,-4.83938,11.473,-46.1327,5.45847,2.88717,-46.1327,-3.76837,2.88717,-47.8591,5.85948,-2.51836,-47.8591,-3.36736,-2.51836,-49.5729,5.81517,-14.2009,-49.5729,-3.41167,-14.2009,-46.7541,4.61349,-26.8276,-46.7541,-4.61335,-26.8276,-38.3147,3.29901,-37.1723,-38.3147,-5.92782,-37.1723,-25.4486,2.44179,-43.5196,-25.4486,-6.78504,-43.5196,-12.599,2.40369,-44.7041,-12.599,-6.82314,-44.7041,-3.25921,2.97517,-40.5597,-3.25921,-6.25166,-40.5597,4.91081,3.92749,-32.3489,4.91081,-5.29934,-32.3489,14.7588,5.31716,-23.1539,14.7588,-3.90968,-23.1539,25.9839,6.30734,-16.6352,25.9839,-2.9195,-16.6352,37.7781,6.30734,-12.364,37.7781,-2.9195,-12.364,46.576,5.31731,-4.5714,46.576,-3.90953,-4.5714,50.9639,3.79558,8.18634,50.9639,-5.43125,8.18634,50.1004,2.01918,22.3219,50.1004,-7.20766,22.3219,43.5126,1.25845,34.3428,43.5126,-7.96838,34.3428,32.1576,2.01918,42.6244,32.1576,-7.20766,42.6244,20.5005,3.77748,45.1864,20.5005,-5.44936,45.1864,10.0969,5.05786,43.8929,10.0969,-4.16898,43.8929,0.660103,5.02286,36.6982,0.660103,-4.20397,36.6982,-8.19295,3.70201,25.9657,-8.19295,-5.52483,25.9657,-19.4187,3.24402,18.2737,-19.4187,-5.98282,18.2737,-33.3113,3.89125,14.3187,-33.3113,-5.33559,14.3187,-43.3812,4.92261,7.66818,-43.3812,-4.30422,7.66818],
28 | "faces":[35,50,3,2,51,0,0,1,2,3,35,1,0,46,47,0,4,5,6,7,35,52,4,5,53,0,8,9,10,11,35,2,3,48,49,0,2,1,12,13,35,54,6,7,55,0,14,15,16,17,35,5,4,50,51,0,10,9,0,3,35,56,8,9,57,0,18,19,20,21,35,7,6,52,53,0,16,15,8,11,35,58,11,10,59,0,22,23,24,25,35,9,8,54,55,0,20,19,14,17,35,60,12,13,61,0,26,27,28,29,35,10,11,56,57,0,24,23,18,21,35,62,14,15,63,0,30,31,32,33,35,13,12,58,59,0,28,27,22,25,35,64,17,16,65,0,34,35,36,37,35,15,14,60,61,0,32,31,26,29,35,66,18,19,67,0,38,39,39,40,35,16,17,62,63,0,36,35,30,33,35,68,20,21,69,0,41,42,43,44,35,19,18,64,65,0,39,39,34,37,35,70,22,23,71,0,45,46,47,48,35,21,20,66,67,0,43,42,38,40,35,72,25,24,73,0,49,50,51,52,35,23,22,68,69,0,47,46,41,44,35,74,26,27,75,0,53,54,55,56,35,24,25,70,71,0,51,50,45,48,35,76,28,29,77,0,57,58,59,60,35,27,26,72,73,0,55,54,49,52,35,78,30,31,79,0,61,62,63,64,35,29,28,74,75,0,59,58,53,56,35,80,33,32,81,0,65,66,67,68,35,31,30,76,77,0,63,62,57,60,35,82,34,35,83,0,69,70,71,72,35,32,33,78,79,0,67,66,61,64,35,84,36,37,85,0,73,74,75,76,35,35,34,80,81,0,71,70,65,68,35,86,38,39,87,0,77,78,79,80,35,37,36,82,83,0,75,74,69,72,35,88,41,40,89,0,81,82,83,84,35,39,38,84,85,0,79,78,73,76,35,90,42,43,91,0,85,86,87,88,35,40,41,86,87,0,83,82,77,80,35,46,44,45,47,0,6,89,90,7,35,43,42,88,89,0,87,86,81,84,35,45,44,90,91,0,90,89,85,88,35,48,0,1,49,0,12,5,4,13],
29 | "name":"ringGeometry.4"
30 | }
--------------------------------------------------------------------------------
/assets/rawfiles/images/decals.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/decals.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/enemy.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/enemy.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/env.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/env.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/face.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/face.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/favicon.ico
--------------------------------------------------------------------------------
/assets/rawfiles/images/font.kra:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/font.kra
--------------------------------------------------------------------------------
/assets/rawfiles/images/font.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/font.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/fx.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/fx.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/icon-128.png
--------------------------------------------------------------------------------
/assets/rawfiles/images/icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/icon-256.png
--------------------------------------------------------------------------------
/assets/rawfiles/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/icon.png
--------------------------------------------------------------------------------
/assets/rawfiles/images/menu.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/menu.psd
--------------------------------------------------------------------------------
/assets/rawfiles/images/sky.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/images/sky.psd
--------------------------------------------------------------------------------
/assets/rawfiles/models/background.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/background.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/border.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/border.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/border_lighting.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/border_lighting.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/bullet.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/bullet.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/deco.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/deco.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/decos.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/decos.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/enemies.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/enemies.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/enemy0.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/enemy0.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/enemy1.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/enemy1.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/enemy2.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/enemy2.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/enemy3.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/enemy3.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/exportWaves.py:
--------------------------------------------------------------------------------
1 | # Exports waves.js file for Mozilla VR's A-Blast demo
2 | # - Each blender layer is a wave
3 | # - Sequences go in groups, alphabetical order
4 | # - Enemies are drawn with Bezier Curves:
5 | # + material name defines movement (single, pingpong, loop)
6 | # + custom property "type" defines enemy name
7 |
8 | import bpy
9 |
10 |
11 | def getOrderedListOfGroupsInLayer(layer):
12 | curves = [ob for ob in bpy.data.objects if ob.layers[layer] and ob.type=='CURVE']
13 | groupset= set()
14 | for curve in curves:
15 | for group in curve.users_group:
16 | groupset.add(group.name)
17 | return sorted(list(groupset), key=int)
18 |
19 | def getCustomData(obj, dataname):
20 | if not dataname in obj.data: return ''
21 | data = obj.data[dataname]
22 | if data:
23 | return ' '+dataname+': '+str(data)+',\n'
24 | return ''
25 |
26 | out = 'var WAVES = [\n'
27 | waves = []
28 | exportwaves = [0,1,2,3,4,5,6,7,8,9]
29 | debugmsg = ''
30 | for l in exportwaves:
31 | wave = ' {\n'
32 | wave += ' name: "WAVE '+str(l+1)+'",\n'
33 | wave += ' sequences: [\n';
34 | groups = getOrderedListOfGroupsInLayer(l)
35 | if not groups: continue
36 | sequences = []
37 | for group in groups:
38 | enemies = []
39 | curves = sorted([ob.name for ob in bpy.data.groups[group].objects])
40 | if not curves: continue
41 | seq = ''
42 | seq += ' {\n start: 0,\n random: 0,\n';
43 | seq += ' subwave: "'+group+'",\n';
44 | seq += ' enemies:[\n'
45 | for i in curves:
46 | curve = bpy.data.objects[i]
47 | points = []
48 | for p in curve.data.splines[0].bezier_points:
49 | points.append('[{0:.3f},{1:.3f},{2:.3f}]'.format( -p.co.x, p.co.z, p.co.y ))
50 |
51 | enemyTimeOffset = getCustomData(curve, 'enemyTimeOffset')
52 | loopStart = getCustomData(curve, 'loopStart')
53 | type = curve.data['type']
54 | if type.find(',') >= 0:
55 | if enemyTimeOffset == '': debugmsg += '// enemyTimeOffset not set in group ' + group + ', curve ' + curve.name + '\n'
56 | type = '["'+'","'.join(type.split(',')) + '"]'
57 | else:
58 | type = '"' + type + '"'
59 | enemies.append(
60 | ' {\n'
61 | ' type: '+type+',\n'+
62 | ' points: ['+','.join(points)+'],\n'+
63 | ' movement: "'+curve.material_slots[0].material.name.lower()+'",\n'+
64 | enemyTimeOffset+
65 | loopStart+
66 | ' random: '+str(0)+',\n'+
67 | ' }')
68 | seq += ',\n'.join(enemies)
69 | seq += '\n ]\n }'
70 | sequences.append(seq)
71 | wave += ',\n'.join(sequences)
72 | wave += '\n ]\n }'
73 | waves.append(wave)
74 | out += ',\n'.join(waves)
75 | out += '\n];'
76 |
77 | f = open(bpy.path.abspath("//")+'../../data/waves.js', 'w+')
78 | f.write(debugmsg + '\n')
79 | f.write(out)
80 | f.close()
81 | print(out)
--------------------------------------------------------------------------------
/assets/rawfiles/models/gameover.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/gameover.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/gun.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/gun.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/gun_anim.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/gun_anim.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/logo.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/logo.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/platform.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/platform.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/player-bullet.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/player-bullet.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/restart.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/restart.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/testsphere.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/testsphere.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/waves.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/waves.blend
--------------------------------------------------------------------------------
/assets/rawfiles/models/welldone.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/models/welldone.blend
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/.bitwig-project:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/.bitwig-project
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/enemy0shoot.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/enemy0shoot.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/explosion0.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/explosion0.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/explosion1.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/explosion1.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/explosion2.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/explosion2.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/explosion3.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/explosion3.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/hitbullet.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/hitbullet.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/hitwall.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/hitwall.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/hurt.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/hurt.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/playershoot.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/playershoot.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/sfx.bwproject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/rawfiles/sounds/sfx/sfx.bwproject
--------------------------------------------------------------------------------
/assets/rawfiles/sounds/sfx/sounds.aup:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/assets/readme/a-blast-10s.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/a-blast-10s.gif
--------------------------------------------------------------------------------
/assets/readme/a-blast-1s.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/a-blast-1s.gif
--------------------------------------------------------------------------------
/assets/readme/a-blast-22s.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/a-blast-22s.gif
--------------------------------------------------------------------------------
/assets/readme/a-blast-3s.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/a-blast-3s.gif
--------------------------------------------------------------------------------
/assets/readme/gun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/gun.png
--------------------------------------------------------------------------------
/assets/readme/mainmenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/mainmenu.png
--------------------------------------------------------------------------------
/assets/readme/mainmenu2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/readme/mainmenu2.png
--------------------------------------------------------------------------------
/assets/sounds/enemy0shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/enemy0shoot.ogg
--------------------------------------------------------------------------------
/assets/sounds/enemy1shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/enemy1shoot.ogg
--------------------------------------------------------------------------------
/assets/sounds/enemy2shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/enemy2shoot.ogg
--------------------------------------------------------------------------------
/assets/sounds/enemy3shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/enemy3shoot.ogg
--------------------------------------------------------------------------------
/assets/sounds/explosion0.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/explosion0.ogg
--------------------------------------------------------------------------------
/assets/sounds/explosion1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/explosion1.ogg
--------------------------------------------------------------------------------
/assets/sounds/explosion2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/explosion2.ogg
--------------------------------------------------------------------------------
/assets/sounds/explosion3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/explosion3.ogg
--------------------------------------------------------------------------------
/assets/sounds/gun.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/gun.ogg
--------------------------------------------------------------------------------
/assets/sounds/hitbullet.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/hitbullet.ogg
--------------------------------------------------------------------------------
/assets/sounds/hitwall.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/hitwall.ogg
--------------------------------------------------------------------------------
/assets/sounds/hurt.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/hurt.ogg
--------------------------------------------------------------------------------
/assets/sounds/intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/intro.ogg
--------------------------------------------------------------------------------
/assets/sounds/music.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/assets/sounds/music.ogg
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | #ablast-ui {
2 | font-family: Helvetica, Arial, sans-serif;
3 | position: absolute;
4 | left: 20px;
5 | bottom: 20px;
6 | line-height: 1.2em;
7 | background-color: rgba(10,10,10,0.8);
8 | border-radius: 6px;
9 | padding: 1.7em 2em;
10 | display: none;
11 | }
12 |
13 | #ablast-ui .help {
14 | color: #ccc;
15 | font-size: 14px;
16 | text-shadow: 0px 2px 1px #000;
17 | }
18 |
19 | #ablast-ui .help h1 {
20 | font-size: 22px;
21 | font-weight: 100;
22 | color: #e42b5a;
23 | margin-top: 0;
24 | }
25 |
26 | #ablast-ui .button {
27 | background-color: #ef2d5e;
28 | color: #fff;
29 | cursor: pointer;
30 | font-size: 12px;
31 | width: 130px;
32 | text-align: center;
33 | max-width: 115px;
34 | padding: 6px;
35 | }
36 |
37 | #ablast-ui .button:hover {
38 | background-color: #f43b6a;
39 | }
40 |
41 | #ablast-ui .form {
42 | display: flex;
43 | font-size: 14px;
44 | align-content: center;
45 | }
46 |
47 | #ablast-ui .hiscore input {
48 | width: 200px;
49 | color: #333;
50 | text-align: center;
51 | }
52 |
53 | #ablast-ui .hide {
54 | display: none !important;
55 | }
56 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aframevr/a-blast/1984d1adb7845399fabd8672909ce1c74ccd5cf7/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | A-Blast
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
84 |
85 |
91 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
124 |
125 |
126 |
130 |
131 |
132 |
139 |
140 |
141 |
142 |
143 |
144 |
149 |
150 |
151 |
152 |
153 |
154 |
163 |
164 |
165 |
166 |
169 |
171 |
172 |
176 |
177 |
178 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
Good job!
190 |
Please enter your name to join the Hall of Fame!
191 |
192 |
196 |
197 |
198 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "en",
3 | "dir": "ltr",
4 | "name": "A-Blast",
5 | "short_name": "A-Blast",
6 | "description": "Save the world from the cutest creatures in the Universe!",
7 | "start_url": "./",
8 | "scope": "./",
9 | "display": "fullscreen",
10 | "theme_color": "#3d4d55",
11 | "background_color": "#3d4d55",
12 | "icons": [
13 | {
14 | "src": "assets/images/icon.png",
15 | "sizes": "1024x1024",
16 | "type": "image/png"
17 | },
18 | {
19 | "src": "assets/images/icon.png",
20 | "sizes": "256x256",
21 | "type": "image/png"
22 | },
23 | {
24 | "src": "assets/images/icon.png",
25 | "sizes": "128x128",
26 | "type": "image/png"
27 | }
28 | ],
29 | "screenshots": [
30 | {
31 | "src": "assets/readme/mainmenu2.png",
32 | "sizes": "1166x707",
33 | "type": "image/png"
34 | },
35 | {
36 | "src": "assets/readme/a-blast-3s.gif",
37 | "sizes": "395x330",
38 | "type": "image/gif"
39 | }
40 | ],
41 | "about_url": "https://blog.mozvr.com/a-blast/",
42 | "vr_default_display": "HTC Vive",
43 | "vr_available_displays": [
44 | "HTC Vive",
45 | "Oculus Rift"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a-blast",
3 | "version": "0.1.0",
4 | "description": "A WebVR FPS mini-game demo using A-Frame by Mozilla VR.",
5 | "scripts": {
6 | "build": "cross-env NODE_ENV=production webpack",
7 | "start": "webpack-dev-server --host 0.0.0.0 --progress --colors --hot -d --inline",
8 | "lint": "semistandard -v | snazzy"
9 | },
10 | "repository": "aframevr/a-blast",
11 | "license": "MIT",
12 | "semistandard": {
13 | "ignore": [
14 | "**/vendor/**"
15 | ]
16 | },
17 | "devDependencies": {
18 | "cross-env": "^3.0.0",
19 | "semistandard": "^9.0.0",
20 | "snazzy": "^4.0.1",
21 | "webpack": "^1.13.2",
22 | "webpack-dev-server": "^1.16.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/a-asset-image.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerElement('a-asset-image', {
2 | prototype: Object.create(AFRAME.ANode.prototype, {
3 | createdCallback: {
4 | value: function () {
5 | this.isAssetItem = true;
6 | }
7 | },
8 |
9 | attachedCallback: {
10 | value: function () {
11 | var src = this.getAttribute('src');
12 | var textureLoader = new THREE.ImageLoader();
13 | textureLoader.load(src, this.onImageLoaded.bind(this));
14 | }
15 | },
16 |
17 | onImageLoaded: {
18 | value : function () {
19 | AFRAME.ANode.prototype.load.call(this);
20 | }
21 | }
22 | })
23 | });
24 |
--------------------------------------------------------------------------------
/src/bullets/enemy-fast.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerBullet(
3 | // name
4 | 'enemy-fast',
5 | // data
6 | {
7 | components: {
8 | bullet: {
9 | name: 'enemy-fast',
10 | maxSpeed: 0.1,
11 | initialSpeed: 0.1,
12 | acceleration: 0.1,
13 | destroyable: true,
14 | color: '#FF7F00'
15 | },
16 | 'collision-helper': {
17 | debug: false,
18 | radius: 0.1
19 | },
20 | 'json-model': {
21 | src: '#enemyBullet'
22 | }
23 | },
24 | poolSize: 10
25 | },
26 | // implementation
27 | {
28 | init: function () {
29 | var el = this.el;
30 | var color = this.bullet.components.bullet.color;
31 | el.setAttribute('material', 'color', color);
32 | el.setAttribute('scale', {x: 0.09, y: 0.09, z: 0.09});
33 | this.trail = null;
34 | this.glow = null;
35 | var self = this;
36 | el.addEventListener('model-loaded', function(event) {
37 | // @todo Do it outside
38 | event.detail.model.children[0].material.color.setStyle(color);
39 | self.trail = self.el.getObject3D('mesh').getObjectByName('trail');
40 | self.trail.scale.setY(0.001);
41 | self.glow = self.el.getObject3D('mesh').getObjectByName('glow');
42 | });
43 | },
44 | reset: function () {
45 | var el = this.el;
46 | el.setAttribute('scale', {x: 0.09, y: 0.09, z: 0.09});
47 | if (this.trail) {
48 | this.trail.scale.setY(0.001);
49 | }
50 | },
51 | tick: function (time, delta) {
52 | //stretch trail
53 | if (this.trail && this.trail.scale.y < 1) {
54 | var trailScale = this.trail.scale.y + delta/1000;
55 | if (trailScale > 1) { trailScale = 1; }
56 | this.trail.scale.setY(trailScale);
57 | }
58 | if (this.glow) {
59 | var sc = 1 + Math.sin(time / 10.0) * 0.1;
60 | this.glow.scale.set(sc, sc, sc);
61 | }
62 | },
63 | onHit: function (type) {
64 | }
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/bullets/enemy-fat.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerBullet(
3 | // name
4 | 'enemy-fat',
5 | // data
6 | {
7 | components: {
8 | bullet: {
9 | name: 'enemy-fat',
10 | maxSpeed: 0.3,
11 | initialSpeed: 0.1,
12 | acceleration: 0.04,
13 | destroyable: true,
14 | color: '#8762FF'
15 | },
16 | 'collision-helper': {
17 | debug: false,
18 | radius: 0.5
19 | },
20 | 'json-model': {
21 | src: '#enemyBullet'
22 | }
23 | },
24 | poolSize: 10
25 | },
26 | // implementation
27 | {
28 | init: function () {
29 | var el = this.el;
30 | var color = this.bullet.components.bullet.color;
31 | el.setAttribute('material', 'color', color);
32 | el.setAttribute('scale', {x: 0.5, y: 0.5, z: 0.5});
33 | this.trail = null;
34 | this.glow = null;
35 | var self = this;
36 | el.addEventListener('model-loaded', function(event) {
37 | // @todo Do it outside
38 | event.detail.model.children[0].material.color.setStyle(color);
39 | self.trail = self.el.getObject3D('mesh').getObjectByName('trail');
40 | self.trail.scale.setY(0.001);
41 | self.glow = self.el.getObject3D('mesh').getObjectByName('glow');
42 | });
43 | },
44 | reset: function () {
45 | var el = this.el;
46 | el.setAttribute('scale', {x: 0.5, y: 0.5, z: 0.5});
47 | if (this.trail) {
48 | this.trail.scale.setY(0.001);
49 | }
50 | },
51 | tick: function (time, delta) {
52 | //stretch trail
53 | if (this.trail && this.trail.scale.y < 0.1) {
54 | var trailScale = this.trail.scale.y + delta/1000;
55 | if (trailScale > 0.1) { trailScale = 0.1; }
56 | this.trail.scale.setY(trailScale);
57 | }
58 | if (this.glow) {
59 | var sc = 1 + Math.abs(Math.sin(time / 80.0) * 0.4);
60 | this.glow.scale.set(sc, sc, sc);
61 | }
62 | },
63 | onHit: function (type) {
64 | }
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/bullets/enemy-medium.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerBullet(
3 | // name
4 | 'enemy-medium',
5 | // data
6 | {
7 | components: {
8 | bullet: {
9 | name: 'enemy-medium',
10 | maxSpeed: 0.5,
11 | initialSpeed: 0.1,
12 | acceleration: 0.06,
13 | destroyable: true,
14 | color: '#FF5533'
15 | },
16 | 'collision-helper': {
17 | debug: false,
18 | radius: 0.16
19 | },
20 | 'json-model': {
21 | src: 'url(assets/models/enemy-bullet.json)'
22 | }
23 | },
24 | poolSize: 10
25 | },
26 | // implementation
27 | {
28 | init: function () {
29 | var el = this.el;
30 | var color = this.bullet.components.bullet.color;
31 | el.setAttribute('material', 'color', color);
32 | el.setAttribute('scale', {x: 0.16, y: 0.16, z: 0.16});
33 | this.trail = null;
34 | this.glow = null;
35 | var self = this;
36 | el.addEventListener('model-loaded', function(event) {
37 | // @todo Do it outside
38 | event.detail.model.children[0].material.color.setStyle(color);
39 | self.trail = self.el.getObject3D('mesh').getObjectByName('trail');
40 | self.trail.scale.setY(0.001);
41 | self.glow = self.el.getObject3D('mesh').getObjectByName('glow');
42 | });
43 | },
44 | reset: function () {
45 | var el = this.el;
46 | el.setAttribute('scale', {x: 0.13, y: 0.13, z: 0.13});
47 | if (this.trail) {
48 | this.trail.scale.setY(0.001);
49 | }
50 | },
51 | tick: function (time, delta) {
52 | //stretch trail
53 | if (this.trail && this.trail.scale.y < 0.3) {
54 | var trailScale = this.trail.scale.y + delta/1000;
55 | if (trailScale > 0.3) { trailScale = 0.3; }
56 | this.trail.scale.setY(trailScale);
57 | }
58 | if (this.glow) {
59 | var sc = 1 + Math.sin(time / 20.0) * 0.1;
60 | this.glow.scale.set(sc, sc, sc);
61 | }
62 | },
63 | onHit: function (type) {
64 | }
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/bullets/enemy-slow.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerBullet(
3 | // name
4 | 'enemy-slow',
5 | // data
6 | {
7 | components: {
8 | bullet: {
9 | name: 'enemy-slow',
10 | maxSpeed: 0.5,
11 | initialSpeed: 0.1,
12 | acceleration: 0.03,
13 | destroyable: true,
14 | color: '#FFB911'
15 | },
16 | 'collision-helper': {
17 | debug: false,
18 | radius: 0.13
19 | },
20 | 'json-model': {
21 | src: '#enemyBullet'
22 | }
23 | },
24 | poolSize: 10
25 | },
26 | // implementation
27 | {
28 | init: function () {
29 | var el = this.el;
30 | var color = this.bullet.components.bullet.color;
31 | el.setAttribute('material', 'color', color);
32 | el.setAttribute('scale', {x: 0.13, y: 0.13, z: 0.13});
33 | this.trail = null;
34 | this.glow = null;
35 | var self = this;
36 | el.addEventListener('model-loaded', function(event) {
37 | // @todo Do it outside
38 | event.detail.model.children[0].material.color.setStyle(color);
39 | self.trail = self.el.getObject3D('mesh').getObjectByName('trail');
40 | self.trail.scale.setY(0.001);
41 | self.glow = self.el.getObject3D('mesh').getObjectByName('glow');
42 | });
43 | },
44 | reset: function () {
45 | var el = this.el;
46 | el.setAttribute('scale', {x: 0.13, y: 0.13, z: 0.13});
47 | if (this.trail) {
48 | this.trail.scale.setY(0.001);
49 | }
50 | },
51 | tick: function (time, delta) {
52 | //stretch trail
53 | if (this.trail && this.trail.scale.y < 0.3) {
54 | var trailScale = this.trail.scale.y + delta/1000;
55 | if (trailScale > 0.3) { trailScale = 0.3; }
56 | this.trail.scale.setY(trailScale);
57 | }
58 | if (this.glow) {
59 | var sc = 1 + Math.sin(time / 20.0) * 0.1;
60 | this.glow.scale.set(sc, sc, sc);
61 | }
62 | },
63 | onHit: function (type) {
64 | }
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/bullets/player.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerBullet(
3 | // name
4 | 'default',
5 | // data
6 | {
7 | components: {
8 | bullet: {
9 | name: 'default',
10 | maxSpeed: 1,
11 | initialSpeed: 0.1,
12 | acceleration: 0.4,
13 | color: '#24CAFF'
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 0.2
18 | },
19 | 'json-model': {
20 | src: '#playerBullet'
21 | }
22 | },
23 | poolSize: 10
24 | },
25 | // implementation
26 | {
27 | init: function () {
28 | var el = this.el;
29 | var color = this.bullet.components.bullet.color;
30 | el.setAttribute('material', 'color', color);
31 | el.setAttribute('scale', {x: 0.2, y: 0.2, z: 0.2});
32 | this.trail = null;
33 | var self = this;
34 | el.addEventListener('model-loaded', function(event) {
35 | // @todo Do it outside
36 | //event.detail.model.children[0].material.color.setRGB(1,0,0);
37 | self.trail = self.el.getObject3D('mesh').getObjectByName('trail');
38 | self.trail.scale.setY(0.001);
39 | });
40 | },
41 | reset: function () {
42 | var el = this.el;
43 | el.setAttribute('scale', {x: 0.2, y: 0.2, z: 0.2});
44 | if (this.trail) {
45 | this.trail.scale.setY(0.001);
46 | }
47 | },
48 | tick: function (time, delta) {
49 | //stretch trail
50 | if (this.trail && this.trail.scale.y < 1) {
51 | var trailScale;
52 | if (this.trail.scale.y < 0.005) {
53 | trailScale = this.trail.scale.y + 0.001;
54 | }
55 | else {
56 | trailScale = this.trail.scale.y + delta/50;
57 | }
58 | if (trailScale > 1) { trailScale = 1; }
59 | this.trail.scale.setY(trailScale);
60 | }
61 | },
62 | onHit: function (type) {
63 | this.el.setAttribute('material', 'color', '#FFF');
64 | }
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/components/animate-message.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerComponent('animate-message', {
2 | init: function () {
3 | var self = this;
4 | this.startMsg = null;
5 | this.el.addEventListener('model-loaded', function(event) {
6 | self.startMsg = self.el.getObject3D('mesh').getObjectByName('start');
7 | });
8 | },
9 | tick: function (time, delta) {
10 | if (this.startMsg) {
11 | this.startMsg.rotation.z = -Math.PI + Math.abs(Math.sin(time / 200) * 0.03);
12 | }
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/src/components/bullet.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST THREE */
2 | AFRAME.registerComponent('bullet', {
3 | schema: {
4 | name: { default: '' },
5 | direction: { type: 'vec3' },
6 | maxSpeed: { default: 5.0 },
7 | initialSpeed: { default: 5.0 },
8 | position: { type: 'vec3' },
9 | acceleration: { default: 0.5 },
10 | destroyable: { default: false },
11 | owner: {default: 'player', oneOf: ['enemy', 'player']},
12 | color: {default: '#fff'}
13 | },
14 |
15 | init: function () {
16 | this.startEnemy = document.getElementById('start_enemy');
17 | this.backgroundEl = document.getElementById('border');
18 | this.bullet = ABLAST.BULLETS[this.data.name];
19 | this.bullet.definition.init.call(this);
20 | this.hit = false;
21 | this.direction = new THREE.Vector3();
22 | this.temps = {
23 | direction: new THREE.Vector3(),
24 | position: new THREE.Vector3()
25 | }
26 | },
27 |
28 | update: function (oldData) {
29 | var data = this.data;
30 | this.owner = this.data.owner;
31 | this.direction.set(data.direction.x, data.direction.y, data.direction.z);
32 | this.currentAcceleration = data.acceleration;
33 | this.speed = data.initialSpeed;
34 | this.startPosition = data.position;
35 | },
36 |
37 | play: function () {
38 | this.initTime = null;
39 | },
40 |
41 | hitObject: function (type, data) {
42 | this.bullet.definition.onHit.call(this);
43 | this.hit = true;
44 | if (this.data.owner === 'enemy') {
45 | this.el.emit('player-hit');
46 | document.getElementById('hurtSound').components.sound.playSound();
47 | }
48 | else {
49 | if (type === 'bullet') {
50 | // data is the bullet entity collided with
51 | data.components.bullet.resetBullet();
52 | this.el.sceneEl.systems.explosion.createExplosion(type, data.object3D.position, data.getAttribute('bullet').color, 1, this.direction);
53 | ABLAST.currentScore.validShoot++;
54 | }
55 | else if (type === 'background') {
56 | this.el.sceneEl.systems.decals.addDecal(data.point, data.face.normal);
57 | var posOffset = data.point.clone().sub(this.direction.clone().multiplyScalar(0.2));
58 | this.el.sceneEl.systems.explosion.createExplosion(type, posOffset, '#fff', 1, this.direction);
59 | }
60 | else if (type === 'enemy') {
61 | var enemy = data.getAttribute('enemy');
62 | if (data.components['enemy'].health <= 0) {
63 | this.el.sceneEl.systems.explosion.createExplosion('enemy', data.object3D.position, enemy.color, enemy.scale, this.direction, enemy.name);
64 | }
65 | else {
66 | this.el.sceneEl.systems.explosion.createExplosion('bullet', this.el.object3D.position, enemy.color, enemy.scale, this.direction);
67 | }
68 | ABLAST.currentScore.validShoot++;
69 | }
70 | }
71 | this.resetBullet();
72 | },
73 |
74 | resetBullet: function () {
75 | this.hit = false;
76 | this.bullet.definition.reset.call(this);
77 | this.initTime = null;
78 |
79 | this.direction.set(this.data.direction.x, this.data.direction.y, this.data.direction.z);
80 |
81 | this.currentAcceleration = this.data.acceleration;
82 | this.speed = this.data.initialSpeed;
83 | this.startPosition = this.data.position;
84 |
85 | this.system.returnBullet(this.data.name, this.el);
86 | },
87 |
88 | tick: (function () {
89 | //var position = new THREE.Vector3();
90 | //var direction = new THREE.Vector3();
91 | return function tick (time, delta) {
92 |
93 | if (!this.initTime) {this.initTime = time;}
94 |
95 | this.bullet.definition.tick.call(this, time, delta);
96 |
97 | // Align the bullet to its direction
98 | this.el.object3D.lookAt(this.direction.clone().multiplyScalar(1000));
99 |
100 | // Update acceleration based on the friction
101 | this.temps.position.copy(this.el.getAttribute('position'));
102 |
103 | // Update speed based on acceleration
104 | this.speed = this.currentAcceleration * .1 * delta;
105 | if (this.speed > this.data.maxSpeed) { this.speed = this.data.maxSpeed; }
106 |
107 | // Set new position
108 | this.temps.direction.copy(this.direction);
109 | var newBulletPosition = this.temps.position.add(this.temps.direction.multiplyScalar(this.speed));
110 | this.el.setAttribute('position', newBulletPosition);
111 |
112 | // Check if the bullet is lost in the sky
113 | if (this.temps.position.length() >= 50) {
114 | this.resetBullet();
115 | return;
116 | }
117 |
118 | var collisionHelper = this.el.getAttribute('collision-helper');
119 | if (!collisionHelper) { return; }
120 |
121 | var bulletRadius = collisionHelper.radius;
122 |
123 | // Detect collision depending on the owner
124 | if (this.data.owner === 'player') {
125 | // megahack
126 |
127 | // Detect collision against enemies
128 | if (this.data.owner === 'player') {
129 | // Detect collision with the start game enemy
130 | var state = this.el.sceneEl.getAttribute('gamestate').state;
131 | if (state === 'STATE_MAIN_MENU') {
132 | var enemy = this.startEnemy;
133 | var helper = enemy.getAttribute('collision-helper');
134 | var radius = helper.radius;
135 | if (newBulletPosition.distanceTo(enemy.object3D.position) < radius + bulletRadius) {
136 | this.el.sceneEl.systems.explosion.createExplosion('enemy', this.el.getAttribute('position'), '#ffb911', 0.5, this.direction, 'enemy_start');
137 | enemy.emit('hit');
138 | document.getElementById('introMusic').components.sound.pauseSound();
139 | document.getElementById('mainThemeMusic').components.sound.playSound();
140 | return;
141 | }
142 | } else if (state === 'STATE_GAME_WIN' || state === 'STATE_GAME_OVER') {
143 | var enemy = document.getElementById('reset');
144 | var helper = enemy.getAttribute('collision-helper');
145 | var radius = helper.radius;
146 | if (newBulletPosition.distanceTo(enemy.object3D.position) < radius * 2 + bulletRadius * 2) {
147 | this.el.sceneEl.systems.explosion.createExplosion('enemy', this.el.getAttribute('position'), '#f00', 0.5, this.direction, 'enemy_start');
148 | this.el.sceneEl.emit('reset');
149 | return;
150 | }
151 | } else {
152 | // Detect collisions with all the active enemies
153 | var enemies = this.el.sceneEl.systems.enemy.activeEnemies;
154 | for (var i = 0; i < enemies.length; i++) {
155 | var enemy = enemies[i];
156 | var helper = enemy.getAttribute('collision-helper');
157 | if (!helper) continue;
158 | var radius = helper.radius;
159 | if (newBulletPosition.distanceTo(enemy.object3D.position) < radius + bulletRadius) {
160 | enemy.emit('hit');
161 | this.hitObject('enemy', enemy);
162 | return;
163 | }
164 | }
165 | }
166 |
167 | var bullets = this.system.activeBullets;
168 | for (var i = 0; i < bullets.length; i++) {
169 | var bullet = bullets[i];
170 | var data = bullet.components['bullet'].data;
171 | if (!data || data.owner === 'player' || !data.destroyable) { continue; }
172 |
173 | var colhelper = bullet.components['collision-helper'];
174 | if (!colhelper) continue;
175 | var enemyBulletRadius = colhelper.data.radius;
176 | if (newBulletPosition.distanceTo(bullet.getAttribute('position')) < enemyBulletRadius + bulletRadius) {
177 | this.hitObject('bullet', bullet);
178 | return;
179 | }
180 | }
181 | }
182 | } else {
183 | // @hack Any better way to get the head position ?
184 | var head = this.el.sceneEl.camera.el.components['look-controls'].dolly.position;
185 | if (newBulletPosition.distanceTo(head) < 0.10 + bulletRadius) {
186 | this.hitObject('player');
187 | return;
188 | }
189 | }
190 |
191 | // Detect collission aginst the background
192 | var ray = new THREE.Raycaster(this.temps.position, this.temps.direction.clone().normalize());
193 | var background = this.backgroundEl.getObject3D('mesh');
194 | if (background) {
195 | var collisionResults = ray.intersectObjects(background.children, true);
196 | var self = this;
197 | collisionResults.forEach(function (collision) {
198 | if (collision.distance < self.temps.position.length()) {
199 | if (!collision.object.el) { return; }
200 | self.hitObject('background', collision);
201 | return;
202 | }
203 | });
204 | }
205 | };
206 | })()
207 | });
208 |
--------------------------------------------------------------------------------
/src/components/collision-helper.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME THREE */
2 | AFRAME.registerComponent('collision-helper', {
3 | schema: {
4 | type: {default: 'sphere', oneOf: ['sphere', 'box']},
5 | radius: {default: 1, if: {type: ['sphere']}},
6 | debug: {default: false},
7 | color: {type: 'color', default: 0x888888}
8 | },
9 |
10 | init: function () {
11 | var data = this.data;
12 |
13 | this.geometry = new THREE.IcosahedronGeometry(1, 1);
14 | this.material = new THREE.MeshBasicMaterial({color: data.color, wireframe: true});
15 | this.helperMesh = null;
16 |
17 | if (data.debug) {
18 | this.createHelperMesh();
19 | }
20 | },
21 |
22 | createHelperMesh: function () {
23 | var radius = this.data.radius;
24 | this.helperMesh = new THREE.Mesh(this.geometry, this.material);
25 | this.helperMesh.visible = true;
26 | this.helperMesh.scale.set(radius, radius, radius);
27 | this.el.setObject3D('collision-helper-mesh', this.helperMesh);
28 | },
29 |
30 | update: function (oldData) {
31 | var data = this.data;
32 | if (!data.debug) { return; }
33 |
34 | if (!this.helperMesh) {
35 | this.createHelperMesh();
36 | } else {
37 | this.material.color.set(data.color);
38 | this.helperMesh.scale.set(data.radius, data.radius, data.radius);
39 | this.helperMesh.visible = data.debug;
40 | }
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/src/components/countdown.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerComponent('countdown', {
2 | schema: {
3 | start: {default: '01:00'},
4 | value: {default: '00:00'},
5 | autostart: {default: false}
6 | },
7 |
8 | init: function () {
9 | this.timeinterval = null;
10 | if (this.data.autostart) {
11 | this.restart();
12 | }
13 |
14 | var self = this;
15 | this.el.sceneEl.addEventListener('gamestate-changed', function (evt) {
16 | if ('state' in evt.detail.diff) {
17 | switch (evt.detail.state.state) {
18 | case 'STATE_PLAYING':
19 | self.restart();
20 | break;
21 | case 'STATE_GAME_OVER':
22 | case 'STATE_GAME_WIN':
23 | case 'STATE_MAIN_MENU':
24 | self.stop();
25 | }
26 | }
27 | });
28 | },
29 |
30 | initializeClock: function (endtime) {
31 | var self = this;
32 |
33 | this.el.sceneEl.emit('countdown-start', endtime);
34 | function updateTimer() {
35 | var total = Date.parse(endtime) - Date.parse(new Date());
36 | var seconds = Math.floor( (total/1000) % 60 );
37 | var minutes = Math.floor( (total/1000/60) % 60 );
38 | var t = {
39 | 'total': total,
40 | 'minutes': minutes,
41 | 'seconds': seconds
42 | };
43 | self.el.sceneEl.emit('countdown-update', t);
44 | if (t.total <= 0) {
45 | clearInterval(self.timeinterval);
46 | self.el.sceneEl.emit('countdown-end');
47 | }
48 | }
49 |
50 | this.timeinterval = setInterval(updateTimer, 1000);
51 | updateTimer();
52 | },
53 |
54 | stop: function () {
55 | clearInterval(this.timeinterval);
56 | this.el.sceneEl.emit('countdown-update', {
57 | 'total': 0,
58 | 'minutes': 0,
59 | 'seconds': 0
60 | });
61 | },
62 |
63 | restart: function () {
64 | this.stop();
65 |
66 | var values = this.data.start.split(':').map(function(value) { return parseInt(value); });
67 | var deadline = new Date(Date.parse(new Date()) + (values[0] * 60 + values[1]) * 1000);
68 | this.initializeClock(deadline);
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/src/components/curve-movement.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME, THREE */
2 |
3 | THREE.Spline = function ( points ) {
4 |
5 | this.points = points;
6 |
7 | var c = [], v3 = { x: 0, y: 0, z: 0 },
8 | point, intPoint, weight, w2, w3,
9 | pa, pb, pc, pd;
10 |
11 | this.initFromArray = function ( a ) {
12 |
13 | this.points = [];
14 |
15 | for ( var i = 0; i < a.length; i ++ ) {
16 |
17 | this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
18 |
19 | }
20 |
21 | };
22 |
23 | this.getPoint = function ( k ) {
24 |
25 | point = ( this.points.length - 1 ) * k;
26 | intPoint = Math.floor( point );
27 | weight = point - intPoint;
28 |
29 | c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
30 | c[ 1 ] = intPoint;
31 | c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
32 | c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;
33 |
34 | pa = this.points[ c[ 0 ] ];
35 | pb = this.points[ c[ 1 ] ];
36 | pc = this.points[ c[ 2 ] ];
37 | pd = this.points[ c[ 3 ] ];
38 |
39 | w2 = weight * weight;
40 | w3 = weight * w2;
41 |
42 | v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
43 | v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
44 | v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );
45 |
46 | return v3;
47 |
48 | };
49 |
50 | this.getControlPointsArray = function () {
51 |
52 | var i, p, l = this.points.length,
53 | coords = [];
54 |
55 | for ( i = 0; i < l; i ++ ) {
56 |
57 | p = this.points[ i ];
58 | coords[ i ] = [ p.x, p.y, p.z ];
59 |
60 | }
61 |
62 | return coords;
63 |
64 | };
65 |
66 | // approximate length by summing linear segments
67 |
68 | this.getLength = function ( nSubDivisions ) {
69 |
70 | var i, index, nSamples, position,
71 | point = 0, intPoint = 0, oldIntPoint = 0,
72 | oldPosition = new THREE.Vector3(),
73 | tmpVec = new THREE.Vector3(),
74 | chunkLengths = [],
75 | totalLength = 0;
76 |
77 | // first point has 0 length
78 |
79 | chunkLengths[ 0 ] = 0;
80 |
81 | if ( ! nSubDivisions ) nSubDivisions = 100;
82 |
83 | nSamples = this.points.length * nSubDivisions;
84 |
85 | oldPosition.copy( this.points[ 0 ] );
86 |
87 | for ( i = 1; i < nSamples; i ++ ) {
88 |
89 | index = i / nSamples;
90 |
91 | position = this.getPoint( index );
92 | tmpVec.copy( position );
93 |
94 | totalLength += tmpVec.distanceTo( oldPosition );
95 |
96 | oldPosition.copy( position );
97 |
98 | point = ( this.points.length - 1 ) * index;
99 | intPoint = Math.floor( point );
100 |
101 | if ( intPoint !== oldIntPoint ) {
102 |
103 | chunkLengths[ intPoint ] = totalLength;
104 | oldIntPoint = intPoint;
105 |
106 | }
107 |
108 | }
109 |
110 | // last point ends with total length
111 |
112 | chunkLengths[ chunkLengths.length ] = totalLength;
113 |
114 | return { chunks: chunkLengths, total: totalLength };
115 |
116 | };
117 |
118 | this.reparametrizeByArcLength = function ( samplingCoef ) {
119 |
120 | var i, j,
121 | index, indexCurrent, indexNext,
122 | realDistance,
123 | sampling, position,
124 | newpoints = [],
125 | tmpVec = new Vector3(),
126 | sl = this.getLength();
127 |
128 | newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
129 |
130 | for ( i = 1; i < this.points.length; i ++ ) {
131 |
132 | //tmpVec.copy( this.points[ i - 1 ] );
133 | //linearDistance = tmpVec.distanceTo( this.points[ i ] );
134 |
135 | realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];
136 |
137 | sampling = Math.ceil( samplingCoef * realDistance / sl.total );
138 |
139 | indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
140 | indexNext = i / ( this.points.length - 1 );
141 |
142 | for ( j = 1; j < sampling - 1; j ++ ) {
143 |
144 | index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
145 |
146 | position = this.getPoint( index );
147 | newpoints.push( tmpVec.copy( position ).clone() );
148 |
149 | }
150 |
151 | newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );
152 |
153 | }
154 |
155 | this.points = newpoints;
156 |
157 | };
158 |
159 | // Catmull-Rom
160 |
161 | function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
162 |
163 | var v0 = ( p2 - p0 ) * 0.5,
164 | v1 = ( p3 - p1 ) * 0.5;
165 |
166 | return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
167 |
168 | }
169 |
170 | }
171 |
172 | /**
173 | * Spline interpolation with waypoints.
174 | */
175 | AFRAME.registerComponent('curve-movement', {
176 | schema: {
177 | debug: {default: false},
178 | type: {default: 'single'},
179 | restTime: {default: 150}, // ms.
180 | speed: {default: 3}, // meters per second.
181 | loopStart: {default: 1},
182 | timeOffset: {default: 0}
183 | },
184 |
185 | init: function () {
186 | this.direction = 1;
187 | },
188 |
189 | isClosed: function () {
190 | return this.data.type === 'loop';
191 | },
192 |
193 | addPoints: function (points) {
194 | var data = this.data;
195 | var spline;
196 | var chunkLengths;
197 |
198 | // Set waypoints.
199 | if (data.type === 'loop') {
200 | points = points.slice(0); // clone array as we'll need to modify it
201 | points.push(points[this.data.loopStart]);
202 | }
203 |
204 | // Build spline.
205 | spline = this.spline = ASpline();
206 | spline.initFromArray(points);
207 |
208 | // Keep track of current point to get to the next point.
209 | this.currentPointIndex = 0;
210 |
211 | // Compute how long to get from each point to the next for each chunk using speed.
212 | chunkLengths = spline.getLength().chunks;
213 | this.cycleTimes = chunkLengths.map(function (chunkLength, i) {
214 | if (i === 0) { return null; }
215 | return (chunkLength - chunkLengths[i - 1]) / data.speed * 1000;
216 | }).filter(function (length) { return length !== null; });
217 |
218 | // Keep a local time to reset at each point, for separate easing from point to point.
219 | this.time = this.data.timeOffset;
220 | this.initTime = null;
221 | this.restTime = 0;
222 | this.direction = 1;
223 | this.end = false;
224 | },
225 |
226 | update: function () {
227 | var data = this.data;
228 | var el = this.el;
229 |
230 | // Visual debug stuff.
231 | if (data.debug) {
232 | el.setAttribute('spline-line', {pointer: 'movement-pattern.movementPattern.spline'});
233 | } else {
234 | el.removeAttribute('spline-line');
235 | }
236 | },
237 | play: function () {
238 | this.time = this.data.timeOffset;
239 | this.initTime = null;
240 | },
241 | tick: function (time, delta) {
242 | var cycleTime;
243 | var data = this.data;
244 | var el = this.el;
245 | var percent;
246 | var point;
247 | var spline = this.spline;
248 |
249 | if (!this.initTime) {
250 | this.initTime = time;
251 | }
252 |
253 | // If not closed and reached the end, just stop (for now).
254 | if (this.end) {return;}
255 | if (!this.isClosed() && this.currentPointIndex === spline.points.length - 1) { return; }
256 |
257 | // Mod the current time to get the current cycle time and divide by total time.
258 | cycleTime = this.cycleTimes[this.currentPointIndex];
259 |
260 | var t = 0;
261 | var jump = false;
262 | if (this.time > cycleTime) {
263 | t = 1;
264 | jump = true;
265 | } else {
266 | t = this.time / cycleTime;
267 | }
268 |
269 | if (this.direction === -1) {
270 | t = 1 - t;
271 | }
272 |
273 | if (data.type === 'single') {
274 | percent = inOutSine(t);
275 | }
276 | else {
277 | percent = t;
278 | }
279 |
280 | this.time = time - this.initTime;
281 |
282 | if (this.time < 0) { console.log(percent); return; }
283 |
284 | point = spline.getPointFrom(percent, this.currentPointIndex);
285 | el.setAttribute('position', {x: point.x, y: point.y, z: point.z});
286 | this.lastPercent = percent;
287 |
288 | if (jump) {
289 | if (this.direction === 1) {
290 | if (this.currentPointIndex === spline.points.length - 2) {
291 | if (data.type === 'single') {
292 | this.end = true;
293 | } else if (data.type === 'loop') {
294 | this.currentPointIndex = this.data.loopStart;
295 | } else {
296 | this.direction = -1;
297 | }
298 | } else {
299 | this.currentPointIndex ++;
300 | }
301 | } else {
302 | this.currentPointIndex --;
303 | if (this.currentPointIndex < this.data.loopStart) {
304 | this.currentPointIndex = this.data.loopStart;
305 | this.direction = 1;
306 | }
307 | }
308 | this.initTime = time;
309 | this.time = 0;
310 | }
311 | }
312 | });
313 |
314 | function inOutSine (k) {
315 | return .5 * (1 - Math.cos(Math.PI * k));
316 | }
317 |
318 | /**
319 | * Spline with point to point interpolation.
320 | */
321 | function ASpline (points) {
322 | var spline = new THREE.Spline(points);
323 |
324 | /**
325 | * Interpolate between pointIndex and the next index.
326 | *
327 | * k {number} - From 0 to 1.
328 | * pointIndex {number} - Starting point index to interpolate from.
329 | */
330 | spline.getPointFrom = function (k, pointIndex) {
331 | var c, pa, pb, pc, pd, points, midpoint, w2, w3, v3, weight;
332 | points = this.points;
333 |
334 | midpoint = pointIndex + k;
335 |
336 | c = [];
337 | c[0] = pointIndex === 0 ? pointIndex : pointIndex - 1;
338 | c[1] = pointIndex;
339 | c[2] = pointIndex > points.length - 2 ? points.length - 1 : pointIndex + 1;
340 | c[3] = pointIndex > points.length - 3 ? points.length - 1 : pointIndex + 2;
341 |
342 | pa = points[c[0]];
343 | pb = points[c[1]];
344 | pc = points[c[2]];
345 | pd = points[c[3]];
346 |
347 | weight = midpoint - pointIndex;
348 | w2 = weight * weight;
349 | w3 = weight * w2;
350 |
351 | v3 = {};
352 | v3.x = interpolate(pa.x, pb.x, pc.x, pd.x, weight, w2, w3);
353 | v3.y = interpolate(pa.y, pb.y, pc.y, pd.y, weight, w2, w3);
354 | v3.z = interpolate(pa.z, pb.z, pc.z, pd.z, weight, w2, w3);
355 | return v3;
356 | };
357 | spline.getPointFrom = spline.getPointFrom.bind(spline);
358 |
359 | /**
360 | * Catmull-Rom
361 | */
362 | function interpolate (p0, p1, p2, p3, t, t2, t3) {
363 | var v0 = (p2 - p0) * 0.5;
364 | var v1 = (p3 - p1) * 0.5;
365 | return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;
366 | }
367 |
368 | return spline;
369 | }
370 |
--------------------------------------------------------------------------------
/src/components/decals.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME THREE */
2 | AFRAME.registerSystem('decals', {
3 | schema: {
4 | size: {default: 0.1},
5 | src: {default: '', type: 'asset'},
6 | maxDecals: {default: 30} // 0 for infinite
7 | },
8 |
9 | init: function () {
10 | this.numDecals = 0;
11 | this.decals = [];
12 | this.oldestDecalIdx = 0;
13 | this.textureSrc = null;
14 |
15 | this.geometry = new THREE.PlaneGeometry(1, 1);
16 | this.material = new THREE.MeshBasicMaterial({
17 | transparent: true,
18 | color: '#24CAFF',
19 | depthTest: true,
20 | depthWrite: false,
21 | polygonOffset: true,
22 | polygonOffsetFactor: -20
23 | });
24 |
25 | this.updateMap();
26 | },
27 |
28 | updateMap: function () {
29 | var src = this.data.src;
30 |
31 | if (src) {
32 | if (src === this.textureSrc) { return; }
33 | // Texture added or changed.
34 | this.textureSrc = src;
35 | this.sceneEl.systems.material.loadTexture(src, {src: src}, setMap.bind(this));
36 | return;
37 | }
38 |
39 | // Texture removed.
40 | if (!this.material.map) { return; }
41 | setMap(null);
42 |
43 | function setMap (texture) {
44 | this.material.map = texture;
45 | this.material.needsUpdate = true;
46 | }
47 | },
48 |
49 | update: function (oldData) {
50 | this.updateMap();
51 | },
52 |
53 | getDecal: function () {
54 | var maxDecals = this.data.maxDecals;
55 | var size = this.data.size;
56 | var decal = null;
57 |
58 | if (maxDecals === 0 || this.numDecals < maxDecals) {
59 | decal = new THREE.Mesh(this.geometry, this.material);
60 | this.numDecals++;
61 | this.decals.push(decal);
62 | } else {
63 | decal = this.decals[this.oldestDecalIdx];
64 | this.oldestDecalIdx = (this.oldestDecalIdx + 1) % this.data.maxDecals;
65 | }
66 | decal.scale.set(size, size, size);
67 |
68 | return decal;
69 | },
70 |
71 | addDecal: function (point, normal) {
72 | var decal = this.getDecal();
73 | if (decal) {
74 | decal.position.set(0, 0, 0);
75 | decal.position.copy(point);
76 | decal.lookAt(normal);
77 | decal.rotation.z += Math.random() * Math.PI * 2;
78 | this.sceneEl.object3D.add(decal);
79 | }
80 | }
81 | });
82 |
--------------------------------------------------------------------------------
/src/components/enemy.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST THREE */
2 | AFRAME.registerComponent('enemy', {
3 | schema: {
4 | name: {default: 'enemy0'},
5 | bulletName: {default: 'enemy-slow'},
6 | shootingDelay: {default: 200}, // ms
7 | health: {default: 1},
8 | color: {default: '#fff'},
9 | scale: {default: 1},
10 | canShoot: {default: true}
11 | },
12 | init: function () {
13 | this.alive = true;
14 | this.hipBone = null;
15 | this.definition = ABLAST.ENEMIES[this.data.name].definition;
16 | this.definition.init.call(this);
17 | var comp = ABLAST.ENEMIES[this.data.name].components.enemy;
18 | this.maxhealth = this.health = comp.health;
19 | this.color = comp.color;
20 | this.scale = comp.scale;
21 | this.gunOffset = new THREE.Vector3(0.0, 0.44, 0.5).multiplyScalar(this.scale);
22 | this.lastShootTime = undefined;
23 | this.shootAt = 0;
24 | this.warmUpTime = 1000;
25 | this.paused = false;
26 |
27 | var self = this;
28 | this.el.addEventListener('model-loaded', function(event) {
29 | self.el.components['json-model'].playAnimation('fly', true);
30 | self.hipBone = self.el.object3D.children[3].children[0];
31 | });
32 |
33 | // gun glow
34 | this.gunGlowMaterial = new THREE.MeshBasicMaterial({
35 | color: this.color,
36 | side: THREE.DoubleSide,
37 | transparent: true,
38 | blending: THREE.AdditiveBlending,
39 | depthTest: true,
40 | depthWrite: false,
41 | visible: false
42 | });
43 | var src = document.querySelector('#fx3').getAttribute('src');
44 | this.el.sceneEl.systems.material.loadTexture(src, {src: src}, setMap.bind(this));
45 |
46 | function setMap (texture) {
47 | this.gunGlowMaterial.alphaMap = texture;
48 | this.gunGlowMaterial.needsUpdate = true;
49 | this.gunGlowMaterial.visible = true;
50 | }
51 | this.gunGlow = new THREE.Mesh(new THREE.PlaneGeometry(this.scale, this.scale), this.gunGlowMaterial);
52 | this.gunGlow.position.copy(this.gunOffset);
53 | this.el.setObject3D('glow', this.gunGlow);
54 |
55 | this.exploding = false;
56 | this.explodingDuration = 500 + Math.floor(Math.random()*300);
57 | this.el.addEventListener('hit', this.collided.bind(this));
58 |
59 | this.sounds = [
60 | document.getElementById('explosion0'),
61 | document.getElementById('explosion1'),
62 | document.getElementById('explosion2')
63 | ];
64 | // @todo Maybe we could send the time in init?
65 | },
66 | update: function (oldData) {
67 | },
68 | play: function () {
69 | this.paused = false;
70 | },
71 | pause: function () {
72 | this.paused = true;
73 | },
74 | collided: function () {
75 | if (this.exploding) {
76 | return;
77 | }
78 |
79 | this.health--;
80 |
81 | if (this.health <= 0) {
82 | this.el.emit('enemy-hit');
83 | this.exploding = true;
84 |
85 | var mesh = this.el.getObject3D('mesh');
86 | this.whiteMaterial = new THREE.MeshBasicMaterial({color: this.color, transparent: true });
87 | mesh.normalMaterial = mesh.material;
88 | mesh.material = this.whiteMaterial;
89 |
90 | this.gunGlow.visible = false;
91 |
92 | this.system.activeEnemies.splice(this.system.activeEnemies.indexOf(this.el), 1);
93 | }
94 | },
95 |
96 | die: function () {
97 | this.alive = false;
98 | this.reset();
99 | this.system.onEnemyDeath(this.data.name, this.el);
100 | },
101 |
102 | reset: function () {
103 | var mesh = this.el.getObject3D('mesh');
104 | if (mesh) {
105 | mesh.material.opacity = 1;
106 | mesh.scale.set(this.scale, this.scale, this.scale);
107 | mesh.material = mesh.normalMaterial;
108 | this.gunGlow.visible = true;
109 | this.gunGlow.scale.set(1, 1, 1);
110 | this.gunGlowMaterial.opacity = 0.3;
111 | }
112 |
113 | this.el.setAttribute('scale', '1 1 1');
114 | this.explodingTime = undefined;
115 | this.lastShootTime = undefined;
116 | this.shootAt = 0;
117 | this.warmUpTime = 1000;
118 |
119 | this.health = this.maxhealth;
120 | this.alive = true;
121 | this.exploding = false;
122 | this.definition.reset.call(this);
123 | },
124 |
125 | shoot: function (time, delta) {
126 | var el = this.el;
127 | if (!el) return;
128 | var data = this.data;
129 | var mesh = el.object3D;
130 | var gunPosition = mesh.localToWorld(this.gunGlow.position.clone());
131 | var head = el.sceneEl.camera.el.components['look-controls'].dolly.position.clone();
132 | var direction = head.sub(mesh.position).normalize();
133 |
134 | this.lastShootTime = time;
135 |
136 | this.gunGlow.scale.set(3, 3, 3);
137 | this.gunGlowMaterial.opacity = 1;
138 |
139 | /*
140 | var explosion = document.createElement('a-entity');
141 | explosion.setAttribute('position', gunPosition);
142 | explosion.setAttribute('explosion', {
143 | type: 'enemygun',
144 | color: this.color,
145 | scale: this.scale,
146 | lookAt: direction
147 | });
148 | explosion.setAttribute('sound', {
149 | src: document.getElementById(this.data.name + 'shoot').src,
150 | volume: 0.5,
151 | poolSize: 8,
152 | autoplay: true
153 | });
154 | this.el.sceneEl.appendChild(explosion);
155 | */
156 | this.el.sceneEl.systems.explosion.createExplosion('enemygun', gunPosition, this.color, this.scale, direction, this.data.name);
157 |
158 | // Ask system for bullet and set bullet position to starting point.
159 | var bulletEntity = el.sceneEl.systems.bullet.getBullet(data.bulletName);
160 | bulletEntity.setAttribute('bullet', {
161 | position: gunPosition,
162 | direction: direction,
163 | owner: 'enemy'
164 | });
165 | bulletEntity.setAttribute('position', gunPosition);
166 | bulletEntity.setAttribute('visible', true);
167 | bulletEntity.play();
168 | },
169 |
170 | willShoot: function (time, delta, warmUpTime) {
171 | this.shootAt = time + warmUpTime;
172 | this.warmUpTime = warmUpTime;
173 | },
174 |
175 | tick: function (time, delta) {
176 | if (!this.alive || this.paused) {
177 | return;
178 | }
179 | if (!this.exploding) {
180 | //gun glow
181 | var glowFadeOutTime = 700;
182 | if (this.lastShootTime === undefined) {
183 | this.lastShootTime = time;
184 | }
185 | else {
186 | if (this.shootAt - time < this.warmUpTime) {
187 | this.gunGlowMaterial.opacity = (this.shootAt - time) / this.warmUpTime;
188 | var glowScale = 1.0 + Math.abs(Math.sin(time / 50));
189 | this.gunGlow.scale.set(glowScale, glowScale, glowScale);
190 | }
191 | else if (time - this.lastShootTime < glowFadeOutTime) {
192 | this.gunGlowMaterial.opacity = 1 - (time - this.lastShootTime) / glowFadeOutTime;
193 | }
194 | }
195 | this.gunGlow.position.copy(this.gunOffset);
196 | if (this.hipBone) {
197 | this.gunGlow.position.y += this.hipBone.position.y;
198 | }
199 | // Make the droid to look the headset
200 | var head = this.el.sceneEl.camera.el.components['look-controls'].dolly.position.clone();
201 | this.el.object3D.lookAt(head);
202 |
203 | this.definition.tick.call(this, time, delta);
204 | } else {
205 | if (!this.explodingTime) {
206 | this.explodingTime = time;
207 | }
208 | var t0 = (time - this.explodingTime) / this.explodingDuration;
209 |
210 | var scale = this.scale + t0 * ( 2 - t0 ); //out easing
211 |
212 | var mesh = this.el.getObject3D('mesh');
213 | mesh.scale.set(scale, scale, scale);
214 | mesh.material.opacity = Math.max(0, 1 - t0 * 2.5);
215 | if (t0 >= 1) {
216 | this.die();
217 | }
218 | }
219 |
220 | }
221 | });
222 |
--------------------------------------------------------------------------------
/src/components/explosion.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST THREE */
2 |
3 | AFRAME.registerComponent('explosion', {
4 | schema: {
5 | type: { default: 'enemy', oneOf: ['enemy', 'bullet', 'background', 'enemygun'] },
6 | duration: { default: 500 },
7 | color: { type: 'color', default: '#FFFFFF' },
8 | lookAt: { type: 'vec3', default: null},
9 | scale: { default: 1 }
10 | },
11 |
12 | update: function (oldData) {
13 | if (this.data.type === 'enemy') {
14 | this.materials[2].color.set(this.data.color);
15 | this.materials[4].color.set(this.data.color);
16 | } else if (this.data.type === 'bullet') {
17 | this.data.scale *= 0.5; // HACK! remove!
18 | this.materials[0].color.set(this.data.color);
19 | this.materials[2].color.set(this.data.color);
20 | } else if (this.data.type === 'enemygun') {
21 | this.materials[0].color.set(this.data.color);
22 | this.data.duration = 300;
23 | } else if (this.data.type === 'background') {
24 | this.data.duration = 300;
25 | }
26 |
27 | for (var i = 0; i < this.meshes.children.length; i++){
28 | var mesh = this.meshes.children[i];
29 | if (mesh.part.billboard && this.data.lookAt) {
30 | mesh.lookAt(this.data.lookAt);
31 | }
32 | }
33 |
34 | this.el.setAttribute('scale', {x: this.data.scale, y: this.data.scale, z: this.data.scale });
35 | },
36 |
37 | init: function () {
38 | this.life = 0;
39 | this.starttime = null;
40 | this.meshes = new THREE.Group();
41 |
42 | this.materials = [];
43 | var textureSrcs = new Array('#fx1', '#fx2', '#fx3', '#fx4', '#fx8');
44 |
45 | this.el.setAttribute('scale', {x: this.data.scale, y: this.data.scale, z: this.data.scale });
46 |
47 | switch(this.data.type) {
48 | case 'enemy':
49 | this.parts = [
50 | {textureIdx: 2, billboard: true, color: 16777215, scale: 1.5, grow: 4, dispersion: 0, copies: 1, speed: 0 },
51 | {textureIdx: 0, billboard: true, color: 16777215, scale: 0.4, grow: 2, dispersion: 2.5, copies: 1, speed: 1 },
52 | {textureIdx: 3, billboard: false, color: this.data.color, scale: 1, grow: 6, dispersion: 0, copies: 1, speed: 0 },
53 | {textureIdx: 1, billboard: true, color: 16577633, scale: 0.04, grow: 2, dispersion: 3, copies: 20, speed: 2},
54 | {textureIdx: 3, billboard: true, color: this.data.color, scale: 0.2, grow: 2, dispersion: 2, copies: 5, speed: 1}
55 | ];
56 | break;
57 | case 'bullet':
58 | //this.data.scale = this.data.scale * 0.5;
59 | this.parts = [
60 | {textureIdx: 2, billboard: true, color: this.data.color, scale: .5, grow: 3, dispersion: 0, copies: 1, speed: 0 },
61 | {textureIdx: 4, billboard: true, color: '#24CAFF', scale: .3, grow: 4, dispersion: 0, copies: 1, speed: 0 },
62 | {textureIdx: 0, billboard: true, color: this.data.color, scale: 0.04, grow: 2, dispersion: 1.5, copies: 8, speed: 1 }
63 | ];
64 | break;
65 | case 'background':
66 | this.parts = [
67 | {textureIdx: 4, billboard: true, color: '#24CAFF', scale: .3, grow: 3, dispersion: 0, copies: 1, speed: 0 },
68 | {textureIdx: 0, billboard: true, color: '#24CAFF', scale: 0.03, grow: 1, dispersion: 0.3, copies: 8, speed: 1.6, noFade: true }
69 | ];
70 | break;
71 | case 'enemygun':
72 | this.parts = [
73 | {textureIdx: 3, billboard: true, color: this.data.color, scale: .5, grow: 3, dispersion: 0, copies: 1, speed: 0 },
74 | ];
75 | break;
76 | }
77 |
78 |
79 | for (var i in this.parts) {
80 | var part = this.parts[i];
81 | part.meshes = [];
82 | var planeGeometry = new THREE.PlaneGeometry(part.scale, part.scale);
83 | var material = new THREE.MeshBasicMaterial({
84 | color: part.color,
85 | side: THREE.DoubleSide,
86 | transparent: true,
87 | blending: THREE.AdditiveBlending,
88 | depthTest: true,
89 | depthWrite: false,
90 | visible: false
91 | });
92 | material['noFade'] = part['noFade'] === true;
93 |
94 | this.materials.push(material);
95 | var src = document.querySelector(textureSrcs[part.textureIdx]).getAttribute('src');
96 | this.el.sceneEl.systems.material.loadTexture(src, {src: src}, setMap.bind(this, i));
97 |
98 | function setMap (idx, texture) {
99 | this.materials[idx].alphaMap = texture;
100 | this.materials[idx].needsUpdate = true;
101 | this.materials[idx].visible = true;
102 | }
103 |
104 | var dispersionCenter = part.dispersion / 2;
105 |
106 | for (var n = 0; n < part.copies; n++) {
107 | var mesh = new THREE.Mesh(planeGeometry, material);
108 | if (!part.billboard) {
109 | mesh.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
110 | }
111 | else if (this.data.lookAt) {
112 | mesh.lookAt(this.data.lookAt);
113 | }
114 | if (part.dispersion > 0) {
115 | mesh.position.set(
116 | Math.random() * part.dispersion - dispersionCenter,
117 | Math.random() * part.dispersion - dispersionCenter,
118 | Math.random() * part.dispersion - dispersionCenter
119 | );
120 | mesh.speed = part.speed + Math.random() / part.dispersion;
121 | }
122 | mesh.part = part;
123 | this.meshes.add(mesh);
124 | part.meshes.push(mesh);
125 | }
126 | }
127 |
128 | this.el.setObject3D('explosion', this.meshes);
129 | },
130 |
131 | reset: function () {
132 | this.life = 0;
133 | this.starttime = null;
134 |
135 | for (var i in this.parts) {
136 | var part = this.parts[i];
137 |
138 | var dispersionCenter = part.dispersion / 2;
139 |
140 | for (var n = 0; n < part.copies; n++) {
141 | var mesh = part.meshes[n];
142 |
143 | if (!part.billboard) {
144 | mesh.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
145 | }
146 | else if (this.data.lookAt) {
147 | mesh.lookAt(this.data.lookAt);
148 | }
149 | if (part.dispersion > 0) {
150 | mesh.position.set(
151 | Math.random() * part.dispersion - dispersionCenter,
152 | Math.random() * part.dispersion - dispersionCenter,
153 | Math.random() * part.dispersion - dispersionCenter
154 | );
155 | mesh.speed = part.speed + Math.random() / part.dispersion;
156 | }
157 | }
158 | }
159 | this.starttime = null;
160 |
161 |
162 | this.system.returnToPool(this.data.type, this.el);
163 | },
164 |
165 | tick: function (time, delta) {
166 | if (this.starttime === null) {
167 | this.starttime = time;
168 | }
169 | this.life = (time - this.starttime) / this.data.duration;
170 |
171 | if (this.life > 1) {
172 | this.reset();
173 | return;
174 | }
175 |
176 | var t = this.life * ( 2 - this.life ); //out easing
177 |
178 | for (var i = 0; i < this.meshes.children.length; i++){
179 | var mesh = this.meshes.children[i];
180 | var s = 1 + t * mesh.part.grow;
181 | mesh.scale.set(s, s, s);
182 | if (mesh.part.speed > 0) {
183 | mesh.position.multiplyScalar(1 + delta / 1000 * mesh.speed);
184 | }
185 | }
186 | for (var i in this.materials) {
187 | if (this.materials[i].noFade) {
188 | continue;
189 | }
190 | this.materials[i].opacity = 1 - t;
191 | }
192 | }
193 | });
194 |
--------------------------------------------------------------------------------
/src/components/gamestate-debug.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME */
2 | /**
3 | * Display entire game state as text.
4 | */
5 | AFRAME.registerComponent('gamestate-debug', {
6 | init: function () {
7 | var el = this.el;
8 | var sceneEl = this.el.sceneEl;
9 |
10 | sceneEl.addEventListener('gamestate-initialized', setText);
11 | sceneEl.addEventListener('gamestate-changed', setText);
12 |
13 | function setText (evt) {
14 | el.setAttribute('bmfont-text', {text: buildText(evt.detail.state), color: '#DADADA'});
15 | }
16 | }
17 | });
18 |
19 | function buildText (state) {
20 | var text = 'DEBUG\n';
21 | Object.keys(state).sort().forEach(function appendText (property) {
22 | text += property + ': ' + state[property] + '\n';
23 | });
24 | return text;
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/gamestate-visuals.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST THREE */
2 | AFRAME.registerComponent('gamestate-visuals', {
3 | schema: {
4 | },
5 | init: function () {
6 | this.logo = document.getElementById('logo');
7 | this.startEnemy = document.getElementById('start_enemy');
8 | this.mainMenuGroup = document.getElementById('mainmenu');
9 | this.messageGroup = document.getElementById('message-group');
10 | this.gameover = document.getElementById('gameover-model');
11 | this.welldone = document.getElementById('welldone-model');
12 | this.reset = document.getElementById('reset');
13 | this.highscores = document.getElementById('highscores');
14 |
15 | var self = this;
16 | this.el.sceneEl.addEventListener('gamestate-changed', function (evt) {
17 | if ('state' in evt.detail.diff) {
18 | if (evt.detail.state.state === 'STATE_PLAYING') {
19 | self.startPlaying();
20 | } else if (evt.detail.state.state === 'STATE_GAME_OVER') {
21 | self.finishPlaying('GAME_OVER');
22 | } else if (evt.detail.state.state === 'STATE_GAME_WIN') {
23 | self.finishPlaying('GAME_WIN');
24 | } else if (evt.detail.state.state === 'STATE_MAIN_MENU') {
25 | self.mainMenu();
26 | }
27 | }
28 | }.bind(this));
29 | },
30 |
31 | startPlaying: function () {
32 | this.highscores.setAttribute('visible', false);
33 |
34 | var self = this;
35 | var rotation = { x: 0.0 };
36 | var tween = new AFRAME.TWEEN.Tween(rotation)
37 | .to({x: Math.PI * 0.6}, 1000)
38 | .onComplete(function () {
39 | self.mainMenuGroup.setAttribute('visible', false);
40 | })
41 | .easing(AFRAME.TWEEN.Easing.Back.InOut)
42 | .onUpdate(function () {
43 | self.logo.object3D.rotation.x = rotation.x
44 | });
45 | tween.start();
46 | this.startEnemy.setAttribute('visible', false);
47 | },
48 |
49 | finishPlaying: function (type) {
50 | var self = this;
51 | var gameover = type === 'GAME_OVER';
52 |
53 | var group = document.getElementById('finished');
54 |
55 | this.highscores.setAttribute('visible', true);
56 | this.gameover.setAttribute('visible', gameover);
57 | this.welldone.setAttribute('visible', !gameover);
58 |
59 | // Move the text info
60 | group.setAttribute('visible', true);
61 | group.object3D.position.y = -5;
62 |
63 | var groupPosition = { y: -5 };
64 | var tweenGroup = new AFRAME.TWEEN.Tween(groupPosition)
65 | .to({y: 1}, 1000)
66 | .easing(AFRAME.TWEEN.Easing.Elastic.Out)
67 | .onUpdate(function () {
68 | group.object3D.position.y = groupPosition.y;
69 | });
70 | tweenGroup.start();
71 |
72 | // Move the reset buttom
73 | this.reset.object3D.position.y = -5;
74 | var resetPosition = { y: -5 };
75 | var tweenReset = new AFRAME.TWEEN.Tween(resetPosition)
76 | .to({y: 0}, 1000)
77 | .delay(3000)
78 | .easing(AFRAME.TWEEN.Easing.Elastic.Out)
79 | .onUpdate(function () {
80 | self.reset.object3D.position.y = resetPosition.y;
81 | });
82 | tweenReset.start();
83 | },
84 |
85 | mainMenu: function () {
86 | var self = this;
87 | this.startEnemy.setAttribute('position', '0 -5 -4');
88 | this.startEnemy.setAttribute('visible', true);
89 | this.mainMenuGroup.setAttribute('visible', true);
90 |
91 | // Move the enemy up
92 | var enemyPosition = { positionY: -5 };
93 | var tweenEnemy = new AFRAME.TWEEN.Tween(enemyPosition)
94 | .to({positionY: 1.4}, 1000)
95 | .delay(1000)
96 | .easing(AFRAME.TWEEN.Easing.Back.InOut)
97 | .onUpdate(function () {
98 | self.startEnemy.setAttribute('position', {x: 0, y: enemyPosition.positionY, z: -4})
99 | });
100 | tweenEnemy.start();
101 |
102 | // Move the gameover & well done down
103 | var group = document.getElementById('finished');
104 |
105 | group.object3D.position.y = 1;
106 |
107 | var textsPosition = { y: 1 };
108 | var tween = new AFRAME.TWEEN.Tween(textsPosition)
109 | .to({y: -5}, 1000)
110 | .easing(AFRAME.TWEEN.Easing.Elastic.In)
111 | .onComplete(function () {
112 | group.setAttribute('visible', false);
113 | })
114 | .onUpdate(function () {
115 | group.object3D.position.y = textsPosition.y;
116 | });
117 | tween.start();
118 |
119 | // A-Blast logo will appears after a 1s delay
120 | this.logo.object3D.rotation.x = Math.PI * 0.6;
121 |
122 | var logoRotation = { x: 0 };
123 | var tween = new AFRAME.TWEEN.Tween(logoRotation)
124 | .to({x: Math.PI * 0.6}, 1000)
125 | .easing(AFRAME.TWEEN.Easing.Elastic.Out)
126 | .delay(1000)
127 | .onUpdate(function () {
128 | self.logo.object3D.rotation.x = Math.PI * 0.6 - logoRotation.x;
129 | });
130 | tween.start();
131 | }
132 | });
133 |
--------------------------------------------------------------------------------
/src/components/gamestate.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME */
2 | ABLAST.currentScore = {
3 | name: '',
4 | points: 0,
5 | time: 0,
6 | shoots: 0,
7 | validShoot: 0
8 | };
9 |
10 | AFRAME.registerComponent('gamestate', {
11 | schema: {
12 | health: {default: 5},
13 | numEnemies: {default: 0},
14 | numSequences: {default: 0},
15 | points: {default: 0},
16 | numEnemiesToWin: {default: 100},
17 | isGameOver: {default: false},
18 | isGameWin: {default: false},
19 | state: {default: 'STATE_MAIN_MENU', oneOf: ['STATE_MAIN_MENU', 'STATE_PLAYING', 'STATE_GAME_OVER', 'STATE_GAME_WIN']},
20 | wave: {default: 0},
21 | waveSequence: {default: 0}
22 | },
23 |
24 | gameEnd: function (newState, win) {
25 | newState.state = 'STATE_GAME_WIN';
26 | newState.isGameWin = true;
27 | document.getElementById('introMusic').components.sound.playSound();
28 | document.getElementById('mainThemeMusic').components.sound.pauseSound();
29 | },
30 | init: function () {
31 | var self = this;
32 | var el = this.el;
33 | var initialState = this.initialState;
34 | var state = this.data;
35 |
36 | // Initial state.
37 | if (!initialState) { initialState = state; }
38 |
39 | el.emit('gamestate-initialized', {state: initialState});
40 | registerHandler('enemy-death', function (newState) {
41 | newState.points++;
42 | ABLAST.currentScore.points++;
43 | if (newState.points >= self.data.numEnemiesToWin) {
44 | self.gameEnd(newState, true);
45 | }
46 |
47 | newState.numEnemies--;
48 | // All enemies killed, advance wave.
49 | if (newState.numEnemies === 0) {
50 | newState.numSequences--;
51 | newState.waveSequence++;
52 | if (newState.numSequences === 0) {
53 | newState.waveSequence = 0;
54 | newState.wave++;
55 | if (newState.wave >= WAVES.length) {
56 | self.gameEnd(newState, true);
57 | }
58 | }
59 | }
60 | return newState;
61 | });
62 |
63 | registerHandler('wave-created', function (newState, params) {
64 | var wave = params.detail.wave;
65 | newState.numSequences = wave.sequences.length;
66 | newState.waveSequence = 0;
67 | return newState;
68 | });
69 |
70 | registerHandler('enemy-spawn', function (newState) {
71 | newState.numEnemies++;
72 | return newState;
73 | });
74 |
75 | registerHandler('start-game', function (newState) {
76 | newState.isGameOver = false;
77 | newState.isGameWin = false;
78 | newState.state = 'STATE_PLAYING';
79 | return newState;
80 | });
81 |
82 | registerHandler('player-hit', function (newState) {
83 | if (newState.state === 'STATE_PLAYING') {
84 | newState.health -= 1;
85 | if (newState.health <= 0) {
86 | newState.isGameOver = true;
87 | newState.numEnemies = 0;
88 | newState.state = 'STATE_GAME_OVER';
89 | document.getElementById('introMusic').components.sound.playSound();
90 | document.getElementById('mainThemeMusic').components.sound.pauseSound();
91 | }
92 | }
93 | return newState;
94 | });
95 |
96 | registerHandler('reset', function () {
97 | ABLAST.currentScore = {
98 | name: '',
99 | points: 0,
100 | time: 0,
101 | shoots: 0,
102 | validShoot: 0
103 | };
104 |
105 | return initialState;
106 | });
107 |
108 | function registerHandler (event, handler) {
109 | el.addEventListener(event, function (param) {
110 | var newState = handler(AFRAME.utils.extend({}, state), param);
111 | publishState(event, newState);
112 | });
113 | }
114 |
115 | function publishState (event, newState) {
116 | var oldState = AFRAME.utils.extend({}, state);
117 | el.setAttribute('gamestate', newState);
118 | state = newState;
119 | el.emit('gamestate-changed', {
120 | event: event,
121 | diff: AFRAME.utils.diff(oldState, newState),
122 | state: newState
123 | });
124 | }
125 | }
126 | });
127 |
128 | /**
129 | * Bind game state to a component property.
130 | */
131 | AFRAME.registerComponent('gamestate-bind', {
132 | schema: {
133 | default: {},
134 | parse: AFRAME.utils.styleParser.parse
135 | },
136 |
137 | update: function () {
138 | var sceneEl = this.el.closestScene();
139 | if (sceneEl.hasLoaded) {
140 | this.updateBinders();
141 | }
142 | sceneEl.addEventListener('loaded', this.updateBinders.bind(this));
143 | },
144 |
145 | updateBinders: function () {
146 | var data = this.data;
147 | var el = this.el;
148 | var subscribed = Object.keys(this.data);
149 |
150 | el.sceneEl.addEventListener('gamestate-changed', function (evt) {
151 | syncState(evt.detail.diff);
152 | });
153 |
154 | el.sceneEl.addEventListener('gamestate-initialized', function (evt) {
155 | syncState(evt.detail.state);
156 | });
157 |
158 | function syncState (state) {
159 | Object.keys(state).forEach(function updateIfNecessary (stateProperty) {
160 | var targetProperty = data[stateProperty];
161 | var value = state[stateProperty];
162 | if (subscribed.indexOf(stateProperty) === -1) { return; }
163 | AFRAME.utils.entity.setComponentProperty(el, targetProperty, value);
164 | });
165 | }
166 | }
167 | });
168 |
--------------------------------------------------------------------------------
/src/components/gun.js:
--------------------------------------------------------------------------------
1 | var WEAPONS = require('./weapon');
2 |
3 | /**
4 | * Spawn bullets on an event.
5 | * Default schema optimized for Vive controllers.
6 | */
7 | AFRAME.registerComponent('shoot', {
8 | schema: {
9 | direction: {type: 'vec3', default: {x: 0, y: -2, z: -1}}, // Event to fire bullet.
10 | on: {default: 'triggerdown'}, // Event to fire bullet.
11 | spaceKeyEnabled: {default: false}, // Keyboard support.
12 | weapon: {default: 'default'} // Weapon definition.
13 | },
14 |
15 | init: function () {
16 | var data = this.data;
17 | var el = this.el;
18 | var self = this;
19 |
20 | this.coolingDown = false; // Limit fire rate.
21 | this.shoot = this.shoot.bind(this);
22 | this.weapon = null;
23 |
24 | // Add event listener.
25 | if (data.on) { el.addEventListener(data.on, this.shoot); }
26 |
27 | // Add keyboard listener.
28 | if (data.spaceKeyEnabled) {
29 | window.addEventListener('keydown', function (evt) {
30 | if (evt.code === 'Space' || evt.keyCode === '32') { self.shoot(); }
31 | });
32 | }
33 | /*
34 | if (AFRAME.utils.device.isMobile())
35 | {
36 | window.addEventListener('click', function (evt) {
37 | self.shoot();
38 | });
39 | }
40 | */
41 | },
42 |
43 | update: function (oldData) {
44 | // Update weapon.
45 | this.weapon = WEAPONS[this.data.weapon];
46 |
47 | if (oldData.on !== this.data.on) {
48 | this.el.removeEventListener(oldData.on, this.shoot);
49 | this.el.addEventListener(this.data.on, this.shoot);
50 | }
51 | },
52 |
53 | shoot: (function () {
54 | var direction = new THREE.Vector3();
55 | var position = new THREE.Vector3();
56 | var quaternion = new THREE.Quaternion();
57 | var scale = new THREE.Vector3();
58 | var translation = new THREE.Vector3();
59 | var incVive = new THREE.Vector3(0.0, -0.23, -0.15);
60 | var incOculus = new THREE.Vector3(0, -0.23, -0.8);
61 | var inc = new THREE.Vector3();
62 |
63 | return function () {
64 | var bulletEntity;
65 | var el = this.el;
66 | var data = this.data;
67 | var matrixWorld;
68 | var self = this;
69 | var weapon = this.weapon;
70 |
71 | if (this.coolingDown) { return; }
72 |
73 | ABLAST.currentScore.shoots++;
74 |
75 | // Get firing entity's transformations.
76 | el.object3D.updateMatrixWorld();
77 | matrixWorld = el.object3D.matrixWorld;
78 | position.setFromMatrixPosition(matrixWorld);
79 | matrixWorld.decompose(translation, quaternion, scale);
80 |
81 | // Set projectile direction.
82 | direction.set(data.direction.x, data.direction.y, data.direction.z);
83 | direction.applyQuaternion(quaternion);
84 | direction.normalize();
85 |
86 | if (el.components['weapon']) {
87 | inc.copy(el.components.weapon.controllerModel === 'oculus-touch-controller' ? incOculus : incVive);
88 | }
89 | inc.applyQuaternion(quaternion);
90 | position.add(inc);
91 |
92 | // Ask system for bullet and set bullet position to starting point.
93 | bulletEntity = el.sceneEl.systems.bullet.getBullet(weapon.bullet);
94 | bulletEntity.setAttribute('position', position);
95 | bulletEntity.setAttribute('bullet', {
96 | direction: direction.clone(),
97 | position: position.clone(),
98 | owner: 'player'
99 | });
100 | bulletEntity.setAttribute('visible', true);
101 | bulletEntity.setAttribute('position', position);
102 | bulletEntity.play();
103 |
104 | // Communicate the shoot.
105 | el.emit('shoot', bulletEntity);
106 |
107 | // Set cooldown period.
108 | this.coolingDown = true;
109 | setTimeout(function () {
110 | self.coolingDown = false;
111 | }, weapon.shootingDelay);
112 | };
113 | })()
114 | });
115 |
--------------------------------------------------------------------------------
/src/components/headset.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME THREE */
2 | AFRAME.registerComponent('headset', {
3 | schema: {
4 | on: { default: 'click' }
5 | },
6 |
7 | init: function () {
8 | },
9 |
10 | tick: function (time, delta) {
11 | var mesh = this.el.getObject3D('mesh');
12 | if (mesh) {
13 | mesh.update(delta / 1000);
14 | }
15 | this.updatePose();
16 | this.updateButtons();
17 | },
18 |
19 | updatePose: (function () {
20 | var controllerEuler = new THREE.Euler();
21 | var controllerPosition = new THREE.Vector3();
22 | var controllerQuaternion = new THREE.Quaternion();
23 | var dolly = new THREE.Object3D();
24 | var standingMatrix = new THREE.Matrix4();
25 | controllerEuler.order = 'YXZ';
26 | return function () {
27 | var controller;
28 | var pose;
29 | var orientation;
30 | var position;
31 | var el = this.el;
32 | var vrDisplay = this.system.vrDisplay;
33 | this.update();
34 | controller = this.controller;
35 | if (!controller) { return; }
36 | pose = controller.pose;
37 | orientation = pose.orientation || [0, 0, 0, 1];
38 | position = pose.position || [0, 0, 0];
39 | controllerQuaternion.fromArray(orientation);
40 | dolly.quaternion.fromArray(orientation);
41 | dolly.position.fromArray(position);
42 | dolly.updateMatrix();
43 | if (vrDisplay && vrDisplay.stageParameters) {
44 | standingMatrix.fromArray(vrDisplay.stageParameters.sittingToStandingTransform);
45 | dolly.applyMatrix(standingMatrix);
46 | }
47 | controllerEuler.setFromRotationMatrix(dolly.matrix);
48 | controllerPosition.setFromMatrixPosition(dolly.matrix);
49 | el.setAttribute('rotation', {
50 | x: THREE.Math.radToDeg(controllerEuler.x),
51 | y: THREE.Math.radToDeg(controllerEuler.y),
52 | z: THREE.Math.radToDeg(controllerEuler.z)
53 | });
54 | el.setAttribute('position', {
55 | x: controllerPosition.x,
56 | y: controllerPosition.y,
57 | z: controllerPosition.z
58 | });
59 | };
60 | })()
61 | });
62 |
--------------------------------------------------------------------------------
/src/components/highscores.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST THREE */
2 | AFRAME.registerSystem('highscores', {
3 | schema: {
4 | maxScores: {default: 10},
5 | },
6 |
7 | init: function () {
8 | if (!this.isHighScore()) {
9 | console.warn('Highscore can\'t be loaded or saved as no localStorage support found!');
10 | return;
11 | }
12 |
13 | this.scores = [];
14 | if (localStorage['highscores']) {
15 | this.scores = JSON.parse(localStorage['highscores']);
16 | } else {
17 | for (var i = 0; i < 5; i++) {
18 | this.scores[i] = {
19 | name: 'nobody',
20 | points: i*10,
21 | shoots: 0,
22 | time: 0,
23 | validShoot: 0
24 | };
25 | }
26 | }
27 |
28 | var self = this;
29 | var ablastUI = document.getElementById('ablast-ui');
30 | document.getElementById('save-score').addEventListener('click', function (event) {
31 | ABLAST.currentScore.name = document.getElementById('player-name').value;
32 | self.addNewScore(ABLAST.currentScore);
33 | ablastUI.style.display = 'none';
34 | });
35 |
36 | this.sceneEl.addEventListener('gamestate-changed', function (evt) {
37 | if ('state' in evt.detail.diff) {
38 | if (evt.detail.state.state === 'STATE_GAME_OVER' || evt.detail.state.state === 'STATE_GAME_WIN') {
39 | ablastUI.style.display = self.isHighScore(ABLAST.currentScore) ? 'block' : 'none';
40 | }
41 | }
42 | });
43 | },
44 |
45 | isHighScore: function ()
46 | {
47 | try {
48 | return 'localStorage' in window && window['localStorage'] !== null;
49 | } catch (e) {
50 | return false;
51 | }
52 | },
53 |
54 | shouldStoreScore: function (data) {
55 | return (this.scores.length < this.data.maxScores ||
56 | this.scores[this.scores.length - 1].points < data.points);
57 | },
58 |
59 | addNewScore: function (data) {
60 | // Check if we need to insert it
61 | if (this.shouldStoreScore(data)) {
62 | this.scores.push(data);
63 |
64 | this.scores.sort(function(a,b) {
65 | return parseInt(a.points) <= parseInt(b.points);
66 | });
67 |
68 | if (this.scores.length > this.data.maxScores) {
69 | this.scores.pop();
70 | }
71 |
72 | localStorage['highscores'] = JSON.stringify(this.scores);
73 | this.sceneEl.emit('highscores-updated', this.scores);
74 | return true;
75 | }
76 | return false;
77 | }
78 | });
79 |
80 | AFRAME.registerComponent('highscores', {
81 | schema: {},
82 |
83 | init: function () {
84 | var el = this.el;
85 | var self = this;
86 | var sceneEl = this.el.sceneEl;
87 |
88 | sceneEl.addEventListener('highscores-updated', function (event) {
89 | el.setAttribute('bmfont-text', {text: buildText(self.system.scores)});
90 | });
91 |
92 | el.setAttribute('bmfont-text', {
93 | fntImage: 'assets/fonts/mozillavr.png',
94 | fnt: 'assets/fonts/mozillavr.fnt',
95 | scale: 0.0015,
96 | baseline: 'top',
97 | lineHeight: 90,
98 | text: buildText(this.system.scores),
99 | color: '#24caff'
100 | });
101 | }
102 | });
103 |
104 | function buildText (scores) {
105 | var text = '';
106 | scores.sort(function(a,b) {
107 | return parseInt(a.points) <= parseInt(b.points);
108 | }).forEach(function appendText (score) {
109 | name = score.name.toLowerCase();
110 | var score = score.points.toString();
111 | for (var i = 10; i <= 100; i *= 10) {
112 | if (score < i) score = '0' + score;
113 | }
114 | text += score.pad(7) + name + '\n';
115 | });
116 | return text;
117 | }
118 |
--------------------------------------------------------------------------------
/src/components/json-model.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME THREE */
2 | AFRAME.registerComponent('json-model', {
3 | schema: {
4 | src: {type: 'asset'},
5 | singleModel: {default: false},
6 | texturePath: {type: 'asset', default: ''},
7 | debugNormals: {default: false},
8 | debugNormalsLength: {default: 0.2},
9 | debugBones: {default: false}
10 | },
11 |
12 | init: function () {
13 | },
14 |
15 | fixNormal: function (vector) {
16 | var t = vector.y;
17 | vector.y = -vector.z;
18 | vector.z = t;
19 | },
20 |
21 | update: function (oldData) {
22 | this.loader = null;
23 | this.helpers = new THREE.Group();
24 | this.mixers = [];
25 | this.animationNames = [];
26 | this.skeletonHelper = null;
27 |
28 | var src = this.data.src;
29 | if (!src || src === oldData.src) { return; }
30 |
31 | if (this.data.singleModel) {
32 | this.loader = new THREE.JSONLoader();
33 | this.loader.setTexturePath(this.data.texturePath);
34 | this.loader.load(src, this.onModelLoaded.bind(this));
35 | }
36 | else {
37 | this.loader = new THREE.ObjectLoader();
38 | this.loader.setCrossOrigin('');
39 | this.loader.load(src, this.onSceneLoaded.bind(this));
40 | }
41 | },
42 |
43 | onModelLoaded: function(geometry, materials) {
44 | this.helpers = new THREE.Group();
45 |
46 | var mesh = new THREE.SkinnedMesh(geometry, materials[0]);
47 | var self = this;
48 | mesh.geometry.faces.forEach(function(face) {
49 | face.vertexNormals.forEach(function(vertex) {
50 | if (!vertex.hasOwnProperty('fixed')) {
51 | self.fixNormal(vertex);
52 | vertex.fixed = true;
53 | }
54 | });
55 | });
56 |
57 | if (mesh.geometry['animations'] !== undefined && mesh.geometry.animations.length > 0){
58 | mesh.material.skinning = true;
59 | var mixer = {mixer: new THREE.AnimationMixer(mesh), clips: {}};
60 | for (var i in mesh.geometry.animations) {
61 | var anim = mesh.geometry.animations[i];
62 | var clip = mixer.mixer.clipAction(anim).stop();
63 | clip.setEffectiveWeight(1);
64 | mixer.clips[anim.name] = clip;
65 | }
66 | this.mixers.push(mixer);
67 | }
68 |
69 | self.addNormalHelpers(mesh);
70 |
71 | this.helpers.visible = this.data.debugNormals;
72 | this.el.setObject3D('helpers', this.helpers);
73 |
74 | this.skeletonHelper = new THREE.SkeletonHelper( mesh );
75 | this.skeletonHelper.material.linewidth = 2;
76 | this.el.setObject3D('skelhelper', this.skeletonHelper );
77 | this.skeletonHelper.visible = this.data.debugBones;
78 |
79 | this.el.setObject3D('mesh', mesh);
80 | this.el.emit('model-loaded', {format: 'json', model: mesh, src: this.data.src});
81 | },
82 |
83 | onSceneLoaded: function(group) {
84 | this.helpers = new THREE.Group();
85 |
86 | if (group['animations'] !== undefined) {
87 | var mixer = {mixer: new THREE.AnimationMixer(group), clips: {}};
88 | for (var i in group.animations) {
89 | var anim = group.animations[i];
90 | var clip = mixer.mixer.clipAction(anim).stop();
91 | mixer.clips[anim.name] = clip;
92 | }
93 | this.mixers.push(mixer);
94 | }
95 | var self = this;
96 | group.traverse(function (child) {
97 | if (!(child instanceof THREE.Mesh)) { return; }
98 |
99 | child.geometry.faces.forEach(function(face) {
100 | face.vertexNormals.forEach(function(vertex) {
101 | if (!vertex.hasOwnProperty('fixed')) {
102 | self.fixNormal(vertex);
103 | vertex.fixed = true;
104 | }
105 | });
106 | });
107 |
108 | self.addNormalHelpers(child);
109 | });
110 |
111 | this.helpers.visible = this.data.debugNormals;
112 | this.el.setObject3D('helpers', this.helpers);
113 | this.el.setObject3D('mesh', group);
114 | this.el.emit('model-loaded', {format: 'json', model: group, src: this.data.src});
115 | },
116 |
117 | addNormalHelpers: function (mesh) {
118 | var fnh = new THREE.FaceNormalsHelper(mesh, this.data.debugNormalsLength);
119 | this.helpers.add(fnh);
120 | var vnh = new THREE.VertexNormalsHelper(mesh, this.data.debugNormalsLength);
121 | this.helpers.add(vnh);
122 |
123 | mesh.geometry.normalsNeedUpdate = true;
124 | mesh.geometry.verticesNeedUpdate = true;
125 | },
126 |
127 | playAnimation: function (animationName, repeat) {
128 | for (var i in this.mixers) {
129 | var clip = this.mixers[i].clips[animationName];
130 | if (clip === undefined) continue;
131 | clip.stop().play();
132 | var repetitions = 0;
133 | if (repeat === true) repetitions = Infinity;
134 | else if (repeat == undefined) repeat = false;
135 | else if (typeof(repeat) == 'number') {
136 | if (repeat === 0) repeat = false;
137 | repetitions = repeat;
138 | }
139 | else repeat = false;
140 | clip.setLoop( repeat ? THREE.LoopRepeat : THREE.LoopOnce, repetitions );
141 | }
142 | },
143 |
144 | stopAnimation: function () {
145 | for (var i in this.mixers) {
146 | for (var j in this.mixers[i].clips) {
147 | this.mixers[i].clips[j].stop();
148 | }
149 | }
150 | },
151 |
152 | tick: function(time, timeDelta) {
153 | for (var i in this.mixers) {
154 | this.mixers[i].mixer.update( timeDelta / 1000 );
155 | }
156 | }
157 | });
158 |
--------------------------------------------------------------------------------
/src/components/lifes-counter.js:
--------------------------------------------------------------------------------
1 | /* global THREE AFRAME */
2 | var LetterPanel = require('../lib/letterpanel');
3 |
4 | AFRAME.registerComponent('lifes-counter', {
5 | schema: {
6 | width: {default: 0.9},
7 | value: {default: ' '},
8 | numSegments: {default: 6},
9 | height: {default: 0.2},
10 | color: {default: 0xff0000}
11 | },
12 |
13 | init: function () {
14 | this.letterPanel = new LetterPanel(this.el.sceneEl.systems.material, this.data);
15 | this.el.setObject3D('mesh', this.letterPanel.group);
16 | },
17 |
18 | update: function () {
19 | var value = this.data.value;
20 | var computed = [0,0,0,0,0].map(function(e,i) {return i >= value ? ' ': '*'}).reverse().join('');
21 | this.letterPanel.update(computed);
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/src/components/points-counter.js:
--------------------------------------------------------------------------------
1 | /* global THREE AFRAME */
2 | var LetterPanel = require('../lib/letterpanel');
3 |
4 | AFRAME.registerComponent('points-counter', {
5 | schema: {
6 | value: {default: 0},
7 | height: {default: 0.7},
8 | numSegments: {default: 3},
9 | width: {default: 0.9},
10 | color: {default: 0x024caff}
11 | },
12 |
13 | init: function () {
14 | this.letterPanel = new LetterPanel(this.el.sceneEl.systems.material, this.data);
15 | this.el.setObject3D('mesh', this.letterPanel.group);
16 |
17 | this.el.sceneEl.addEventListener('enemy-death', function () {
18 |
19 | });
20 | },
21 |
22 | update: function () {
23 | this.letterPanel.update(this.data.value.padLeft(3));
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/proxy_event.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerComponent('proxy_event', {
2 | schema: {
3 | event: { default: '' },
4 | dst: { type: 'selector' },
5 | bubbles: { default: false }
6 | },
7 |
8 | init: function () {
9 | this.el.sceneEl.addEventListener(this.data.event, function (event) {
10 | this.data.dst.emit(this.data.event, event, this.data.bubbles);
11 | }.bind(this));
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/restrict-position.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerComponent('restrict-position', {
2 | schema: {
3 | },
4 |
5 | init: function () {
6 | this.active = !AFRAME.utils.device.checkHeadsetConnected();
7 | this.radius = 2;
8 | },
9 |
10 | tick: function (time, delta) {
11 | if (!this.active) { return; }
12 | var fromCircleToObject = new THREE.Vector3();
13 | var y = this.el.object3D.position.y;
14 | fromCircleToObject.copy(this.el.object3D.position);
15 | var len = this.radius / fromCircleToObject.length();
16 | if (len < 0.98) {
17 | fromCircleToObject.multiplyScalar(this.radius / fromCircleToObject.length());
18 | this.el.setAttribute('position', {
19 | x: fromCircleToObject.x,
20 | y: y,
21 | z: fromCircleToObject.z
22 | });
23 | }
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/shoot-controls.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME */
2 | AFRAME.registerComponent('shoot-controls', {
3 | // dependencies: ['tracked-controls'],
4 | schema: {
5 | hand: { default: 'left' }
6 | },
7 |
8 | init: function () {
9 | var self = this;
10 |
11 | this.onButtonChanged = this.onButtonChanged.bind(this);
12 | this.onButtonDown = function (evt) { self.onButtonEvent(evt.detail.id, 'down'); };
13 | this.onButtonUp = function (evt) { self.onButtonEvent(evt.detail.id, 'up'); };
14 | },
15 |
16 | play: function () {
17 | var el = this.el;
18 | el.addEventListener('buttonchanged', this.onButtonChanged);
19 | el.addEventListener('buttondown', this.onButtonDown);
20 | el.addEventListener('buttonup', this.onButtonUp);
21 | },
22 |
23 | pause: function () {
24 | var el = this.el;
25 | el.removeEventListener('buttonchanged', this.onButtonChanged);
26 | el.removeEventListener('buttondown', this.onButtonDown);
27 | el.removeEventListener('buttonup', this.onButtonUp);
28 | },
29 |
30 | // buttonId
31 | // 0 - trackpad
32 | // 1 - trigger ( intensity value from 0.5 to 1 )
33 | // 2 - grip
34 | // 3 - menu ( dispatch but better for menu options )
35 | // 4 - system ( never dispatched on this layer )
36 | mapping: {
37 | axis0: 'trackpad',
38 | axis1: 'trackpad',
39 | button0: 'trackpad',
40 | button1: 'trigger',
41 | button2: 'grip',
42 | button3: 'menu',
43 | button4: 'system'
44 | },
45 |
46 | onButtonChanged: function (evt) {
47 | var buttonName = this.mapping['button' + evt.detail.id];
48 | if (buttonName !== 'trigger') { return; }
49 | var value = evt.detail.state.value;
50 | this.el.components['weapon'].setTriggerPressure(value);
51 | },
52 |
53 | onButtonEvent: function (id, evtName) {
54 | var buttonName = this.mapping['button' + id];
55 | this.el.emit(buttonName + evtName);
56 | },
57 |
58 | update: function () {
59 | var data = this.data;
60 | var el = this.el;
61 | el.setAttribute('vive-controls', {hand: data.hand, model: false});
62 | el.setAttribute('oculus-touch-controls', {hand: data.hand, model: false});
63 | el.setAttribute('windows-motion-controls', {hand: data.hand, model: false});
64 | if (data.hand === 'right') {
65 | el.setAttribute('daydream-controls', {hand: data.hand, model: false});
66 | el.setAttribute('gearvr-controls', {hand: data.hand, model: false});
67 | }
68 | }
69 | });
70 |
--------------------------------------------------------------------------------
/src/components/sound-fade.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME ABLAST */
2 |
3 | AFRAME.registerComponent('sound-fade', {
4 | schema: {
5 | from: {default: 0.0},
6 | to: {default: 1.0},
7 | duration: {default: 1000},
8 | },
9 |
10 | init: function () {
11 | if (this.el.getAttribute('sound')) {
12 | this.el.setAttribute('sound', 'volume', this.data.from);
13 | this.fadeEnded = false;
14 | this.diff = this.data.to - this.data.from;
15 | }
16 | else {
17 | this.fadeEnded = true;
18 | }
19 | },
20 |
21 | update: function (oldData) {
22 | this.endTime = undefined;
23 | this.fadeEnded = false;
24 | this.diff = this.data.to - this.data.from;
25 | },
26 |
27 | tick: function (time, delta) {
28 | if (this.fadeEnded) {
29 | return;
30 | }
31 | if (this.endTime === undefined) {
32 | this.endTime = time + this.data.duration;
33 | return;
34 | }
35 |
36 | var ease = 1 - (this.endTime - time) / this.data.duration;
37 | ease = Math.max(0, Math.min(1, ease * ease)); //easeQuadIn
38 | var vol = this.data.from + this.diff * ease;
39 | this.el.setAttribute('sound', 'volume', vol);
40 | if (ease === 1) {
41 | this.fadeEnded = true;
42 | }
43 | }
44 | });
--------------------------------------------------------------------------------
/src/components/spline-line.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME, THREE */
2 |
3 | /**
4 | * Draw spline.
5 | * Grab the spline object using `pointer`, which reaches into a component for the spline.
6 | * Some extra code done to generalize + decouple the component.
7 | */
8 | AFRAME.registerComponent('spline-line', {
9 | schema: {
10 | pointer: {default: ''}, // `[componentName].[member]`.
11 | numPoints: {default: 250}
12 | },
13 |
14 | init: function () {
15 | var componentName;
16 | var data = this.data;
17 | var el = this.el;
18 | var self = this;
19 | var spline;
20 |
21 | this.pointMeshes = [];
22 |
23 | // TODO: Get `component-initialized` event.
24 | spline = getSpline();
25 | if (spline) {
26 | this.drawLine(spline);
27 | } else {
28 | componentName = data.pointer.split('.')[0];
29 | el.addEventListener('componentchanged', function (evt) {
30 | if (evt.detail.name !== componentName) { return; }
31 | self.drawLine(getSpline());
32 | });
33 | }
34 |
35 | function getSpline () {
36 | var split = data.pointer.split('.');
37 | var componentName = split.shift();
38 | var member = el.components[componentName];
39 | while (split.length) {
40 | if (!member) { return; }
41 | member = member[split.shift()];
42 | }
43 | return member;
44 | }
45 | },
46 |
47 | drawLine: function (spline) {
48 | var data = this.data;
49 | var el = this.el;
50 | var geometry;
51 | var i;
52 | var pointMeshes = this.pointMeshes;
53 | var material;
54 |
55 | // Create line.
56 | geometry = new THREE.Geometry();
57 | material = new THREE.LineBasicMaterial({
58 | color: new THREE.Color(Math.random(), Math.random(), Math.random())
59 | });
60 | for (i = 0; i < data.numPoints; i++) {
61 | var point = spline.getPoint(i / data.numPoints);
62 | geometry.vertices.push(new THREE.Vector3(point.x, point.y, point.z));
63 | }
64 | geometry.verticesNeedsUpdate = true;
65 |
66 | // Draw points.
67 | spline.points.forEach(function addWaypoint (point) {
68 | var geometry = new THREE.SphereGeometry(0.2, 16, 16);
69 | var material = new THREE.MeshBasicMaterial({color: '#111'});
70 | var mesh = new THREE.Mesh(geometry, material);
71 | mesh.position.set(point.x, point.y, point.z);
72 | pointMeshes.push(mesh);
73 | el.sceneEl.object3D.add(mesh);
74 | });
75 |
76 | // Append line to scene.
77 | this.line = new THREE.Line(geometry, material);
78 | el.sceneEl.object3D.add(this.line);
79 | },
80 |
81 | remove: function () {
82 | var scene = this.el.sceneEl.object3D;
83 | if (!this.line) { return; }
84 | scene.remove(this.line);
85 | this.pointMeshes.forEach(function (point) {
86 | scene.remove(point);
87 | });
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/src/components/timer-counter.js:
--------------------------------------------------------------------------------
1 | var LetterPanel = require('../lib/letterpanel');
2 |
3 | /* global THREE AFRAME */
4 | AFRAME.registerComponent('timer-counter', {
5 | schema: {
6 | width: {default: 0.9},
7 | value: {default: ''},
8 | numSegments: {default: 5},
9 | height: {default: 0.35},
10 | color: {default: 0x024caff}
11 | },
12 |
13 | init: function () {
14 | this.letterPanel = new LetterPanel(this.el.sceneEl.systems.material, this.data);
15 | this.el.setObject3D('mesh', this.letterPanel.group);
16 | var self = this;
17 | this.el.sceneEl.addEventListener('countdown-update', function(event) {
18 | var t = event.detail;
19 | var value = t.minutes.padLeft(2) + ':' + t.seconds.padLeft(2);
20 | self.letterPanel.material.color.set(t.total <= 10000 ? 0xff0000 : 0x24caff);
21 | self.el.setAttribute('timer-counter', {value: value});
22 | });
23 | },
24 |
25 | update: function () {
26 | this.letterPanel.update(this.data.value);
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/vr-analytics.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerComponent('vr-analytics', {
2 | init: function () {
3 | var el = this.el;
4 | var emitted = false;
5 |
6 | el.addEventListener('enter-vr', function () {
7 | if (emitted || !AFRAME.utils.device.checkHeadsetConnected() ||
8 | AFRAME.utils.device.isMobile()) { return; }
9 | ga('send', 'event', 'General', 'entervr');
10 | emitted = true;
11 | });
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/weapon.js:
--------------------------------------------------------------------------------
1 | // Weapon definitions.
2 | var WEAPONS = {
3 | default: {
4 | model: {
5 | url: 'url(assets/models/gun.json)',
6 | positionOffset: [0, 0, 0],
7 | rotationOffset: [0, 0, 0]
8 | },
9 | shootSound: 'url(assets/sounds/gun.ogg)',
10 | shootingDelay: 100, // In ms
11 | bullet: 'default'
12 | }
13 | };
14 |
15 |
16 | /**
17 | * Tracked controls, gun model, firing animation, shooting effects.
18 | */
19 | AFRAME.registerComponent('weapon', {
20 | dependencies: ['shoot-controls'],
21 |
22 | schema: {
23 | enabled: { default: true },
24 | type: { default: 'default' }
25 | },
26 |
27 | updateWeapon: function () {
28 | console.log(this.controllerModel);
29 | if (this.controllerModel === 'oculus-touch-controller') {
30 | this.model.applyMatrix(new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, 0), 0.8));
31 | this.el.setAttribute('shoot', {direction: '0 -0.3 -1'});
32 | } else if (this.controllerModel === 'daydream-controls') {
33 | document.getElementById('rightHandPivot').setAttribute('position', '-0.2 0 -0.5');
34 | this.el.setAttribute('shoot', {on: 'trackpaddown'});
35 | }
36 | },
37 | init: function () {
38 | var el = this.el;
39 | var self = this;
40 |
41 | this.model = null;
42 | this.isGamepadConnected = false;
43 | this.controllerModel = null;
44 | this.weapon = WEAPONS[ this.data.type ];
45 |
46 | el.setAttribute('json-model', {src: this.weapon.model.url});
47 |
48 | el.setAttribute('sound', {
49 | src: this.weapon.shootSound,
50 | on: 'shoot',
51 | volume: 0.5,
52 | poolSize: 10
53 | });
54 |
55 | this.fires = [];
56 | this.trigger = null;
57 |
58 | el.addEventListener('controllerconnected', function (evt) {
59 | console.log(evt);
60 | self.controllerModel = evt.detail.name;
61 | if (self.model == null) {
62 | self.isGamepadConnected = true;
63 | } else {
64 | self.updateWeapon();
65 | }
66 | });
67 |
68 | el.addEventListener('model-loaded', function (evt) {
69 | this.model = evt.detail.model;
70 | var modelWithPivot = new THREE.Group();
71 | modelWithPivot.add(this.model);
72 | el.setObject3D('mesh', modelWithPivot);
73 |
74 | for (var i = 0; i < 3; i++){
75 | var fire = this.model.getObjectByName('fire'+i);
76 | if (fire) {
77 | fire.material.depthWrite = false;
78 | fire.visible = false;
79 | this.fires.push(fire);
80 | }
81 | }
82 |
83 | if (this.isGamepadConnected) {
84 | this.updateWeapon();
85 | }
86 |
87 | this.trigger = this.model.getObjectByName('trigger');
88 |
89 | }.bind(this));
90 |
91 | var self = this;
92 | el.addEventListener('shoot', function (evt) {
93 | el.components['json-model'].playAnimation('default');
94 | self.light.components.light.light.intensity = self.lightIntensity;
95 | for (var i in self.fires){
96 | self.fires[i].visible = true;
97 | self.fires[i].life = 50 + Math.random() * 100;
98 | }
99 | });
100 |
101 | this.lightIntensity = 3.0;
102 | this.life = this.data.lifespan;
103 | this.canShoot = true;
104 |
105 | this.light = document.createElement('a-entity');
106 | el.appendChild(this.light);
107 |
108 | this.light.setAttribute('light', {color: '#24CAFF', intensity: 0.0, type: 'point'});
109 | this.light.setAttribute('position', {x: 0, y: -0.22, z: -0.14});
110 | var self = this;
111 | this.light.addEventListener('loaded', function () {
112 | self.lightObj = self.light.components.light.light; // threejs light
113 | })
114 | },
115 |
116 | tick: function (time, delta) {
117 | if (this.lightObj && this.lightObj.intensity > 0.0) {
118 | this.light.visible = true;
119 | this.lightObj.intensity -= delta / 1000 * 10;
120 | if (this.lightObj.intensity < 0.0) {
121 | this.lightObj.intensity = 0.0;
122 | this.light.visible = false;
123 | }
124 | for (var i in this.fires) {
125 | if (!this.fires[i].visible) continue;
126 | this.fires[i].life -= delta;
127 | if (i == 0) {
128 | this.fires[i].rotation.set(0, Math.random() * Math.PI * 2, 0);
129 | }
130 | else {
131 | this.fires[i].rotation.set(0, Math.random() * 1 - 0.5 + (Math.random() > 0.5 ? Math.PI: 0) , 0);
132 | }
133 | if (this.fires[i].life < 0){
134 | this.fires[i].visible = false;
135 | }
136 | }
137 | }
138 | },
139 |
140 | update: function () {
141 | var data = this.data;
142 | this.weapon = WEAPONS[ data.type ];
143 | },
144 |
145 | setTriggerPressure: function (pressure) {
146 | if (this.trigger) {
147 | this.trigger.position.setY(pressure * 0.01814);
148 | }
149 | }
150 | });
151 |
152 | module.exports = WEAPONS;
153 |
--------------------------------------------------------------------------------
/src/enemies/enemy0.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerEnemy(
3 | // name
4 | 'enemy0',
5 | // data
6 | {
7 | components: {
8 | enemy: {
9 | name: 'enemy0',
10 | bulletName: 'enemy-slow',
11 | color: '#FFB911',
12 | scale: 0.9,
13 | health: 1
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 0.4
18 | },
19 | 'json-model': {
20 | src: '#enemy0',
21 | texturePath: 'url(assets/images/)',
22 | singleModel: true
23 | }
24 | },
25 | poolSize: 10
26 | },
27 | // implementation
28 | {
29 | init: function () {
30 | this.shootingDelay = 3000;
31 | this.warmUpTime = 1000;
32 | this.reset();
33 | },
34 | reset: function () {
35 | var el = this.el;
36 | var sc = this.data.scale;
37 | this.actualShootingDelay = this.shootingDelay + Math.floor(this.shootingDelay * Math.random());
38 |
39 | el.addEventListener('model-loaded', function(event) {
40 | el.getObject3D('mesh').scale.set(sc, sc, sc);
41 | });
42 | this.lastShoot = undefined;
43 | this.willShootEmited = false;
44 | },
45 | tick: function (time, delta) {
46 | if (this.lastShoot == undefined ) {
47 | this.lastShoot = time;
48 | }
49 | else if (time - this.lastShoot > this.actualShootingDelay) {
50 | // don't shoot when behind the player
51 | var pos = this.el.getAttribute('position');
52 | if (pos.z < 0 && pos.y > 0) {
53 | this.el.components.enemy.shoot(time, delta);
54 | this.lastShoot = time;
55 | this.willShootEmited = false;
56 | this.actualShootingDelay = this.shootingDelay * (Math.random() < 0.7 ? 2 : 1);
57 | }
58 | }
59 | else if (!this.willShootEmited && time - this.lastShoot > this.actualShootingDelay - this.warmUpTime) {
60 | this.el.components.enemy.willShoot(time, delta, this.warmUpTime);
61 | this.willShootEmited = true;
62 | }
63 | },
64 | onHit: function (type) {}
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/enemies/enemy1.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerEnemy(
3 | // name
4 | 'enemy1',
5 | // data
6 | {
7 | components: {
8 | enemy: {
9 | name: 'enemy1',
10 | bulletName: 'enemy-fast',
11 | color: '#FF7D00',
12 | scale: 0.6,
13 | health: 1
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 0.3
18 | },
19 | 'json-model': {
20 | src: '#enemy1',
21 | texturePath: 'url(assets/images/)',
22 | singleModel: true
23 | }
24 | },
25 | poolSize: 10
26 | },
27 | // implementation
28 | {
29 | init: function () {
30 | this.shootingDelay = 5000;
31 | this.warmUpTime = 1000;
32 | this.reset();
33 | },
34 | reset: function () {
35 | var el = this.el;
36 | var sc = this.data.scale;
37 | this.actualShootingDelay = this.shootingDelay + Math.floor(this.shootingDelay * Math.random());
38 |
39 | el.addEventListener('model-loaded', function(event) {
40 | el.getObject3D('mesh').scale.set(sc, sc, sc);
41 | });
42 | this.lastShoot = undefined;
43 | this.willShootEmited = false;
44 | },
45 | tick: function (time, delta) {
46 | if (this.lastShoot == undefined ) {
47 | this.lastShoot = time;
48 | }
49 | else if (time - this.lastShoot > this.actualShootingDelay) {
50 | // don't shoot when behind the player
51 | var pos = this.el.getAttribute('position');
52 | if (pos.z < 0 && pos.y > 0) {
53 | this.el.components.enemy.shoot(time, delta);
54 | this.lastShoot = time;
55 | this.willShootEmited = false;
56 | this.actualShootingDelay = this.shootingDelay * (Math.random() < 0.5 ? 2 : 1);
57 | }
58 | }
59 | else if (!this.willShootEmited && time - this.lastShoot > this.actualShootingDelay - this.warmUpTime) {
60 | this.el.components.enemy.willShoot(time, delta, this.warmUpTime);
61 | this.willShootEmited = true;
62 | }
63 | },
64 | onHit: function (type) {}
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/enemies/enemy2.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerEnemy(
3 | // name
4 | 'enemy2',
5 | // data
6 | {
7 | components: {
8 | enemy: {
9 | name: 'enemy2',
10 | bulletName: 'enemy-medium',
11 | color: '#FF5533',
12 | scale: 1.3,
13 | health: 3
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 0.6
18 | },
19 | 'json-model': {
20 | src: '#enemy2',
21 | texturePath: 'url(assets/images/)',
22 | singleModel: true
23 | }
24 | },
25 | poolSize: 10
26 | },
27 | // implementation
28 | {
29 | init: function () {
30 | this.shootingDelay = 2000;
31 | this.warmUpTime = 800;
32 | this.reset();
33 | },
34 | reset: function () {
35 | var el = this.el;
36 | var sc = this.data.scale;
37 | this.actualShootingDelay = this.shootingDelay + Math.floor(this.shootingDelay * Math.random());
38 |
39 | el.addEventListener('model-loaded', function(event) {
40 | el.getObject3D('mesh').scale.set(sc, sc, sc);
41 | });
42 | this.lastShoot = undefined;
43 | this.willShootEmited = false;
44 | },
45 | tick: function (time, delta) {
46 | if (this.lastShoot == undefined ) {
47 | this.lastShoot = time;
48 | }
49 | else if (time - this.lastShoot > this.actualShootingDelay) {
50 | // don't shoot when behind the player
51 | var pos = this.el.getAttribute('position');
52 | if (pos.z < 0 && pos.y > 0) {
53 | this.el.components.enemy.shoot(time, delta);
54 | this.lastShoot = time;
55 | this.willShootEmited = false;
56 | this.actualShootingDelay = this.shootingDelay * (Math.random() < 0.2 ? 2 : 1);
57 | }
58 | }
59 | else if (!this.willShootEmited && time - this.lastShoot > this.actualShootingDelay - this.warmUpTime) {
60 | this.el.components.enemy.willShoot(time, delta, this.warmUpTime);
61 | this.willShootEmited = true;
62 | }
63 | },
64 | onHit: function (type) {}
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/enemies/enemy3.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 | ABLAST.registerEnemy(
3 | // name
4 | 'enemy3',
5 | // data
6 | {
7 | components: {
8 | enemy: {
9 | name: 'enemy3',
10 | bulletName: 'enemy-fat',
11 | color: '#8762FF',
12 | scale: 2.5,
13 | health: 30
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 1.2
18 | },
19 | 'json-model': {
20 | src: '#enemy3',
21 | texturePath: 'url(assets/images/)',
22 | singleModel: true
23 | }
24 | },
25 | poolSize: 4
26 | },
27 | // implementation
28 | {
29 | init: function () {
30 | this.shootingDelay = 800;
31 | this.warmUpTime = 500;
32 | this.reset();
33 | },
34 | reset: function () {
35 | var el = this.el;
36 | var sc = this.data.scale;
37 | this.actualShootingDelay = this.shootingDelay + Math.floor(this.shootingDelay * Math.random());
38 |
39 | el.addEventListener('model-loaded', function(event) {
40 | el.getObject3D('mesh').scale.set(sc, sc, sc);
41 | });
42 | this.lastShoot = undefined;
43 | this.willShootEmited = false;
44 | },
45 | tick: function (time, delta) {
46 | if (this.lastShoot == undefined ) {
47 | this.lastShoot = time;
48 | }
49 | else if (time - this.lastShoot > this.actualShootingDelay) {
50 | // don't shoot when behind the player
51 | var pos = this.el.getAttribute('position');
52 | if (pos.z < 0 && pos.y > 0) {
53 | this.el.components.enemy.shoot(time, delta);
54 | this.lastShoot = time;
55 | this.willShootEmited = false;
56 | this.actualShootingDelay = this.shootingDelay * (Math.random() < 0.3 ? 2 : 1);
57 | }
58 | }
59 | else if (!this.willShootEmited && time - this.lastShoot > this.actualShootingDelay - this.warmUpTime) {
60 | this.el.components.enemy.willShoot(time, delta, this.warmUpTime);
61 | this.willShootEmited = true;
62 | }
63 | },
64 | onHit: function (type) {}
65 | }
66 | );
67 |
--------------------------------------------------------------------------------
/src/enemies/enemy_start.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST */
2 |
3 | ABLAST.registerEnemy(
4 | // name
5 | 'enemy_start',
6 | // data
7 | {
8 | components: {
9 | enemy: {
10 | name: 'enemy_start',
11 | color: '#FFB911',
12 | scale: 0.9,
13 | health: 1
14 | },
15 | 'collision-helper': {
16 | debug: false,
17 | radius: 0.4
18 | },
19 | 'json-model': {
20 | src: 'url(assets/models/enemy0.json)',
21 | texturePath: 'url(assets/images/)',
22 | singleModel: true
23 | }
24 | },
25 | poolSize: 1
26 | },
27 | // implementation
28 | {
29 | init: function () {
30 | this.shootingDelay = 3000;
31 | this.warmUpTime = 1000;
32 | this.reset();
33 | },
34 | reset: function () {
35 | var el = this.el;
36 | var sc = this.data.scale;
37 | el.addEventListener('model-loaded', function(event) {
38 | el.getObject3D('mesh').scale.set(sc, sc, sc);
39 | });
40 | this.lastShoot = undefined;
41 | this.willShootEmited = false;
42 | },
43 | tick: function (time, delta) {
44 | this.el.components.enemy.willShoot(time, delta, this.warmUpTime);
45 | },
46 | onHit: function (type) {}
47 | }
48 | );
49 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | window.ABLAST = {};
2 |
3 | // Assets managment
4 | require('./a-asset-image.js')
5 |
6 | require('./lib/utils.js');
7 |
8 | // Systems.
9 | require('./systems/bullet.js');
10 | require('./systems/enemy.js');
11 | require('./systems/explosion.js');
12 |
13 | // Bullets.
14 | require('./bullets/player.js');
15 | require('./bullets/enemy-slow.js');
16 | require('./bullets/enemy-medium.js');
17 | require('./bullets/enemy-fast.js');
18 | require('./bullets/enemy-fat.js');
19 |
20 | // Enemies.
21 | require('./enemies/enemy_start.js');
22 | require('./enemies/enemy0.js');
23 | require('./enemies/enemy1.js');
24 | require('./enemies/enemy2.js');
25 | require('./enemies/enemy3.js');
26 |
27 | // Components
28 | require('./components/highscores.js');
29 | require('./components/proxy_event.js');
30 | require('./components/countdown.js');
31 | require('./components/decals.js');
32 | require('./components/curve-movement.js');
33 | require('./components/collision-helper.js');
34 | require('./components/gamestate.js');
35 | require('./components/gamestate-debug.js');
36 | require('./components/shoot-controls.js');
37 | require('./components/bullet.js');
38 | require('./components/lifes-counter.js');
39 | require('./components/points-counter.js');
40 | require('./components/timer-counter.js');
41 | require('./components/enemy.js');
42 | require('./components/weapon.js');
43 | require('./components/gun.js');
44 | require('./components/headset.js');
45 | require('./components/json-model.js');
46 | require('./components/spline-line.js');
47 | require('./components/explosion.js');
48 | require('./components/animate-message.js');
49 | require('./components/gamestate-visuals.js');
50 | require('./components/sound-fade.js');
51 | require('./components/restrict-position.js');
52 |
--------------------------------------------------------------------------------
/src/lib/letterpanel.js:
--------------------------------------------------------------------------------
1 | var fontText = '0123456789:* ';
2 |
3 | function LetterPanel (materialSystem, data) {
4 |
5 | this.width = data.width;
6 | this.height = data.height;
7 | this.numSegments = data.numSegments;
8 |
9 | var src = 'assets/images/font.png';
10 |
11 | materialSystem.loadTexture(src, {src: src}, setMap.bind(this));
12 | this.material = new THREE.MeshBasicMaterial({
13 | side: THREE.DoubleSide,
14 | color: data.color,
15 | transparent: true
16 | });
17 |
18 | function setMap (texture) {
19 | this.material.map = texture;
20 | this.material.needsUpdate = true;
21 | }
22 |
23 | this.group = new THREE.Group();
24 | var segmentWidth = this.width / this.numSegments;
25 | this.numLetters = fontText.length;
26 | for (var i = 0; i < this.numSegments; i++) {
27 | plane = new THREE.Mesh(new THREE.PlaneBufferGeometry(segmentWidth, this.height), this.material);
28 | plane.position.x = i * segmentWidth;
29 | this.group.add(plane);
30 | }
31 | }
32 |
33 | LetterPanel.prototype = {
34 | update: function (string) {
35 | string = string.split('').map(function(char){
36 | return fontText.indexOf(char);
37 | });
38 |
39 | var inc = 1 / this.numLetters;
40 | for (var i = 0; i < this.numSegments; i++) {
41 | var uv = this.group.children[i].geometry.attributes.uv;
42 | var array = uv.array;
43 | var x1 = string[i] * inc;
44 | var x2 = (string[i] + 1) * inc;
45 | array[0] = x1;
46 | array[4] = x1;
47 | array[2] = x2;
48 | array[6] = x2;
49 | uv.needsUpdate = true;
50 | }
51 | }
52 | }
53 |
54 | module.exports = LetterPanel;
55 |
--------------------------------------------------------------------------------
/src/lib/poolhelper.js:
--------------------------------------------------------------------------------
1 | var createMixin = require('./utils').createMixin;
2 |
3 | var PoolHelper = function (groupName, data, sceneEl) {
4 | this.groupName = groupName;
5 | this.sceneEl = sceneEl || document.querySelector('a-scene');
6 | this.initializePools(groupName, data);
7 | };
8 |
9 | PoolHelper.prototype = {
10 | initializePools: function (groupName, data) {
11 | var self = this;
12 | Object.keys(data).forEach(function (name) {
13 | var item = data[name];
14 | var components = item.components;
15 | var mixinName = groupName + name;
16 | createMixin(mixinName, components, self.sceneEl);
17 |
18 | self.sceneEl.setAttribute('pool__' + mixinName,
19 | {
20 | size: item.poolSize,
21 | mixin: mixinName,
22 | dynamic: true
23 | });
24 | });
25 | },
26 |
27 | returnEntity: function (name, entity) {
28 | var mixinName = this.groupName + name;
29 | var poolName = 'pool__' + mixinName;
30 | this.sceneEl.components[poolName].returnEntity(entity);
31 | },
32 |
33 | requestEntity: function (name) {
34 | var mixinName = this.groupName + name;
35 | var poolName = 'pool__' + mixinName;
36 | var entity = this.sceneEl.components[poolName].requestEntity();
37 | // entity.id= this.groupName + Math.floor(Math.random() * 1000);
38 | return entity;
39 | }
40 | };
41 |
42 | module.exports = PoolHelper;
43 |
--------------------------------------------------------------------------------
/src/lib/utils.js:
--------------------------------------------------------------------------------
1 | /* globals AFRAME */
2 | function createMixin (id, obj, scene) {
3 | var mixinEl = document.createElement('a-mixin');
4 | mixinEl.setAttribute('id', id);
5 | Object.keys(obj).forEach(function (componentName) {
6 | var value = obj[componentName];
7 | if (typeof value === 'object') {
8 | value = AFRAME.utils.styleParser.stringify(value);
9 | }
10 | mixinEl.setAttribute(componentName, value);
11 | });
12 |
13 | var assetsEl = scene ? scene.querySelector('a-assets') : document.querySelector('a-assets');
14 | if (!assetsEl) {
15 | assetsEl = document.createElement('a-assets');
16 | scene.appendChild(assetsEl);
17 | }
18 | assetsEl.appendChild(mixinEl);
19 |
20 | return mixinEl;
21 | }
22 |
23 | Number.prototype.padLeft = function (n,str) {
24 | return Array(n-String(this).length+1).join(str||'0')+this;
25 | }
26 |
27 | String.prototype.pad = function (n,left, str) {
28 | var string = String(this).substr(0,n);
29 | var empty = Array(n-string.length+1).join(str||' ');
30 | return left ? empty + this : this + empty;
31 | }
32 |
33 | module.exports = {
34 | createMixin: createMixin
35 | };
36 |
--------------------------------------------------------------------------------
/src/systems/bullet.js:
--------------------------------------------------------------------------------
1 | /* global AFRAME ABLAST */
2 | var PoolHelper = require('../lib/poolhelper.js');
3 |
4 | ABLAST.BULLETS = {};
5 |
6 | ABLAST.registerBullet = function (name, data, definition) {
7 | if (ABLAST.BULLETS[name]) {
8 | throw new Error('The bullet `' + name + '` has been already registered. ' +
9 | 'Check that you are not loading two versions of the same bullet ' +
10 | 'or two different bullets of the same name.');
11 | }
12 |
13 | ABLAST.BULLETS[name] = {
14 | poolSize: data.poolSize,
15 | components: data.components,
16 | definition: definition
17 | };
18 |
19 | console.info('Bullet registered ', name);
20 | };
21 |
22 | AFRAME.registerSystem('bullet', {
23 | init: function () {
24 | var self = this;
25 | this.poolHelper = new PoolHelper('bullet', ABLAST.BULLETS, this.sceneEl);
26 | this.activeBullets = [];
27 |
28 | this.sceneEl.addEventListener('gamestate-changed', function (evt) {
29 | if ('state' in evt.detail.diff) {
30 | if (evt.detail.state.state === 'STATE_GAME_OVER' || evt.detail.state.state === 'STATE_GAME_WIN') {
31 | self.reset();
32 | }
33 | }
34 | });
35 | },
36 |
37 | reset: function (entity) {
38 | var self = this;
39 | this.activeBullets.forEach(function (bullet) {
40 | self.returnBullet(bullet.getAttribute('bullet').name, bullet);
41 | });
42 | },
43 |
44 | returnBullet: function (name, entity) {
45 | this.activeBullets.splice(this.activeBullets.indexOf(entity), 1);
46 | this.poolHelper.returnEntity(name, entity);
47 | },
48 |
49 | getBullet: function (name) {
50 | var self = this;
51 | var bullet = this.poolHelper.requestEntity(name);
52 | this.activeBullets.push(bullet);
53 | return bullet;
54 | }
55 | });
56 |
--------------------------------------------------------------------------------
/src/systems/enemy.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST AFRAME */
2 | var PoolHelper = require('../lib/poolhelper.js');
3 |
4 | ABLAST.ENEMIES = {};
5 |
6 | ABLAST.registerEnemy = function (name, data, definition) {
7 | if (ABLAST.ENEMIES[name]) {
8 | throw new Error('The enemy `' + name + '` has been already registered. ' +
9 | 'Check that you are not loading two versions of the same enemy ' +
10 | 'or two different enemies of the same name.');
11 | }
12 |
13 | ABLAST.ENEMIES[name] = {
14 | poolSize: data.poolSize,
15 | components: data.components,
16 | definition: definition,
17 | name: name
18 | };
19 |
20 | console.info('Enemy registered ', name);
21 | };
22 |
23 | AFRAME.registerSystem('enemy', {
24 | schema: {
25 | wave: {default: 0}
26 | },
27 |
28 | init: function () {
29 | var self = this;
30 | var sceneEl = this.sceneEl;
31 |
32 | if (!sceneEl.hasLoaded) {
33 | sceneEl.addEventListener('loaded', this.init.bind(this));
34 | return;
35 | }
36 |
37 | this.poolHelper = new PoolHelper('enemy', ABLAST.ENEMIES, this.sceneEl);
38 |
39 | this.activeEnemies = [];
40 |
41 | // TODO: Enable A-Frame `System.update()` to decouple from gamestate.
42 | sceneEl.addEventListener('gamestate-changed', function (evt) {
43 | if ('state' in evt.detail.diff) {
44 | if (evt.detail.state.state === 'STATE_PLAYING') {
45 | setTimeout(function(){
46 | self.createWave(0);
47 | }, 1000);
48 | }
49 | else if (evt.detail.state.state === 'STATE_GAME_OVER'
50 | || evt.detail.state.state === 'STATE_GAME_WIN'
51 | ||evt.detail.state.state === 'STATE_MAIN_MENU') {
52 | self.reset();
53 | return;
54 | }
55 | }
56 |
57 | if ('waveSequence' in evt.detail.diff) {
58 | self.createSequence(evt.detail.state.waveSequence);
59 | }
60 |
61 | if ('wave' in evt.detail.diff) {
62 | self.createWave(evt.detail.state.wave);
63 | }
64 | });
65 | },
66 |
67 | getEnemy: function (name) {
68 | return this.poolHelper.requestEntity(name);
69 | },
70 |
71 | onEnemyDeath: function (name, entity) {
72 | if (this.sceneEl.getAttribute('gamestate').state === 'STATE_MAIN_MENU') {
73 | this.sceneEl.emit('start-game');
74 | } else {
75 | this.poolHelper.returnEntity(name, entity);
76 | this.sceneEl.emit('enemy-death');
77 | }
78 | },
79 |
80 | createSequence: function (sequenceNumber) {
81 | var self = this;
82 | var startOffset = this.currentWave.sequences[sequenceNumber].start || 0;
83 | setTimeout(function initFirstSequence() {
84 | self.currentSequence = sequenceNumber;
85 | var sequence = self.currentWave.sequences[sequenceNumber];
86 | sequence.enemies.forEach(function createEnemyFromDef (enemyDef) {
87 | self.createEnemies(enemyDef);
88 | });
89 | }, startOffset);
90 | },
91 |
92 | createWave: function (waveNumber) {
93 | this.currentWave = WAVES[waveNumber % WAVES.length];
94 | // console.log('Creating wave', waveNumber);
95 | this.createSequence(0);
96 | this.sceneEl.emit('wave-created', {wave: this.currentWave});
97 | },
98 |
99 | createEnemy: function (enemyType, enemyDefinition, timeOffset) {
100 | var self = this;
101 | var entity = this.getEnemy(enemyType);
102 |
103 | entity.setAttribute('enemy', {shootingDelay: 3000});
104 | entity.setAttribute('curve-movement', {
105 | type: enemyDefinition.movement,
106 | loopStart: enemyDefinition.loopStart || 1,
107 | timeOffset: timeOffset || 0
108 | });
109 |
110 | function activateEnemy(entity) {
111 | entity.setAttribute('visible', true);
112 | entity.components['curve-movement'].addPoints(enemyDefinition.points);
113 | entity.play();
114 | self.activeEnemies.push(entity);
115 | self.sceneEl.emit('enemy-spawn', {enemy: entity});
116 | }
117 |
118 | if (timeOffset) {
119 | if (timeOffset < 0) {
120 | entity.setAttribute('visible', false);
121 | setTimeout(function() {
122 | activateEnemy(entity);
123 | }, -timeOffset);
124 | } else {
125 |
126 | }
127 | } else {
128 | activateEnemy(entity);
129 | }
130 | },
131 |
132 | createEnemies: function (enemyDefinition) {
133 | if (Array.isArray(enemyDefinition.type)) {
134 | for (var i = 0; i < enemyDefinition.type.length; i++) {
135 | var type = enemyDefinition.type[i];
136 | var timeOffset = (enemyDefinition.enemyTimeOffset || 0) * i;
137 | this.createEnemy(type, enemyDefinition, timeOffset);
138 | }
139 | } else {
140 | this.createEnemy(enemyDefinition.type, enemyDefinition);
141 | }
142 | },
143 |
144 | reset: function (entity) {
145 | var self = this;
146 | this.activeEnemies.forEach(function (enemy) {
147 | self.poolHelper.returnEntity(enemy.getAttribute('enemy').name, enemy);
148 | });
149 | }
150 | });
151 |
--------------------------------------------------------------------------------
/src/systems/explosion.js:
--------------------------------------------------------------------------------
1 | /* globals ABLAST AFRAME */
2 | var PoolHelper = require('../lib/poolhelper.js');
3 |
4 | ABLAST.EXPLOSIONS = {};
5 |
6 | ABLAST.registerExplosion = function (name, data, definition) {
7 | if (ABLAST.EXPLOSIONS[name]) {
8 | throw new Error('The explosion `' + name + '` has been already registered. ' +
9 | 'Check that you are not loading two versions of the same explosion ' +
10 | 'or two different enemies of the same name.');
11 | }
12 |
13 | ABLAST.EXPLOSIONS[name] = {
14 | poolSize: data.poolSize,
15 | components: data.components,
16 | definition: definition,
17 | name: name
18 | };
19 |
20 | console.info('Explosion registered ', name);
21 | };
22 |
23 | AFRAME.registerSystem('explosion', {
24 | schema: {
25 | wave: {default: 0}
26 | },
27 |
28 | init: function () {
29 | this.poolHelper = new PoolHelper('explosion', ABLAST.EXPLOSIONS, this.sceneEl);
30 | this.activeExplosions = [];
31 |
32 | this.sounds = {
33 | 'enemy_start': document.getElementById('explosion0'),
34 | 'enemy0': document.getElementById('explosion0'),
35 | 'enemy1': document.getElementById('explosion1'),
36 | 'enemy2': document.getElementById('explosion2'),
37 | 'enemy3': document.getElementById('explosion3'),
38 | 'bullet': document.getElementById('hitbullet'),
39 | 'background': document.getElementById('hitbullet')
40 | };
41 |
42 | this.soundVolumes = {
43 | 'enemy_start': 0.7,
44 | 'enemy0': 1,
45 | 'enemy1': 1,
46 | 'enemy2': 1,
47 | 'enemy3': 1,
48 | 'bullet': 0.4,
49 | 'background': 0.2
50 | };
51 | },
52 |
53 | reset: function (entity) {
54 | var self = this;
55 | this.activeExplosions.forEach(function (entity) {
56 | self.returnToPool(entity.getAttribute('explosion').name, entity);
57 | });
58 | },
59 |
60 | returnToPool: function (name, entity) {
61 | this.activeExplosions.splice(this.activeExplosions.indexOf(entity), 1);
62 | this.poolHelper.returnEntity(name, entity);
63 | },
64 |
65 | getFromPool: function (name) {
66 | var entity = this.poolHelper.requestEntity(name);
67 | this.activeExplosions.push(entity);
68 | return entity;
69 | },
70 |
71 | createExplosion: function (type, position, color, scale, direction, enemyName) {
72 | var explosionEntity = this.getFromPool(type);
73 | explosionEntity.setAttribute('position', position || this.el.getAttribute('position'));
74 | explosionEntity.setAttribute('explosion', {
75 | type: type,
76 | lookAt: direction.clone(),
77 | color: color || '#FFF',
78 | scale: scale || 1.0
79 | });
80 |
81 | // This should be done by the pool!!
82 | explosionEntity.setAttribute('sound', {
83 | src: this.sounds[enemyName || type].src,
84 | volume: this.soundVolumes[enemyName || type],
85 | poolSize: 15,
86 | autoplay: true
87 | });
88 | explosionEntity.setAttribute('visible', true);
89 |
90 | explosionEntity.play();
91 |
92 | }
93 | });
94 |
95 |
96 | /* globals ABLAST */
97 | ABLAST.registerExplosion(
98 | // name
99 | 'enemy',
100 | // data
101 | {
102 | components: {
103 | explosion: {
104 | type: 'enemy',
105 | },
106 | },
107 | poolSize: 10
108 | },
109 | // implementation
110 | {
111 | }
112 | );
113 |
114 | /* globals ABLAST */
115 | ABLAST.registerExplosion(
116 | // name
117 | 'enemygun',
118 | // data
119 | {
120 | components: {
121 | explosion: {
122 | type: 'enemygun',
123 | },
124 | },
125 | poolSize: 10
126 | },
127 | // implementation
128 | {
129 | }
130 | );
131 |
132 |
133 | /* globals ABLAST */
134 | ABLAST.registerExplosion(
135 | // name
136 | 'bullet',
137 | // data
138 | {
139 | components: {
140 | explosion: {
141 | type: 'bullet',
142 | },
143 | },
144 | poolSize: 10
145 | },
146 | // implementation
147 | {
148 | }
149 | );
150 |
151 | /* globals ABLAST */
152 | ABLAST.registerExplosion(
153 | // name
154 | 'background',
155 | // data
156 | {
157 | components: {
158 | explosion: {
159 | type: 'background',
160 | },
161 | },
162 | poolSize: 10
163 | },
164 | // implementation
165 | {
166 | }
167 | );
168 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | var PLUGINS = [];
4 | if (process.env.NODE_ENV === 'production') {
5 | PLUGINS.push(new webpack.optimize.UglifyJsPlugin());
6 | }
7 |
8 | module.exports = {
9 | entry: './src/index.js',
10 | output: {
11 | path: __dirname,
12 | filename: 'build/build.js'
13 | },
14 | plugins: PLUGINS,
15 | devServer: {
16 | disableHostCheck: true
17 | }
18 | };
19 |
--------------------------------------------------------------------------------