feat: Implement level selection menu with video background, add level 2 assets and wave configurations

- Added Menu scene with video background, audio, and UI elements including player name input and level selection
- Implemented dynamic level loading based on user selection in Level scene
- Added level 2 assets (JSON map, TMX tileset, terrain images)
- Updated wave configurations for both levels with new enemy spawn schedules
- Refactored file structure by moving scenes to src/scenes/ directory
- Enhanced tower explosion sounds with increased volume
- Improved camera fade effects in menu and level scenes
This commit is contained in:
Brian Fertig 2025-09-06 20:51:53 -06:00
parent ff5f9db876
commit d2dc2f38ad
16 changed files with 765 additions and 14 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 KiB

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

138
assets/level2.json Normal file
View File

@ -0,0 +1,138 @@
{ "compressionlevel":-1,
"height":9,
"infinite":true,
"layers":[
{
"chunks":[
{
"data":[4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
4, 4, 12, 2, 2, 1, 2, 2, 4, 4, 4, 12, 0, 0, 0, 0,
2, 2, 2, 2, 2, 1, 2, 2, 4, 4, 4, 2, 0, 0, 0, 0,
2, 7, 7, 7, 7, 1, 2, 1, 1, 1, 1, 2, 0, 0, 0, 0,
2, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 0, 0, 0, 0,
2, 1, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 0, 0, 0, 0,
2, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 2, 0, 0, 0, 0,
2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 2, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":0,
"y":0
}],
"height":16,
"id":1,
"name":"main",
"opacity":1,
"startx":0,
"starty":0,
"type":"tilelayer",
"visible":true,
"width":16,
"x":0,
"y":0
},
{
"chunks":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0,
16, 0, 16, 0, 0, 16, 16, 0, 0, 0, 0, 16, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":0,
"y":0
}],
"height":16,
"id":2,
"name":"platforms",
"opacity":1,
"startx":0,
"starty":0,
"type":"tilelayer",
"visible":true,
"width":16,
"x":0,
"y":0
}],
"nextlayerid":3,
"nextobjectid":1,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.11.2",
"tileheight":200,
"tilesets":[
{
"columns":5,
"firstgid":1,
"image":"terrain.png",
"imageheight":1000,
"imagewidth":1000,
"margin":0,
"name":"terrain",
"spacing":0,
"tilecount":25,
"tileheight":200,
"tiles":[
{
"id":1,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":3,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":6,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
},
{
"id":11,
"properties":[
{
"name":"collides",
"type":"bool",
"value":true
}]
}],
"tilewidth":200
}],
"tilewidth":200,
"type":"map",
"version":"1.10",
"width":16
}

BIN
assets/menuBackground.mp4 Normal file

Binary file not shown.

BIN
assets/menuInterface.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

BIN
assets/menuLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="16" height="9" tilewidth="200" tileheight="200" infinite="1" nextlayerid="3" nextobjectid="1">
<tileset firstgid="1" name="terrain" tilewidth="200" tileheight="200" tilecount="25" columns="5">
<image source="../terrain.png" width="1000" height="1000"/>
<tile id="1">
<properties>
<property name="collides" type="bool" value="true"/>
</properties>
</tile>
<tile id="3">
<properties>
<property name="collides" type="bool" value="true"/>
</properties>
</tile>
<tile id="6">
<properties>
<property name="collides" type="bool" value="true"/>
</properties>
</tile>
<tile id="11">
<properties>
<property name="collides" type="bool" value="true"/>
</properties>
</tile>
</tileset>
<layer id="1" name="main" width="16" height="9">
<data encoding="csv">
<chunk x="0" y="0" width="16" height="16">
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
4,4,12,2,2,1,2,2,4,4,4,12,0,0,0,0,
2,2,2,2,2,1,2,2,4,4,4,2,0,0,0,0,
2,7,7,7,7,1,2,1,1,1,1,2,0,0,0,0,
2,1,1,1,1,1,2,1,2,2,1,2,0,0,0,0,
2,1,2,2,2,2,2,1,2,2,1,2,0,0,0,0,
2,1,2,2,1,1,1,1,2,2,1,2,0,0,0,0,
2,1,1,1,1,2,2,2,2,2,1,2,0,0,0,0,
2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
</data>
</layer>
<layer id="2" name="platforms" width="16" height="9">
<data encoding="csv">
<chunk x="0" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,
0,0,0,0,16,0,0,16,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,16,0,0,16,0,0,0,0,0,0,
0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,16,16,0,0,0,0,0,0,
16,0,16,0,0,16,16,0,0,0,0,16,0,0,0,0,
0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,16,0,0,16,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
</data>
</layer>
</map>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 KiB

After

Width:  |  Height:  |  Size: 656 KiB

Binary file not shown.

View File

@ -1,5 +1,6 @@
import { Level } from './levels/level.js'; import { Level } from './scenes/level.js';
import { UIScene } from './uiScene.js'; import { UIScene } from './scenes/uiScene.js';
import { Menu } from './scenes/menu.js';
const GAME_CONFIG = { const GAME_CONFIG = {
type: Phaser.AUTO, type: Phaser.AUTO,
@ -13,6 +14,7 @@ const GAME_CONFIG = {
parent: 'game-container', parent: 'game-container',
backgroundColor: '#bb7432ff', backgroundColor: '#bb7432ff',
scene: [ scene: [
Menu,
Level, Level,
UIScene UIScene
], ],

View File

@ -12,11 +12,14 @@ export class Level extends Phaser.Scene {
} }
init(data) { init(data) {
console.log('Selected level:', data.level);
console.log('Player name:', data.playerName);
this.level = data.level;
this.playerName = data.playerName;
} }
preload() { preload() {
this.load.tilemapTiledJSON('level1', 'assets/level1.json'); this.load.tilemapTiledJSON(`level${this.level}`, `assets/level${this.level}.json`);
this.load.image('terrain', 'assets/terrain.png'); this.load.image('terrain', 'assets/terrain.png');
this.load.spritesheet('ammo', 'assets/ammo.png', { this.load.spritesheet('ammo', 'assets/ammo.png', {
frameWidth: 50, frameWidth: 50,
@ -59,7 +62,7 @@ export class Level extends Phaser.Scene {
} }
create() { create() {
this.levelMap = this.make.tilemap({ key: 'level1' }); this.levelMap = this.make.tilemap({ key: `level${this.level}` });
const terrainTiles = this.levelMap.addTilesetImage('terrain', 'terrain'); const terrainTiles = this.levelMap.addTilesetImage('terrain', 'terrain');
this.mainLayer = this.levelMap.createLayer('main', terrainTiles) this.mainLayer = this.levelMap.createLayer('main', terrainTiles)
.setCollisionByProperty({ collides: true }); .setCollisionByProperty({ collides: true });
@ -78,6 +81,9 @@ export class Level extends Phaser.Scene {
this.enemies = this.physics.add.group(); this.enemies = this.physics.add.group();
this.towers = this.physics.add.group(); this.towers = this.physics.add.group();
// Fade in camera at the beginning of the scene
this.cameras.main.fadeIn(1000, 0, 0, 0);
this.bgMusic = this.sound.add('level-bg1', { volume: 1 }); this.bgMusic = this.sound.add('level-bg1', { volume: 1 });
this.bgMusic.loop = true; this.bgMusic.loop = true;
this.bgMusic.play(); this.bgMusic.play();

259
src/scenes/menu.js Normal file
View File

@ -0,0 +1,259 @@
export class Menu extends Phaser.Scene {
constructor() {
super({ key: 'Menu' });
}
preload() {
this.load.font('neuropol', 'assets/NEUROPOL.ttf');
this.load.video('menuBackground', 'assets/menuBackground.mp4');
this.load.audio('main-bg', 'assets/music/main-bg.mp3');
this.load.image('menuLogo', 'assets/menuLogo.png');
this.load.image('menuInterface', 'assets/menuInterface.png');
}
create() {
// Create video sprite that fills the screen
const video = this.add.video(0, 0, 'menuBackground');
// Set video to fill the entire screen
video.setOrigin(0);
video.setScale(this.game.config.width / 1280, this.game.config.height / 720);
// Make sure it's at the back layer
video.setDepth(-1);
// Play the video in a loop
video.play(true);
// Create audio and play in a loop
this.bgMusic = this.sound.add('main-bg');
this.bgMusic.loop = true;
this.bgMusic.play();
const menuInterface = this.add.image(-450, 600, 'menuInterface').setOrigin(0.5);
const menuLogo = this.add.image(475, 325, 'menuLogo').setOrigin(0.5).setAlpha(.3).setRotation(-5).setVisible(false);
const newGameText = this.add.text(475, 550, 'New Game', {
fontFamily: 'neuropol, arial',
fontSize: '48px',
fill: '#ffd900ff',
stroke: '#c48f00ff',
strokeThickness: 2,
shadow: {
offsetX: 5,
offsetY: 5,
color: '#000000',
blur: 5,
stroke: false,
fill: true
}
}).setOrigin(0.5).setRotation(-5).setAlpha(0);
newGameText.setInteractive();
// Add Fullscreen text below New Game
const fullscreenText = this.add.text(475, 625, 'Toggle Fullscreen', {
fontFamily: 'neuropol, arial',
fontSize: '32px',
fill: '#ffffff',
stroke: '#888888',
strokeThickness: 1,
shadow: {
offsetX: 3,
offsetY: 3,
color: '#000000',
blur: 3,
stroke: false,
fill: true
}
}).setOrigin(0.5).setRotation(-5).setAlpha(0);
fullscreenText.setInteractive();
// Create player name input field below fullscreen text
const playerNameInput = this.add.text(475, 680, 'Player1', {
fontFamily: 'neuropol, arial',
fontSize: '24px',
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 1,
backgroundColor: '#333333',
padding: {
left: 10,
right: 10,
top: 5,
bottom: 5
}
}).setOrigin(0.5).setRotation(-5).setAlpha(0);
playerNameInput.setInteractive();
// Make it look like an input field
playerNameInput.on('pointerdown', () => {
this.input.keyboard.enabled = true;
const name = prompt('Enter your player name:', playerNameInput.text);
if (name) {
playerNameInput.setText(name);
}
});
// Fade in camera at the beginning of the scene
this.cameras.main.fadeIn(1000, 0, 0, 0);
newGameText.on('pointerdown', () => {
// Fade out camera
this.cameras.main.fadeOut(1000, 0, 0, 0);
// Fade out music
this.bgMusic.stop();
// Start Level scene after fade completes
this.cameras.main.on('camerafadeoutcomplete', () => {
this.scene.start('Level', {
level: this.selectedLevel,
playerName: playerNameInput.text
});
});
});
// Add fullscreen functionality
fullscreenText.on('pointerdown', () => {
if (this.scale.isFullscreen) {
this.scale.stopFullscreen();
} else {
this.scale.startFullscreen();
}
});
this.tweens.add({
targets: menuInterface,
x: 450,
duration: 2000,
onComplete: () => {
menuLogo.setVisible(true);
this.tweens.add({
targets: menuLogo,
scale: .4,
alpha: 1,
rotation: 0.02,
duration: 2000,
onComplete: () => {
this.tweens.add({
targets: menuLogo,
scale: .5,
duration: 5000,
yoyo: true,
repeat: -1
});
}
});
this.tweens.add({
targets: [newGameText, fullscreenText, playerNameInput],
alpha: 1,
rotation: 0.02,
delay: 1500,
duration: 500
})
}
});
// Create level selection area on the right side of the screen
const levelSelectWidth = this.game.config.width / 4;
const levelSelectHeight = this.game.config.height * 2 / 3 - 50;
const levelSelectX = this.game.config.width - levelSelectWidth -50;
const levelSelectY = this.game.config.height / 3;
// Semi-transparent cyan rectangle
const levelSelectRect = this.add.rectangle(
levelSelectX,
levelSelectY,
levelSelectWidth,
levelSelectHeight,
0x00ffff,
0.3
).setOrigin(0);
// "Choose A Level" text above the selection area
const chooseLevelText = this.add.text(
levelSelectX + levelSelectWidth / 2,
levelSelectY - 40,
'Choose A Level',
{
fontFamily: 'neuropol, arial',
fontSize: '32px',
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 2
}
).setOrigin(0.5);
// Create level buttons (1-10)
this.levelButtons = [];
const buttonWidth = levelSelectWidth - 40;
const buttonHeight = 40;
const spacing = 15;
// Calculate starting Y position to center the buttons
const totalButtonArea = (buttonHeight + spacing) * 10 - spacing; // Total height for all buttons
const startY = levelSelectY + (levelSelectHeight - totalButtonArea) / 2;
for (let i = 1; i <= 10; i++) {
const buttonX = levelSelectX + 20;
const buttonY = startY + (buttonHeight + spacing) * (i - 1);
// Create rectangle for button
const buttonRect = this.add.rectangle(
buttonX,
buttonY,
buttonWidth,
buttonHeight,
i === 1 ? 0x00ff00 : 0x888888, // Green for selected (level 1), gray otherwise
i === 1 ? 0.7 : 0.3 // Higher opacity for selected
).setOrigin(0).setInteractive();
// Add text to button
const buttonText = this.add.text(
buttonX + buttonWidth / 2,
buttonY + buttonHeight / 2,
`Level ${i}`,
{
fontFamily: 'neuropol, arial',
fontSize: '18px',
fill: i === 1 ? '#000000' : '#ffffff',
stroke: '#000000',
strokeThickness: 1
}
).setOrigin(0.5);
// Store button reference
this.levelButtons.push({
rect: buttonRect,
text: buttonText,
level: i,
selected: i === 1
});
// Add pointer events to button
buttonRect.on('pointerdown', () => {
// Deselect all buttons first
this.levelButtons.forEach(btn => {
btn.rect.setFillStyle(0x888888, 0.3);
btn.text.setFill('#ffffff');
btn.selected = false;
});
// Select new level
buttonRect.setFillStyle(0x00ff00, 0.7);
buttonText.setFill('#000000');
this.selectedLevel = i; // Set the selected level
console.log('Switching to LEVEL ',this.selectedLevel);
});
}
// Set initial current level
this.selectedLevel = 1;
}
update(time, delta) {
}
}

View File

@ -1,4 +1,4 @@
import { InterfaceManager } from './support/interfaceManager.js'; import { InterfaceManager } from '../support/interfaceManager.js';
export class UIScene extends Phaser.Scene { export class UIScene extends Phaser.Scene {
@ -11,7 +11,6 @@ export class UIScene extends Phaser.Scene {
} }
preload() { preload() {
this.load.font('neuropol', 'assets/NEUROPOL.ttf');
this.load.image('intMain', 'assets/intMain.png'); this.load.image('intMain', 'assets/intMain.png');
this.load.image('redArrow', 'assets/redArrow.png'); this.load.image('redArrow', 'assets/redArrow.png');
this.load.image('intTop', 'assets/intTop.png'); this.load.image('intTop', 'assets/intTop.png');

View File

@ -472,14 +472,14 @@ export class TowerManager {
onComplete: () => { onComplete: () => {
fireMissle.destroy(); fireMissle.destroy();
tower.setVisible(true); tower.setVisible(true);
this.scene.sound.play('rocket-explosion'); this.scene.sound.play('rocket-explosion', { volume: 1.5});
const damage = Phaser.Math.Between(dmgLow, dmgHigh); const damage = Phaser.Math.Between(dmgLow, dmgHigh);
const aoeDistance = config.aoe; const aoeDistance = config.aoe;
const enemiesInRange = this.scene.enemies.getChildren().filter(e => { const enemiesInRange = this.scene.enemies.getChildren().filter(e => {
return Phaser.Math.Distance.Between(e.x, e.y, enemy.x, enemy.y) <= aoeDistance; return Phaser.Math.Distance.Between(e.x, e.y, enemy.x, enemy.y) <= aoeDistance;
}); });
const explosion = this.scene.add.sprite(enemy.x, enemy.y, 'towers', 34).setDepth(12); const explosion = this.scene.add.sprite(enemy.x, enemy.y, 'towers', 34).setDepth(12);
explosion.play('icbmExplosion'); explosion.play('icbmExplosion', { volume: 1.5 });
this.scene.tweens.add({ this.scene.tweens.add({
targets:explosion, targets:explosion,
duration: 1000, duration: 1000,

View File

@ -165,12 +165,12 @@ export const WAVE_CONFIG = {
medium2: 6, medium2: 6,
}, },
3: { 3: {
begin: 45, begin: 35,
medium2: 5, medium2: 5,
basic4: 15 basic4: 15
}, },
4: { 4: {
begin: 35, begin: 45,
advanced1: 1, advanced1: 1,
basic3: 10 basic3: 10
} }
@ -190,16 +190,293 @@ export const WAVE_CONFIG = {
medium2: 6, medium2: 6,
}, },
3: { 3: {
begin: 45, begin: 40,
advanced3: 2, advanced3: 2,
basic4: 15 basic4: 15
}, },
4: { 4: {
begin: 35, begin: 55,
medium1: 4, medium1: 4,
medium2: 4, medium2: 4,
basic4: 20 basic4: 20
} }
},
//Wave
10: {
//Schedule
1: {
begin: 0,
advanced3: 2,
advanced1: 2,
medium1: 4,
basic4: 4,
basic3: 4
},
2: {
begin: 25,
advanced2: 2,
medium2: 4,
basic2: 10
},
3: {
begin: 45,
advanced3: 4,
basic4: 20
},
4: {
begin: 50,
advanced1: 1,
advanced2: 1,
basic3: 10,
medium1: 20,
medium2: 10
},
4: {
begin: 60,
medium1: 10,
medium2: 10,
basic4: 20
}
}
},
// Level
2: {
//Spawn Point
spawnX: 5,
spawnY: 0,
endX: 10,
endY: 11,
// Wave
1: {
// Schedule
1: {
begin: 0,
basic1: 5,
},
2: {
begin: 15,
basic1: 8,
},
3: {
begin: 30,
basic1: 10,
}
},
// Wave
2: {
// Schedule
1: {
begin: 0,
basic1: 8,
basic2: 1
},
2: {
begin: 15,
basic1: 5,
basic2: 2
},
3: {
begin: 30,
basic1: 5,
basic2: 4
}
},
// Wave
3: {
// Schedule
1: {
begin: 0,
basic2: 5
},
2: {
begin: 15,
basic2: 6
},
3: {
begin: 30,
basic2: 8,
basic3: 1
}
},
// Wave
4: {
// Schedule
1: {
begin: 0,
basic1: 10,
basic3: 2,
},
2: {
begin: 15,
basic1: 8,
basic2: 3,
basic3: 2
},
3: {
begin: 30,
basic3: 4
}
},
//Wave
5: {
//Schedule
1: {
begin: 0,
basic1: 10,
basic2: 2,
basic3: 1
},
2: {
begin: 15,
advanced1: 1,
},
3: {
begin: 25,
basic1: 8,
basic2: 3,
basic3: 2
},
4: {
begin: 35,
basic1: 25
},
5: {
begin: 40,
advanced1: 1
}
},
//Wave
6: {
//Schedule
1: {
begin: 0,
basic2: 10,
advanced2: 1
},
2: {
begin: 15,
basic1: 20,
},
3: {
begin: 25,
basic3: 6,
advanced1: 1
},
4: {
begin: 35,
advanced2: 1
}
},
//Wave
7: {
//Schedule
1: {
begin: 0,
medium1: 5,
basic3: 5
},
2: {
begin: 15,
basic2: 10,
basic4: 2,
advanced1: 1
},
3: {
begin: 25,
medium1: 6,
medium2: 2
},
4: {
begin: 35,
advanced2: 1
}
},
//Wave
8: {
//Schedule
1: {
begin: 0,
advanced2: 1,
basic4: 20
},
2: {
begin: 25,
basic2: 10,
medium2: 6,
},
3: {
begin: 35,
medium2: 5,
basic4: 15
},
4: {
begin: 45,
advanced1: 1,
basic3: 10
}
},
//Wave
9: {
//Schedule
1: {
begin: 0,
advanced3: 1,
medium1: 4,
basic4: 6
},
2: {
begin: 25,
basic3: 10,
medium2: 6,
},
3: {
begin: 40,
advanced3: 2,
basic4: 15
},
4: {
begin: 55,
medium1: 4,
medium2: 4,
basic4: 20
}
},
//Wave
10: {
//Schedule
1: {
begin: 0,
advanced3: 2,
advanced1: 2,
medium1: 4,
basic4: 4,
basic3: 4
},
2: {
begin: 25,
advanced2: 2,
medium2: 4,
basic2: 10
},
3: {
begin: 45,
advanced3: 4,
basic4: 20
},
4: {
begin: 50,
advanced1: 1,
advanced2: 1,
basic3: 10,
medium1: 20,
medium2: 10
},
4: {
begin: 60,
medium1: 10,
medium2: 10,
basic4: 20
}
} }
} }
} }