source: pycodeshooter/trunk/shooter/system.js @ 132

Revision 132, 20.2 KB checked in by atzm, 13 years ago (diff)
  • add score board
  • Property svn:keywords set to Id
Line 
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
16System = {
17    "screen": {
18        "canvas": null,
19        "ctx":    null,
20        "width":  0,
21        "height": 0
22    },
23    "sound":            {},
24    "message":          null,
25    "enemyImages":      new Array(),
26    "enemies":          new Array(),
27    "players":          new Array(),
28    "score":            {},
29    "stage":            -1,
30    "backgroundObject": new Array(),
31    "deathPieces":      new Array(),
32    "items":            new Array(),
33    "mainIntervalId":   0
34};
35
36
37/*
38 *  Tiun Tiun Utilities
39 */
40var DeathPiece = function(sizes, colors, x, y, dir, speed) {
41    var that = new LinerBullet(sizes[0], colors[0], null,
42                               System.screen.width, System.screen.height,
43                               x, y, dir, speed);
44
45    var sizeIdx  = -1;
46    var colorIdx = -1;
47
48    that.getSize = function() {
49        if (++sizeIdx >= sizes.length)
50            sizeIdx = 0;
51        return sizes[sizeIdx];
52    };
53
54    that.getColor = function() {
55        if (++colorIdx >= colors.length)
56            colorIdx = 0;
57        return colors[colorIdx];
58    };
59
60    return that;
61};
62
63function addDeathPieces(x, y, sizes, colors, speed, way) {
64    if (way % 2)
65        way++;
66
67    var pieces = new Array();
68    var angle  = 0;
69    var delta  = 2 / way;
70
71    for(var i = 0; i < way; i++) {
72        pieces.push(new DeathPiece(sizes, colors, x, y, angle * Math.PI, speed));
73        angle += delta;
74    }
75
76    System.deathPieces.push(pieces);
77}
78
79function updateDeathPieces(ctx, width, height) {
80    var newObjs = new Array();
81
82    for (var i = 0; i < System.deathPieces.length; i++) {
83        var pieces    = System.deathPieces[i];
84        var newPieces = new Array();
85
86        for (var k = 0; k < pieces.length; k++) {
87            pieces[k].next();
88            pieces[k].draw(ctx);
89            if (!pieces[k].vanished(width, height))
90                newPieces.push(pieces[k]);
91        }
92
93        if (newPieces.length)
94            newObjs.push(newPieces);
95    }
96
97    System.deathPieces = newObjs;
98}
99
100
101/*
102 *  Item Utilities
103 */
104function addItem(x, y, angle, size, color, speed, attrs) {
105    var frame = {"style": "rect", "color": attrs.color, "width": size * 2, "height": size * 2};
106    var item  = new YExtendBullet(size, color, frame,
107                                  System.screen.width, System.screen.height,
108                                  x, y, angle * Math.PI, speed);
109    item.attrs = attrs;
110    System.items.push(item);
111}
112
113function updateItems(ctx, width, height, troopers) {
114    var newObjs = new Array();
115
116  ITEMLOOP:
117    for (var i = 0; i < System.items.length; i++) {
118        var item = System.items[i];
119
120        item.next();
121        item.draw(ctx, function(bullet, ctx) {
122            drawString(
123                ctx, "source-over", item.attrs.symbol,
124                bullet.x, bullet.y,
125                item.attrs.color, item.size + "pt monospace", "center"
126            );
127        });
128
129        for (var j = 0; j < troopers.length; j++) {
130            var trooper = troopers[j];
131
132            if (touch(trooper.x, trooper.y, trooper.size, item.x, item.y, item.size)) {
133                switch (item.attrs.type) {
134                case "score":
135                    playSound("se_item_score");
136                    if (System.score[trooper.name] !== undefined)
137                        System.score[trooper.name] += item.attrs.quantity;
138                    break;
139                case "shot":
140                    playSound("se_item_shot");
141                    for (var k = 0; k < trooper.barrages.length; k++) {
142                        trooper.barrages[k].way += item.attrs.quantity;
143                        if (trooper.barrages[k].way >= 5)
144                            trooper.barrages[k].way = 5;
145                    }
146                    break;
147                case "wide":
148                    playSound("se_item_wide");
149                    for (var k = 0; k < trooper.barrages.length; k++) {
150                        trooper.barrages[k].size += item.attrs.quantity;
151                        if (trooper.barrages[k].size >= 12)
152                            trooper.barrages[k].size = 12;
153                    }
154                    break;
155                case "bomb":
156                    playSound("se_item_bomb");
157                    trooper.numBombs += item.attrs.quantity;
158                    break;
159                case "life":
160                    playSound("se_item_life");
161                    trooper.life += item.attrs.quantity;
162                    break;
163                }
164
165                continue ITEMLOOP;
166            }
167        }
168
169        if (!item.vanished(width, height))
170            newObjs.push(item);
171    }
172
173    System.items = newObjs;
174}
175
176
177/*
178 *  Utility Functions
179 */
180function setMessage(elem, msg) {
181    if (elem)
182        elem.innerHTML = msg;
183}
184
185function addMessage(elem, msg) {
186    if (elem)
187        elem.innerHTML += msg;
188}
189
190function updateBackground(ctx, width, height, size, color, max) {
191    if (System.backgroundObject.length < max) {
192        var x = Math.ceil(Math.random() * width);
193        var s = Math.ceil(Math.random() * 5);
194        System.backgroundObject.push(
195            new LinerBullet(size, color, null,
196                            System.screen.width, System.screen.height,
197                            x, 0, 0.5 * Math.PI, s));
198    }
199
200    var newObjs = new Array();
201
202    for (var i = 0; i < System.backgroundObject.length; i++) {
203        System.backgroundObject[i].next();
204        System.backgroundObject[i].draw(ctx);
205        if (!System.backgroundObject[i].vanished(width, height))
206            newObjs.push(System.backgroundObject[i]);
207    }
208
209    System.backgroundObject = newObjs;
210}
211
212function drawScreen(ctx, op, style, width, height) {
213    var c = ctx.globalCompositeOperation;
214    ctx.globalCompositeOperation = op;
215    ctx.beginPath();
216    ctx.fillStyle = style;
217    ctx.fillRect(0, 0, width, height);
218    ctx.fill();
219    ctx.closePath();
220    ctx.globalCompositeOperation = c;
221}
222
223function drawLifeGauge(ctx, op, trooper, x, y, width) {
224    var length = trooper.life;
225
226    if (length > width - 20)
227        length = width - 20;
228
229    var c = ctx.globalCompositeOperation;
230    ctx.globalCompositeOperation = op;
231    ctx.beginPath();
232    ctx.fillStyle = trooper.color;
233    ctx.fillRect(x, y, length, 10);
234    ctx.fill();
235    ctx.closePath();
236    ctx.globalCompositeOperation = c;
237
238    drawString(ctx, op, trooper.life, x + 2, y + 8, trooper.color,
239               "6pt monospace", "left");
240}
241
242function drawString(ctx, op, string, x, y, color, font, align) {
243    var a = ctx.textAlign;
244    var f = ctx.font;
245    var c = ctx.globalCompositeOperation;
246    ctx.globalCompositeOperation = op;
247    ctx.beginPath();
248    ctx.textAlign = align;
249    ctx.fillStyle = color;
250    ctx.font      = font;
251    ctx.fillText(string, x, y);
252    ctx.fill();
253    ctx.textAlign = a;
254    ctx.font      = f;
255    ctx.closePath();
256    ctx.globalCompositeOperation = c;
257}
258
259function updateTrooper(trooper, enemyKey) {
260    trooper.update(System[enemyKey]);
261
262    var aliveEnemies = new Array();
263    for (var i = 0; i < System[enemyKey].length; i++) {
264        var enemy = System[enemyKey][i];
265
266        if (enemy.isDead()) {
267            if (System.score[trooper.name] !== undefined) {
268                System.score[trooper.name] += enemy.maxLife * 100;
269            }
270        }
271        else {
272            aliveEnemies.push(enemy);
273        }
274    }
275    System[enemyKey] = aliveEnemies;
276
277    trooper.draw(System.screen.ctx);
278}
279
280function getStageNumber() {
281    return System.stage;
282}
283
284function switchStage(base) {
285    var scores   = Object.keys(System.score);
286    var sum      = 0;
287    var stages   = new Array();
288    var score    = 0;
289    var stage    = 1;
290    var switched = false;
291
292    for (var i = 0; i < scores.length; i++) {
293        sum += System.score[scores[i]];
294    }
295
296    for (var name in System.sound) {
297        if (!name.match(/^bgm_stage/)) continue;
298        stages.push(name);
299    }
300
301    score        = Math.round(sum / scores.length);
302    stage        = Math.floor((score % (stages.length * base)) / base) + 1;
303    switched     = System.stage != stage;
304    System.stage = stage;
305
306    return switched;
307}
308
309function switchBgm(stage) {
310    for (var name in System.sound) {
311        if (!name.match(/^bgm_stage/))
312            continue;
313        if (("bgm_stage" + stage) == name)
314            playSound(name);
315        else
316            pauseSound(name, true);
317    }
318}
319
320function toggleSound(val) {
321    for (var name in System.sound)
322        System.sound[name].muted = !val;
323}
324
325function registerSound(name, audio) {
326    System.sound[name] = audio;
327}
328
329function playSound(name) {
330    if (System.sound[name])
331        System.sound[name].play();
332}
333
334function pauseSound(name, stop) {
335    if (System.sound[name]) {
336        System.sound[name].pause();
337        if (stop)
338            System.sound[name].currentTime = 0;
339    }
340}
341
342function getEnemiesOnScreen() {
343    return System.enemies.length;
344}
345
346function addEnemyImage(image) {
347    System.enemyImages.push(image);
348}
349
350function addEnemy(enemyData) {
351    var actList = EnemyActionLists[enemyData.mtime % EnemyActionLists.length];
352    var shot    = EnemyShots[enemyData.hitpoint % EnemyShots.length];
353    var numAct  = enemyData.agility       % (EnemyActions.length  - 1) + 1;
354    var numBlt  = enemyData.skills.length % (EnemyBullets.length  - 1) + 1;
355    var numBrrg = enemyData.skills.length % (EnemyBarrages.length - 1) + 1;
356    var acts    = new Array();
357    var brrgs   = new Array();
358
359    var bulletWay         = Math.ceil(enemyData.concentration / 10);
360    var bulletInterval    = Math.round(50 * 1 / Math.log(enemyData.skillpoint + 0.1));
361    var bulletSize        = Math.round(Math.log(enemyData.luck + 1));
362    var bulletFrameWidth  = (bulletSize + 5) * 2;
363    var bulletFrameHeight = (bulletSize + 5) * 4;
364    var bulletSpeed       = enemyData.strength / 15;
365
366    bulletSpeed = Math.log(bulletSpeed < 1.5 ? 1.5 : bulletSpeed);
367
368    for (var i = 0; i < numAct; i++) {
369        var idx = (enemyData.agility + i) % EnemyActions.length;
370        acts.push(new EnemyActions[idx](new shot()));
371    }
372
373    for (var i = 0; i < numBrrg; i++) {
374        var idx     = (enemyData.skillpoint + i * (enemyData.skills.length + 1)) % EnemyBarrages.length;
375        var brrgCls = EnemyBarrages[idx];
376        var frameColor;
377
378        switch(idx % 3) {
379        case 0:
380            frameColor = "rgba(128,32,32,0.7)";
381            break;
382        case 1:
383            frameColor = "rgba(32,128,32,0.7)";
384            break;
385        default:
386            frameColor = "rgba(64,64,128,0.7)";
387        }
388
389        for (var k = 0; k < numBlt; k++) {
390            var iidx = (enemyData.skills.length + i + k) % EnemyBullets.length;
391            brrgs.push(
392                new brrgCls(
393                    EnemyBullets[iidx],
394                    bulletSize,
395                    "#FF3",
396                    {"style": "rect", "color": frameColor,
397                     "width": bulletFrameWidth, "height": bulletFrameHeight},
398                    bulletInterval,
399                    bulletSpeed,
400                    bulletWay
401                )
402            );
403        }
404    }
405
406    var size  = Math.ceil((System.screen.width / 2) * (1 / enemyData.defense));
407    var enemy = new Trooper(
408        enemyData.name,
409        new actList(acts),
410        System.enemyImages[enemyData.hitpoint % System.enemyImages.length],
411        size,
412        size,
413        "#F33",
414        "#F33",
415        Math.floor(Math.random() * System.screen.width),
416        Math.floor(Math.random() * (System.screen.height / 4)),
417        System.screen.width,
418        System.screen.height,
419        Math.floor(enemyData.hitpoint / 25) + 1,
420        Math.log(enemyData.agility + 0.1) * 3,
421        0,
422        ["rgba(255,0,0,0.3)", "rgba(0,0,255,0.3)"],
423        brrgs
424    );
425
426    enemy.registerCallback("damaged", function() {
427        if (enemy.isDead()) {
428            playSound("se_destroy");
429
430            addDeathPieces(
431                enemy.x, enemy.y,
432                [6, 8, 10], ["#F55", "#FAA"], 3, 8
433            );
434
435            var itemAttr;
436            var itemRand = Math.floor(Math.random() * 100);
437            if (itemRand < 40) {
438                // nothing
439            }
440            else if (itemRand < 70) {
441                itemAttr = {"color": "#AAF", "symbol": "S", "type": "score", "quantity": enemy.maxLife * 200};
442            }
443            else if (itemRand < 85) {
444                itemAttr = {"color": "#FAA", "symbol": "P", "type": "shot", "quantity": 1};
445            }
446            else if (itemRand < 95) {
447                itemAttr = {"color": "#A5F", "symbol": "W", "type": "wide", "quantity": 1};
448            }
449            else if (itemRand < 98) {
450                itemAttr = {"color": "#FFA", "symbol": "B", "type": "bomb", "quantity": 1};
451            }
452            else {
453                itemAttr = {"color": "#AFA", "symbol": "L", "type": "life", "quantity": 1};
454            }
455
456            if (itemAttr)
457                addItem(enemy.x, enemy.y, 0.5, 5, "#AFA", 1, itemAttr);
458        }
459        else {
460            playSound("se_damage");
461        }
462    });
463
464    System.enemies.push(enemy);
465}
466
467
468/*
469 *  Main loop
470 */
471function mainLoop() {
472    // clear screen
473    drawScreen(
474        System.screen.ctx,
475        "source-over",
476        "rgba(8,8,8,0.8)",
477        System.screen.width,
478        System.screen.height
479    );
480
481    // update background objects
482    updateBackground(
483        System.screen.ctx,
484        System.screen.width,
485        System.screen.height,
486        1, "#CAF", 10
487    );
488
489    // switch stage
490    if (switchStage(50000))
491        switchBgm(getStageNumber());
492
493    // draw stage number
494    drawString(
495        System.screen.ctx,
496        "source-over",
497        "STAGE " + getStageNumber(),
498        System.screen.width - 10,
499        15,
500        "#ACF", "9pt monospace", "right"
501    );
502
503    // draw score
504    var scoreNames = Object.keys(System.score).sort();
505    for (var i = 0; i < scoreNames.length; i++) {
506        var name = scoreNames[i];
507        drawString(
508            System.screen.ctx,
509            "source-over",
510            name + " SCORE " + System.score[name],
511            (System.screen.width - 10),
512            i * 16 + 30,
513            "#ACF", "9pt monospace", "right"
514        );
515    }
516
517    // update/draw items
518    updateItems(System.screen.ctx,
519                System.screen.width,
520                System.screen.height,
521                System.players);
522
523    // update/draw troopers
524    for (var i = 0; i < System.players.length; i++) {
525        var player = System.players[i];
526
527        updateTrooper(player, "enemies");
528
529        drawLifeGauge(
530            System.screen.ctx,
531            "lighter",
532            player,
533            10,
534            (System.screen.height - 20) - (i * 33),
535            System.screen.width
536        );
537
538        drawString(
539            System.screen.ctx,
540            "source-over",
541            player.name,
542            10,
543            (System.screen.height - 23) - (i * 33),
544            "#ACF", "9pt monospace", "left"
545        );
546
547        drawString(
548            System.screen.ctx,
549            "source-over",
550            "BOMB " + player.numBombs,
551            (System.screen.width - 10),
552            (System.screen.height - 23) - (i * 33),
553            "#ACF", "9pt monospace", "right"
554        );
555    }
556
557    // update/draw enemies
558    for (var i = 0; i < System.enemies.length; i++) {
559        var enemy = System.enemies[i];
560
561        updateTrooper(enemy, "players");
562
563        drawLifeGauge(
564            System.screen.ctx,
565            "lighter",
566            enemy, 10, i * 33 + 10,
567            System.screen.width
568        );
569
570        drawString(
571            System.screen.ctx,
572            "source-over",
573            enemy.name, 10, i * 33 + 33,
574            "#FCA", "9pt monospace", "left"
575        );
576    }
577
578    updateDeathPieces(System.screen.ctx,
579                      System.screen.width,
580                      System.screen.height);
581
582    if (!System.players.length) {
583        drawString(
584            System.screen.ctx, "source-over",
585            "GAME OVER",
586            System.screen.width / 2,
587            System.screen.height / 2,
588            "#ACF", "24pt monospace", "center"
589        );
590    }
591}
592
593
594/*
595 *  Initializer
596 */
597function initGame(canvas, msg, playerData, scoreCallback) {
598    System.screen.canvas = canvas;
599    System.message       = msg;
600    System.screen.ctx    = System.screen.canvas.getContext("2d");
601    System.screen.width  = System.screen.canvas.width;
602    System.screen.height = System.screen.canvas.height;
603    System.gameOver      = false;
604
605    System.screen.ctx.globalCompositeOperation = "lighter";
606
607    if (System.mainIntervalId) {
608        clearInterval(System.mainIntervalId);
609        System.mainIntervalId   = 0;
610        System.players          = new Array();
611        System.enemies          = new Array();
612        System.backgroundObject = new Array();
613        System.deathPieces      = new Array();
614    }
615
616    var trooper = new Trooper(
617        playerData.name,
618        new ActionList([new ManualAction(new ManualShot())]),
619        playerData.image,
620        playerData.size,
621        playerData.hitsize,
622        "#33F",
623        "#F33",
624        System.screen.width / 2,
625        System.screen.height - System.screen.height / 7,
626        System.screen.width,
627        System.screen.height,
628        playerData.hitpoint,
629        playerData.speed,
630        playerData.numbombs,
631        ["rgba(255,0,0,0.3)", "rgba(0,0,255,0.3)"],
632        [new LinerBarrage(YExtendBullet,
633                          playerData.shotsize,
634                          "rgba(64,64,128,0.4)",
635                          null,
636                          playerData.shotinterval,
637                          playerData.shotspeed,
638                          playerData.shotlevel,
639                          -0.5),
640         new LinerBarrage(LinerBullet,
641                          playerData.shotsize,
642                          "rgba(64,64,128,0.4)",
643                          null,
644                          playerData.shotinterval,
645                          playerData.shotspeed,
646                          playerData.shotlevel,
647                          -0.5)]
648    );
649    trooper.registerCallback("addBomb", function() { playSound("se_bomb"); });
650    trooper.registerCallback("damaged", function() {
651        playSound("se_destroy");
652
653        addDeathPieces(
654            trooper.x, trooper.y,
655            [6, 8, 10], ["#55F", "#AAF"], 3, 8
656        );
657
658        for (var i = 0; i < System.enemies.length; i++) {
659            System.enemies[i].clearBullet();
660        }
661
662        trooper.x = System.screen.width / 2;
663        trooper.y = System.screen.height - System.screen.height / 7;
664        trooper.numBombs = playerData.numbombs;
665        trooper.barrages = [
666            new LinerBarrage(YExtendBullet,
667                             playerData.shotsize,
668                             "rgba(64,64,128,0.4)",
669                             null,
670                             playerData.shotinterval,
671                             playerData.shotspeed,
672                             playerData.shotlevel,
673                             -0.5),
674            new LinerBarrage(LinerBullet,
675                             playerData.shotsize,
676                             "rgba(64,64,128,0.4)",
677                             null,
678                             playerData.shotinterval,
679                             playerData.shotspeed,
680                             playerData.shotlevel,
681                             -0.5)
682        ];
683
684        if (trooper.isDead() && scoreCallback) {
685            scoreCallback(trooper.name, System.score[trooper.name]);
686        }
687    });
688
689    System.players.push(trooper);
690
691    for (var i = 0; i < System.players.length; i++) {
692        System.score[System.players[i].name] = 0;
693    }
694
695    drawScreen(
696        System.screen.ctx,
697        "source-over",
698        "rgba(0,0,0,1)",
699        System.screen.width,
700        System.screen.height
701    );
702
703    document.onkeydown  = function (ev) { setKeyDown(ev.keyCode); };
704    document.onkeyup    = function (ev) { setKeyUp(ev.keyCode); };
705    document.onkeypress = function (ev) { setKeyPress(ev.charCode); };
706
707    System.mainIntervalId = setInterval(mainLoop, 20);
708}
Note: See TracBrowser for help on using the repository browser.