diff --git a/public/assets/gui/gui_01_checkbox_01_bg01.png b/public/assets/gui/button_small.png similarity index 100% rename from public/assets/gui/gui_01_checkbox_01_bg01.png rename to public/assets/gui/button_small.png diff --git a/public/assets/gui/gui_01_button_01.png b/public/assets/gui/gui_01_button_01.png deleted file mode 100755 index 5df1548..0000000 Binary files a/public/assets/gui/gui_01_button_01.png and /dev/null differ diff --git a/public/assets/gui/icons/account.png b/public/assets/gui/icons/account.png new file mode 100755 index 0000000..03227a1 Binary files /dev/null and b/public/assets/gui/icons/account.png differ diff --git a/public/assets/gui/icons/cross.png b/public/assets/gui/icons/cross.png new file mode 100755 index 0000000..deda6cb Binary files /dev/null and b/public/assets/gui/icons/cross.png differ diff --git a/public/assets/gui/icons/exclamation.png b/public/assets/gui/icons/exclamation.png new file mode 100755 index 0000000..060a4fd Binary files /dev/null and b/public/assets/gui/icons/exclamation.png differ diff --git a/public/assets/gui/icons/fastforward.png b/public/assets/gui/icons/fastforward.png new file mode 100755 index 0000000..8c8e1e5 Binary files /dev/null and b/public/assets/gui/icons/fastforward.png differ diff --git a/public/assets/gui/icons/hammer.png b/public/assets/gui/icons/hammer.png new file mode 100755 index 0000000..85e88d1 Binary files /dev/null and b/public/assets/gui/icons/hammer.png differ diff --git a/public/assets/gui/icons/home.png b/public/assets/gui/icons/home.png new file mode 100755 index 0000000..d62633d Binary files /dev/null and b/public/assets/gui/icons/home.png differ diff --git a/public/assets/gui/icons/pause.png b/public/assets/gui/icons/pause.png new file mode 100755 index 0000000..ca8fe99 Binary files /dev/null and b/public/assets/gui/icons/pause.png differ diff --git a/public/assets/gui/icons/play.png b/public/assets/gui/icons/play.png new file mode 100755 index 0000000..fd1ea8e Binary files /dev/null and b/public/assets/gui/icons/play.png differ diff --git a/public/assets/gui/icons/plus.png b/public/assets/gui/icons/plus.png new file mode 100755 index 0000000..7576aca Binary files /dev/null and b/public/assets/gui/icons/plus.png differ diff --git a/public/assets/gui/icons/settings.png b/public/assets/gui/icons/settings.png new file mode 100755 index 0000000..3344399 Binary files /dev/null and b/public/assets/gui/icons/settings.png differ diff --git a/public/assets/gui/icons/skull.png b/public/assets/gui/icons/skull.png new file mode 100755 index 0000000..21c06c4 Binary files /dev/null and b/public/assets/gui/icons/skull.png differ diff --git a/public/assets/gui/icons/speaker.png b/public/assets/gui/icons/speaker.png new file mode 100755 index 0000000..1a489e9 Binary files /dev/null and b/public/assets/gui/icons/speaker.png differ diff --git a/public/assets/gui/icons/weights.png b/public/assets/gui/icons/weights.png new file mode 100755 index 0000000..6f1ffd6 Binary files /dev/null and b/public/assets/gui/icons/weights.png differ diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index e6ce8d1..2c58d16 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -13,10 +13,16 @@ export default class GameAssets { public static GreenBackground: PIXI.Texture; public static Button01Texture: PIXI.Texture; public static Button02Texture: PIXI.Texture; + public static ButtonSmallTexture: PIXI.Texture; public static HealthTexture: PIXI.Texture; public static GoldTexture: PIXI.Texture; public static WaveTexture: PIXI.Texture; + public static PlayIconTexture: PIXI.Texture; + public static PauseIconTexture: PIXI.Texture; + public static ExclamationIconTexture: PIXI.Texture; + public static HomeIconTexture: PIXI.Texture; + public static Missions: MissionDefinition[]; public static MissionBackgrounds: PIXI.Texture[] = []; public static TowerSprites: PIXI.Texture[] = []; @@ -26,7 +32,6 @@ export default class GameAssets { private static text; private static async Load(src) { this.text.text = 'Loading asset: ' + src; - console.log('LOADING ' + src); return await PIXI.Assets.load({ src: src, }); @@ -66,6 +71,7 @@ export default class GameAssets { this.Load('/aclonica.woff2'), this.Load('/assets/gui/button_01.png').then((texture) => (this.Button01Texture = texture)), this.Load('/assets/gui/button_02.png').then((texture) => (this.Button02Texture = texture)), + this.Load('/assets/gui/button_small.png').then((texture) => (this.ButtonSmallTexture = texture)), this.Load('/assets/gui/frame_01.png').then((texture) => (this.Frame01Texture = texture)), this.Load('/assets/gui/frame_02.png').then((texture) => (this.Frame02Texture = texture)), this.Load('/assets/gui/frame_03.png').then((texture) => (this.Frame03Texture = texture)), @@ -77,6 +83,10 @@ export default class GameAssets { 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)), + 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/home.png').then((texture) => (this.HomeIconTexture = texture)), this.LoadMissions(), this.LoadTowers(), this.LoadCreeps(), @@ -114,7 +124,6 @@ export default class GameAssets { for (let idx = 0; idx < this.Towers.length; idx++) { const tower = this.Towers[idx]; for (let i = 0; i < tower.projectileTexturesArrayLength; i++) { - console.log(`WANT TO LOAD /assets/projectiles/${tower.sprite}/${i}.png`); const texture = await this.Load(`/assets/projectiles/${tower.sprite}/${i}.png`); tower.projectileTextures[i] = texture; console.log(tower.projectileTextures); @@ -124,6 +133,7 @@ export default class GameAssets { let index = this.TowerSprites.length - 1; if (index == -1) index = 0; this.TowerSprites[index] = await this.Load(`/assets/towers/${tower.sprite}.png`); + tower.texture = this.TowerSprites[index]; }); } diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 1209044..74aeacd 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -4,14 +4,21 @@ import { GameMapDefinition, TerrainType } from '../Definitions'; import GameAssets from '../Assets'; import { Engine } from '../Bastion'; import Creep, { CreepEvents } from './Creep'; +import { TowerEvents } from './Tower'; + +export enum GridEvents { + CellMouseOver = 'cellmouseover', +} export class Cell extends GameObject { public type: TerrainType; public row: number; public column: number; public isPath: boolean = false; - private g: PIXI.Graphics; + public g: PIXI.Graphics; + public hasTowerPlaced: boolean = false; public clickDetector: PIXI.Graphics; + public rangePreview: PIXI.Graphics; constructor(type: TerrainType, row: number, column: number, isPath: boolean) { super(); @@ -31,36 +38,58 @@ export class Cell extends GameObject { zIndex: 99, interactive: true, }); + this.rangePreview = new PIXI.Graphics({ + zIndex: 6, + }); this.clickDetector.rect(0, 0, this.bb.width, this.bb.height); this.clickDetector.fill({ color: 0xff0000, alpha: 0 }); this.container.addChild(this.clickDetector); - this.clickDetector.onpointerdown = (e) => { + this.container.addChild(this.rangePreview); + this.clickDetector.on('pointerup', (e) => { Engine.Grid.onGridCellClicked(row, column); - }; + }); + this.clickDetector.on('pointerenter', (e) => { + Engine.GameScene.events.emit(GridEvents.CellMouseOver, this); + }); + this.clickDetector.on('pointerleave', (e) => { + this.rangePreview.clear(); + }); + Engine.GameScene.events.on(TowerEvents.TowerPlacedEvent, (_, row, col) => { + if (row == this.row && col == this.column) { + console.log('SETTING'); + this.setHasTowerPlaced(true); + console.log(this); + this.rangePreview.clear(); + } + }); + Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (_, row, col) => { + if (row == this.row && col == this.column) { + this.hasTowerPlaced = false; + } + }); + } + private setHasTowerPlaced(v) { + console.log(' CALLLEd'); + this.hasTowerPlaced = v; } public gDraw() { this.g = new PIXI.Graphics({ zIndex: 5, }); this.g.rect(0, 0, this.bb.width, this.bb.height); - switch (this.type) { - case TerrainType.Restricted: - this.g.fill({ color: 0x222222, alpha: 0.5 }); - break; - case TerrainType.Path: - this.g.fill({ color: 0x222222, alpha: 0.5 }); - break; - case TerrainType.Buildable: - this.g.stroke({ color: 0x00ff00, alpha: 0.9 }); - break; + if (this.type == TerrainType.Restricted) { + this.g.fill({ color: 0x222222, alpha: 0.5 }); + } else if (this.hasTowerPlaced) { + this.g.fill({ color: 0xff0000, alpha: 0.5 }); + } else if (this.type == TerrainType.Path) { + this.g.fill({ color: 0x222222, alpha: 0.5 }); + } else if (this.type == TerrainType.Buildable) { + this.g.stroke({ color: 0x00ff00, alpha: 0.9 }); } this.container.addChild(this.g); } public gClear() { - if (this.g != null) { - this.container.removeChild(this.g); - this.g.destroy(); - } + this.g.clear(); } public update() {} } diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index d2506d8..501dc53 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -29,6 +29,7 @@ export type TowerInstance = { export enum TowerEvents { TowerPlacedEvent = 'towerPlacedEvent', + TowerSoldEvent = 'towerSoldEvent', } export class Tower extends GameObject { @@ -40,6 +41,8 @@ export class Tower extends GameObject { private sprite: PIXI.Sprite; private ticksUntilNextShot: number; private graphics: PIXI.Graphics = new PIXI.Graphics(); + private parent: Cell; + constructor(row, column, texture, definition, behaviour) { super(); this.row = row; @@ -47,7 +50,7 @@ export class Tower extends GameObject { this.behaviour = behaviour; this.definition = definition; this.ticksUntilNextShot = 0; - let parent: Cell = Engine.Grid.getCellByRowAndCol(row, column); + this.parent = Engine.Grid.getCellByRowAndCol(row, column); this.sprite = new PIXI.Sprite({ texture: texture, height: Engine.GridCellSize, @@ -55,23 +58,29 @@ export class Tower extends GameObject { zIndex: 10, }); this.container.addChild(this.sprite); - parent.container.addChild(this.container); - parent.clickDetector.onmouseenter = (e) => { - this.showRangeDisplay(); - }; - parent.clickDetector.onmouseleave = (e) => { - this.graphics.clear(); - }; + this.parent.container.addChild(this.container); + this.container.interactiveChildren = true; + this.parent.clickDetector.on('pointerenter', this.onParentCellEnter); + this.parent.clickDetector.on('pointerleave', this.onParentCellLeave); Engine.GameMaster.currentScene.stage.addChild(this.graphics); - this.showRangeDisplay(); + //this.showRangeDisplay(); } + + private onParentCellEnter = (e) => { + this.showRangeDisplay(); + }; + + private onParentCellLeave = (e) => { + this.graphics.clear(); + }; + public showRangeDisplay() { this.graphics.circle( this.column * Engine.GridCellSize + Engine.GridCellSize / 2, this.row * Engine.GridCellSize + Engine.GridCellSize / 2, this.definition.stats.range * Engine.GridCellSize ); - this.graphics.fill({ color: 0xff0000, alpha: 0.5 }); + this.graphics.fill({ color: 0xffffff, alpha: 0.5 }); } public GetCreepsInRange() { let creeps = Engine.Grid.creeps; @@ -112,4 +121,11 @@ export class Tower extends GameObject { } } } + + override destroy(): void { + super.destroy(); + this.parent.clickDetector.off('pointerenter', this.onParentCellEnter); + this.parent.clickDetector.off('pointerleave', this.onParentCellLeave); + this.graphics.destroy(); + } } diff --git a/src/classes/game/TowerManager.ts b/src/classes/game/TowerManager.ts index e4fcd73..7b0d497 100644 --- a/src/classes/game/TowerManager.ts +++ b/src/classes/game/TowerManager.ts @@ -3,7 +3,7 @@ import { Engine } from '../Bastion'; import { TerrainType, TowerDefinition } from '../Definitions'; import GameAssets from '../Assets'; import { Tower, TowerEvents } from './Tower'; -import { Cell } from './Grid'; +import { Cell, GridEvents } from './Grid'; export enum TowerBehaviours { BasicTowerBehaviour = 'BasicTowerBehaviour', @@ -13,9 +13,42 @@ export default class TowerManager { public isPlacingTower: boolean = false; public canPlaceTowers: boolean = true; private selectedTower: TowerDefinition | null = null; + private previewSprite: PIXI.Sprite = new PIXI.Sprite({ + parent: Engine.GameMaster.currentScene.stage, + zIndex: 10, + width: 64, + height: 64, + alpha: 0.8, + }); private towers: Tower[] = []; constructor() { Engine.TowerManager = this; + Engine.GameScene.events.on(GridEvents.CellMouseOver, (cell: Cell) => { + //console.log(`${cell.column}x${cell.row}, ${cell.type}, ${this.isPlacingTower}`); + if (this.isPlacingTower) { + let cantPlace = + this.GetTowerByRowAndCol(cell.row, cell.column) || + cell.hasTowerPlaced || + cell.isPath || + cell.type == TerrainType.Path || + cell.type == TerrainType.Restricted; + cell.rangePreview.circle( + Engine.GridCellSize / 2, + Engine.GridCellSize / 2, + this.selectedTower.stats.range * Engine.GridCellSize + ); + if (cantPlace) { + cell.rangePreview.fill({ color: 0xff0000, alpha: 0.4 }); + this.previewSprite.tint = 0xff0000; + } else { + cell.rangePreview.fill({ color: 0xffffff, alpha: 0.4 }); + this.previewSprite.tint = 0xffffff; + } + this.previewSprite.x = cell.column * Engine.GridCellSize; + this.previewSprite.y = cell.row * Engine.GridCellSize; + this.previewSprite.texture = this.selectedTower.texture; + } + }); } public ToggleChoosingTowerLocation(towerName: string) { if (!this.canPlaceTowers) return; @@ -27,6 +60,7 @@ export default class TowerManager { } }); } else { + this.previewSprite.texture = null; this.selectedTower = null; } this.isPlacingTower = !this.isPlacingTower; @@ -51,6 +85,7 @@ export default class TowerManager { this.towers.forEach((tower) => { if (tower.row == row && tower.column == col) returnTower = tower; }); + // console.log(returnTower, row, col); return returnTower; } public PlaceTower(definition: TowerDefinition, row, column, behaviour: string, ignoreCost?) { @@ -60,7 +95,7 @@ export default class TowerManager { }); const sprite = GameAssets.TowerSprites[idx]; if (!Engine.GameScene.MissionStats.hasEnoughGold(definition.stats.cost) && !ignoreCost) - return console.warn('Does not have enough gold.'); + return Engine.NotificationManager.Notify('Not enough gold.', 'warn'); if ( !this.GetTowerByRowAndCol(row, column) && Engine.Grid.getCellByRowAndCol(row, column).type != TerrainType.Path && @@ -71,7 +106,8 @@ export default class TowerManager { this.towers.push(tower); this.ToggleChoosingTowerLocation('RESET'); this.selectedTower = null; - Engine.GameScene.events.emit(TowerEvents.TowerPlacedEvent, definition.name); + this.previewSprite.x = -100; + Engine.GameScene.events.emit(TowerEvents.TowerPlacedEvent, definition.name, row, column); } else { Engine.NotificationManager.Notify( 'Can not place tower on path or other tower, choose another spot.', diff --git a/src/classes/gui/Button.ts b/src/classes/gui/Button.ts index bf2ce64..05fb20a 100644 --- a/src/classes/gui/Button.ts +++ b/src/classes/gui/Button.ts @@ -14,6 +14,8 @@ export default class Button extends GuiObject { private buttonSprite: PIXI.NineSliceSprite; private buttonText: PIXI.Text; + // Used for custom button logic. + public buttonIcon: PIXI.Sprite; setCaption(caption: string) { this.caption = caption; @@ -57,4 +59,8 @@ export default class Button extends GuiObject { this.container.x = this.bounds.x; this.container.y = this.bounds.y; } + + public CustomButtonLogic() { + console.warn(this.name + ' - Button.CustomButtonLogic() is not implemented.'); + } } diff --git a/src/classes/gui/TowerTab.ts b/src/classes/gui/TowerTab.ts index 9db2696..41773e3 100644 --- a/src/classes/gui/TowerTab.ts +++ b/src/classes/gui/TowerTab.ts @@ -37,11 +37,11 @@ class TowerButton extends GuiObject { Engine.GameScene.events.on(TowerEvents.TowerPlacedEvent, (name) => { this.frameSprite.tint = 0xffffff; // reset the tint after a tower has been placed }); - this.container.onmouseenter = (e) => { + this.container.onpointerenter = (e) => { // add on mouse over info (banner next to sidebar) }; - this.container.onmouseleave = (e) => {}; + this.container.onpointerleave = (e) => {}; } public onClick(e: PIXI.FederatedPointerEvent): void { if (this.frameSprite.tint == 0x00ff00) this.frameSprite.tint = 0xffffff; diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 930d890..be16af2 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -71,20 +71,29 @@ export class GameScene extends Scene { this.MissionStats.earnGold(playerAward); }); this.sidebar = new Sidebar(GameUIConstants.SidebarRect); - this.changeRoundButton = new Button( - GameUIConstants.ChangeRoundButtonRect, - 'Start', - ButtonTexture.Button01, - true - ); + this.changeRoundButton = new Button(GameUIConstants.ChangeRoundButtonRect, '', ButtonTexture.Button01, true); this.changeRoundButton.container.removeFromParent(); this.sidebar.container.addChild(this.changeRoundButton.container); + // Added custom button logic to still keep all the regular events for the button, just have an icon instead of text. + // TODO: maybe make this better? add like a seperate class for icon buttons or smth + this.changeRoundButton.CustomButtonLogic = () => { + this.changeRoundButton.buttonIcon = new PIXI.Sprite({ + texture: GameAssets.PlayIconTexture, + x: this.changeRoundButton.container.width / 2, + y: this.changeRoundButton.container.height / 2, + scale: 0.2, + }); + this.changeRoundButton.buttonIcon.anchor.set(0.5, 0.5); + this.changeRoundButton.container.addChild(this.changeRoundButton.buttonIcon); + }; + this.changeRoundButton.CustomButtonLogic(); this.changeRoundButton.onClick = () => { if (this.playerWon) return this.ReturnToMain(); - if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'warn'); - this.changeRoundButton.setEnabled(false); - this.changeRoundButton.setCaption('WAVE IN PROGRESS'); + if (this.roundMode == RoundMode.Combat) + return Engine.NotificationManager.Notify('Wave is already in progress.', 'warn'); + if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'danger'); this.setRoundMode(RoundMode.Combat); + this.changeRoundButton.buttonIcon.texture = GameAssets.ExclamationIconTexture; this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`); }; @@ -103,9 +112,8 @@ export class GameScene extends Scene { Engine.TowerManager.update(elapsedMS); if (this.isWaveManagerFinished && Engine.Grid.creeps.length == 0) { this.isWaveManagerFinished = false; - this.changeRoundButton.setEnabled(true); - this.changeRoundButton.setCaption('Start'); this.setRoundMode(RoundMode.Purchase); + this.changeRoundButton.buttonIcon.texture = GameAssets.PlayIconTexture; Engine.NotificationManager.Notify( `Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`, 'info' @@ -115,7 +123,7 @@ export class GameScene extends Scene { } if (this.currentRound + 1 == this.mission.rounds.length) { Engine.NotificationManager.Notify(`Mission victory!!`, 'reward'); - this.changeRoundButton.setCaption('Return to menu'); + this.changeRoundButton.buttonIcon.texture = GameAssets.HomeIconTexture; this.playerWon = true; } else this.currentRound++; }