improvement
@ -12,3 +12,7 @@ The [design document](/docs/design.md) contains the starting idea which was init
|
||||
## Todos
|
||||
|
||||
Todos are available [here](/docs/todos.md) which contain self-made tasks which should get done or are already done.
|
||||
|
||||
## Extra documentation
|
||||
|
||||
Extra documentation is available [here](/docs)
|
||||
|
43
docs/linecount.md
Normal file
@ -0,0 +1,43 @@
|
||||
Generated by: `find ./src -name '*.ts' | xargs wc -l`
|
||||
92 ./src/main.ts
|
||||
1 ./src/vite-env.d.ts
|
||||
17 ./src/classes/GameUIConstants.ts
|
||||
145 ./src/classes/gui/TowerTab.ts
|
||||
375 ./src/classes/gui/TowerPanel.ts
|
||||
52 ./src/classes/gui/TextInput.ts
|
||||
211 ./src/classes/gui/ModalDialog.ts
|
||||
51 ./src/classes/gui/Sidebar.ts
|
||||
122 ./src/classes/gui/HighScoreDialog.ts
|
||||
70 ./src/classes/gui/Button.ts
|
||||
60 ./src/classes/gui/Gemsmith.ts
|
||||
154 ./src/classes/gui/EndGameDialog.ts
|
||||
220 ./src/classes/gui/GemTab.ts
|
||||
59 ./src/classes/gui/GamePausedDialog.ts
|
||||
36 ./src/classes/gui/MessageBox.ts
|
||||
205 ./src/classes/gui/Tooltip.ts
|
||||
126 ./src/classes/Definitions.ts
|
||||
77 ./src/classes/game/WaveManager.ts
|
||||
260 ./src/classes/game/Grid.ts
|
||||
51 ./src/classes/game/Gem.ts
|
||||
114 ./src/classes/game/TowerBehaviours.ts
|
||||
154 ./src/classes/game/Tower.ts
|
||||
180 ./src/classes/game/MissionStats.ts
|
||||
100 ./src/classes/game/AnimationManager.ts
|
||||
177 ./src/classes/game/Creep.ts
|
||||
47 ./src/classes/game/KeyboardManager.ts
|
||||
104 ./src/classes/game/Projectile.ts
|
||||
86 ./src/classes/game/NotificationManager.ts
|
||||
130 ./src/classes/game/TowerManager.ts
|
||||
71 ./src/classes/game/HighScoreManager.ts
|
||||
76 ./src/classes/GuiObject.ts
|
||||
203 ./src/classes/Assets.ts
|
||||
52 ./src/classes/GameObject.ts
|
||||
68 ./src/classes/Bastion.ts
|
||||
30 ./src/classes/Events.ts
|
||||
37 ./src/scenes/Scene.ts
|
||||
17 ./src/scenes/Settings.ts
|
||||
67 ./src/scenes/HowToPlay.ts
|
||||
59 ./src/scenes/Main.ts
|
||||
353 ./src/scenes/Game.ts
|
||||
27 ./src/scenes/MissionPicker.ts
|
||||
`4536 total`
|
@ -17,6 +17,7 @@ List of things to implement following the "release" of the minimum viable produc
|
||||
- [x] Tower info on click
|
||||
- [x] Animate projectiles
|
||||
- [x] Better mouseover tracking when placing tower and showing radius
|
||||
- [x] Sell tower button
|
||||
|
||||
## Gems
|
||||
|
||||
@ -25,8 +26,8 @@ List of things to implement following the "release" of the minimum viable produc
|
||||
|
||||
## Other
|
||||
|
||||
- [ ] Create mission authoring tool
|
||||
- [ ] Disable player action during combat phase.
|
||||
- [ ] Add sound effects
|
||||
- [ ] Tutorial image/mission
|
||||
- [x] Tutorial image/mission
|
||||
- [ ] Pause menu
|
||||
- [x] Score screen when winning/losing map
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="64" tileheight="64" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" source="Tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="30" height="17">
|
||||
<data encoding="csv">
|
||||
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
|
||||
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
|
||||
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,5,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
|
||||
18,18,18,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,3,4,4,4,4,4,4,4,4,4,4,
|
||||
32,32,32,32,32,32,32,32,32,32,32,7,18,19,15,15,15,15,15,17,18,18,18,18,18,18,18,18,18,18,
|
||||
15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,6,32,32,32,32,32,32,32,32,
|
||||
15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,3,4,4,4,4,4,4,4,4,21,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,17,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,17,18,6,32,32,32,32,32,32,32,32,33,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,17,18,19,15,15,15,15,15,15,15,15,15,15,15,15,15,15,17,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,17,18,20,4,4,4,4,4,4,4,4,4,4,4,4,4,4,21,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,15,15,15,15,15,15,15,15,
|
||||
15,15,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,15,15,15,15,15,15,15,15,
|
||||
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
public/assets/gui/main_background.jpg
Normal file
After Width: | Height: | Size: 112 KiB |
@ -49,5 +49,56 @@
|
||||
"frostfire": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cloaker",
|
||||
"textures": [],
|
||||
"textureArrayLength": 12,
|
||||
"stats": {
|
||||
"health": 12,
|
||||
"speed": 2,
|
||||
"special": null,
|
||||
"resistance": {
|
||||
"physical": 0,
|
||||
"divine": 0,
|
||||
"fire": 0,
|
||||
"ice": 0,
|
||||
"frostfire": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "demon",
|
||||
"textures": [],
|
||||
"textureArrayLength": 8,
|
||||
"stats": {
|
||||
"health": 12,
|
||||
"speed": 2,
|
||||
"special": null,
|
||||
"resistance": {
|
||||
"physical": 0,
|
||||
"divine": 0,
|
||||
"fire": 0,
|
||||
"ice": 0,
|
||||
"frostfire": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "maker",
|
||||
"textures": [],
|
||||
"textureArrayLength": 11,
|
||||
"stats": {
|
||||
"health": 11,
|
||||
"speed": 2,
|
||||
"special": null,
|
||||
"resistance": {
|
||||
"physical": 0,
|
||||
"divine": 0,
|
||||
"fire": 0,
|
||||
"ice": 0,
|
||||
"frostfire": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Fire Gem",
|
||||
"description": "Forged from molten lava, the Fire Gem imbues your tower's attacks and adds 50% extra fire damage. It can be merged with any gem and is common. This text shouldn't be long.",
|
||||
"description": "Forged from molten lava, the Fire Gem imbues your tower's attacks and adds 50% extra fire damage. It can be merged with any gem and is common.",
|
||||
"color": "red",
|
||||
"type": "Fire",
|
||||
"totalLevels": 2,
|
||||
@ -12,16 +12,16 @@
|
||||
"genericImprovements": [
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"attackSpeedUp": 0,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 0
|
||||
},
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"attackSpeedUp": 0,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 10
|
||||
@ -58,7 +58,7 @@
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 0
|
||||
@ -66,7 +66,7 @@
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 10
|
||||
@ -103,7 +103,7 @@
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 0
|
||||
@ -163,7 +163,7 @@
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 0
|
||||
@ -171,7 +171,7 @@
|
||||
{
|
||||
"damageUp": 2,
|
||||
"attackSpeedUp": 10,
|
||||
"rangeUp": 0.5,
|
||||
"rangeUp": 0,
|
||||
"timeToLiveUp": 0,
|
||||
"pierceUp": 1,
|
||||
"gemValueUp": 10
|
||||
|
@ -9,11 +9,11 @@
|
||||
"description": "The building block of society, nothing more basic exists.",
|
||||
"stats": {
|
||||
"damage": 2,
|
||||
"cooldown": 120,
|
||||
"cooldown": 2000,
|
||||
"gemSlotsAmount": 2,
|
||||
"cost": 100,
|
||||
"range": 3,
|
||||
"timeToLive": 120,
|
||||
"range": 4,
|
||||
"timeToLive": 20,
|
||||
"pierce": 1
|
||||
}
|
||||
},
|
||||
@ -27,11 +27,11 @@
|
||||
"description": "If you feel a little circular.",
|
||||
"stats": {
|
||||
"damage": 2,
|
||||
"cooldown": 120,
|
||||
"cooldown": 2000,
|
||||
"gemSlotsAmount": 3,
|
||||
"cost": 125,
|
||||
"range": 2,
|
||||
"timeToLive": 8,
|
||||
"range": 2.5,
|
||||
"timeToLive": 12,
|
||||
"pierce": 30
|
||||
}
|
||||
}
|
||||
|
BIN
public/assets/tutorial/tutorial01.jpg
Normal file
After Width: | Height: | Size: 239 KiB |
BIN
public/assets/tutorial/tutorial02.jpg
Normal file
After Width: | Height: | Size: 250 KiB |
BIN
public/assets/tutorial/tutorial03.jpg
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
public/assets/tutorial/tutorial04.jpg
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
public/assets/tutorial/tutorial05.jpg
Normal file
After Width: | Height: | Size: 296 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
@ -3,6 +3,8 @@ import { CreepDefinition, GemDefinition, MissionDefinition, TowerDefinition } fr
|
||||
import { Engine } from './Bastion';
|
||||
|
||||
export default class GameAssets {
|
||||
public static MainBackground: PIXI.Texture;
|
||||
|
||||
public static Frame01Texture: PIXI.Texture;
|
||||
public static Frame02Texture: PIXI.Texture;
|
||||
public static Frame03Texture: PIXI.Texture;
|
||||
@ -26,9 +28,16 @@ export default class GameAssets {
|
||||
public static BannerGemsmith: PIXI.Texture;
|
||||
public static EndScreenDialog: PIXI.Texture;
|
||||
|
||||
public static Tutorial01: PIXI.Texture;
|
||||
public static Tutorial02: PIXI.Texture;
|
||||
public static Tutorial03: PIXI.Texture;
|
||||
public static Tutorial04: PIXI.Texture;
|
||||
public static Tutorial05: PIXI.Texture;
|
||||
|
||||
public static PlayIconTexture: PIXI.Texture;
|
||||
public static PauseIconTexture: PIXI.Texture;
|
||||
public static ExclamationIconTexture: PIXI.Texture;
|
||||
public static FastForwardIconTexture: PIXI.Texture;
|
||||
public static HomeIconTexture: PIXI.Texture;
|
||||
public static HammerIconTexture: PIXI.Texture;
|
||||
public static XIconTexture: PIXI.Texture;
|
||||
@ -98,6 +107,14 @@ export default class GameAssets {
|
||||
this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)),
|
||||
this.Load('./assets/gui/banner_01.png').then((texture) => (this.BannerGemsmith = texture)),
|
||||
this.Load('./assets/gui/note.png').then((texture) => (this.EndScreenDialog = texture)),
|
||||
this.Load('./assets/gui/main_background.jpg').then((texture) => (this.MainBackground = texture)),
|
||||
|
||||
this.Load('./assets/tutorial/tutorial01.jpg').then((texture) => (this.Tutorial01 = texture)),
|
||||
this.Load('./assets/tutorial/tutorial02.jpg').then((texture) => (this.Tutorial02 = texture)),
|
||||
this.Load('./assets/tutorial/tutorial03.jpg').then((texture) => (this.Tutorial03 = texture)),
|
||||
this.Load('./assets/tutorial/tutorial04.jpg').then((texture) => (this.Tutorial04 = texture)),
|
||||
this.Load('./assets/tutorial/tutorial05.jpg').then((texture) => (this.Tutorial05 = texture)),
|
||||
|
||||
this.Load('./assets/gui/heart.png').then((texture) => (this.HealthTexture = texture)),
|
||||
this.Load('./assets/gui/money.png').then((texture) => (this.GoldTexture = texture)),
|
||||
this.Load('./assets/gui/wave.png').then((texture) => (this.WaveTexture = texture)),
|
||||
@ -105,7 +122,8 @@ export default class GameAssets {
|
||||
this.Load('./assets/gui/title01.png').then((texture) => (this.TitleTexture = texture)),
|
||||
this.Load('./assets/gui/icons/play.png').then((texture) => (this.PlayIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/pause.png').then((texture) => (this.PauseIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/exclamation.png').then((texture) => (this.ExclamationIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/pause.png').then((texture) => (this.PauseIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/fastforward.png').then((texture) => (this.FastForwardIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/home.png').then((texture) => (this.HomeIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/hammer.png').then((texture) => (this.HammerIconTexture = texture)),
|
||||
this.Load('./assets/gui/icons/cross.png').then((texture) => (this.XIconTexture = texture)),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import GameObject from './GameObject';
|
||||
import GuiObject from './GuiObject';
|
||||
import Scene from '../scenes/Scene';
|
||||
import { Grid } from './game/Grid';
|
||||
@ -10,7 +9,6 @@ import { AnimationManager } from './game/AnimationManager';
|
||||
import NotificationManager from './game/NotificationManager';
|
||||
import Gem from './game/Gem';
|
||||
import GameAssets from './Assets';
|
||||
import { GemType } from './Definitions';
|
||||
|
||||
export class Engine {
|
||||
public static app: PIXI.Application;
|
||||
@ -33,10 +31,10 @@ export class Engine {
|
||||
public static TestSuite() {
|
||||
let params = new URLSearchParams(location.href);
|
||||
if (params.entries().next().value[1] != 'game') return;
|
||||
|
||||
Engine.NotificationManager.Notify('Loaded testing suite.', 'danger');
|
||||
let tower = GameAssets.Towers[0];
|
||||
Engine.TowerManager.ToggleChoosingTowerLocation('RESET');
|
||||
Engine.TowerManager.PlaceTower(GameAssets.Towers[1], 6, 10, GameAssets.Towers[1].behaviour, true);
|
||||
Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true);
|
||||
for (let i = 0; i < 29; i++) {
|
||||
this.GameScene.MissionStats.giveGem(new Gem(i % 4), true);
|
||||
}
|
||||
|
@ -108,6 +108,9 @@ export enum CreepType {
|
||||
Basic = 0,
|
||||
Quick = 1,
|
||||
Tank = 2,
|
||||
Cloaker = 3,
|
||||
Demon = 4,
|
||||
Maker = 5,
|
||||
}
|
||||
|
||||
export enum GemType {
|
||||
|
@ -131,8 +131,8 @@ export default class Creep extends GameObject {
|
||||
this.sprite.scale.x *= -1;
|
||||
}
|
||||
}
|
||||
let deltaX = this.speed * elapsedMS * directionX;
|
||||
let deltaY = this.speed * elapsedMS * directionY;
|
||||
let deltaX = this.speed * elapsedMS * directionX * Engine.GameScene.gameSpeedMultiplier;
|
||||
let deltaY = this.speed * elapsedMS * directionY * Engine.GameScene.gameSpeedMultiplier;
|
||||
let increaseIndex = false;
|
||||
|
||||
if (deltaX > 0 && this.x + deltaX > targetX) {
|
||||
|
@ -67,6 +67,7 @@ export class Cell extends GameObject {
|
||||
Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (_, row, col) => {
|
||||
if (row == this.row && col == this.column) {
|
||||
this.hasTowerPlaced = false;
|
||||
Engine.Grid.rangePreview.clear();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Handles keyboard events.
|
||||
*/
|
||||
class KeyboardManager {
|
||||
export default class KeyboardManager {
|
||||
private static listeners: ((event: KeyboardEvent) => void)[] = [];
|
||||
|
||||
public static init() {
|
||||
@ -35,7 +35,7 @@ class KeyboardManager {
|
||||
|
||||
private static handleKeyDown(event: KeyboardEvent) {
|
||||
if (KeyboardManager.listeners.length > 0) {
|
||||
console.log(`Key down: ${event.key}`);
|
||||
// console.log(`Key down: ${event.key}`);
|
||||
for (let i = KeyboardManager.listeners.length - 1; i >= 0; i--) {
|
||||
KeyboardManager.listeners[i](event);
|
||||
if (event.defaultPrevented) {
|
||||
@ -45,5 +45,3 @@ class KeyboardManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default KeyboardManager;
|
||||
|
@ -64,7 +64,7 @@ export default class Projectile extends GameObject {
|
||||
if (this.deleteMe) return;
|
||||
if (this.x > 2000 || this.x < 0 || this.y > 2000 || this.y < 0 || this.pierce <= 0 || this.timeToLive <= 0)
|
||||
return this.destroy();
|
||||
this.timeToLive--;
|
||||
this.timeToLive -= Engine.GameScene.gameSpeedMultiplier;
|
||||
Engine.Grid.creeps.forEach((creep) => {
|
||||
if (this.pierce <= 0) return;
|
||||
if (creep && creep.container && this.checkCollision(creep)) {
|
||||
@ -77,19 +77,25 @@ export default class Projectile extends GameObject {
|
||||
}
|
||||
}
|
||||
});
|
||||
this.x += Math.cos(this.angle) * this.speed * elapsedMS;
|
||||
this.y += Math.sin(this.angle) * this.speed * elapsedMS;
|
||||
this.x += Math.cos(this.angle) * this.speed * elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
this.y += Math.sin(this.angle) * this.speed * elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
|
||||
this.container.x = this.x;
|
||||
this.container.y = this.y;
|
||||
}
|
||||
|
||||
public onCollide(creep) {
|
||||
/*
|
||||
Note:
|
||||
Right now it is possible for the bullet to 'overshoot' the creep if the bullet speed is too fast and the position is updated so that the
|
||||
new position is beyond the creep (i.e. the bullet is never 'in the creep').
|
||||
This should be fixed so that we calculate the hit if the creep is in a line from the previous position to the new position.
|
||||
*/
|
||||
Engine.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, this.damage, this.gemResistanceModifications);
|
||||
}
|
||||
|
||||
public checkCollision(creep: Creep) {
|
||||
console.debug(creep);
|
||||
//console.debug(creep);
|
||||
if (creep == null || creep.container == null || creep.container._position == null) return;
|
||||
let mybb = this.copyContainerToBB();
|
||||
let otherbb = creep.copyContainerToBB();
|
||||
|
@ -16,16 +16,18 @@ export function distance(x1, y1, x2, y2) {
|
||||
export class Tower extends GameObject {
|
||||
public row: number;
|
||||
public column: number;
|
||||
public setAsSold: boolean = false;
|
||||
public sold: boolean = false;
|
||||
public definition: TowerDefinition;
|
||||
public slottedGems: Array<Gem> = [];
|
||||
public damageDealt: number = 0;
|
||||
public projectiles: Projectile[] = [];
|
||||
public behaviour: string;
|
||||
public sprite: PIXI.Sprite;
|
||||
public ticksUntilNextShot: number;
|
||||
public millisecondsUntilNextShot: number;
|
||||
public graphics: PIXI.Graphics = new PIXI.Graphics();
|
||||
public computedDamageToDeal: number;
|
||||
public computedAttackSpeed: number;
|
||||
public computedCooldown: number;
|
||||
public computedRange: number;
|
||||
public computedTimeToLive: number;
|
||||
public computedPierce: number;
|
||||
@ -38,7 +40,7 @@ export class Tower extends GameObject {
|
||||
this.column = column;
|
||||
this.behaviour = behaviour;
|
||||
this.definition = definition;
|
||||
this.ticksUntilNextShot = 0;
|
||||
this.millisecondsUntilNextShot = 0;
|
||||
this.parent = Engine.Grid.getCellByRowAndCol(row, column);
|
||||
this.sprite = new PIXI.Sprite({
|
||||
texture: texture,
|
||||
@ -74,6 +76,7 @@ export class Tower extends GameObject {
|
||||
}
|
||||
public UnslotGem(index) {
|
||||
const gem = this.slottedGems.splice(index, 1)[0];
|
||||
if (gem == null || !gem) return console.warn('UnslotGem: Gem is null.');
|
||||
Engine.GameScene.MissionStats.giveGem(gem, true);
|
||||
for (let i = index; i < this.slottedGems.length - 1; i++) {
|
||||
if (this.slottedGems[i] == null) {
|
||||
@ -113,14 +116,6 @@ export class Tower extends GameObject {
|
||||
}
|
||||
combinedTint = color;
|
||||
}
|
||||
// this.slottedGems.forEach((gem) => {
|
||||
// let rgb = new PIXI.Color(gem.definition.color).toRgb();
|
||||
// combinedTint =
|
||||
// ((combinedTint & 0xff0000) + (rgb.r << 16)) |
|
||||
// ((combinedTint & 0x00ff00) + (rgb.g << 8)) |
|
||||
// ((combinedTint & 0x0000ff) + rgb.b);
|
||||
// });
|
||||
// combinedTint = new PIXI.Color(this.slottedGems[0].definition.color).
|
||||
let proj = new Projectile(
|
||||
x,
|
||||
y,
|
||||
@ -132,10 +127,20 @@ export class Tower extends GameObject {
|
||||
this.computedPierce,
|
||||
this.totalGemResistanceModifications
|
||||
);
|
||||
const time = new Date().toISOString();
|
||||
console.log(`${time} ${this.definition.name} shot at ${angle} degrees`);
|
||||
this.projectiles.push(proj);
|
||||
return proj;
|
||||
}
|
||||
public Sell() {
|
||||
this.setAsSold = true;
|
||||
// Selling logic is handled in TowerManager.update()
|
||||
}
|
||||
public update(elapsedMS: any): void {
|
||||
if (this.sold) return;
|
||||
if (this.setAsSold) {
|
||||
this.sold = true;
|
||||
}
|
||||
if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) BasicTowerBehaviour(this, elapsedMS);
|
||||
if (this.behaviour == TowerBehaviours.CircleTowerBehaviour) CircleTowerBehaviour(this, elapsedMS);
|
||||
}
|
||||
|
@ -12,11 +12,13 @@ import { Tower } from './Tower';
|
||||
*/
|
||||
function projectileCheck(tower: Tower, elapsedMS: number) {
|
||||
tower.projectiles.forEach((proj) => {
|
||||
if (proj.deleteMe) {
|
||||
if (proj.deleteMe || tower.sold) {
|
||||
proj.collidedCreepIDs.forEach(() => {
|
||||
tower.damageDealt += tower.computedDamageToDeal;
|
||||
});
|
||||
proj.collidedCreepIDs = [];
|
||||
tower.projectiles.splice(tower.projectiles.indexOf(proj), 1);
|
||||
proj.destroy();
|
||||
proj = null;
|
||||
} else proj.update(elapsedMS);
|
||||
});
|
||||
@ -57,7 +59,7 @@ export function computeGemImprovements(tower: Tower) {
|
||||
tower.totalGemResistanceModifications.frostfire += gemResMod.frostfire;
|
||||
});
|
||||
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
|
||||
tower.computedAttackSpeed = tower.definition.stats.cooldown - gemAttackSpeedUp;
|
||||
tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp;
|
||||
tower.computedRange = tower.definition.stats.range + gemRangeUp;
|
||||
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
|
||||
tower.computedPierce = tower.definition.stats.pierce + gemPierceUp;
|
||||
@ -71,30 +73,32 @@ export function computeGemImprovements(tower: Tower) {
|
||||
* @param elapsedMS - The elapsed time in milliseconds since the last update.
|
||||
*/
|
||||
export function BasicTowerBehaviour(tower: Tower, elapsedMS: number) {
|
||||
if (tower.ticksUntilNextShot % 2 == 0) computeGemImprovements(tower);
|
||||
computeGemImprovements(tower);
|
||||
projectileCheck(tower, elapsedMS);
|
||||
if (tower.ticksUntilNextShot > 0) tower.ticksUntilNextShot--;
|
||||
if (tower.millisecondsUntilNextShot > 0)
|
||||
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
let creepsInRange = tower.GetCreepsInRange();
|
||||
if (creepsInRange.length > 0) {
|
||||
let focus = creepsInRange[0];
|
||||
if (tower.ticksUntilNextShot <= 0) {
|
||||
if (tower.millisecondsUntilNextShot <= 0) {
|
||||
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
|
||||
let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2;
|
||||
tower.ticksUntilNextShot = tower.computedAttackSpeed;
|
||||
tower.millisecondsUntilNextShot = tower.computedCooldown;
|
||||
tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function CircleTowerBehaviour(tower: Tower, elapsedMS: number) {
|
||||
if (tower.ticksUntilNextShot % 2 == 0) computeGemImprovements(tower);
|
||||
computeGemImprovements(tower);
|
||||
projectileCheck(tower, elapsedMS);
|
||||
if (tower.ticksUntilNextShot > 0) tower.ticksUntilNextShot--;
|
||||
if (tower.millisecondsUntilNextShot > 0)
|
||||
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
let creepsInRange = tower.GetCreepsInRange();
|
||||
if (creepsInRange.length > 0) {
|
||||
let focus = creepsInRange[0];
|
||||
if (tower.ticksUntilNextShot <= 0) {
|
||||
tower.ticksUntilNextShot = tower.computedAttackSpeed;
|
||||
if (tower.millisecondsUntilNextShot <= 0) {
|
||||
tower.millisecondsUntilNextShot = tower.computedCooldown;
|
||||
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
|
||||
let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2;
|
||||
tower.Shoot(calculateAngleToPoint(x, y, x, y + 10)); // Up
|
||||
|
@ -114,8 +114,17 @@ export default class TowerManager {
|
||||
}
|
||||
}
|
||||
public update(elapsedMS) {
|
||||
this.towers.forEach((twr) => {
|
||||
twr.update(elapsedMS);
|
||||
this.towers.forEach((twr, idx) => {
|
||||
if (twr.sold) {
|
||||
twr.slottedGems = twr.slottedGems.filter((gem) => gem != null);
|
||||
while (twr.slottedGems.length > 0) {
|
||||
twr.UnslotGem(0);
|
||||
}
|
||||
Engine.GameScene.MissionStats.earnGold(twr.definition.stats.cost);
|
||||
twr.destroy();
|
||||
this.towers.splice(idx, 1);
|
||||
Engine.GameScene.events.emit(TowerEvents.TowerSoldEvent, twr.name, twr.row, twr.column);
|
||||
} else twr.update(elapsedMS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ export default class WaveManager extends GameObject {
|
||||
}
|
||||
public update(elapsedMS: number): void {
|
||||
if (this.started == false) return;
|
||||
this.ticks += elapsedMS;
|
||||
this.ticks += elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
this.creeps.forEach((creep) => {
|
||||
if (!creep.spawned && creep.tickToSpawnAt <= this.ticks) {
|
||||
creep.spawned = true;
|
||||
|
59
src/classes/gui/GamePausedDialog.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import ModalDialogBase from './ModalDialog';
|
||||
import Button, { ButtonTexture } from './Button';
|
||||
import { Engine } from '../Bastion';
|
||||
import { MissionPickerScene } from '../../scenes/MissionPicker';
|
||||
import { GameScene } from '../../scenes/Game';
|
||||
import KeyboardManager from '../game/KeyboardManager';
|
||||
|
||||
export default class GamePausedDialog extends ModalDialogBase {
|
||||
private btnMainMenu: Button;
|
||||
private btnRetry: Button;
|
||||
private btnContinue: Button;
|
||||
private _unsubKeypress: () => void;
|
||||
|
||||
constructor() {
|
||||
super([]);
|
||||
this._unsubKeypress = KeyboardManager.onKeyPressed(this.onContinueClick.bind(this));
|
||||
}
|
||||
|
||||
protected override createContent(): PIXI.Container {
|
||||
const container = new PIXI.Container();
|
||||
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Main Menu', ButtonTexture.Button01);
|
||||
this.btnMainMenu.onClick = this.onMainMenuClick.bind(this);
|
||||
container.addChild(this.btnMainMenu.container);
|
||||
this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01);
|
||||
this.btnRetry.onClick = this.onRetryClick.bind(this);
|
||||
container.addChild(this.btnRetry.container);
|
||||
|
||||
this.btnContinue = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Continue', ButtonTexture.Button01);
|
||||
this.btnContinue.onClick = this.onContinueClick.bind(this);
|
||||
container.addChild(this.btnContinue.container);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private onMainMenuClick(): void {
|
||||
this.close();
|
||||
this._unsubKeypress();
|
||||
Engine.GameScene.UnpauseGame();
|
||||
Engine.GameScene.destroy();
|
||||
Engine.GameMaster.changeScene(new MissionPickerScene());
|
||||
}
|
||||
|
||||
private onRetryClick(): void {
|
||||
const missionName = Engine.GameScene.mission.name;
|
||||
this.close();
|
||||
this._unsubKeypress();
|
||||
Engine.GameScene.UnpauseGame();
|
||||
Engine.GameScene.destroy();
|
||||
Engine.GameMaster.changeScene(new MissionPickerScene());
|
||||
Engine.GameMaster.changeScene(new GameScene(missionName));
|
||||
Engine.NotificationManager.Notify('Retrying mission.', 'green');
|
||||
}
|
||||
|
||||
private onContinueClick(): void {
|
||||
this.close();
|
||||
Engine.GameScene.UnpauseGame();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import ModalDialogBase from './ModalDialog';
|
||||
import GuiObject from '../GuiObject';
|
||||
|
||||
export default class MessageBox extends ModalDialogBase {
|
||||
private caption: string;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import GuiObject from '../GuiObject';
|
||||
import Assets from '../Assets';
|
||||
import { Engine } from '../Bastion';
|
||||
import GameAssets from '../Assets';
|
||||
import Button, { ButtonTexture } from './Button';
|
||||
@ -46,9 +45,9 @@ export default abstract class ModalDialogBase extends GuiObject {
|
||||
const contentBounds = `x: ${Math.round(this.dialogContent.x)}, y: ${Math.round(
|
||||
this.dialogContent.y
|
||||
)}, width: ${Math.round(this.dialogContent.width)}, height: ${Math.round(this.dialogContent.height)}`;
|
||||
console.debug(
|
||||
`ModalDialogBase.show(dialog: ${dialogBounds}, content: ${contentBounds}, buttons: ${this.buttonCaptions})`
|
||||
);
|
||||
// console.debug(
|
||||
// `ModalDialogBase.show(dialog: ${dialogBounds}, content: ${contentBounds}, buttons: ${this.buttonCaptions})`
|
||||
// );
|
||||
return new Promise((resolve, reject) => {
|
||||
Engine.app.stage.addChild(this.container);
|
||||
this.onClosed = (button) => {
|
||||
|
@ -179,9 +179,14 @@ export default class Tooltip extends GuiObject {
|
||||
this.gemDescriptionText.alpha = 1;
|
||||
|
||||
this.titleText.text = `Lv. ${gem.level} ` + gem.definition.name;
|
||||
let costToLevelUp;
|
||||
if (!gem.isMaxLevel())
|
||||
costToLevelUp = `Costs ${gem.definition.genericImprovements[gem.level].gemValueUp} gold to level up.`;
|
||||
else costToLevelUp = 'Max level.';
|
||||
this.gemDescriptionText.text =
|
||||
`Valued at ${gem.definition.initialGemValue + gem.currentGemImprovement().gemValueUp} gold. ` +
|
||||
gem.definition.description;
|
||||
`${costToLevelUp} Valued at ${
|
||||
gem.definition.initialGemValue + gem.currentGemImprovement().gemValueUp
|
||||
} gold. ` + gem.definition.description;
|
||||
}
|
||||
public Show(x, y) {
|
||||
this.container.alpha = 1;
|
||||
|
@ -73,7 +73,7 @@ export class VisualGemSlot extends GuiObject {
|
||||
this.container.addChild(this.background);
|
||||
this.container.addChild(this.iconSprite);
|
||||
this.container.addChild(this.frame);
|
||||
let txt = gem ? gem.id : '';
|
||||
let txt = gem ? gem.level : '';
|
||||
let dbgText = new PIXI.Text({
|
||||
text: txt,
|
||||
zIndex: 11,
|
||||
@ -114,6 +114,7 @@ export default class TowerPanel extends GuiObject {
|
||||
public frostFireResDamage: PIXI.Text;
|
||||
public divineResDamage: PIXI.Text;
|
||||
public physicalResDamage: PIXI.Text;
|
||||
private sellButton: Button;
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
super(false);
|
||||
@ -156,6 +157,7 @@ export default class TowerPanel extends GuiObject {
|
||||
zIndex: 5,
|
||||
style: new PIXI.TextStyle({
|
||||
fill: 0xffffff,
|
||||
fontSize: 25,
|
||||
stroke: {
|
||||
color: 0x000000,
|
||||
width: 2,
|
||||
@ -280,6 +282,14 @@ export default class TowerPanel extends GuiObject {
|
||||
}),
|
||||
});
|
||||
this.container.addChild(this.physicalResDamage);
|
||||
this.sellButton = new Button(
|
||||
new PIXI.Rectangle(5, this.towerPanel.height - 70, this.towerPanel.width - 115, 60),
|
||||
'Sell',
|
||||
ButtonTexture.Button02,
|
||||
true
|
||||
);
|
||||
this.sellButton.container.removeFromParent();
|
||||
this.container.addChild(this.sellButton.container);
|
||||
}
|
||||
private MakeSlots(tower: Tower) {
|
||||
this.vGems.forEach((vGem) => {
|
||||
@ -319,7 +329,7 @@ export default class TowerPanel extends GuiObject {
|
||||
this.MakeSlots(tower);
|
||||
this.showingTower = tower;
|
||||
Engine.GameScene.sidebar.gemTab.selectingGemTowerObject = tower;
|
||||
if (tower.container.parent.x < 900) {
|
||||
if (tower.container.parent.x < 1270) {
|
||||
this.ShowRight();
|
||||
} else {
|
||||
this.ShowLeft();
|
||||
@ -331,13 +341,18 @@ export default class TowerPanel extends GuiObject {
|
||||
this.damageText.text = 'Deals ' + tower.computedDamageToDeal + ' damage';
|
||||
this.totalDamage.text = 'Damage dealt: ' + tower.damageDealt + ' damage';
|
||||
this.attackSpeedText.x = this.damageText.width + 10;
|
||||
this.attackSpeedText.text = ` every ${Math.floor((tower.computedAttackSpeed / 60) * 100) / 100}s`;
|
||||
this.attackSpeedText.text = ` every ${Math.floor((tower.computedCooldown / 60) * 100) / 100}s`;
|
||||
|
||||
this.fireResDamage.text = `+${tower.totalGemResistanceModifications.fire * 100}% Fire damage`;
|
||||
this.iceResDamage.text = `+${tower.totalGemResistanceModifications.ice * 100}% Ice damage`;
|
||||
this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`;
|
||||
this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`;
|
||||
this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`;
|
||||
this.sellButton.setCaption('Sell for ' + tower.definition.stats.cost + ' gold');
|
||||
this.sellButton.onClick = () => {
|
||||
tower.Sell();
|
||||
this.Hide();
|
||||
};
|
||||
}
|
||||
private ShowLeft() {
|
||||
this.towerPanel.x = -100;
|
||||
|
18
src/main.ts
@ -46,6 +46,7 @@ import { GemType } from './classes/Definitions';
|
||||
}
|
||||
Engine.latestCommit = await fetch('/latest_commit').then((res) => res.text());
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
resize();
|
||||
await Assets.LoadAssets();
|
||||
GameUIConstants.init();
|
||||
@ -71,4 +72,21 @@ import { GemType } from './classes/Definitions';
|
||||
return 'You are about to leave.';
|
||||
};
|
||||
else Engine.TestSuite();
|
||||
|
||||
let gamePausedDueToBlur = false;
|
||||
|
||||
window.addEventListener('blur', () => {
|
||||
console.log('blur');
|
||||
if (Engine.GameScene && !Engine.GameScene.isPaused) {
|
||||
Engine.GameScene.PauseGame();
|
||||
gamePausedDueToBlur = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener('focus', () => {
|
||||
console.log('focus');
|
||||
if (Engine.GameScene && gamePausedDueToBlur && Engine.GameScene.isPaused) {
|
||||
gamePausedDueToBlur = false;
|
||||
Engine.GameScene.UnpauseGame();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
@ -18,11 +18,12 @@ import TowerPanel, { VisualGemSlot } from '../classes/gui/TowerPanel';
|
||||
import Gem from '../classes/game/Gem';
|
||||
import EndGameDialog from '../classes/gui/EndGameDialog';
|
||||
import HighScoreDialog, { HighScoreDialogButtons } from '../classes/gui/HighScoreDialog';
|
||||
import GamePausedDialog from '../classes/gui/GamePausedDialog';
|
||||
|
||||
enum RoundMode {
|
||||
Purchase = 0,
|
||||
Combat = 1,
|
||||
OfferingGems = 2,
|
||||
Misc = 2,
|
||||
}
|
||||
|
||||
export class GameScene extends Scene {
|
||||
@ -36,6 +37,10 @@ export class GameScene extends Scene {
|
||||
public sidebar: Sidebar;
|
||||
public tooltip: Tooltip;
|
||||
public towerPanel: TowerPanel;
|
||||
public isPaused: boolean = false;
|
||||
public gameSpeedMultiplier: number = 1;
|
||||
|
||||
private pauseButton: Button;
|
||||
private visualGems: VisualGemSlot[] = [];
|
||||
private currentRound: number = 0;
|
||||
private isWaveManagerFinished: boolean = false;
|
||||
@ -47,10 +52,12 @@ export class GameScene extends Scene {
|
||||
y: 0,
|
||||
zIndex: 120,
|
||||
});
|
||||
private windowTitle: string;
|
||||
|
||||
constructor(name: string) {
|
||||
super();
|
||||
Engine.GameScene = this;
|
||||
this.windowTitle = document.title;
|
||||
GameAssets.Missions.forEach((mission, index) => {
|
||||
if (mission.name == name) {
|
||||
this.mission = mission;
|
||||
@ -101,15 +108,21 @@ export class GameScene extends Scene {
|
||||
this.changeRoundButton.CustomButtonLogic();
|
||||
this.changeRoundButton.onClick = () => {
|
||||
if (this.playerWon) return this.ReturnToMain();
|
||||
if (this.roundMode == RoundMode.Combat)
|
||||
return Engine.NotificationManager.Notify('Wave is already in progress.', 'warn');
|
||||
if (this.roundMode == RoundMode.Combat) {
|
||||
if (this.gameSpeedMultiplier !== 1) {
|
||||
this.UpdateGameSpeedMultiplier(1);
|
||||
} else {
|
||||
this.UpdateGameSpeedMultiplier(2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'danger');
|
||||
if (this.roundMode == RoundMode.OfferingGems) return;
|
||||
if (this.roundMode == RoundMode.Misc) return;
|
||||
this.setRoundMode(RoundMode.Combat);
|
||||
this.changeRoundButton.buttonIcon.texture = GameAssets.ExclamationIconTexture;
|
||||
this.changeRoundButton.buttonIcon.texture = GameAssets.FastForwardIconTexture;
|
||||
this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`);
|
||||
};
|
||||
this.MissionStats = new MissionStats(100, 200);
|
||||
this.MissionStats = new MissionStats(125, 450);
|
||||
this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => {
|
||||
if (gem == null) {
|
||||
if (!this.MissionStats.checkIfPlayerHasAnyGems())
|
||||
@ -120,6 +133,30 @@ export class GameScene extends Scene {
|
||||
}
|
||||
this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower);
|
||||
});
|
||||
|
||||
this.pauseButton = new Button(new PIXI.Rectangle(5, 5, 120, 80), '', ButtonTexture.Button01, true);
|
||||
this.pauseButton.container.removeFromParent();
|
||||
this.stage.addChild(this.pauseButton.container);
|
||||
this.pauseButton.CustomButtonLogic = () => {
|
||||
this.pauseButton.buttonIcon = new PIXI.Sprite({
|
||||
texture: GameAssets.PauseIconTexture,
|
||||
x: this.pauseButton.container.width / 2,
|
||||
y: this.pauseButton.container.height / 2,
|
||||
scale: 0.2,
|
||||
});
|
||||
this.pauseButton.buttonIcon.anchor.set(0.5, 0.5);
|
||||
this.pauseButton.container.addChild(this.pauseButton.buttonIcon);
|
||||
};
|
||||
this.pauseButton.CustomButtonLogic();
|
||||
this.pauseButton.onClick = () => {
|
||||
if (this.isPaused) {
|
||||
this.UnpauseGame();
|
||||
} else {
|
||||
this.ShowPauseDialog();
|
||||
this.PauseGame();
|
||||
}
|
||||
};
|
||||
|
||||
this.ticker = new PIXI.Ticker();
|
||||
this.ticker.maxFPS = 60;
|
||||
this.ticker.minFPS = 30;
|
||||
@ -130,10 +167,10 @@ export class GameScene extends Scene {
|
||||
|
||||
this.ticker.add(() => {
|
||||
if (this.update) this.update(this.ticker.elapsedMS);
|
||||
// if (this.isFastForwarded) this.update(this.ticker.elapsedMS);
|
||||
});
|
||||
this.ticker.start();
|
||||
}
|
||||
|
||||
public update(elapsedMS) {
|
||||
if (this.isGameOver) {
|
||||
if (this.destroyTicker) {
|
||||
@ -183,7 +220,7 @@ export class GameScene extends Scene {
|
||||
Engine.Grid.gridInteractionEnabled = false;
|
||||
Engine.GameScene.sidebar.towerTab.resetTint();
|
||||
Engine.TowerManager.ResetChooseTower();
|
||||
this.setRoundMode(RoundMode.OfferingGems);
|
||||
this.setRoundMode(RoundMode.Misc);
|
||||
let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems;
|
||||
this.DarkenScreen();
|
||||
this.offerGemsSprite = new PIXI.NineSliceSprite({
|
||||
@ -245,6 +282,21 @@ export class GameScene extends Scene {
|
||||
this.setRoundMode(RoundMode.Purchase);
|
||||
}
|
||||
|
||||
public PauseGame() {
|
||||
this.isPaused = true;
|
||||
this.ticker.stop();
|
||||
document.title = '[PAUSED] ' + this.windowTitle;
|
||||
}
|
||||
public UnpauseGame() {
|
||||
this.isPaused = false;
|
||||
this.ticker.start();
|
||||
document.title = this.windowTitle;
|
||||
}
|
||||
public ShowPauseDialog() {
|
||||
const gamePausedDialog = new GamePausedDialog();
|
||||
gamePausedDialog.show();
|
||||
}
|
||||
|
||||
private async ShowEndgameDialog(lost) {
|
||||
const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost);
|
||||
await endGameDialog.show();
|
||||
@ -292,4 +344,10 @@ export class GameScene extends Scene {
|
||||
private ReturnToMain() {
|
||||
Engine.GameMaster.changeScene(new MissionPickerScene());
|
||||
}
|
||||
|
||||
private UpdateGameSpeedMultiplier(newMultiplier: number) {
|
||||
this.gameSpeedMultiplier = newMultiplier;
|
||||
if (newMultiplier === 1) Engine.NotificationManager.Notify('Regular speed.', 'info');
|
||||
else Engine.NotificationManager.Notify('Fast forward activated.', 'info');
|
||||
}
|
||||
}
|
||||
|
67
src/scenes/HowToPlay.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import GameAssets from '../classes/Assets';
|
||||
import Assets from '../classes/Assets';
|
||||
import { Engine } from '../classes/Bastion';
|
||||
import Button, { ButtonTexture } from '../classes/gui/Button';
|
||||
import { GameScene } from './Game';
|
||||
import { MainScene } from './Main';
|
||||
import Scene from './Scene';
|
||||
import * as PIXI from 'pixi.js';
|
||||
|
||||
export class HowToPlay extends Scene {
|
||||
public currentImg = 1;
|
||||
public sprite;
|
||||
public init() {
|
||||
let sprites = [
|
||||
null,
|
||||
GameAssets.Tutorial01,
|
||||
GameAssets.Tutorial02,
|
||||
GameAssets.Tutorial03,
|
||||
GameAssets.Tutorial04,
|
||||
GameAssets.Tutorial05,
|
||||
];
|
||||
this.sprite = new PIXI.Sprite({
|
||||
texture: GameAssets.Tutorial01,
|
||||
scale: 0.6,
|
||||
x: 250,
|
||||
y: 150,
|
||||
});
|
||||
this.stage.addChild(this.sprite);
|
||||
let leftButton = new Button(
|
||||
new PIXI.Rectangle(250, this.sprite.height + 160, 120, 60),
|
||||
'Back',
|
||||
ButtonTexture.Button01
|
||||
);
|
||||
leftButton.container.alpha = 0;
|
||||
leftButton.onClick = () => {
|
||||
if (leftButton.container.alpha == 0 || this.currentImg == 1) return;
|
||||
this.currentImg--;
|
||||
if (this.currentImg == 3) this.sprite.scale = 1.1;
|
||||
else this.sprite.scale = 0.6;
|
||||
this.sprite.texture = sprites[this.currentImg];
|
||||
if (this.currentImg == 1) leftButton.container.alpha = 0;
|
||||
};
|
||||
|
||||
let right = new Button(
|
||||
new PIXI.Rectangle(this.sprite.width + 130, this.sprite.height + 160, 120, 60),
|
||||
'Next',
|
||||
ButtonTexture.Button01
|
||||
);
|
||||
right.onClick = () => {
|
||||
if (right.container.alpha == 0) return;
|
||||
this.currentImg++;
|
||||
if (this.currentImg == 3) this.sprite.scale = 1.1;
|
||||
else this.sprite.scale = 0.6;
|
||||
if (this.currentImg != 1) leftButton.container.alpha = 1;
|
||||
this.sprite.texture = sprites[this.currentImg];
|
||||
if (this.currentImg == 5) right.container.alpha = 0;
|
||||
};
|
||||
const button = new Button(
|
||||
new PIXI.Rectangle(this.sprite.width - 540, this.sprite.height + 160, 200, 60),
|
||||
'Main menu',
|
||||
ButtonTexture.Button01
|
||||
);
|
||||
button.onClick = (e) => {
|
||||
Engine.GameMaster.changeScene(new MainScene());
|
||||
};
|
||||
}
|
||||
}
|
@ -1,66 +1,59 @@
|
||||
import GameAssets from '../classes/Assets';
|
||||
import { Engine } from '../classes/Bastion';
|
||||
import { FadeInOut, Tween } from '../classes/game/AnimationManager';
|
||||
import Button, { ButtonTexture } from '../classes/gui/Button';
|
||||
import { HowToPlay } from './HowToPlay';
|
||||
import { MissionPickerScene } from './MissionPicker';
|
||||
import Scene from './Scene';
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { SettingsScene } from './Settings';
|
||||
|
||||
export class MainScene extends Scene {
|
||||
public init() {
|
||||
// Background
|
||||
this.addMainBackground();
|
||||
|
||||
const NewGameButton = {
|
||||
caption: 'New Game',
|
||||
rect: new PIXI.Rectangle(
|
||||
Engine.app.canvas.width / 2 - 300 / 2,
|
||||
Engine.app.canvas.height / 5 + 3 * 80,
|
||||
300,
|
||||
60
|
||||
),
|
||||
texture: ButtonTexture.Button02,
|
||||
rect: new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + 0 * 70, 300, 60),
|
||||
|
||||
texture: ButtonTexture.Button01,
|
||||
};
|
||||
const TutorialButton = {
|
||||
caption: 'How to play',
|
||||
rect: new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + 1 * 70, 300, 60),
|
||||
texture: ButtonTexture.Button01,
|
||||
};
|
||||
|
||||
const SettingsButton = {
|
||||
caption: 'Settings',
|
||||
rect: new PIXI.Rectangle(
|
||||
Engine.app.canvas.width / 2 - 300 / 2,
|
||||
Engine.app.canvas.height / 5 + 4 * 80,
|
||||
300,
|
||||
60
|
||||
),
|
||||
texture: ButtonTexture.Button02,
|
||||
rect: new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + 2 * 70, 300, 60),
|
||||
texture: ButtonTexture.Button01,
|
||||
};
|
||||
let text = new PIXI.Text({
|
||||
x: Engine.app.canvas.width / 2 - 300 / 2,
|
||||
y: Engine.app.canvas.height / 5 + 1 * 80,
|
||||
text: 'BASTION',
|
||||
style: {
|
||||
fill: 0xffaa00,
|
||||
fontFamily: 'Aclonica',
|
||||
fontSize: 100,
|
||||
},
|
||||
});
|
||||
text.x = text.x - text.width / 5;
|
||||
Engine.GameMaster.currentScene.stage.addChild(text);
|
||||
|
||||
let text2 = new PIXI.Text({
|
||||
x: 0,
|
||||
y: 0,
|
||||
text: 'Latest commit: ' + Engine.latestCommit,
|
||||
style: {
|
||||
fill: 0x000000,
|
||||
fill: 0xffffff,
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
Engine.GameMaster.currentScene.stage.addChild(text2);
|
||||
this.stage.addChild(text2);
|
||||
const button01 = new Button(NewGameButton.rect, NewGameButton.caption, NewGameButton.texture, true);
|
||||
button01.onClick = (e) => {
|
||||
Engine.GameMaster.currentScene.stage.removeChild(text);
|
||||
Engine.GameMaster.currentScene.stage.removeChild(text2);
|
||||
Engine.GameMaster.changeScene(new MissionPickerScene());
|
||||
};
|
||||
|
||||
let b2 = new Button(SettingsButton.rect, SettingsButton.caption, SettingsButton.texture, true);
|
||||
b2.onClick = (e) => {
|
||||
Engine.NotificationManager.Notify('Not finished.', 'info');
|
||||
// let b2 = new Button(SettingsButton.rect, SettingsButton.caption, SettingsButton.texture, true);
|
||||
// b2.onClick = (e) => {
|
||||
// Engine.GameMaster.changeScene(new SettingsScene());
|
||||
// };
|
||||
let b3 = new Button(TutorialButton.rect, TutorialButton.caption, TutorialButton.texture, true);
|
||||
b3.onClick = (e) => {
|
||||
Engine.GameMaster.changeScene(new HowToPlay());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,18 +8,14 @@ import * as PIXI from 'pixi.js';
|
||||
|
||||
export class MissionPickerScene extends Scene {
|
||||
public init() {
|
||||
const button = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Back to main', ButtonTexture.Button01);
|
||||
this.addMainBackground();
|
||||
const button = new Button(new PIXI.Rectangle(10, 10, 300, 60), 'Back to main', ButtonTexture.Button01);
|
||||
button.onClick = (e) => {
|
||||
Engine.GameMaster.changeScene(new MainScene());
|
||||
};
|
||||
Assets.Missions.forEach((mission, index) => {
|
||||
const button = new Button(
|
||||
new PIXI.Rectangle(
|
||||
Engine.app.canvas.width / 2 - 300 / 2,
|
||||
Engine.app.canvas.height / 5 + index * 80,
|
||||
300,
|
||||
60
|
||||
),
|
||||
new PIXI.Rectangle(Engine.app.canvas.width / 2 - 300 / 2, 400 + index * 70, 300, 60),
|
||||
mission.name,
|
||||
ButtonTexture.Button01
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import GameAssets from '../classes/Assets';
|
||||
import { Engine } from '../classes/Bastion';
|
||||
import GuiObject from '../classes/GuiObject';
|
||||
import * as PIXI from 'pixi.js';
|
||||
@ -18,6 +19,14 @@ export default class Scene {
|
||||
});
|
||||
}
|
||||
|
||||
public addMainBackground() {
|
||||
// Background
|
||||
const sprite = new PIXI.Sprite(GameAssets.MainBackground);
|
||||
sprite.width = Engine.app.canvas.width;
|
||||
sprite.height = Engine.app.canvas.height;
|
||||
this.stage.addChild(sprite);
|
||||
}
|
||||
|
||||
public get events(): PIXI.EventEmitter {
|
||||
return this._events;
|
||||
}
|
||||
|
17
src/scenes/Settings.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import Assets from '../classes/Assets';
|
||||
import { Engine } from '../classes/Bastion';
|
||||
import Button, { ButtonTexture } from '../classes/gui/Button';
|
||||
import { GameScene } from './Game';
|
||||
import { MainScene } from './Main';
|
||||
import Scene from './Scene';
|
||||
import * as PIXI from 'pixi.js';
|
||||
|
||||
export class SettingsScene extends Scene {
|
||||
public init() {
|
||||
this.addMainBackground();
|
||||
const button = new Button(new PIXI.Rectangle(10, 10, 300, 60), 'Back to main', ButtonTexture.Button01);
|
||||
button.onClick = (e) => {
|
||||
Engine.GameMaster.changeScene(new MainScene());
|
||||
};
|
||||
}
|
||||
}
|