├── .gitignore ├── .hgignore ├── LICENSE-MIT-AtomizeJS ├── README.md ├── app.js ├── bomberman ├── bomberman-compat.js ├── bomberman.js └── index.html ├── mongo-app.js ├── package.json └── test ├── onewriter-compat.js ├── onewriter.html ├── onewriter.js ├── queue-compat.js ├── queue.html ├── queue.js ├── retry-compat.js ├── retry.html ├── retry.js ├── unittests.html └── unittests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *~ 3 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | ~$ 2 | ^node_modules/ 3 | -------------------------------------------------------------------------------- /LICENSE-MIT-AtomizeJS: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011-2012 VMware, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Atomize-examples 2 | 3 | This repository contains some tests and some examples of using 4 | AtomizeJS. 5 | 6 | The simplest way to get these up and running is: 7 | 8 | git clone https://github.com/atomizejs/atomize-examples.git 9 | npm install ./atomize-examples 10 | sh $(npm bin)/atomize-examples-server 11 | 12 | Please see the main [AtomizeJS site](http://atomizejs.github.com/) for 13 | further details. 14 | 15 | * test/ 16 | * unittests.html 17 | * Unit tests for the AtomizeJS client and server. 18 | * retry.html 19 | * Demonstrates using the `retry` functionality to wait for 20 | another client to join before setting a value. Watch the 21 | browser JavaScript console. 22 | * onewriter.html 23 | * Demonstrates many clients writing to the same variable 24 | safely. 25 | * queue.html 26 | * Demonstrates a broadcast queue: only one writer, but 27 | multiple readers, and every reader gets all messages added 28 | to the queue after the reader has joined. 29 | * bomberman/ 30 | * index.html 31 | * An implementation of the classic game 32 | [Bomberman](http://en.wikipedia.org/wiki/Bomberman). The 33 | entire multiplayer game is written in client-side JavaScript 34 | using just the AtomizeJS features to safely modify the game 35 | board. 36 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /*global process, require */ 4 | /*jslint devel: true */ 5 | 6 | var express = require('express'); 7 | var http = require('http'); 8 | var atomize = require('atomize-server'); 9 | var path = require('path'); 10 | var app = express(); 11 | var httpServer = http.createServer(app); 12 | var port = 9999; 13 | var port_index = process.argv.indexOf('--port'); 14 | if (port_index > -1) { 15 | port = process.argv[port_index + 1]; 16 | } 17 | 18 | app.configure(function(){ 19 | app.use(express.logger('dev')); 20 | app.use(app.router); 21 | app.use(express.static(__dirname)); 22 | }); 23 | 24 | function serveJS (fileName, packageName) { 25 | var p; 26 | try { 27 | p = require.resolve(packageName); 28 | p = path.dirname(p); 29 | p = path.join(path.join(p, 'lib'), fileName); 30 | } catch (err) { 31 | p = require.resolve('atomize-server'); 32 | p = path.dirname(p); 33 | p = path.join( 34 | path.join( 35 | path.join( 36 | path.join(p, 'node_modules'), 37 | packageName), 38 | 'lib'), 39 | fileName); 40 | } 41 | app.get('/' + fileName, function (req, res) { 42 | res.sendfile(p); 43 | }); 44 | } 45 | 46 | serveJS('atomize.js', 'atomize-client'); 47 | serveJS('cereal.js', 'cereal'); 48 | serveJS('compat.js', 'atomize-client'); 49 | 50 | var atomizeServer = atomize.create(httpServer, '[/]atomize'); 51 | var atomizeClient = atomizeServer.client(); 52 | 53 | atomizeClient.atomically(function () { 54 | atomizeClient.root.clients = atomizeClient.lift({server: {}}); 55 | }, function () { 56 | atomizeServer.on('connection', function (client) { 57 | 58 | client.on('close', function (client) { 59 | atomizeClient.atomically(function () { 60 | delete atomizeClient.root.clients[client.connection.id]; 61 | }, function () { 62 | console.log("Connection death: " + client.connection.id); 63 | }); 64 | }); 65 | 66 | // Example of how to implement authentication 67 | /* client.on('data', function (message, client) { 68 | var text = JSON.parse(message).text; 69 | if (text === "wibble") { 70 | client.connection.write(JSON.stringify({text: "wobble"})); 71 | 72 | atomizeClient.atomically(function () { 73 | atomizeClient.root.clients[client.connection.id] = atomizeClient.lift({}); 74 | }, function () { 75 | console.log("New connection id: " + client.connection.id); 76 | client.isAuthenticated = true; 77 | }); 78 | } else { 79 | client.connection.write(JSON.stringify({text: "denied"})); 80 | } 81 | }); */ 82 | 83 | console.log("New connection id: " + client.connection.id); 84 | // disable this next line if you want to do some sort of real auth 85 | client.isAuthenticated = true; 86 | }); 87 | }); 88 | 89 | console.log(" [*] Listening on 0.0.0.0:" + port); 90 | httpServer.listen(port); 91 | -------------------------------------------------------------------------------- /bomberman/bomberman-compat.js: -------------------------------------------------------------------------------- 1 | // atomize-translate bomberman.js bomberman-compat.js atomize Bomberman Player Cell Bomb this 2 | 3 | var atomize, bomberman, canvas, ctx, clientWidth = 0, clientHeight = 0; 4 | function Cell (bomberman, x, y, raw) { 5 | var self = this; 6 | this.bomberman = bomberman; 7 | this.x = x; 8 | this.y = y; 9 | this.raw = raw; 10 | this.clearCount = 0; 11 | if (((((x === 0) || ((x + 1) === atomize.access(bomberman, "width"))) || (y === 0)) || ((y + 1) === atomize.access(bomberman, "height"))) || (((x % 2) === 0) && ((y % 2) === 0))) { 12 | this.wall = true; 13 | } 14 | atomize.atomically(function () { 15 | if (undefined === atomize.access(atomize.access(self, "raw"), "wall")) { 16 | atomize.assign(atomize.access(self, "raw"), "wall", atomize.access(self, "wall")); 17 | atomize.assign(atomize.access(self, "raw"), "fatal", atomize.access(self, "fatal")); 18 | } 19 | }); 20 | } 21 | Cell.prototype = {watching: false, wall: false, fatal: false, fatalTimer: 1000, setFatal: function () { 22 | var self, occupant, fun, bomb; 23 | self = this; 24 | atomize.atomically(function () { 25 | atomize.assign(atomize.access(self, "raw"), "fatal", true); 26 | occupant = atomize.access(atomize.access(self, "raw"), "occupant"); 27 | atomize.erase(atomize.access(self, "raw"), "occupant"); 28 | return occupant; 29 | }, function (occupant) { 30 | fun = function () { 31 | atomize.access(self, "clearFatal")(); 32 | }; 33 | setTimeout(fun, atomize.access(self, "fatalTimer")); 34 | atomize.assign(self, "clearCount", atomize.access(self, "clearCount") + 1); 35 | if (((undefined !== occupant) && ("bomb" === atomize.access(occupant, "type"))) && (undefined !== atomize.access(occupant, "id"))) { 36 | bomb = atomize.access(atomize.access(atomize.access(self, "bomberman"), "bombs"), atomize.access(occupant, "id")); 37 | if (undefined != bomb) { 38 | atomize.access(bomb, "explode")(); 39 | } 40 | } 41 | }); 42 | }, clearFatal: function () { 43 | var self = this; 44 | atomize.assign(self, "clearCount", atomize.access(self, "clearCount") - 1); 45 | if (atomize.access(self, "clearCount") === 0) { 46 | atomize.atomically(function () { 47 | atomize.assign(atomize.access(self, "raw"), "fatal", false); 48 | }); 49 | } 50 | }, render: function (ctx, scale) { 51 | var offset; 52 | if (this.wall) { 53 | atomize.access(ctx, "beginPath")(); 54 | atomize.assign(ctx, "fillStyle", "#000000"); 55 | atomize.access(ctx, "fillRect")(this.x * scale, this.y * scale, scale, scale); 56 | atomize.access(ctx, "closePath")(); 57 | } else 58 | if (this.fatal) { 59 | offset = (scale * 0.05); 60 | atomize.access(ctx, "beginPath")(); 61 | atomize.assign(ctx, "fillStyle", "#E00000"); 62 | atomize.access(ctx, "fillRect")(offset + (this.x * scale), offset + (this.y * scale), scale * 0.9, scale * 0.9); 63 | atomize.access(ctx, "closePath")(); 64 | } 65 | }, occupied: function () { 66 | return this.wall || (undefined !== this.occupant); 67 | }, placeBomb: function (bomb, cont) { 68 | var self = this; 69 | atomize.atomically(function () { 70 | if ((undefined === atomize.access(atomize.access(self, "raw"), "occupant")) || ("player" === atomize.access(atomize.access(atomize.access(self, "raw"), "occupant"), "type"))) { 71 | atomize.assign(atomize.access(self, "raw"), "occupant", atomize.access(bomb, "raw")); 72 | return true; 73 | } else { 74 | return false; 75 | } 76 | }, cont); 77 | }, occupy: function (player, cont) { 78 | var self = this; 79 | if (atomize.access(self, "wall")) { 80 | cont(false); 81 | } else { 82 | atomize.atomically(function () { 83 | if (atomize.access(atomize.access(self, "raw"), "fatal")) { 84 | return false; 85 | } else 86 | if (undefined === atomize.access(atomize.access(self, "raw"), "occupant")) { 87 | atomize.assign(atomize.access(self, "raw"), "occupant", atomize.access(player, "raw")); 88 | return true; 89 | } else { 90 | return false; 91 | } 92 | }, cont); 93 | } 94 | }, unoccupy: function (player) { 95 | var self = this; 96 | atomize.atomically(function () { 97 | if (atomize.access(atomize.access(self, "raw"), "occupant") === atomize.access(player, "raw")) { 98 | atomize.erase(atomize.access(self, "raw"), "occupant"); 99 | } 100 | }); 101 | }, watch: function () { 102 | var self, fun; 103 | if (this.wall || this.watching) { 104 | return; 105 | } 106 | this.watching = true; 107 | self = this; 108 | fun = function (props) { 109 | atomize.atomically(function () { 110 | if ((atomize.access(props, "occupant") === atomize.access(atomize.access(self, "raw"), "occupant")) && (atomize.access(props, "fatal") === atomize.access(atomize.access(self, "raw"), "fatal"))) { 111 | atomize.retry(); 112 | } else { 113 | return {occupant: atomize.access(atomize.access(self, "raw"), "occupant"), fatal: atomize.access(atomize.access(self, "raw"), "fatal")}; 114 | } 115 | }, function (props) { 116 | if (undefined === atomize.access(props, "occupant")) { 117 | atomize.erase(self, "occupant"); 118 | } else { 119 | atomize.assign(self, "occupant", atomize.access(props, "occupant")); 120 | } 121 | atomize.assign(self, "fatal", atomize.access(props, "fatal")); 122 | fun({occupant: atomize.access(self, "occupant"), fatal: atomize.access(self, "fatal")}); 123 | }); 124 | }; 125 | fun({occupant: atomize.access(self, "occupant"), fatal: atomize.access(self, "fatal")}); 126 | }}; 127 | function Bomb (bomberman, raw) { 128 | this.bomberman = bomberman; 129 | this.x = -1; 130 | this.y = -1; 131 | this.raw = raw; 132 | } 133 | Bomb.prototype = {exploded: false, timer: 1500, startTimer: function () { 134 | var self, explode; 135 | self = this; 136 | explode = function () { 137 | atomize.access(self, "explode")(); 138 | }; 139 | setTimeout(explode, atomize.access(self, "timer")); 140 | }, explode: function () { 141 | var self, exploded, i, cells; 142 | self = this; 143 | atomize.atomically(function () { 144 | var alreadyExploded = atomize.access(atomize.access(self, "raw"), "exploded"); 145 | atomize.assign(atomize.access(self, "raw"), "exploded", true); 146 | return alreadyExploded; 147 | }, function (alreadyExploded) { 148 | atomize.assign(self, "exploded", true); 149 | atomize.access(atomize.access(self, "bomberman"), "deleteBomb")(self); 150 | if (alreadyExploded) { 151 | return; 152 | } 153 | cells = [atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y"))]; 154 | if (!atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") - 1), "wall")) { 155 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") - 1)); 156 | if ((undefined !== atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") - 2)) && !atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") - 2), "wall")) { 157 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") - 2)); 158 | } 159 | } 160 | if (!atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") + 1), "wall")) { 161 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") + 1)); 162 | if ((undefined !== atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") + 2)) && !atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") + 2), "wall")) { 163 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x")), atomize.access(self, "y") + 2)); 164 | } 165 | } 166 | if (!atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") - 1), atomize.access(self, "y")), "wall")) { 167 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") - 1), atomize.access(self, "y"))); 168 | if ((undefined !== atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") - 2)) && !atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") - 2), atomize.access(self, "y")), "wall")) { 169 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") - 2), atomize.access(self, "y"))); 170 | } 171 | } 172 | if (!atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") + 1), atomize.access(self, "y")), "wall")) { 173 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") + 1), atomize.access(self, "y"))); 174 | if ((undefined !== atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") + 2)) && !atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") + 2), atomize.access(self, "y")), "wall")) { 175 | atomize.access(cells, "push")(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), atomize.access(self, "x") + 2), atomize.access(self, "y"))); 176 | } 177 | } 178 | for (i = 0; i < atomize.access(cells, "length"); i += 1) { 179 | atomize.access(atomize.access(cells, i), "setFatal")(); 180 | } 181 | }); 182 | }, maybeInit: function () { 183 | var self = this; 184 | atomize.atomically(function () { 185 | return {x: atomize.access(atomize.access(self, "raw"), "x"), y: atomize.access(atomize.access(self, "raw"), "y")}; 186 | }, function (pos) { 187 | atomize.assign(self, "x", atomize.access(pos, "x")); 188 | atomize.assign(self, "y", atomize.access(pos, "y")); 189 | atomize.atomically(function () { 190 | if (undefined === atomize.access(atomize.access(self, "raw"), "id")) { 191 | atomize.retry(); 192 | } else { 193 | return atomize.access(atomize.access(self, "raw"), "id"); 194 | } 195 | }, function (id) { 196 | atomize.assign(self, "id", id); 197 | }); 198 | }); 199 | }, render: function (ctx, scale) { 200 | x = ((this.x + 0.5) * scale); 201 | y = ((this.y + 0.5) * scale); 202 | atomize.access(ctx, "beginPath")(); 203 | atomize.assign(ctx, "fillStyle", "#A00000"); 204 | atomize.access(ctx, "arc")(x, y, 0.45 * scale, 0, atomize.access(Math, "PI") * 2, true); 205 | atomize.access(ctx, "closePath")(); 206 | atomize.access(ctx, "fill")(); 207 | }}; 208 | function Player (bomberman, raw) { 209 | this.bomberman = bomberman; 210 | this.raw = raw; 211 | this.x = -1; 212 | this.y = -1; 213 | this.xCell = -1; 214 | this.yCell = -1; 215 | this.bombs = []; 216 | } 217 | Player.prototype = {watching: false, ready: false, dead: false, respawnTime: 5000, blocked: false, north: function () { 218 | this.xv = 0; 219 | this.yv = -0.1; 220 | }, south: function () { 221 | this.xv = 0; 222 | this.yv = 0.1; 223 | }, east: function () { 224 | this.xv = 0.1; 225 | this.yv = 0; 226 | }, west: function () { 227 | this.xv = -0.1; 228 | this.yv = 0; 229 | }, dropBomb: function () { 230 | var bombs, i, bomb, fun, self; 231 | bombs = []; 232 | for (i = 0; i < atomize.access(this.bombs, "length"); i += 1) { 233 | if (!atomize.access(atomize.access(this.bombs, i), "exploded")) { 234 | atomize.access(bombs, "push")(atomize.access(this.bombs, i)); 235 | } 236 | } 237 | this.bombs = bombs; 238 | if (atomize.access(this.bombs, "length") > 4) { 239 | return; 240 | } 241 | bomb = new Bomb(this.bomberman, atomize.lift({type: "bomb", x: this.xCell, y: this.yCell, exploded: false})); 242 | atomize.access(bomb, "maybeInit")(); 243 | self = this; 244 | fun = function (success) { 245 | if (success) { 246 | atomize.access(bomb, "startTimer")(); 247 | atomize.access(atomize.access(self, "bombs"), "push")(bomb); 248 | } 249 | } , atomize.access(this.bomberman, "dropBomb")(this.xCell, this.yCell, bomb, fun); 250 | }, render: function (ctx, scale) { 251 | var x, y; 252 | if (this.dead) { 253 | return; 254 | } 255 | x = (this.x * scale); 256 | y = (this.y * scale); 257 | atomize.access(ctx, "beginPath")(); 258 | if (this === atomize.access(this.bomberman, "me")) { 259 | atomize.assign(ctx, "fillStyle", "#00D0D0"); 260 | } else { 261 | atomize.assign(ctx, "fillStyle", "#00A000"); 262 | } 263 | atomize.access(ctx, "arc")(x, y, 0.25 * scale, 0, atomize.access(Math, "PI") * 2, true); 264 | atomize.access(ctx, "closePath")(); 265 | atomize.access(ctx, "fill")(); 266 | }, step: function () { 267 | var xNew, yNew, xCell, yCell, self, fun; 268 | if ((this.blocked || this.dead) || !this.ready) { 269 | return; 270 | } 271 | this.blocked = true; 272 | self = this; 273 | xNew = (this.x + this.xv); 274 | yNew = (this.y + this.yv); 275 | xCell = atomize.access(Math, "floor")(xNew); 276 | yCell = atomize.access(Math, "floor")(yNew); 277 | if (atomize.access(atomize.access(atomize.access(atomize.access(this.bomberman, "grid"), this.xCell), this.yCell), "fatal") || atomize.access(atomize.access(atomize.access(atomize.access(this.bomberman, "grid"), xCell), yCell), "fatal")) { 278 | atomize.atomically(function () { 279 | atomize.assign(atomize.access(self, "raw"), "dead", true); 280 | atomize.access(atomize.access(self, "bomberman"), "unoccupy")(atomize.access(self, "xCell"), atomize.access(self, "yCell"), self); 281 | }, function () { 282 | atomize.assign(self, "dead", true); 283 | atomize.assign(self, "blocked", false); 284 | fun = function () { 285 | atomize.access(self, "spawn")(); 286 | }; 287 | setTimeout(fun, atomize.access(self, "respawnTime")); 288 | }); 289 | return; 290 | } 291 | if ((xCell !== this.xCell) || (yCell !== this.yCell)) { 292 | if (atomize.access(atomize.access(atomize.access(atomize.access(this.bomberman, "grid"), xCell), yCell), "occupied")()) { 293 | this.blocked = false; 294 | return; 295 | } else { 296 | self = this; 297 | fun = function (success) { 298 | if (success) { 299 | atomize.access(atomize.access(self, "bomberman"), "unoccupy")(atomize.access(self, "xCell"), atomize.access(self, "yCell"), self); 300 | atomize.assign(self, "xCell", xCell); 301 | atomize.assign(self, "yCell", yCell); 302 | atomize.assign(self, "x", xNew); 303 | atomize.assign(self, "y", yNew); 304 | atomize.atomically(function () { 305 | atomize.assign(atomize.access(self, "raw"), "x", xNew); 306 | atomize.assign(atomize.access(self, "raw"), "y", yNew); 307 | }, function () { 308 | atomize.assign(self, "blocked", false); 309 | }); 310 | } 311 | }; 312 | atomize.access(this.bomberman, "occupy")(xCell, yCell, self, fun); 313 | } 314 | } else { 315 | atomize.atomically(function () { 316 | atomize.assign(atomize.access(self, "raw"), "x", xNew); 317 | atomize.assign(atomize.access(self, "raw"), "y", yNew); 318 | }, function () { 319 | atomize.assign(self, "x", xNew); 320 | atomize.assign(self, "y", yNew); 321 | atomize.assign(self, "blocked", false); 322 | }); 323 | } 324 | }, watch: function () { 325 | var self, fun; 326 | if (this.watching) { 327 | return; 328 | } 329 | this.watching = true; 330 | self = this; 331 | fun = function () { 332 | atomize.atomically(function () { 333 | if (((atomize.access(self, "x") === atomize.access(atomize.access(self, "raw"), "x")) && (atomize.access(self, "y") === atomize.access(atomize.access(self, "raw"), "y"))) && (atomize.access(self, "dead") === atomize.access(atomize.access(self, "raw"), "dead"))) { 334 | atomize.retry(); 335 | } else { 336 | return {x: atomize.access(atomize.access(self, "raw"), "x"), y: atomize.access(atomize.access(self, "raw"), "y"), dead: atomize.access(atomize.access(self, "raw"), "dead")}; 337 | } 338 | }, function (pos) { 339 | atomize.assign(self, "x", atomize.access(pos, "x")); 340 | atomize.assign(self, "y", atomize.access(pos, "y")); 341 | atomize.assign(self, "dead", atomize.access(pos, "dead")); 342 | fun(); 343 | }); 344 | }; 345 | fun(); 346 | }, spawn: function () { 347 | var self, fun, x, y, directions, keys; 348 | self = this; 349 | x = atomize.access(Math, "round")((atomize.access(atomize.access(self, "bomberman"), "width") - 1) * atomize.access(Math, "random")()); 350 | y = atomize.access(Math, "round")((atomize.access(atomize.access(self, "bomberman"), "height") - 1) * atomize.access(Math, "random")()); 351 | fun = function (success) { 352 | if (success) { 353 | atomize.atomically(function () { 354 | atomize.assign(atomize.access(self, "raw"), "x", x + 0.5); 355 | atomize.assign(atomize.access(self, "raw"), "y", y + 0.5); 356 | atomize.assign(atomize.access(self, "raw"), "dead", false); 357 | }, function () { 358 | atomize.assign(self, "x", x + 0.5); 359 | atomize.assign(self, "y", y + 0.5); 360 | atomize.assign(self, "xCell", x); 361 | atomize.assign(self, "yCell", y); 362 | atomize.assign(self, "ready", true); 363 | atomize.assign(self, "dead", false); 364 | directions = {north: function () { 365 | atomize.access(self, "north")(); 366 | }, south: function () { 367 | atomize.access(self, "south")(); 368 | }, east: function () { 369 | atomize.access(self, "east")(); 370 | }, west: function () { 371 | atomize.access(self, "west")(); 372 | }}; 373 | if (atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), x - 1), y), "wall")) { 374 | atomize.erase(directions, "west"); 375 | } 376 | if (atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), x + 1), y), "wall")) { 377 | atomize.erase(directions, "east"); 378 | } 379 | if (atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), x), y - 1), "wall")) { 380 | atomize.erase(directions, "north"); 381 | } 382 | if (atomize.access(atomize.access(atomize.access(atomize.access(atomize.access(self, "bomberman"), "grid"), x), y + 1), "wall")) { 383 | atomize.erase(directions, "south"); 384 | } 385 | keys = atomize.access(Object, "keys")(directions); 386 | atomize.access(directions, atomize.access(keys, atomize.access(Math, "round")((atomize.access(keys, "length") - 1) * atomize.access(Math, "random")())))(); 387 | }); 388 | } else { 389 | atomize.access(self, "spawn")(); 390 | } 391 | }; 392 | atomize.access(atomize.access(self, "bomberman"), "occupy")(x, y, self, fun); 393 | }}; 394 | function Bomberman (raw) { 395 | this.raw = raw; 396 | this.grid = []; 397 | this.players = {}; 398 | this.bombs = {}; 399 | } 400 | Bomberman.prototype = {width: 25, height: 25, dropBomb: function (x, y, bomb, cont) { 401 | var self, fun; 402 | self = this; 403 | fun = function (success) { 404 | if (success) { 405 | atomize.atomically(function () { 406 | atomize.assign(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount", atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount") + 1); 407 | atomize.assign(atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "bombs"), atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount"), atomize.access(bomb, "raw")); 408 | atomize.assign(atomize.access(bomb, "raw"), "id", atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount")); 409 | return true; 410 | }, cont); 411 | } else { 412 | cont(false); 413 | } 414 | }; 415 | atomize.access(atomize.access(atomize.access(atomize.access(self, "grid"), x), y), "placeBomb")(bomb, fun); 416 | }, deleteBomb: function (bomb) { 417 | var self = this; 418 | atomize.atomically(function () { 419 | if (atomize.access(bomb, "raw") === atomize.access(atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "bombs"), atomize.access(bomb, "id"))) { 420 | atomize.assign(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount", atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount") + 1); 421 | atomize.erase(atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "bombs"), atomize.access(bomb, "id")); 422 | } 423 | }); 424 | }, occupy: function (x, y, player, cont) { 425 | atomize.access(atomize.access(atomize.access(this.grid, x), y), "occupy")(player, cont); 426 | }, unoccupy: function (x, y, player) { 427 | atomize.access(atomize.access(atomize.access(this.grid, x), y), "unoccupy")(player); 428 | }, watchGrid: function () { 429 | var x, y; 430 | for (x = 0; x < atomize.access(this.grid, "length"); x += 1) { 431 | for (y = 0; y < atomize.access(atomize.access(this.grid, x), "length"); y += 1) { 432 | atomize.access(atomize.access(atomize.access(this.grid, x), y), "watch")(); 433 | } 434 | } 435 | }, watchPlayers: function () { 436 | var fun, self, players, keys, i; 437 | self = this; 438 | fun = function (eventCount) { 439 | atomize.atomically(function () { 440 | if (atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "eventCount") === eventCount) { 441 | atomize.retry(); 442 | } else { 443 | players = {}; 444 | keys = atomize.access(Object, "keys")(atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "players")); 445 | for (i = 0; i < atomize.access(keys, "length"); i += 1) { 446 | atomize.assign(players, atomize.access(keys, i), atomize.access(atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "players"), atomize.access(keys, i))); 447 | } 448 | return {players: players, eventCount: atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "eventCount")}; 449 | } 450 | }, function (result) { 451 | atomize.assign(self, "players", {}); 452 | keys = atomize.access(Object, "keys")(atomize.access(result, "players")); 453 | for (i = 0; i < atomize.access(keys, "length"); i += 1) { 454 | if (atomize.access(atomize.access(result, "players"), atomize.access(keys, i)) === atomize.access(atomize.access(self, "me"), "raw")) { 455 | atomize.assign(atomize.access(self, "players"), atomize.access(keys, i), atomize.access(self, "me")); 456 | } else { 457 | atomize.assign(atomize.access(self, "players"), atomize.access(keys, i), new Player(self, atomize.access(atomize.access(result, "players"), atomize.access(keys, i)))); 458 | atomize.access(atomize.access(atomize.access(self, "players"), atomize.access(keys, i)), "watch")(); 459 | } 460 | } 461 | fun(atomize.access(result, "eventCount")); 462 | }); 463 | }; 464 | fun(0); 465 | }, watchBombs: function () { 466 | var fun, self, bombs, keys, i; 467 | self = this; 468 | fun = function (eventCount) { 469 | atomize.atomically(function () { 470 | if (atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount") === eventCount) { 471 | atomize.retry(); 472 | } else { 473 | bombs = {}; 474 | keys = atomize.access(Object, "keys")(atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "bombs")); 475 | for (i = 0; i < atomize.access(keys, "length"); i += 1) { 476 | atomize.assign(bombs, atomize.access(keys, i), atomize.access(atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "bombs"), atomize.access(keys, i))); 477 | } 478 | return {bombs: bombs, eventCount: atomize.access(atomize.access(atomize.access(self, "raw"), "bombs"), "eventCount")}; 479 | } 480 | }, function (result) { 481 | atomize.assign(self, "bombs", {}); 482 | keys = atomize.access(Object, "keys")(atomize.access(result, "bombs")); 483 | for (i = 0; i < atomize.access(keys, "length"); i += 1) { 484 | atomize.assign(atomize.access(self, "bombs"), atomize.access(keys, i), new Bomb(self, atomize.access(atomize.access(result, "bombs"), atomize.access(keys, i)))); 485 | atomize.access(atomize.access(atomize.access(self, "bombs"), atomize.access(keys, i)), "maybeInit")(); 486 | } 487 | fun(atomize.access(result, "eventCount")); 488 | }); 489 | }; 490 | fun(0); 491 | }, maybeInit: function () { 492 | var self, x, y, raw, cell; 493 | self = this; 494 | atomize.atomically(function () { 495 | if (undefined === atomize.access(atomize.access(self, "raw"), "players")) { 496 | atomize.assign(atomize.access(self, "raw"), "players", atomize.lift({eventCount: 0, players: {}})); 497 | } 498 | if (undefined === atomize.access(atomize.access(self, "raw"), "bombs")) { 499 | atomize.assign(atomize.access(self, "raw"), "bombs", atomize.lift({eventCount: 0, bombs: {}})); 500 | } 501 | if (undefined === atomize.access(atomize.access(self, "raw"), "grid")) { 502 | atomize.assign(atomize.access(self, "raw"), "grid", atomize.lift([])); 503 | for (x = 0; x < atomize.access(self, "width"); x += 1) { 504 | atomize.assign(atomize.access(atomize.access(self, "raw"), "grid"), x, atomize.lift([])); 505 | atomize.assign(atomize.access(self, "grid"), x, []); 506 | for (y = 0; y < atomize.access(self, "height"); y += 1) { 507 | raw = atomize.lift({}); 508 | atomize.assign(atomize.access(atomize.access(atomize.access(self, "raw"), "grid"), x), y, raw); 509 | cell = new Cell(self, x, y, raw); 510 | atomize.assign(atomize.access(atomize.access(self, "grid"), x), y, cell); 511 | } 512 | } 513 | } else { 514 | atomize.assign(self, "width", atomize.access(atomize.access(atomize.access(self, "raw"), "grid"), "length")); 515 | atomize.assign(self, "height", atomize.access(atomize.access(atomize.access(atomize.access(self, "raw"), "grid"), 0), "length")); 516 | for (x = 0; x < atomize.access(self, "width"); x += 1) { 517 | atomize.assign(atomize.access(self, "grid"), x, []); 518 | for (y = 0; y < atomize.access(self, "height"); y += 1) { 519 | cell = new Cell(self, x, y, atomize.access(atomize.access(atomize.access(atomize.access(self, "raw"), "grid"), x), y)); 520 | atomize.assign(atomize.access(atomize.access(self, "grid"), x), y, cell); 521 | } 522 | } 523 | } 524 | return atomize.lift({type: "player", dead: false}); 525 | }, function (me) { 526 | atomize.assign(self, "me", new Player(self, me)); 527 | atomize.access(self, "watchGrid")(); 528 | atomize.access(self, "watchPlayers")(); 529 | atomize.access(self, "watchBombs")(); 530 | atomize.atomically(function () { 531 | atomize.assign(atomize.access(atomize.access(self, "raw"), "players"), "eventCount", atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "eventCount") + 1); 532 | atomize.assign(atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "players"), atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "eventCount"), me); 533 | atomize.assign(atomize.access(atomize.access(self, "me"), "raw"), "id", atomize.access(atomize.access(atomize.access(self, "raw"), "players"), "eventCount")); 534 | }, function () { 535 | atomize.access(atomize.access(self, "me"), "spawn")(); 536 | }); 537 | }); 538 | }, render: function (ctx) { 539 | var minDim, maxDim, wallLen, x, y, keys; 540 | minDim = atomize.access(Math, "min")(clientWidth, clientHeight); 541 | maxDim = atomize.access(Math, "max")(this.width, this.height); 542 | wallLen = (minDim / maxDim); 543 | for (x = 0; x < atomize.access(this.grid, "length"); x += 1) { 544 | for (y = 0; y < atomize.access(atomize.access(this.grid, x), "length"); y += 1) { 545 | atomize.access(atomize.access(atomize.access(this.grid, x), y), "render")(ctx, wallLen); 546 | } 547 | } 548 | keys = atomize.access(Object, "keys")(this.players); 549 | for (x = 0; x < atomize.access(keys, "length"); x += 1) { 550 | atomize.access(atomize.access(this.players, atomize.access(keys, x)), "render")(ctx, wallLen); 551 | } 552 | keys = atomize.access(Object, "keys")(this.bombs); 553 | for (x = 0; x < atomize.access(keys, "length"); x += 1) { 554 | atomize.access(atomize.access(this.bombs, atomize.access(keys, x)), "render")(ctx, wallLen); 555 | } 556 | }}; 557 | function resizeCanvas () { 558 | var e; 559 | if (undefined !== canvas) { 560 | atomize.assign(canvas, "width", atomize.access(atomize.access(canvas, "parentNode"), "offsetWidth")); 561 | atomize.assign(canvas, "height", atomize.access(atomize.access(canvas, "parentNode"), "offsetHeight")); 562 | clientWidth = atomize.access(canvas, "width"); 563 | clientHeight = atomize.access(canvas, "height"); 564 | e = atomize.access(canvas, "parentNode"); 565 | while ((undefined !== e) && (null !== e)) { 566 | if ((((undefined !== atomize.access(e, "clientHeight")) && (undefined !== atomize.access(e, "clientWidth"))) && (atomize.access(e, "clientHeight") > 0)) && (atomize.access(e, "clientWidth") > 0)) { 567 | clientHeight = atomize.access(Math, "min")(clientHeight, atomize.access(e, "clientHeight")); 568 | clientWidth = atomize.access(Math, "min")(clientWidth, atomize.access(e, "clientWidth")); 569 | } 570 | e = atomize.access(e, "parentNode"); 571 | } 572 | canvasLeft = 10; 573 | canvasTop = 10; 574 | e = atomize.access(canvas, "parentNode"); 575 | while ((undefined !== e) && (null !== e)) { 576 | if ((undefined !== atomize.access(e, "offsetLeft")) && (undefined !== atomize.access(e, "offsetTop"))) { 577 | canvasLeft += atomize.access(e, "offsetLeft"); 578 | canvasTop += atomize.access(e, "offsetTop"); 579 | } 580 | e = atomize.access(e, "parentNode"); 581 | } 582 | } 583 | } 584 | function initCanvas () { 585 | resizeCanvas(); 586 | try { 587 | ctx = atomize.access(canvas, "getContext")("2d"); 588 | } catch (e) { 589 | } 590 | if (!ctx) { 591 | alert("Could not initialise 2D canvas. Change browser?"); 592 | } 593 | } 594 | function drawScene () { 595 | atomize.access(ctx, "clearRect")(0, 0, clientWidth, clientHeight); 596 | atomize.assign(ctx, "lineWidth", 1); 597 | atomize.assign(ctx, "lineCap", "round"); 598 | atomize.assign(ctx, "lineJoin", "round"); 599 | atomize.assign(ctx, "strokeStyle", "black"); 600 | atomize.access(bomberman, "render")(ctx); 601 | } 602 | requestAnimFrame = (function () { 603 | return ((((this.requestAnimationFrame || this.webkitRequestAnimationFrame) || this.mozRequestAnimationFrame) || this.oRequestAnimationFrame) || this.msRequestAnimationFrame) || function (callback, element) { 604 | setTimeout(callback, 1000 / 60); 605 | } 606 | }()); 607 | function tick () { 608 | if ((undefined !== bomberman) && (undefined !== atomize.access(bomberman, "me"))) { 609 | atomize.access(atomize.access(bomberman, "me"), "step")(); 610 | } 611 | drawScene(); 612 | requestAnimFrame(tick); 613 | } 614 | function doKeyDown (event) { 615 | switch (atomize.access(event, "keyCode")) { 616 | case 38: 617 | atomize.access(atomize.access(bomberman, "me"), "north")(); 618 | break; 619 | 620 | case 40: 621 | atomize.access(atomize.access(bomberman, "me"), "south")(); 622 | break; 623 | 624 | case 37: 625 | atomize.access(atomize.access(bomberman, "me"), "west")(); 626 | break; 627 | 628 | case 39: 629 | atomize.access(atomize.access(bomberman, "me"), "east")(); 630 | break; 631 | 632 | case 32: 633 | atomize.access(atomize.access(bomberman, "me"), "dropBomb")(); 634 | break; 635 | 636 | } 637 | } 638 | function init () { 639 | atomize = new Atomize(); 640 | canvas = atomize.access(document, "getElementById")("game_canvas"); 641 | initCanvas(); 642 | atomize.onAuthenticated = function () { 643 | atomize.atomically(function () { 644 | if (undefined === atomize.access(atomize.root, "bomberman")) { 645 | atomize.assign(atomize.root, "bomberman", atomize.lift({})); 646 | } 647 | return atomize.access(atomize.root, "bomberman"); 648 | }, function (raw) { 649 | bomberman = new Bomberman(raw); 650 | atomize.access(bomberman, "maybeInit")(); 651 | requestAnimFrame(tick); 652 | atomize.access(window, "addEventListener")("keydown", doKeyDown, true); 653 | }); 654 | }; 655 | atomize.connect(); 656 | } 657 | -------------------------------------------------------------------------------- /bomberman/bomberman.js: -------------------------------------------------------------------------------- 1 | // atomize-translate bomberman.js bomberman-compat.js atomize Bomberman Player Cell Bomb this 2 | 3 | var atomize, 4 | bomberman, 5 | canvas, 6 | ctx, 7 | clientWidth = 0, 8 | clientHeight = 0; 9 | 10 | function Cell(bomberman, x, y, raw) { 11 | var self = this; 12 | this.bomberman = bomberman; 13 | this.x = x; 14 | this.y = y; 15 | this.raw = raw; 16 | this.clearCount = 0; 17 | if (x === 0 || x + 1 === bomberman.width || 18 | y === 0 || y + 1 === bomberman.height || 19 | (x % 2 === 0 && y % 2 === 0)) { 20 | this.wall = true; 21 | } 22 | atomize.atomically(function () { 23 | if (undefined === self.raw.wall) { 24 | self.raw.wall = self.wall; 25 | self.raw.fatal = self.fatal; 26 | } 27 | }); 28 | } 29 | 30 | Cell.prototype = { 31 | watching: false, 32 | wall: false, 33 | fatal: false, 34 | fatalTimer: 1000, 35 | 36 | setFatal: function () { 37 | var self, occupant, fun, bomb; 38 | self = this; 39 | atomize.atomically(function () { 40 | self.raw.fatal = true; 41 | occupant = self.raw.occupant; 42 | delete self.raw.occupant; 43 | return occupant; 44 | }, function (occupant) { 45 | fun = function () { self.clearFatal(); }; 46 | setTimeout(fun, self.fatalTimer); 47 | self.clearCount += 1; 48 | if (undefined !== occupant && "bomb" === occupant.type && undefined !== occupant.id) { 49 | bomb = self.bomberman.bombs[occupant.id]; 50 | if (undefined != bomb) { 51 | bomb.explode(); 52 | } 53 | } 54 | }); 55 | }, 56 | 57 | clearFatal: function () { 58 | var self = this; 59 | self.clearCount -= 1; 60 | if (self.clearCount === 0) { 61 | atomize.atomically(function () { 62 | self.raw.fatal = false; 63 | }); 64 | } 65 | }, 66 | 67 | render: function (ctx, scale) { 68 | var offset; 69 | if (this.wall) { 70 | ctx.beginPath(); 71 | ctx.fillStyle="#000000"; 72 | ctx.fillRect(this.x*scale, this.y*scale, scale, scale); 73 | ctx.closePath(); 74 | } else if (this.fatal) { 75 | offset = scale*0.05; 76 | ctx.beginPath(); 77 | ctx.fillStyle="#E00000"; 78 | ctx.fillRect(offset + this.x*scale, offset + this.y*scale, scale*0.9, scale*0.9); 79 | ctx.closePath(); 80 | } 81 | }, 82 | 83 | occupied: function () { 84 | return this.wall || undefined !== this.occupant; 85 | }, 86 | 87 | placeBomb: function (bomb, cont) { 88 | var self = this; 89 | atomize.atomically(function () { 90 | if (undefined === self.raw.occupant || 91 | "player" === self.raw.occupant.type) { 92 | self.raw.occupant = bomb.raw; 93 | return true; 94 | } else { 95 | return false; 96 | } 97 | }, cont); 98 | }, 99 | 100 | occupy: function (player, cont) { 101 | var self = this; 102 | if (self.wall) { 103 | cont(false); 104 | } else { 105 | atomize.atomically(function () { 106 | if (self.raw.fatal) { 107 | return false; 108 | } else if (undefined === self.raw.occupant) { 109 | self.raw.occupant = player.raw; 110 | return true; 111 | } else { 112 | return false; 113 | } 114 | }, cont); 115 | } 116 | }, 117 | 118 | unoccupy: function (player) { 119 | var self = this; 120 | atomize.atomically(function () { 121 | if (self.raw.occupant === player.raw) { 122 | delete self.raw.occupant; 123 | } 124 | }); 125 | }, 126 | 127 | watch: function () { 128 | var self, fun; 129 | if (this.wall || this.watching) { 130 | return; 131 | } 132 | this.watching = true; 133 | self = this; 134 | fun = function (props) { 135 | atomize.atomically(function () { 136 | if (props.occupant === self.raw.occupant && 137 | props.fatal === self.raw.fatal) { 138 | atomize.retry(); 139 | } else { 140 | return {occupant: self.raw.occupant, 141 | fatal: self.raw.fatal}; 142 | } 143 | }, function (props) { 144 | if (undefined === props.occupant) { 145 | delete self.occupant; 146 | } else { 147 | self.occupant = props.occupant; 148 | } 149 | self.fatal = props.fatal; 150 | fun({occupant: self.occupant, fatal: self.fatal}); 151 | }) 152 | }; 153 | fun({occupant: self.occupant, fatal: self.fatal}); 154 | } 155 | }; 156 | 157 | function Bomb(bomberman, raw) { 158 | this.bomberman = bomberman; 159 | this.x = -1; 160 | this.y = -1; 161 | this.raw = raw; 162 | } 163 | 164 | Bomb.prototype = { 165 | exploded: false, 166 | timer: 1500, 167 | 168 | startTimer: function () { 169 | var self, explode; 170 | self = this; 171 | explode = function () { 172 | self.explode(); 173 | } 174 | setTimeout(explode, self.timer); 175 | }, 176 | 177 | explode: function () { 178 | var self, exploded, i, cells; 179 | self = this; 180 | atomize.atomically(function () { 181 | var alreadyExploded = self.raw.exploded; 182 | self.raw.exploded = true; 183 | return alreadyExploded; 184 | }, function (alreadyExploded) { 185 | self.exploded = true; 186 | self.bomberman.deleteBomb(self); 187 | if (alreadyExploded) { 188 | return; 189 | } 190 | cells = [self.bomberman.grid[self.x][self.y]]; 191 | 192 | if (! self.bomberman.grid[self.x][self.y - 1].wall) { 193 | cells.push(self.bomberman.grid[self.x][self.y - 1]); 194 | if (undefined !== self.bomberman.grid[self.x][self.y - 2] && 195 | ! self.bomberman.grid[self.x][self.y - 2].wall) { 196 | cells.push(self.bomberman.grid[self.x][self.y - 2]); 197 | } 198 | } 199 | if (! self.bomberman.grid[self.x][self.y + 1].wall) { 200 | cells.push(self.bomberman.grid[self.x][self.y + 1]); 201 | if (undefined !== self.bomberman.grid[self.x][self.y + 2] && 202 | ! self.bomberman.grid[self.x][self.y + 2].wall) { 203 | cells.push(self.bomberman.grid[self.x][self.y + 2]); 204 | } 205 | } 206 | 207 | if (! self.bomberman.grid[self.x - 1][self.y].wall) { 208 | cells.push(self.bomberman.grid[self.x - 1][self.y]); 209 | if (undefined !== self.bomberman.grid[self.x - 2] && 210 | ! self.bomberman.grid[self.x - 2][self.y].wall) { 211 | cells.push(self.bomberman.grid[self.x - 2][self.y]); 212 | } 213 | } 214 | if (! self.bomberman.grid[self.x + 1][self.y].wall) { 215 | cells.push(self.bomberman.grid[self.x + 1][self.y]); 216 | if (undefined !== self.bomberman.grid[self.x + 2] && 217 | ! self.bomberman.grid[self.x + 2][self.y].wall) { 218 | cells.push(self.bomberman.grid[self.x + 2][self.y]); 219 | } 220 | } 221 | 222 | for (i = 0; i < cells.length; i += 1) { 223 | cells[i].setFatal(); 224 | } 225 | }); 226 | }, 227 | 228 | maybeInit: function () { 229 | var self = this; 230 | atomize.atomically(function () { 231 | return {x: self.raw.x, y: self.raw.y} 232 | }, function (pos) { 233 | self.x = pos.x; 234 | self.y = pos.y; 235 | atomize.atomically(function () { 236 | if (undefined === self.raw.id) { 237 | atomize.retry(); 238 | } else { 239 | return self.raw.id; 240 | } 241 | }, function (id) { 242 | self.id = id; 243 | }); 244 | }); 245 | }, 246 | 247 | render: function (ctx, scale) { 248 | x = (this.x + 0.5) * scale; 249 | y = (this.y + 0.5) * scale; 250 | ctx.beginPath(); 251 | ctx.fillStyle="#A00000"; 252 | ctx.arc(x,y,0.45*scale,0,Math.PI*2,true); 253 | ctx.closePath(); 254 | ctx.fill(); 255 | } 256 | } 257 | 258 | function Player(bomberman, raw) { 259 | this.bomberman = bomberman; 260 | this.raw = raw; 261 | this.x = -1; 262 | this.y = -1; 263 | this.xCell = -1; 264 | this.yCell = -1; 265 | this.bombs = []; 266 | } 267 | 268 | Player.prototype = { 269 | watching: false, 270 | ready: false, 271 | dead: false, 272 | respawnTime: 5000, 273 | blocked: false, 274 | 275 | north: function () { 276 | this.xv = 0; 277 | this.yv = -0.1; 278 | }, 279 | 280 | south: function () { 281 | this.xv = 0; 282 | this.yv = 0.1; 283 | }, 284 | 285 | east: function () { 286 | this.xv = 0.1; 287 | this.yv = 0; 288 | }, 289 | 290 | west: function () { 291 | this.xv = -0.1; 292 | this.yv = 0; 293 | }, 294 | 295 | dropBomb: function () { 296 | var bombs, i, bomb, fun, self; 297 | bombs = []; 298 | for (i = 0; i < this.bombs.length; i += 1) { 299 | if (! this.bombs[i].exploded) { 300 | bombs.push(this.bombs[i]); 301 | } 302 | } 303 | this.bombs = bombs; 304 | if (this.bombs.length > 4) { 305 | return; 306 | } 307 | bomb = new Bomb(this.bomberman, atomize.lift({type: "bomb", 308 | x: this.xCell, 309 | y: this.yCell, 310 | exploded: false})); 311 | bomb.maybeInit(); 312 | self = this; 313 | fun = function (success) { 314 | if (success) { 315 | bomb.startTimer(); 316 | self.bombs.push(bomb); 317 | } 318 | }, 319 | this.bomberman.dropBomb(this.xCell, this.yCell, bomb, fun); 320 | }, 321 | 322 | render: function (ctx, scale) { 323 | var x, y; 324 | if (this.dead) { 325 | return; 326 | } 327 | x = this.x * scale; 328 | y = this.y * scale; 329 | ctx.beginPath(); 330 | if (this === this.bomberman.me) { 331 | ctx.fillStyle="#00D0D0"; 332 | } else { 333 | ctx.fillStyle="#00A000"; 334 | } 335 | ctx.arc(x, y, 0.25*scale, 0, Math.PI*2, true); 336 | ctx.closePath(); 337 | ctx.fill(); 338 | }, 339 | 340 | step: function () { 341 | var xNew, yNew, xCell, yCell, self, fun; 342 | if (this.blocked || this.dead || ! this.ready) { 343 | return; 344 | } 345 | this.blocked = true; 346 | self = this; 347 | xNew = this.x + this.xv; 348 | yNew = this.y + this.yv; 349 | xCell = Math.floor(xNew); 350 | yCell = Math.floor(yNew); 351 | if (this.bomberman.grid[this.xCell][this.yCell].fatal || 352 | this.bomberman.grid[xCell][yCell].fatal) { 353 | atomize.atomically(function () { 354 | self.raw.dead = true; 355 | self.bomberman.unoccupy(self.xCell, self.yCell, self); 356 | }, function () { 357 | self.dead = true; 358 | self.blocked = false; 359 | fun = function () { 360 | self.spawn(); 361 | }; 362 | setTimeout(fun, self.respawnTime); 363 | }); 364 | return; 365 | } 366 | if (xCell !== this.xCell || yCell !== this.yCell) { 367 | if (this.bomberman.grid[xCell][yCell].occupied()) { 368 | this.blocked = false; 369 | return; 370 | } else { 371 | self = this; 372 | fun = function (success) { 373 | if (success) { 374 | self.bomberman.unoccupy(self.xCell, self.yCell, self); 375 | self.xCell = xCell; 376 | self.yCell = yCell; 377 | self.x = xNew; 378 | self.y = yNew; 379 | atomize.atomically(function () { 380 | self.raw.x = xNew; 381 | self.raw.y = yNew; 382 | }, function () { 383 | self.blocked = false; 384 | }); 385 | } 386 | } 387 | this.bomberman.occupy(xCell, yCell, self, fun); 388 | } 389 | } else { 390 | atomize.atomically(function () { 391 | self.raw.x = xNew; 392 | self.raw.y = yNew; 393 | }, function () { 394 | self.x = xNew; 395 | self.y = yNew; 396 | self.blocked = false; 397 | }); 398 | } 399 | }, 400 | 401 | watch: function () { 402 | var self, fun; 403 | if (this.watching) { 404 | return; 405 | } 406 | this.watching = true; 407 | self = this; 408 | fun = function () { 409 | atomize.atomically(function () { 410 | if (self.x === self.raw.x && self.y === self.raw.y && self.dead === self.raw.dead) { 411 | atomize.retry(); 412 | } else { 413 | return {x: self.raw.x, y: self.raw.y, dead: self.raw.dead} 414 | } 415 | }, function (pos) { 416 | self.x = pos.x; 417 | self.y = pos.y 418 | self.dead = pos.dead; 419 | fun(); 420 | }); 421 | }; 422 | fun(); 423 | }, 424 | 425 | spawn: function () { 426 | var self, fun, x, y, directions, keys; 427 | self = this; 428 | x = Math.round((self.bomberman.width - 1) * Math.random()); 429 | y = Math.round((self.bomberman.height - 1) * Math.random()); 430 | fun = function (success) { 431 | if (success) { 432 | atomize.atomically(function () { 433 | self.raw.x = x + 0.5; 434 | self.raw.y = y + 0.5; 435 | self.raw.dead = false; 436 | }, function () { 437 | self.x = x + 0.5; 438 | self.y = y + 0.5; 439 | self.xCell = x; 440 | self.yCell = y; 441 | self.ready = true; 442 | self.dead = false; 443 | directions = {north: function () { self.north() }, 444 | south: function () { self.south() }, 445 | east: function () { self.east() }, 446 | west: function () { self.west() }}; 447 | if (self.bomberman.grid[x-1][y].wall) { 448 | delete directions.west; 449 | } 450 | if (self.bomberman.grid[x+1][y].wall) { 451 | delete directions.east; 452 | } 453 | if (self.bomberman.grid[x][y-1].wall) { 454 | delete directions.north; 455 | } 456 | if (self.bomberman.grid[x][y+1].wall) { 457 | delete directions.south; 458 | } 459 | keys = Object.keys(directions); 460 | directions[keys[Math.round((keys.length - 1) * Math.random())]](); 461 | }); 462 | } else { 463 | self.spawn(); 464 | } 465 | }; 466 | self.bomberman.occupy(x, y, self, fun); 467 | } 468 | }; 469 | 470 | function Bomberman(raw) { 471 | this.raw = raw; 472 | this.grid = []; 473 | this.players = {}; 474 | this.bombs = {}; 475 | } 476 | 477 | Bomberman.prototype = { 478 | width: 25, 479 | height: 25, 480 | 481 | dropBomb: function (x, y, bomb, cont) { 482 | var self, fun; 483 | self = this; 484 | fun = function (success) { 485 | if (success) { 486 | atomize.atomically(function () { 487 | self.raw.bombs.eventCount += 1; 488 | self.raw.bombs.bombs[self.raw.bombs.eventCount] = bomb.raw; 489 | bomb.raw.id = self.raw.bombs.eventCount; 490 | return true; 491 | }, cont); 492 | } else { 493 | cont(false); 494 | } 495 | }; 496 | self.grid[x][y].placeBomb(bomb, fun); 497 | }, 498 | 499 | deleteBomb: function (bomb) { 500 | var self = this; 501 | atomize.atomically(function () { 502 | if (bomb.raw === self.raw.bombs.bombs[bomb.id]) { 503 | self.raw.bombs.eventCount += 1; 504 | delete self.raw.bombs.bombs[bomb.id]; 505 | } 506 | }); 507 | }, 508 | 509 | occupy: function (x, y, player, cont) { 510 | this.grid[x][y].occupy(player, cont); 511 | }, 512 | 513 | unoccupy: function (x, y, player) { 514 | this.grid[x][y].unoccupy(player); 515 | }, 516 | 517 | watchGrid: function () { 518 | var x, y; 519 | for (x = 0; x < this.grid.length; x += 1) { 520 | for (y = 0; y < this.grid[x].length; y += 1) { 521 | this.grid[x][y].watch(); 522 | } 523 | } 524 | }, 525 | 526 | watchPlayers: function () { 527 | var fun, self, players, keys, i; 528 | self = this; 529 | fun = function (eventCount) { 530 | atomize.atomically( 531 | function () { 532 | if (self.raw.players.eventCount === eventCount) { 533 | atomize.retry(); 534 | } else { 535 | players = {}; 536 | keys = Object.keys(self.raw.players.players); 537 | for (i = 0; i < keys.length; i += 1) { 538 | players[keys[i]] = self.raw.players.players[keys[i]]; 539 | } 540 | return {players: players, eventCount: self.raw.players.eventCount}; 541 | } 542 | }, function (result) { 543 | self.players = {}; 544 | keys = Object.keys(result.players); 545 | for (i = 0; i < keys.length; i += 1) { 546 | if (result.players[keys[i]] === self.me.raw) { 547 | self.players[keys[i]] = self.me; 548 | } else { 549 | self.players[keys[i]] = new Player(self, result.players[keys[i]]); 550 | self.players[keys[i]].watch(); 551 | } 552 | } 553 | fun(result.eventCount); 554 | }); 555 | }; 556 | fun(0); 557 | }, 558 | 559 | watchBombs: function () { 560 | var fun, self, bombs, keys, i; 561 | self = this; 562 | fun = function (eventCount) { 563 | atomize.atomically(function () { 564 | if (self.raw.bombs.eventCount === eventCount) { 565 | atomize.retry(); 566 | } else { 567 | bombs = {}; 568 | keys = Object.keys(self.raw.bombs.bombs); 569 | for (i = 0; i < keys.length; i += 1) { 570 | bombs[keys[i]] = self.raw.bombs.bombs[keys[i]]; 571 | } 572 | return {bombs: bombs, eventCount: self.raw.bombs.eventCount}; 573 | } 574 | }, function (result) { 575 | self.bombs = {}; 576 | keys = Object.keys(result.bombs); 577 | for (i = 0; i < keys.length; i += 1) { 578 | self.bombs[keys[i]] = new Bomb(self, result.bombs[keys[i]]); 579 | self.bombs[keys[i]].maybeInit(); 580 | 581 | } 582 | fun(result.eventCount); 583 | }); 584 | }; 585 | fun(0); 586 | }, 587 | 588 | maybeInit: function () { 589 | var self, x, y, raw, cell; 590 | self = this; 591 | atomize.atomically( 592 | function () { 593 | if (undefined === self.raw.players) { 594 | self.raw.players = atomize.lift({eventCount: 0, players: {}}); 595 | } 596 | if (undefined === self.raw.bombs) { 597 | self.raw.bombs = atomize.lift({eventCount: 0, bombs: {}}); 598 | } 599 | if (undefined === self.raw.grid) { 600 | self.raw.grid = atomize.lift([]); 601 | for (x = 0; x < self.width; x += 1) { 602 | self.raw.grid[x] = atomize.lift([]); 603 | self.grid[x] = []; 604 | for (y = 0; y < self.height; y += 1) { 605 | raw = atomize.lift({}); 606 | self.raw.grid[x][y] = raw; 607 | cell = new Cell(self, x, y, raw); 608 | self.grid[x][y] = cell; 609 | } 610 | } 611 | } else { 612 | self.width = self.raw.grid.length; 613 | self.height = self.raw.grid[0].length; 614 | for (x = 0; x < self.width; x += 1) { 615 | self.grid[x] = []; 616 | for (y = 0; y < self.height; y += 1) { 617 | cell = new Cell(self, x, y, self.raw.grid[x][y]); 618 | self.grid[x][y] = cell; 619 | } 620 | } 621 | } 622 | return atomize.lift({type: "player", dead: false}); 623 | }, function (me) { 624 | self.me = new Player(self, me); 625 | self.watchGrid(); 626 | self.watchPlayers(); 627 | self.watchBombs(); 628 | atomize.atomically(function () { 629 | self.raw.players.eventCount += 1; 630 | self.raw.players.players[self.raw.players.eventCount] = me; 631 | self.me.raw.id = self.raw.players.eventCount; 632 | }, function () { 633 | self.me.spawn(); 634 | }); 635 | }); 636 | }, 637 | 638 | render: function (ctx) { 639 | var minDim, maxDim, wallLen, x, y, keys; 640 | minDim = Math.min(clientWidth, clientHeight); 641 | maxDim = Math.max(this.width, this.height); 642 | wallLen = minDim / maxDim; 643 | 644 | for (x = 0; x < this.grid.length; x += 1) { 645 | for (y = 0; y < this.grid[x].length; y += 1) { 646 | this.grid[x][y].render(ctx, wallLen); 647 | } 648 | } 649 | 650 | keys = Object.keys(this.players); 651 | for (x = 0; x < keys.length; x += 1) { 652 | this.players[keys[x]].render(ctx, wallLen); 653 | } 654 | 655 | keys = Object.keys(this.bombs); 656 | for (x = 0; x < keys.length; x += 1) { 657 | this.bombs[keys[x]].render(ctx, wallLen); 658 | } 659 | } 660 | }; 661 | 662 | function resizeCanvas() { 663 | var e; 664 | if (undefined !== canvas) { 665 | canvas.width = canvas.parentNode.offsetWidth; 666 | canvas.height = canvas.parentNode.offsetHeight; 667 | clientWidth = canvas.width; 668 | clientHeight = canvas.height; 669 | e = canvas.parentNode; 670 | while (undefined !== e && null !== e) { 671 | if (undefined !== e.clientHeight && undefined !== e.clientWidth && 672 | e.clientHeight > 0 && e.clientWidth > 0) { 673 | clientHeight = Math.min(clientHeight, e.clientHeight); 674 | clientWidth = Math.min(clientWidth, e.clientWidth); 675 | } 676 | e = e.parentNode; 677 | } 678 | canvasLeft = 10; 679 | canvasTop = 10; 680 | e = canvas.parentNode; 681 | while (undefined !== e && null !== e) { 682 | if (undefined !== e.offsetLeft && undefined !== e.offsetTop) { 683 | canvasLeft += e.offsetLeft; 684 | canvasTop += e.offsetTop; 685 | } 686 | e = e.parentNode; 687 | } 688 | } 689 | } 690 | 691 | function initCanvas() { 692 | resizeCanvas(); 693 | try { 694 | ctx = canvas.getContext("2d"); 695 | } catch (e) { 696 | } 697 | if (!ctx) { 698 | alert("Could not initialise 2D canvas. Change browser?"); 699 | } 700 | } 701 | 702 | function drawScene() { 703 | ctx.clearRect(0, 0, clientWidth, clientHeight); 704 | ctx.lineWidth = 1.0; 705 | ctx.lineCap = "round"; 706 | ctx.lineJoin = "round"; 707 | ctx.strokeStyle = "black"; 708 | bomberman.render(ctx); 709 | } 710 | 711 | requestAnimFrame = (function () { 712 | return (this.requestAnimationFrame || 713 | this.webkitRequestAnimationFrame || 714 | this.mozRequestAnimationFrame || 715 | this.oRequestAnimationFrame || 716 | this.msRequestAnimationFrame || 717 | function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 718 | setTimeout(callback, 1000 / 60); 719 | }); 720 | })(); 721 | 722 | function tick () { 723 | if (undefined !== bomberman && undefined !== bomberman.me) { 724 | bomberman.me.step(); 725 | } 726 | drawScene(); 727 | requestAnimFrame(tick); 728 | } 729 | 730 | function doKeyDown (event) { 731 | switch (event.keyCode) { 732 | case 38: // Up 733 | bomberman.me.north(); 734 | break; 735 | case 40: // Down 736 | bomberman.me.south(); 737 | break; 738 | case 37: // Left 739 | bomberman.me.west(); 740 | break; 741 | case 39: // Right 742 | bomberman.me.east(); 743 | break; 744 | case 32: // Space 745 | bomberman.me.dropBomb(); 746 | break; 747 | } 748 | } 749 | 750 | function init () { 751 | atomize = new Atomize(); 752 | canvas = document.getElementById("game_canvas"); 753 | initCanvas(); 754 | atomize.onAuthenticated = function () { 755 | atomize.atomically( 756 | function () { 757 | if (undefined === atomize.root.bomberman) { 758 | atomize.root.bomberman = atomize.lift({}); 759 | } 760 | return atomize.root.bomberman; 761 | }, function (raw) { 762 | bomberman = new Bomberman(raw); 763 | bomberman.maybeInit(); 764 | requestAnimFrame(tick); 765 | window.addEventListener('keydown', doKeyDown, true); 766 | }); 767 | }; 768 | atomize.connect(); 769 | } 770 | -------------------------------------------------------------------------------- /bomberman/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |