feat: add 10-level progression for all games and new Vortex game
- Expand all existing games (ColoradoDefense, CodeBugInvaders, DotDude, SmashOut) from 2 levels to 10 levels with progressive difficulty scaling - Add new game "Vortex" with 10 levels of increasing difficulty - Update game config to include Vortex in the game pool - Add Vortex music asset and configuration - Improve GameManager robustness with duplicate event guards - Clean up JSON formatting in all game config files - Remove version query parameters from config loading where appropriate
This commit is contained in:
parent
3527a1e3ef
commit
d044b1f319
Binary file not shown.
|
|
@ -2,7 +2,15 @@
|
|||
"lives": 3,
|
||||
"levels": {
|
||||
"1": { "gameCount": 3 },
|
||||
"2": { "gameCount": 4 }
|
||||
"2": { "gameCount": 4 },
|
||||
"3": { "gameCount": 4 },
|
||||
"4": { "gameCount": 4 },
|
||||
"5": { "gameCount": 5 },
|
||||
"6": { "gameCount": 5 },
|
||||
"7": { "gameCount": 5 },
|
||||
"8": { "gameCount": 5 },
|
||||
"9": { "gameCount": 5 },
|
||||
"10": { "gameCount": 5 }
|
||||
},
|
||||
"gamePool": ["ColoradoDefense", "CodeBugInvaders", "DotDude", "SmashOut"]
|
||||
"gamePool": ["ColoradoDefense", "CodeBugInvaders", "DotDude", "SmashOut", "Vortex"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export default class CodeBugInvaders extends Phaser.Scene {
|
|||
constructor() { super({ key: 'CodeBugInvaders' }); }
|
||||
|
||||
preload() {
|
||||
this.load.json('cbiConfig', 'src/games/code-bug-invaders/config.json?v=2');
|
||||
this.load.json('cbiConfig', 'src/games/code-bug-invaders/config.json');
|
||||
this.load.audio('cbiMusic', 'assets/music/02-code-bug-invaders.mp3');
|
||||
this.load.audio('cbiComplete', 'assets/fx/complete.mp3');
|
||||
this.load.audio('cbifire', 'assets/fx/fire.mp3');
|
||||
|
|
|
|||
|
|
@ -1,24 +1,184 @@
|
|||
{
|
||||
"levels": {
|
||||
"1": {
|
||||
"cols": 10, "rows": 4,
|
||||
"bugSpacingX": 120, "bugSpacingY": 70,
|
||||
"startX": 180, "startY": 100,
|
||||
"formationSpeed": 60, "formationStepDown": 30,
|
||||
"rightBound": 1540, "leftBound": 60,
|
||||
"bugFireInterval": 1800, "bugBulletSpeed": 280,
|
||||
"playerSpeed": 420, "playerBulletSpeed": 700,
|
||||
"dangerY": 720, "bugsToPass": 15
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 60,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 1800,
|
||||
"bugBulletSpeed": 280,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 15
|
||||
},
|
||||
"2": {
|
||||
"cols": 10, "rows": 4,
|
||||
"bugSpacingX": 120, "bugSpacingY": 70,
|
||||
"startX": 180, "startY": 100,
|
||||
"formationSpeed": 85, "formationStepDown": 30,
|
||||
"rightBound": 1540, "leftBound": 60,
|
||||
"bugFireInterval": 1300, "bugBulletSpeed": 350,
|
||||
"playerSpeed": 420, "playerBulletSpeed": 700,
|
||||
"dangerY": 720, "bugsToPass": 28
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 85,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 1300,
|
||||
"bugBulletSpeed": 350,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 20
|
||||
},
|
||||
"3": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 105,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 1010,
|
||||
"bugBulletSpeed": 400,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 23
|
||||
},
|
||||
"4": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 120,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 795,
|
||||
"bugBulletSpeed": 440,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 26
|
||||
},
|
||||
"5": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 133,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 642,
|
||||
"bugBulletSpeed": 473,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 29
|
||||
},
|
||||
"6": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 144,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 530,
|
||||
"bugBulletSpeed": 499,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 31
|
||||
},
|
||||
"7": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 152,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 447,
|
||||
"bugBulletSpeed": 519,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 33
|
||||
},
|
||||
"8": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 159,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 384,
|
||||
"bugBulletSpeed": 535,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 35
|
||||
},
|
||||
"9": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 164,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 336,
|
||||
"bugBulletSpeed": 547,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 37
|
||||
},
|
||||
"10": {
|
||||
"cols": 10,
|
||||
"rows": 4,
|
||||
"bugSpacingX": 120,
|
||||
"bugSpacingY": 70,
|
||||
"startX": 180,
|
||||
"startY": 100,
|
||||
"formationSpeed": 168,
|
||||
"formationStepDown": 30,
|
||||
"rightBound": 1540,
|
||||
"leftBound": 60,
|
||||
"bugFireInterval": 298,
|
||||
"bugBulletSpeed": 557,
|
||||
"playerSpeed": 420,
|
||||
"playerBulletSpeed": 700,
|
||||
"dangerY": 720,
|
||||
"bugsToPass": 38
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export default class ColoradoDefense extends Phaser.Scene {
|
|||
constructor() { super({ key: 'ColoradoDefense' }); }
|
||||
|
||||
preload() {
|
||||
this.load.json('cdConfig', 'src/games/colorado-defense/config.json?v=2');
|
||||
this.load.json('cdConfig', 'src/games/colorado-defense/config.json');
|
||||
this.load.json('cdCities', 'src/games/colorado-defense/cities.json');
|
||||
this.load.audio('cdMusic', 'assets/music/01-colorado-defense.mp3');
|
||||
this.load.audio('cdComplete', 'assets/fx/complete.mp3');
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
{ "count": 9, "speed": 80, "spawnInterval": 2000 },
|
||||
{ "count": 14, "speed": 110, "spawnInterval": 1400 }
|
||||
],
|
||||
"interceptorSpeed": 420, "explosionRadius": 75,
|
||||
"maxCityDeaths": 3, "wavesToPass": 2
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 3,
|
||||
"wavesToPass": 1
|
||||
},
|
||||
"2": {
|
||||
"waves": [
|
||||
|
|
@ -15,8 +17,98 @@
|
|||
{ "count": 12, "speed": 100, "spawnInterval": 1600 },
|
||||
{ "count": 16, "speed": 130, "spawnInterval": 1100 }
|
||||
],
|
||||
"interceptorSpeed": 420, "explosionRadius": 75,
|
||||
"maxCityDeaths": 2, "wavesToPass": 3
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 2,
|
||||
"wavesToPass": 2
|
||||
},
|
||||
"3": {
|
||||
"waves": [
|
||||
{ "count": 10, "speed": 82, "spawnInterval": 1900 },
|
||||
{ "count": 14, "speed": 118, "spawnInterval": 1350 },
|
||||
{ "count": 18, "speed": 148, "spawnInterval": 980 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 2,
|
||||
"wavesToPass": 2
|
||||
},
|
||||
"4": {
|
||||
"waves": [
|
||||
{ "count": 11, "speed": 93, "spawnInterval": 1650 },
|
||||
{ "count": 16, "speed": 132, "spawnInterval": 1175 },
|
||||
{ "count": 21, "speed": 163, "spawnInterval": 875 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 2,
|
||||
"wavesToPass": 2
|
||||
},
|
||||
"5": {
|
||||
"waves": [
|
||||
{ "count": 12, "speed": 102, "spawnInterval": 1455 },
|
||||
{ "count": 17, "speed": 144, "spawnInterval": 1040 },
|
||||
{ "count": 23, "speed": 176, "spawnInterval": 790 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 2,
|
||||
"wavesToPass": 3
|
||||
},
|
||||
"6": {
|
||||
"waves": [
|
||||
{ "count": 13, "speed": 110, "spawnInterval": 1290 },
|
||||
{ "count": 19, "speed": 154, "spawnInterval": 932 },
|
||||
{ "count": 25, "speed": 187, "spawnInterval": 717 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 2,
|
||||
"wavesToPass": 3
|
||||
},
|
||||
"7": {
|
||||
"waves": [
|
||||
{ "count": 14, "speed": 117, "spawnInterval": 1155 },
|
||||
{ "count": 20, "speed": 163, "spawnInterval": 840 },
|
||||
{ "count": 27, "speed": 197, "spawnInterval": 653 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 1,
|
||||
"wavesToPass": 3
|
||||
},
|
||||
"8": {
|
||||
"waves": [
|
||||
{ "count": 15, "speed": 122, "spawnInterval": 1042 },
|
||||
{ "count": 22, "speed": 170, "spawnInterval": 763 },
|
||||
{ "count": 29, "speed": 205, "spawnInterval": 597 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 1,
|
||||
"wavesToPass": 3
|
||||
},
|
||||
"9": {
|
||||
"waves": [
|
||||
{ "count": 16, "speed": 127, "spawnInterval": 946 },
|
||||
{ "count": 23, "speed": 177, "spawnInterval": 696 },
|
||||
{ "count": 31, "speed": 213, "spawnInterval": 548 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 1,
|
||||
"wavesToPass": 3
|
||||
},
|
||||
"10": {
|
||||
"waves": [
|
||||
{ "count": 18, "speed": 130, "spawnInterval": 862 },
|
||||
{ "count": 25, "speed": 183, "spawnInterval": 638 },
|
||||
{ "count": 34, "speed": 220, "spawnInterval": 505 }
|
||||
],
|
||||
"interceptorSpeed": 420,
|
||||
"explosionRadius": 75,
|
||||
"maxCityDeaths": 1,
|
||||
"wavesToPass": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default class DotDude extends Phaser.Scene {
|
|||
constructor() { super({ key: 'DotDude' }); }
|
||||
|
||||
preload() {
|
||||
this.load.json('ddConfig', 'src/games/dot-dude/config.json?v=2');
|
||||
this.load.json('ddConfig', 'src/games/dot-dude/config.json');
|
||||
this.load.audio('ddMusic', 'assets/music/03-dot-dude.mp3');
|
||||
this.load.audio('ddPellet', 'assets/fx/pellet.mp3');
|
||||
this.load.audio('ddComplete', 'assets/fx/complete.mp3');
|
||||
|
|
|
|||
|
|
@ -1,14 +1,74 @@
|
|||
{
|
||||
"levels": {
|
||||
"1": {
|
||||
"playerSpeed": 155, "ghostSpeed": 90,
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 90,
|
||||
"ghostStartDelay": [2000, 4000, 6500],
|
||||
"dotRadius": 5, "pelletsToPass": 20
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 20
|
||||
},
|
||||
"2": {
|
||||
"playerSpeed": 155, "ghostSpeed": 120,
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 120,
|
||||
"ghostStartDelay": [1500, 3000, 4500],
|
||||
"dotRadius": 5, "pelletsToPass": 40
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 25
|
||||
},
|
||||
"3": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 143,
|
||||
"ghostStartDelay": [1100, 2200, 3500],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 28
|
||||
},
|
||||
"4": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 160,
|
||||
"ghostStartDelay": [850, 1700, 2700],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 31
|
||||
},
|
||||
"5": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 175,
|
||||
"ghostStartDelay": [650, 1300, 2100],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 34
|
||||
},
|
||||
"6": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 188,
|
||||
"ghostStartDelay": [500, 1000, 1650],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 37
|
||||
},
|
||||
"7": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 199,
|
||||
"ghostStartDelay": [380, 760, 1280],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 40
|
||||
},
|
||||
"8": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 208,
|
||||
"ghostStartDelay": [290, 580, 980],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 43
|
||||
},
|
||||
"9": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 215,
|
||||
"ghostStartDelay": [220, 440, 750],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 46
|
||||
},
|
||||
"10": {
|
||||
"playerSpeed": 155,
|
||||
"ghostSpeed": 220,
|
||||
"ghostStartDelay": [160, 320, 540],
|
||||
"dotRadius": 5,
|
||||
"pelletsToPass": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default class SmashOut extends Phaser.Scene {
|
|||
constructor() { super({ key: 'SmashOut' }); }
|
||||
|
||||
preload() {
|
||||
this.load.json('soConfig', 'src/games/smash-out/config.json?v=2');
|
||||
this.load.json('soConfig', 'src/games/smash-out/config.json');
|
||||
this.load.audio('soMusic', 'assets/music/04-smash-out.mp3');
|
||||
this.load.audio('soComplete', 'assets/fx/complete.mp3');
|
||||
this.load.audio('soLaunch', 'assets/fx/launch.mp3');
|
||||
|
|
|
|||
|
|
@ -1,17 +1,173 @@
|
|||
{
|
||||
"levels": {
|
||||
"1": {
|
||||
"paddleWidth": 200, "paddleHeight": 18, "paddleY": 855,
|
||||
"paddleSpeed": 480, "ballRadius": 12, "ballSpeed": 530,
|
||||
"blockCols": 8, "blockRows": 5, "blockWidth": 140, "blockHeight": 30,
|
||||
"blockGapX": 20, "blockGapY": 12, "blocksStartX": 170, "blocksStartY": 70,
|
||||
"bricksToPass": 15
|
||||
"paddleWidth": 200,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 480,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 530,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 8
|
||||
},
|
||||
"2": {
|
||||
"paddleWidth": 170, "paddleHeight": 18, "paddleY": 855,
|
||||
"paddleSpeed": 520, "ballRadius": 12, "ballSpeed": 640,
|
||||
"blockCols": 8, "blockRows": 5, "blockWidth": 140, "blockHeight": 30,
|
||||
"blockGapX": 20, "blockGapY": 12, "blocksStartX": 170, "blocksStartY": 70,
|
||||
"paddleWidth": 170,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 520,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 640,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 10
|
||||
},
|
||||
"3": {
|
||||
"paddleWidth": 150,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 555,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 725,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 12
|
||||
},
|
||||
"4": {
|
||||
"paddleWidth": 134,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 583,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 800,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 15
|
||||
},
|
||||
"5": {
|
||||
"paddleWidth": 121,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 607,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 865,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 18
|
||||
},
|
||||
"6": {
|
||||
"paddleWidth": 111,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 627,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 920,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 20
|
||||
},
|
||||
"7": {
|
||||
"paddleWidth": 103,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 643,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 968,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 23
|
||||
},
|
||||
"8": {
|
||||
"paddleWidth": 97,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 655,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 1008,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 25
|
||||
},
|
||||
"9": {
|
||||
"paddleWidth": 92,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 665,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 1042,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 27
|
||||
},
|
||||
"10": {
|
||||
"paddleWidth": 88,
|
||||
"paddleHeight": 18,
|
||||
"paddleY": 855,
|
||||
"paddleSpeed": 672,
|
||||
"ballRadius": 12,
|
||||
"ballSpeed": 1070,
|
||||
"blockCols": 8,
|
||||
"blockRows": 5,
|
||||
"blockWidth": 140,
|
||||
"blockHeight": 30,
|
||||
"blockGapX": 20,
|
||||
"blockGapY": 12,
|
||||
"blocksStartX": 170,
|
||||
"blocksStartY": 70,
|
||||
"bricksToPass": 30
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,426 @@
|
|||
import { playMusic, stopMusic } from '../../audio.js';
|
||||
|
||||
const TAU = Math.PI * 2;
|
||||
|
||||
export default class Vortex extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'Vortex' });
|
||||
}
|
||||
|
||||
preload() {
|
||||
this.load.json('vortexConfig', 'src/games/vortex/config.json?v=1');
|
||||
this.load.audio('vortexMusic', 'assets/music/05-vortex.mp3');
|
||||
this.load.audio('vortexComplete', 'assets/fx/complete.mp3');
|
||||
this.load.audio('vortexFire', 'assets/fx/laser.mp3');
|
||||
this.load.audio('vortexHit', 'assets/fx/destroy.mp3');
|
||||
this.load.audio('vortexBreach', 'assets/fx/short_death.mp3');
|
||||
}
|
||||
|
||||
init(data) {
|
||||
this.lives = data.lives ?? 3;
|
||||
this.level = String(data.level ?? 1);
|
||||
}
|
||||
|
||||
create() {
|
||||
const all = this.cache.json.get('vortexConfig');
|
||||
const cfg = all.levels[this.level] ?? all.levels['1'];
|
||||
this.cfg = cfg;
|
||||
|
||||
this.cx = 800;
|
||||
this.cy = 470;
|
||||
this.TWO_PI_N = TAU / cfg.numLanes;
|
||||
|
||||
this.playerLane = 0;
|
||||
this.moveCooldown = 0;
|
||||
this.keyAPressed = false;
|
||||
|
||||
this.bullets = [];
|
||||
this.enemies = [];
|
||||
this.kills = 0;
|
||||
this.misses = 0;
|
||||
this.spawnTimer = cfg.spawnInterval * 0.5; // first spawn after half interval
|
||||
this.gameActive = true;
|
||||
|
||||
this.cursors = this.input.keyboard.createCursorKeys();
|
||||
this.keyA = this.input.keyboard.addKey('A');
|
||||
|
||||
this.bgGfx = this.add.graphics();
|
||||
this.mainGfx = this.add.graphics();
|
||||
|
||||
this._drawBackground();
|
||||
|
||||
this.killsText = this.add.text(800, 16, '', {
|
||||
fontSize: '26px', fontFamily: 'monospace',
|
||||
color: '#00ffff', stroke: '#000000', strokeThickness: 4,
|
||||
}).setOrigin(0.5, 0).setDepth(10);
|
||||
|
||||
this.shieldsText = this.add.text(20, 16, '', {
|
||||
fontSize: '26px', fontFamily: 'monospace',
|
||||
color: '#ffaa00', stroke: '#000000', strokeThickness: 4,
|
||||
}).setDepth(10);
|
||||
|
||||
this.livesText = this.add.text(1580, 16, '', {
|
||||
fontSize: '26px', fontFamily: 'monospace',
|
||||
color: '#ff4444', stroke: '#000000', strokeThickness: 4,
|
||||
}).setOrigin(1, 0).setDepth(10);
|
||||
|
||||
this.levelText = this.add.text(800, 860, `LEVEL ${this.level} | Arrow Keys: Move A: Fire`, {
|
||||
fontSize: '20px', fontFamily: 'monospace',
|
||||
color: '#446688', stroke: '#000000', strokeThickness: 3,
|
||||
}).setOrigin(0.5, 1).setDepth(10);
|
||||
|
||||
playMusic(this, 'vortexMusic');
|
||||
this._updateHUD();
|
||||
}
|
||||
|
||||
update(time, delta) {
|
||||
if (!this.gameActive) return;
|
||||
const dt = delta / 1000;
|
||||
|
||||
this._handleInput(dt);
|
||||
this._spawnEnemies(dt);
|
||||
this._updateBullets(dt);
|
||||
this._updateEnemies(dt);
|
||||
this._draw();
|
||||
this._updateHUD();
|
||||
}
|
||||
|
||||
// ─── Input ────────────────────────────────────────────────────────────────
|
||||
|
||||
_handleInput(dt) {
|
||||
this.moveCooldown = Math.max(0, this.moveCooldown - dt * 1000);
|
||||
|
||||
if (this.moveCooldown <= 0) {
|
||||
if (this.cursors.left.isDown) {
|
||||
this.playerLane = (this.playerLane - 1 + this.cfg.numLanes) % this.cfg.numLanes;
|
||||
this.moveCooldown = this.cfg.moveCooldown;
|
||||
} else if (this.cursors.right.isDown) {
|
||||
this.playerLane = (this.playerLane + 1) % this.cfg.numLanes;
|
||||
this.moveCooldown = this.cfg.moveCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.keyA.isDown && !this.keyAPressed) {
|
||||
this.keyAPressed = true;
|
||||
this._shoot();
|
||||
}
|
||||
if (!this.keyA.isDown) this.keyAPressed = false;
|
||||
}
|
||||
|
||||
_shoot() {
|
||||
this.bullets.push({ lane: this.playerLane, t: 0.98 });
|
||||
this._playFx('vortexFire', 0.45);
|
||||
}
|
||||
|
||||
// ─── Spawn ────────────────────────────────────────────────────────────────
|
||||
|
||||
_spawnEnemies(dt) {
|
||||
this.spawnTimer += dt;
|
||||
if (this.spawnTimer < this.cfg.spawnInterval) return;
|
||||
if (this.enemies.length >= this.cfg.maxActiveEnemies) return;
|
||||
this.spawnTimer = 0;
|
||||
|
||||
const lane = Phaser.Math.Between(0, this.cfg.numLanes - 1);
|
||||
this.enemies.push({ lane, t: 0.04, alive: true, flash: 0 });
|
||||
}
|
||||
|
||||
// ─── Update ───────────────────────────────────────────────────────────────
|
||||
|
||||
_updateBullets(dt) {
|
||||
const speed = this.cfg.bulletSpeed;
|
||||
for (const b of this.bullets) {
|
||||
b.t -= speed * dt;
|
||||
}
|
||||
|
||||
// Bullet–enemy collision
|
||||
for (const b of this.bullets) {
|
||||
if (b.t <= 0) continue;
|
||||
for (const e of this.enemies) {
|
||||
if (!e.alive || e.lane !== b.lane) continue;
|
||||
if (Math.abs(b.t - e.t) < 0.07) {
|
||||
e.alive = false;
|
||||
b.t = -1;
|
||||
this.kills++;
|
||||
this._playFx('vortexHit', 0.6);
|
||||
if (this.kills >= this.cfg.killsToWin) {
|
||||
this._endGame(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.bullets = this.bullets.filter(b => b.t > 0);
|
||||
}
|
||||
|
||||
_updateEnemies(dt) {
|
||||
const speed = this.cfg.enemySpeed;
|
||||
for (const e of this.enemies) {
|
||||
if (!e.alive) continue;
|
||||
e.t += speed * dt;
|
||||
if (e.flash > 0) e.flash -= dt;
|
||||
}
|
||||
|
||||
// Check rim breaches
|
||||
for (const e of this.enemies) {
|
||||
if (!e.alive || e.t < 1.0) continue;
|
||||
e.alive = false;
|
||||
this.misses++;
|
||||
this._playFx('vortexBreach', 0.7);
|
||||
if (this.misses >= this.cfg.maxMisses) {
|
||||
this._endGame(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.enemies = this.enemies.filter(e => e.alive);
|
||||
}
|
||||
|
||||
// ─── Drawing ──────────────────────────────────────────────────────────────
|
||||
|
||||
_drawBackground() {
|
||||
this.bgGfx.fillStyle(0x000814);
|
||||
this.bgGfx.fillRect(0, 0, 1600, 900);
|
||||
}
|
||||
|
||||
_draw() {
|
||||
const g = this.mainGfx;
|
||||
g.clear();
|
||||
|
||||
this._drawTube(g);
|
||||
this._drawPlayerLaneHighlight(g);
|
||||
this._drawEnemies(g);
|
||||
this._drawBullets(g);
|
||||
this._drawPlayer(g);
|
||||
}
|
||||
|
||||
_drawTube(g) {
|
||||
const { numLanes, rimRadius } = this.cfg;
|
||||
const cx = this.cx, cy = this.cy;
|
||||
const step = this.TWO_PI_N;
|
||||
const RINGS = 7;
|
||||
|
||||
// Depth rings (concentric polygons shrinking toward center)
|
||||
for (let r = 1; r <= RINGS; r++) {
|
||||
const t = r / RINGS;
|
||||
const depth = t * t; // non-linear for stronger perspective
|
||||
const radius = rimRadius * depth;
|
||||
const alpha = 0.15 + t * 0.25;
|
||||
const blue = Math.floor(60 + t * 140);
|
||||
g.lineStyle(1, Phaser.Display.Color.GetColor(0, blue / 3, blue), alpha);
|
||||
g.beginPath();
|
||||
for (let i = 0; i <= numLanes; i++) {
|
||||
const angle = i * step - Math.PI / 2;
|
||||
const x = cx + radius * Math.cos(angle);
|
||||
const y = cy + radius * Math.sin(angle);
|
||||
if (i === 0) g.moveTo(x, y); else g.lineTo(x, y);
|
||||
}
|
||||
g.closePath();
|
||||
g.strokePath();
|
||||
}
|
||||
|
||||
// Lane dividers: lines from each rim vertex to center
|
||||
for (let i = 0; i < numLanes; i++) {
|
||||
const angle = i * step - Math.PI / 2;
|
||||
const rx = cx + rimRadius * Math.cos(angle);
|
||||
const ry = cy + rimRadius * Math.sin(angle);
|
||||
const isEdge = i === this.playerLane || i === (this.playerLane + 1) % numLanes;
|
||||
if (isEdge) {
|
||||
g.lineStyle(2, 0x00eeff, 0.75);
|
||||
} else {
|
||||
g.lineStyle(1, 0x003355, 0.55);
|
||||
}
|
||||
g.lineBetween(cx, cy, rx, ry);
|
||||
}
|
||||
|
||||
// Outer rim
|
||||
g.lineStyle(3, 0x00ccff, 1.0);
|
||||
g.beginPath();
|
||||
for (let i = 0; i <= numLanes; i++) {
|
||||
const angle = i * step - Math.PI / 2;
|
||||
const x = cx + rimRadius * Math.cos(angle);
|
||||
const y = cy + rimRadius * Math.sin(angle);
|
||||
if (i === 0) g.moveTo(x, y); else g.lineTo(x, y);
|
||||
}
|
||||
g.closePath();
|
||||
g.strokePath();
|
||||
|
||||
// Center vanishing point glow
|
||||
g.fillStyle(0x00aaff, 0.6);
|
||||
g.fillCircle(cx, cy, 5);
|
||||
g.fillStyle(0x002244, 0.4);
|
||||
g.fillCircle(cx, cy, 12);
|
||||
}
|
||||
|
||||
_drawPlayerLaneHighlight(g) {
|
||||
const { numLanes, rimRadius } = this.cfg;
|
||||
const cx = this.cx, cy = this.cy;
|
||||
const step = this.TWO_PI_N;
|
||||
const pl = this.playerLane;
|
||||
const a0 = pl * step - Math.PI / 2;
|
||||
const a1 = (pl + 1) * step - Math.PI / 2;
|
||||
|
||||
g.fillStyle(0x00ffff, 0.06);
|
||||
g.beginPath();
|
||||
g.moveTo(cx, cy);
|
||||
const SEGS = 6;
|
||||
for (let i = 0; i <= SEGS; i++) {
|
||||
const a = a0 + (a1 - a0) * (i / SEGS);
|
||||
g.lineTo(cx + rimRadius * Math.cos(a), cy + rimRadius * Math.sin(a));
|
||||
}
|
||||
g.closePath();
|
||||
g.fillPath();
|
||||
}
|
||||
|
||||
_drawEnemies(g) {
|
||||
for (const e of this.enemies) {
|
||||
if (!e.alive) continue;
|
||||
const pos = this._lanePos(e.lane, e.t);
|
||||
const angle = this._laneAngle(e.lane);
|
||||
const size = 13;
|
||||
const perp = angle + Math.PI / 2;
|
||||
|
||||
// Triangle pointing outward (toward rim)
|
||||
const tx = pos.x + Math.cos(angle) * size;
|
||||
const ty = pos.y + Math.sin(angle) * size;
|
||||
const lx = pos.x - Math.cos(angle) * (size * 0.4) + Math.cos(perp) * (size * 0.65);
|
||||
const ly = pos.y - Math.sin(angle) * (size * 0.4) + Math.sin(perp) * (size * 0.65);
|
||||
const rx = pos.x - Math.cos(angle) * (size * 0.4) - Math.cos(perp) * (size * 0.65);
|
||||
const ry = pos.y - Math.sin(angle) * (size * 0.4) - Math.sin(perp) * (size * 0.65);
|
||||
|
||||
// Pulse red as they get closer to rim
|
||||
const danger = e.t;
|
||||
const r = Math.floor(200 + danger * 55);
|
||||
const gv = Math.floor(80 - danger * 70);
|
||||
const color = Phaser.Display.Color.GetColor(r, Math.max(0, gv), 0);
|
||||
|
||||
g.fillStyle(color, 1.0);
|
||||
g.beginPath();
|
||||
g.moveTo(tx, ty);
|
||||
g.lineTo(lx, ly);
|
||||
g.lineTo(rx, ry);
|
||||
g.closePath();
|
||||
g.fillPath();
|
||||
|
||||
g.lineStyle(1.5, 0xff8800, 0.7);
|
||||
g.beginPath();
|
||||
g.moveTo(tx, ty);
|
||||
g.lineTo(lx, ly);
|
||||
g.lineTo(rx, ry);
|
||||
g.closePath();
|
||||
g.strokePath();
|
||||
}
|
||||
}
|
||||
|
||||
_drawBullets(g) {
|
||||
for (const b of this.bullets) {
|
||||
const pos = this._lanePos(b.lane, b.t);
|
||||
const trailPos = this._lanePos(b.lane, Math.min(1, b.t + 0.08));
|
||||
|
||||
g.lineStyle(2, 0xffff00, 0.55);
|
||||
g.lineBetween(pos.x, pos.y, trailPos.x, trailPos.y);
|
||||
|
||||
g.fillStyle(0xffffff, 1.0);
|
||||
g.fillCircle(pos.x, pos.y, 5);
|
||||
g.fillStyle(0xffff00, 0.9);
|
||||
g.fillCircle(pos.x, pos.y, 3);
|
||||
}
|
||||
}
|
||||
|
||||
_drawPlayer(g) {
|
||||
const { rimRadius } = this.cfg;
|
||||
const cx = this.cx, cy = this.cy;
|
||||
const pl = this.playerLane;
|
||||
const angle = this._laneAngle(pl);
|
||||
const perp = angle + Math.PI / 2;
|
||||
|
||||
// Position slightly outside rim
|
||||
const px = cx + (rimRadius + 22) * Math.cos(angle);
|
||||
const py = cy + (rimRadius + 22) * Math.sin(angle);
|
||||
|
||||
const arm = 18;
|
||||
const depth = 14;
|
||||
|
||||
// Claw: two arms that open toward center, connected at the back
|
||||
const tipX = px + Math.cos(angle) * 6;
|
||||
const tipY = py + Math.sin(angle) * 6;
|
||||
const backX = px - Math.cos(angle) * depth;
|
||||
const backY = py - Math.sin(angle) * depth;
|
||||
|
||||
g.lineStyle(2.5, 0x00ffff, 1.0);
|
||||
|
||||
// Left arm
|
||||
g.lineBetween(tipX, tipY,
|
||||
backX + Math.cos(perp) * arm, backY + Math.sin(perp) * arm);
|
||||
// Right arm
|
||||
g.lineBetween(tipX, tipY,
|
||||
backX - Math.cos(perp) * arm, backY - Math.sin(perp) * arm);
|
||||
// Back connector
|
||||
g.lineBetween(
|
||||
backX + Math.cos(perp) * arm, backY + Math.sin(perp) * arm,
|
||||
backX - Math.cos(perp) * arm, backY - Math.sin(perp) * arm);
|
||||
|
||||
// Tip dot
|
||||
g.fillStyle(0x00ffff, 1.0);
|
||||
g.fillCircle(tipX, tipY, 4);
|
||||
|
||||
// Glow ring
|
||||
g.lineStyle(1, 0x00ffff, 0.3);
|
||||
g.strokeCircle(px, py, 26);
|
||||
}
|
||||
|
||||
// ─── Geometry helpers ─────────────────────────────────────────────────────
|
||||
|
||||
_laneAngle(lane) {
|
||||
return (lane + 0.5) * this.TWO_PI_N - Math.PI / 2;
|
||||
}
|
||||
|
||||
_lanePos(lane, t) {
|
||||
const angle = this._laneAngle(lane);
|
||||
const r = t * this.cfg.rimRadius;
|
||||
return {
|
||||
x: this.cx + r * Math.cos(angle),
|
||||
y: this.cy + r * Math.sin(angle),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── HUD ──────────────────────────────────────────────────────────────────
|
||||
|
||||
_updateHUD() {
|
||||
const cfg = this.cfg;
|
||||
this.killsText.setText(`KILLS ${this.kills} / ${cfg.killsToWin}`);
|
||||
const shieldsLeft = cfg.maxMisses - this.misses;
|
||||
this.shieldsText.setText('■ '.repeat(shieldsLeft).trim() + '□ '.repeat(this.misses).trim());
|
||||
this.livesText.setText('♥ '.repeat(this.lives).trim());
|
||||
}
|
||||
|
||||
// ─── End game ─────────────────────────────────────────────────────────────
|
||||
|
||||
_endGame(success) {
|
||||
if (!this.gameActive) return;
|
||||
this.gameActive = false;
|
||||
|
||||
if (success) this._playFx('vortexComplete', 0.8);
|
||||
|
||||
const msg = success ? 'VICTORY!' : 'BREACHED!';
|
||||
const color = success ? '#00ff44' : '#ff2222';
|
||||
this.add.text(800, 440, msg, {
|
||||
fontSize: '80px', fontFamily: 'monospace',
|
||||
color, stroke: '#000000', strokeThickness: 6,
|
||||
}).setOrigin(0.5).setDepth(20);
|
||||
|
||||
this.time.delayedCall(2200, () => {
|
||||
this.game.events.emit('miniGameResult', { success });
|
||||
});
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
stopMusic(this);
|
||||
}
|
||||
|
||||
_playFx(key, volume = 0.6) {
|
||||
const snd = this.sound.add(key, { volume });
|
||||
snd.once('complete', () => snd.destroy());
|
||||
snd.play();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"levels": {
|
||||
"1": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.18,
|
||||
"spawnInterval": 2.2,
|
||||
"maxActiveEnemies": 4,
|
||||
"killsToWin": 5,
|
||||
"bulletSpeed": 1.4,
|
||||
"moveCooldown": 120,
|
||||
"maxMisses": 3
|
||||
},
|
||||
"2": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.27,
|
||||
"spawnInterval": 1.5,
|
||||
"maxActiveEnemies": 6,
|
||||
"killsToWin": 7,
|
||||
"bulletSpeed": 1.7,
|
||||
"moveCooldown": 100,
|
||||
"maxMisses": 3
|
||||
},
|
||||
"3": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.33,
|
||||
"spawnInterval": 1.25,
|
||||
"maxActiveEnemies": 6,
|
||||
"killsToWin": 9,
|
||||
"bulletSpeed": 1.85,
|
||||
"moveCooldown": 88,
|
||||
"maxMisses": 3
|
||||
},
|
||||
"4": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.38,
|
||||
"spawnInterval": 1.1,
|
||||
"maxActiveEnemies": 7,
|
||||
"killsToWin": 11,
|
||||
"bulletSpeed": 1.95,
|
||||
"moveCooldown": 78,
|
||||
"maxMisses": 3
|
||||
},
|
||||
"5": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.43,
|
||||
"spawnInterval": 0.98,
|
||||
"maxActiveEnemies": 7,
|
||||
"killsToWin": 13,
|
||||
"bulletSpeed": 2.05,
|
||||
"moveCooldown": 70,
|
||||
"maxMisses": 3
|
||||
},
|
||||
"6": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.47,
|
||||
"spawnInterval": 0.88,
|
||||
"maxActiveEnemies": 8,
|
||||
"killsToWin": 15,
|
||||
"bulletSpeed": 2.1,
|
||||
"moveCooldown": 64,
|
||||
"maxMisses": 2
|
||||
},
|
||||
"7": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.51,
|
||||
"spawnInterval": 0.80,
|
||||
"maxActiveEnemies": 8,
|
||||
"killsToWin": 19,
|
||||
"bulletSpeed": 2.15,
|
||||
"moveCooldown": 58,
|
||||
"maxMisses": 2
|
||||
},
|
||||
"8": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.54,
|
||||
"spawnInterval": 0.73,
|
||||
"maxActiveEnemies": 9,
|
||||
"killsToWin": 23,
|
||||
"bulletSpeed": 2.2,
|
||||
"moveCooldown": 54,
|
||||
"maxMisses": 2
|
||||
},
|
||||
"9": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.57,
|
||||
"spawnInterval": 0.67,
|
||||
"maxActiveEnemies": 9,
|
||||
"killsToWin": 27,
|
||||
"bulletSpeed": 2.25,
|
||||
"moveCooldown": 50,
|
||||
"maxMisses": 2
|
||||
},
|
||||
"10": {
|
||||
"numLanes": 16,
|
||||
"rimRadius": 340,
|
||||
"enemySpeed": 0.60,
|
||||
"spawnInterval": 0.62,
|
||||
"maxActiveEnemies": 10,
|
||||
"killsToWin": 33,
|
||||
"bulletSpeed": 2.3,
|
||||
"moveCooldown": 46,
|
||||
"maxMisses": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import ColoradoDefense from './games/colorado-defense/ColoradoDefense.js';
|
|||
import CodeBugInvaders from './games/code-bug-invaders/CodeBugInvaders.js';
|
||||
import DotDude from './games/dot-dude/DotDude.js';
|
||||
import SmashOut from './games/smash-out/SmashOut.js';
|
||||
import Vortex from './games/vortex/Vortex.js';
|
||||
|
||||
new Phaser.Game({
|
||||
type: Phaser.AUTO,
|
||||
|
|
@ -16,5 +17,5 @@ new Phaser.Game({
|
|||
mode: Phaser.Scale.FIT,
|
||||
autoCenter: Phaser.Scale.CENTER_BOTH,
|
||||
},
|
||||
scene: [MainMenu, GameManager, ColoradoDefense, CodeBugInvaders, DotDude, SmashOut, GameOver, LevelComplete],
|
||||
scene: [MainMenu, GameManager, ColoradoDefense, CodeBugInvaders, DotDude, SmashOut, Vortex, GameOver, LevelComplete],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default class GameManager extends Phaser.Scene {
|
|||
init(data) {
|
||||
this.config = data.config;
|
||||
this.level = data.level || 1;
|
||||
this.lives = data.lives !== undefined ? data.lives : this.config.lives;
|
||||
this.lives = data.lives ?? this.config.lives ?? 3;
|
||||
this.queue = [];
|
||||
this.queueIndex = 0;
|
||||
}
|
||||
|
|
@ -14,10 +14,14 @@ export default class GameManager extends Phaser.Scene {
|
|||
create() {
|
||||
this.announceTween = null;
|
||||
this.announceTimer = null;
|
||||
this.resultHandled = false;
|
||||
|
||||
// Remove any stale listener from a previous session before adding a fresh one
|
||||
this.game.events.off('miniGameResult', this._onResult, this);
|
||||
this.game.events.on('miniGameResult', this._onResult, this);
|
||||
|
||||
this._buildQueue();
|
||||
this._showAnnouncement();
|
||||
|
||||
this.game.events.on('miniGameResult', this._onResult, this);
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
|
|
@ -86,12 +90,16 @@ export default class GameManager extends Phaser.Scene {
|
|||
}
|
||||
this.cameras.main.setBackgroundColor('#000000');
|
||||
|
||||
this.resultHandled = false; // ready to accept exactly one result per launch
|
||||
const key = this.queue[this.queueIndex];
|
||||
this.scene.launch(key, { lives: this.lives, level: this.level });
|
||||
this.scene.bringToTop(key);
|
||||
}
|
||||
|
||||
_onResult({ success }) {
|
||||
if (this.resultHandled) return; // guard against duplicate events
|
||||
this.resultHandled = true;
|
||||
|
||||
const key = this.queue[this.queueIndex];
|
||||
if (this.scene.isActive(key)) {
|
||||
this.scene.stop(key);
|
||||
|
|
@ -131,6 +139,7 @@ export default class GameManager extends Phaser.Scene {
|
|||
CodeBugInvaders: 'CODE BUG INVADERS',
|
||||
DotDude: 'DOT DUDE',
|
||||
SmashOut: 'SMASH OUT',
|
||||
Vortex: 'VORTEX',
|
||||
};
|
||||
return names[key] || key;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default class MainMenu extends Phaser.Scene {
|
|||
}
|
||||
|
||||
preload() {
|
||||
this.load.json('gameConfig', 'config/game.json');
|
||||
this.load.json('gameConfig', 'config/game.json?v=2');
|
||||
this.load.audio('mainTheme', 'assets/music/00-main-theme.mp3');
|
||||
this.load.image('logo-title', 'assets/images/logo-title.png');
|
||||
this.load.image('logo-people', 'assets/images/logo-people.png');
|
||||
|
|
|
|||
Loading…
Reference in New Issue