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

Revision 286, 21.0 KB checked in by atzm, 8 years ago (diff)

add the `round'

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