| 1 | /* -*- coding: utf-8 -*- | 
|---|
| 2 | * | 
|---|
| 3 | * Copyright (C) 2010 by Atzm WATANABE <atzm@atzm.org> | 
|---|
| 4 | * | 
|---|
| 5 | *  This program is free software; you can redistribute it and/or modify it | 
|---|
| 6 | *  under the terms of the GNU General Public License (version 2) as | 
|---|
| 7 | *  published by the Free Software Foundation.  It is distributed in the | 
|---|
| 8 | *  hope that it will be useful, but WITHOUT ANY WARRANTY; without even the | 
|---|
| 9 | *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | 
|---|
| 10 | *  PURPOSE.  See the GNU General Public License for more details. | 
|---|
| 11 | * | 
|---|
| 12 | * $Id$ | 
|---|
| 13 | * | 
|---|
| 14 | */ | 
|---|
| 15 |  | 
|---|
| 16 | System = { | 
|---|
| 17 | "screen": { | 
|---|
| 18 | "canvas": null, | 
|---|
| 19 | "ctx":    null, | 
|---|
| 20 | "width":  0, | 
|---|
| 21 | "height": 0 | 
|---|
| 22 | }, | 
|---|
| 23 | "message":          null, | 
|---|
| 24 | "enemyImages":      new Array(), | 
|---|
| 25 | "enemies":          new Array(), | 
|---|
| 26 | "players":          new Array(), | 
|---|
| 27 | "backgroundObject": new Array(), | 
|---|
| 28 | "deathPieces":      new Array(), | 
|---|
| 29 | "mainIntervalId":   0 | 
|---|
| 30 | }; | 
|---|
| 31 |  | 
|---|
| 32 |  | 
|---|
| 33 | /* | 
|---|
| 34 | *  Tiun Tiun Utilities | 
|---|
| 35 | */ | 
|---|
| 36 | var DeathPiece = function(sizes, colors, x, y, dir, speed) { | 
|---|
| 37 | var that = new LinerBullet(sizes[0], colors[0], null, x, y, dir, speed); | 
|---|
| 38 |  | 
|---|
| 39 | var sizeIdx  = -1; | 
|---|
| 40 | var colorIdx = -1; | 
|---|
| 41 |  | 
|---|
| 42 | that.getSize = function() { | 
|---|
| 43 | if (++sizeIdx >= sizes.length) | 
|---|
| 44 | sizeIdx = 0; | 
|---|
| 45 | return sizes[sizeIdx]; | 
|---|
| 46 | }; | 
|---|
| 47 |  | 
|---|
| 48 | that.getColor = function() { | 
|---|
| 49 | if (++colorIdx >= colors.length) | 
|---|
| 50 | colorIdx = 0; | 
|---|
| 51 | return colors[colorIdx]; | 
|---|
| 52 | }; | 
|---|
| 53 |  | 
|---|
| 54 | return that; | 
|---|
| 55 | }; | 
|---|
| 56 |  | 
|---|
| 57 | function addDeathPieces(x, y, sizes, colors, speed, way) { | 
|---|
| 58 | if (way % 2) | 
|---|
| 59 | way++; | 
|---|
| 60 |  | 
|---|
| 61 | var pieces = new Array(); | 
|---|
| 62 | var angle  = 0; | 
|---|
| 63 | var delta  = 2 / way; | 
|---|
| 64 |  | 
|---|
| 65 | for(var i = 0; i < way; i++) { | 
|---|
| 66 | pieces.push(new DeathPiece(sizes, colors, x, y, angle * Math.PI, speed)); | 
|---|
| 67 | angle += delta; | 
|---|
| 68 | } | 
|---|
| 69 |  | 
|---|
| 70 | System.deathPieces.push(pieces); | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | function updateDeathPieces(ctx, width, height) { | 
|---|
| 74 | var newObjs = new Array(); | 
|---|
| 75 |  | 
|---|
| 76 | for (var i = 0; i < System.deathPieces.length; i++) { | 
|---|
| 77 | var pieces    = System.deathPieces[i]; | 
|---|
| 78 | var newPieces = new Array(); | 
|---|
| 79 |  | 
|---|
| 80 | for (var k = 0; k < pieces.length; k++) { | 
|---|
| 81 | pieces[k].next(); | 
|---|
| 82 | pieces[k].draw(ctx); | 
|---|
| 83 | if (!pieces[k].vanished(width, height)) | 
|---|
| 84 | newPieces.push(pieces[k]); | 
|---|
| 85 | } | 
|---|
| 86 |  | 
|---|
| 87 | if (newPieces.length) | 
|---|
| 88 | newObjs.push(newPieces); | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 | System.deathPieces = newObjs; | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 |  | 
|---|
| 95 | /* | 
|---|
| 96 | *  Utility Functions | 
|---|
| 97 | */ | 
|---|
| 98 | function setMessage(elem, msg) { | 
|---|
| 99 | if (elem) | 
|---|
| 100 | elem.innerHTML = msg; | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|
| 103 | function addMessage(elem, msg) { | 
|---|
| 104 | if (elem) | 
|---|
| 105 | elem.innerHTML += msg; | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | function updateBackground(ctx, width, height, size, color, max) { | 
|---|
| 109 | if (System.backgroundObject.length < max) { | 
|---|
| 110 | var x = Math.ceil(Math.random() * width); | 
|---|
| 111 | var s = Math.ceil(Math.random() * 5); | 
|---|
| 112 | System.backgroundObject.push( | 
|---|
| 113 | new LinerBullet(size, color, null, x, 0, 0.5 * Math.PI, s)); | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | var newObjs = new Array(); | 
|---|
| 117 |  | 
|---|
| 118 | for (var i = 0; i < System.backgroundObject.length; i++) { | 
|---|
| 119 | System.backgroundObject[i].next(); | 
|---|
| 120 | System.backgroundObject[i].draw(ctx); | 
|---|
| 121 | if (!System.backgroundObject[i].vanished(width, height)) | 
|---|
| 122 | newObjs.push(System.backgroundObject[i]); | 
|---|
| 123 | } | 
|---|
| 124 |  | 
|---|
| 125 | System.backgroundObject = newObjs; | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | function drawScreen(ctx, op, style, width, height) { | 
|---|
| 129 | var c = ctx.globalCompositeOperation; | 
|---|
| 130 | ctx.globalCompositeOperation = op; | 
|---|
| 131 | ctx.beginPath(); | 
|---|
| 132 | ctx.fillStyle = style; | 
|---|
| 133 | ctx.fillRect(0, 0, width, height); | 
|---|
| 134 | ctx.fill(); | 
|---|
| 135 | ctx.closePath(); | 
|---|
| 136 | ctx.globalCompositeOperation = c; | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | function drawLifeGauge(ctx, op, trooper, x, y, width) { | 
|---|
| 140 | var length = trooper.life; | 
|---|
| 141 |  | 
|---|
| 142 | if (length > width - 20) | 
|---|
| 143 | length = width - 20; | 
|---|
| 144 |  | 
|---|
| 145 | var c = ctx.globalCompositeOperation; | 
|---|
| 146 | ctx.globalCompositeOperation = op; | 
|---|
| 147 | ctx.beginPath(); | 
|---|
| 148 | ctx.fillStyle = trooper.color; | 
|---|
| 149 | ctx.fillRect(x, y, length, 10); | 
|---|
| 150 | ctx.fill(); | 
|---|
| 151 | ctx.closePath(); | 
|---|
| 152 | ctx.globalCompositeOperation = c; | 
|---|
| 153 |  | 
|---|
| 154 | drawString(ctx, op, trooper.life, x + 2, y + 8, trooper.color, | 
|---|
| 155 | "6pt monospace", "left"); | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | function drawString(ctx, op, string, x, y, color, font, align) { | 
|---|
| 159 | var a = ctx.textAlign; | 
|---|
| 160 | var f = ctx.font; | 
|---|
| 161 | var c = ctx.globalCompositeOperation; | 
|---|
| 162 | ctx.globalCompositeOperation = op; | 
|---|
| 163 | ctx.beginPath(); | 
|---|
| 164 | ctx.textAlign = align; | 
|---|
| 165 | ctx.fillStyle = color; | 
|---|
| 166 | ctx.font      = font; | 
|---|
| 167 | ctx.fillText(string, x, y); | 
|---|
| 168 | ctx.fill(); | 
|---|
| 169 | ctx.textAlign = a; | 
|---|
| 170 | ctx.font      = f; | 
|---|
| 171 | ctx.closePath(); | 
|---|
| 172 | ctx.globalCompositeOperation = c; | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | function updateTrooper(trooper, enemyKey) { | 
|---|
| 176 | trooper.update(System[enemyKey]); | 
|---|
| 177 |  | 
|---|
| 178 | var aliveEnemies = new Array(); | 
|---|
| 179 | for (var i = 0; i < System[enemyKey].length; i++) { | 
|---|
| 180 | var enemy = System[enemyKey][i]; | 
|---|
| 181 |  | 
|---|
| 182 | if (enemy.isDead()) { | 
|---|
| 183 | addDeathPieces( | 
|---|
| 184 | enemy.x, enemy.y, | 
|---|
| 185 | [6, 8, 10], ["#55F", "#AAF"], 3, 8 | 
|---|
| 186 | ); | 
|---|
| 187 | } | 
|---|
| 188 | else { | 
|---|
| 189 | aliveEnemies.push(enemy); | 
|---|
| 190 | } | 
|---|
| 191 | } | 
|---|
| 192 | System[enemyKey] = aliveEnemies; | 
|---|
| 193 |  | 
|---|
| 194 | trooper.draw(System.screen.ctx); | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | function numEnemies() { | 
|---|
| 198 | return System.enemies.length; | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | function addEnemyImage(image) { | 
|---|
| 202 | System.enemyImages.push(image); | 
|---|
| 203 | } | 
|---|
| 204 |  | 
|---|
| 205 | function addEnemy(enemyData) { | 
|---|
| 206 | var actList = EnemyActionLists[enemyData.mtime % EnemyActionLists.length]; | 
|---|
| 207 | var shot    = EnemyShots[enemyData.hitpoint % EnemyShots.length]; | 
|---|
| 208 | var numAct  = enemyData.agility       % (EnemyActions.length  - 1) + 1; | 
|---|
| 209 | var numBlt  = enemyData.skills.length % (EnemyBullets.length  - 1) + 1; | 
|---|
| 210 | var numBrrg = enemyData.skills.length % (EnemyBarrages.length - 1) + 1; | 
|---|
| 211 | var acts    = new Array(); | 
|---|
| 212 | var brrgs   = new Array(); | 
|---|
| 213 |  | 
|---|
| 214 | var bulletWay         = Math.ceil(enemyData.concentration / 10); | 
|---|
| 215 | var bulletInterval    = Math.round(50 * 1 / Math.log(enemyData.skillpoint + 0.1)); | 
|---|
| 216 | var bulletSize        = Math.round(Math.log(enemyData.luck + 1)); | 
|---|
| 217 | var bulletFrameWidth  = (bulletSize + 5) * 2; | 
|---|
| 218 | var bulletFrameHeight = (bulletSize + 5) * 4; | 
|---|
| 219 | var bulletSpeed       = enemyData.strength / 15; | 
|---|
| 220 |  | 
|---|
| 221 | bulletSpeed = Math.log(bulletSpeed < 1.5 ? 1.5 : bulletSpeed); | 
|---|
| 222 |  | 
|---|
| 223 | for (var i = 0; i < numAct; i++) { | 
|---|
| 224 | var idx = (enemyData.agility + i) % EnemyActions.length; | 
|---|
| 225 | acts.push(new EnemyActions[idx](new shot())); | 
|---|
| 226 | } | 
|---|
| 227 |  | 
|---|
| 228 | for (var i = 0; i < numBrrg; i++) { | 
|---|
| 229 | var idx     = (enemyData.skillpoint + i * (enemyData.skills.length + 1)) % EnemyBarrages.length; | 
|---|
| 230 | var brrgCls = EnemyBarrages[idx]; | 
|---|
| 231 |  | 
|---|
| 232 | for (var k = 0; k < numBlt; k++) { | 
|---|
| 233 | var iidx = (enemyData.skills.length + i + k) % EnemyBullets.length; | 
|---|
| 234 | brrgs.push( | 
|---|
| 235 | new brrgCls( | 
|---|
| 236 | EnemyBullets[iidx], | 
|---|
| 237 | bulletSize, | 
|---|
| 238 | "#FF3", | 
|---|
| 239 | {"style": "rect", "color": "rgba(128,32,32,0.5)", | 
|---|
| 240 | "width": bulletFrameWidth, "height": bulletFrameHeight}, | 
|---|
| 241 | bulletInterval, | 
|---|
| 242 | bulletSpeed, | 
|---|
| 243 | bulletWay | 
|---|
| 244 | ) | 
|---|
| 245 | ); | 
|---|
| 246 | } | 
|---|
| 247 | } | 
|---|
| 248 |  | 
|---|
| 249 | var size  = Math.ceil((System.screen.width / 2) * (1 / enemyData.defense)); | 
|---|
| 250 | var enemy = new Trooper( | 
|---|
| 251 | enemyData.name, | 
|---|
| 252 | new actList(acts), | 
|---|
| 253 | System.enemyImages[enemyData.hitpoint % System.enemyImages.length], | 
|---|
| 254 | size, | 
|---|
| 255 | size, | 
|---|
| 256 | "#F33", | 
|---|
| 257 | "#F33", | 
|---|
| 258 | System.screen.width / 2, | 
|---|
| 259 | System.screen.height / 7, | 
|---|
| 260 | System.screen.width, | 
|---|
| 261 | System.screen.height, | 
|---|
| 262 | Math.floor(enemyData.hitpoint / 25) + 1, | 
|---|
| 263 | Math.log(enemyData.agility + 0.1) * 3, | 
|---|
| 264 | 0, | 
|---|
| 265 | ["rgba(255,0,0,0.3)", "rgba(0,0,255,0.3)"], | 
|---|
| 266 | brrgs | 
|---|
| 267 | ); | 
|---|
| 268 |  | 
|---|
| 269 | System.enemies.push(enemy); | 
|---|
| 270 | } | 
|---|
| 271 |  | 
|---|
| 272 |  | 
|---|
| 273 | /* | 
|---|
| 274 | *  Main loop | 
|---|
| 275 | */ | 
|---|
| 276 | function mainLoop() { | 
|---|
| 277 | // clear screen | 
|---|
| 278 | drawScreen( | 
|---|
| 279 | System.screen.ctx, | 
|---|
| 280 | "source-over", | 
|---|
| 281 | "rgba(8,8,8,0.8)", | 
|---|
| 282 | System.screen.width, | 
|---|
| 283 | System.screen.height | 
|---|
| 284 | ); | 
|---|
| 285 |  | 
|---|
| 286 | // update background objects | 
|---|
| 287 | updateBackground( | 
|---|
| 288 | System.screen.ctx, | 
|---|
| 289 | System.screen.width, | 
|---|
| 290 | System.screen.height, | 
|---|
| 291 | 1, "#CAF", 10 | 
|---|
| 292 | ); | 
|---|
| 293 |  | 
|---|
| 294 | // update/draw troopers | 
|---|
| 295 | for (var i = 0; i < System.players.length; i++) { | 
|---|
| 296 | var player = System.players[i]; | 
|---|
| 297 |  | 
|---|
| 298 | updateTrooper(player, "enemies"); | 
|---|
| 299 |  | 
|---|
| 300 | drawLifeGauge( | 
|---|
| 301 | System.screen.ctx, | 
|---|
| 302 | "lighter", | 
|---|
| 303 | player, | 
|---|
| 304 | 10, | 
|---|
| 305 | (System.screen.height - 20) - (i * 33), | 
|---|
| 306 | System.screen.width | 
|---|
| 307 | ); | 
|---|
| 308 |  | 
|---|
| 309 | drawString( | 
|---|
| 310 | System.screen.ctx, | 
|---|
| 311 | "source-over", | 
|---|
| 312 | player.name, | 
|---|
| 313 | 10, | 
|---|
| 314 | (System.screen.height - 23) - (i * 33), | 
|---|
| 315 | "#ACF", "9pt monospace", "left" | 
|---|
| 316 | ); | 
|---|
| 317 | } | 
|---|
| 318 |  | 
|---|
| 319 | // update/draw enemies | 
|---|
| 320 | for (var i = 0; i < System.enemies.length; i++) { | 
|---|
| 321 | var enemy = System.enemies[i]; | 
|---|
| 322 |  | 
|---|
| 323 | updateTrooper(enemy, "players"); | 
|---|
| 324 |  | 
|---|
| 325 | drawLifeGauge( | 
|---|
| 326 | System.screen.ctx, | 
|---|
| 327 | "lighter", | 
|---|
| 328 | enemy, 10, i * 33 + 10, | 
|---|
| 329 | System.screen.width | 
|---|
| 330 | ); | 
|---|
| 331 |  | 
|---|
| 332 | drawString( | 
|---|
| 333 | System.screen.ctx, | 
|---|
| 334 | "source-over", | 
|---|
| 335 | enemy.name, 10, i * 33 + 33, | 
|---|
| 336 | "#FCA", "9pt monospace", "left" | 
|---|
| 337 | ); | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | updateDeathPieces(System.screen.ctx, | 
|---|
| 341 | System.screen.width, | 
|---|
| 342 | System.screen.height); | 
|---|
| 343 |  | 
|---|
| 344 | if (!System.players.length) { | 
|---|
| 345 | drawString( | 
|---|
| 346 | System.screen.ctx, "source-over", | 
|---|
| 347 | "GAME OVER", | 
|---|
| 348 | System.screen.width / 2, | 
|---|
| 349 | System.screen.height / 2, | 
|---|
| 350 | "#ACF", "24pt monospace", "center" | 
|---|
| 351 | ); | 
|---|
| 352 | } | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 |  | 
|---|
| 356 | /* | 
|---|
| 357 | *  Initializer | 
|---|
| 358 | */ | 
|---|
| 359 | function initGame(canvas, msg, playerData) { | 
|---|
| 360 | System.screen.canvas = canvas; | 
|---|
| 361 | System.message       = msg; | 
|---|
| 362 | System.screen.ctx    = System.screen.canvas.getContext("2d"); | 
|---|
| 363 | System.screen.width  = System.screen.canvas.width; | 
|---|
| 364 | System.screen.height = System.screen.canvas.height; | 
|---|
| 365 | System.gameOver      = false; | 
|---|
| 366 |  | 
|---|
| 367 | System.screen.ctx.globalCompositeOperation = "lighter"; | 
|---|
| 368 |  | 
|---|
| 369 | if (System.mainIntervalId) { | 
|---|
| 370 | clearInterval(System.mainIntervalId); | 
|---|
| 371 | System.mainIntervalId = 0; | 
|---|
| 372 | } | 
|---|
| 373 |  | 
|---|
| 374 | drawScreen( | 
|---|
| 375 | System.screen.ctx, | 
|---|
| 376 | "source-over", | 
|---|
| 377 | "rgba(0,0,0,1)", | 
|---|
| 378 | System.screen.width, | 
|---|
| 379 | System.screen.height | 
|---|
| 380 | ); | 
|---|
| 381 |  | 
|---|
| 382 | System.players.push(new Trooper( | 
|---|
| 383 | playerData.name, | 
|---|
| 384 | new ActionList([new ManualAction(new ManualShot())]), | 
|---|
| 385 | playerData.image, | 
|---|
| 386 | playerData.size, | 
|---|
| 387 | playerData.hitsize, | 
|---|
| 388 | "#33F", | 
|---|
| 389 | "#F33", | 
|---|
| 390 | System.screen.width / 2, | 
|---|
| 391 | System.screen.height - System.screen.height / 7, | 
|---|
| 392 | System.screen.width, | 
|---|
| 393 | System.screen.height, | 
|---|
| 394 | playerData.hitpoint, | 
|---|
| 395 | playerData.speed, | 
|---|
| 396 | playerData.numbombs, | 
|---|
| 397 | ["rgba(255,0,0,0.3)", "rgba(0,0,255,0.3)"], | 
|---|
| 398 | [new LinerBarrage(YExtendBullet, | 
|---|
| 399 | playerData.shotsize, | 
|---|
| 400 | "rgba(64,64,128,0.7)", | 
|---|
| 401 | null, | 
|---|
| 402 | playerData.shotinterval, | 
|---|
| 403 | playerData.shotspeed, | 
|---|
| 404 | playerData.shotlevel, | 
|---|
| 405 | -0.5), | 
|---|
| 406 | new LinerBarrage(YExtendBullet, | 
|---|
| 407 | playerData.shotsize, | 
|---|
| 408 | "rgba(64,64,128,0.7)", | 
|---|
| 409 | null, | 
|---|
| 410 | playerData.shotinterval, | 
|---|
| 411 | playerData.shotspeed, | 
|---|
| 412 | playerData.shotlevel, | 
|---|
| 413 | 0.5), | 
|---|
| 414 | new CircularBarrage(LinerBullet, | 
|---|
| 415 | playerData.shotsize, | 
|---|
| 416 | "rgba(64,64,128,0.7)", | 
|---|
| 417 | null, | 
|---|
| 418 | playerData.shotinterval, | 
|---|
| 419 | playerData.shotspeed, | 
|---|
| 420 | playerData.shotlevel + 2, | 
|---|
| 421 | -0.5)] | 
|---|
| 422 | )); | 
|---|
| 423 |  | 
|---|
| 424 | System.backgroundObject = new Array(); | 
|---|
| 425 | System.mainIntervalId   = setInterval(mainLoop, 20); | 
|---|
| 426 | } | 
|---|