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

Revision 129, 15.6 KB checked in by atzm, 12 years ago (diff)

modified damaged behavior

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