From 70218e5425d74f68e3abb46aacc1e64fe6547621 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Tue, 21 Jan 2025 01:09:29 +0100 Subject: [PATCH 01/10] tower panel begin --- package.json | 2 +- src/classes/Assets.ts | 2 + src/classes/game/Gem.ts | 1 + src/classes/game/Grid.ts | 3 +- src/classes/game/Tower.ts | 23 ++----- src/classes/game/TowerManager.ts | 2 +- src/classes/gui/SelectedTowerPanel.ts | 32 --------- src/classes/gui/TowerPanel.ts | 97 +++++++++++++++++++++++++++ src/classes/gui/TowerTab.ts | 1 + src/scenes/Game.ts | 3 + 10 files changed, 113 insertions(+), 53 deletions(-) create mode 100644 src/classes/game/Gem.ts delete mode 100644 src/classes/gui/SelectedTowerPanel.ts create mode 100644 src/classes/gui/TowerPanel.ts diff --git a/package.json b/package.json index 7693224..7ea34d0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "tsc && vite build", "preview": "vite preview" }, diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 85ff4cb..ef06319 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -28,6 +28,7 @@ export default class GameAssets { public static ExclamationIconTexture: PIXI.Texture; public static HomeIconTexture: PIXI.Texture; public static HammerIconTexture: PIXI.Texture; + public static XIconTexture: PIXI.Texture; public static GemAmountIcons: PIXI.Texture[] = []; public static Missions: MissionDefinition[]; @@ -99,6 +100,7 @@ export default class GameAssets { 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.Load('/assets/gui/icons/hammer.png').then((texture) => (this.HammerIconTexture = texture)), + this.Load('/assets/gui/icons/cross.png').then((texture) => (this.XIconTexture = texture)), this.LoadMissions(), this.LoadTowers(), this.LoadCreeps(), diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts new file mode 100644 index 0000000..4a51259 --- /dev/null +++ b/src/classes/game/Gem.ts @@ -0,0 +1 @@ +export default class Gem {} diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 81a09d9..3706226 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -38,7 +38,6 @@ export class Cell extends GameObject { zIndex: 99, interactive: true, }); - // ? TODO: make range preview 1 global graphics obj, child. fix this.g = new PIXI.Graphics({ zIndex: 5, @@ -83,6 +82,8 @@ export class Cell extends GameObject { } public OpenSelectedTowerPanel() { if (this.hasTowerPlaced) { + const tower = Engine.TowerManager.GetTowerByRowAndCol(this.row, this.column); + Engine.GameScene.towerPanel.Show(tower); } } public checkIfCantPlace() { diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index 9bbd0c3..8e58ed0 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -5,28 +5,13 @@ import { TowerDefinition } from '../Definitions'; import { Cell } from './Grid'; import { TowerBehaviours } from './TowerManager'; import Projectile, { calculateAngleToPoint } from './Projectile'; -import GameAssets from '../Assets'; import Creep from './Creep'; +import Gem from './Gem'; function distance(x1, y1, x2, y2) { return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); } -export type TowerInstance = { - row: number; - column: number; - sprite: PIXI.Sprite; - projectiles: Array; - baseDamage: number; - damage: number; - cooldown: number; - ticksToFireAt: number; - slottedGems: Array; - cost: number; - baseRange: number; - range: number; -}; - export enum TowerEvents { TowerPlacedEvent = 'towerPlacedEvent', TowerSoldEvent = 'towerSoldEvent', @@ -35,9 +20,11 @@ export enum TowerEvents { export class Tower extends GameObject { public row: number; public column: number; + public definition: TowerDefinition; + public slottedGems: Array; + public damageDealt: number = 0; private projectiles: Projectile[] = []; private behaviour: string; - private definition: TowerDefinition; private sprite: PIXI.Sprite; private ticksUntilNextShot: number; private graphics: PIXI.Graphics = new PIXI.Graphics(); @@ -51,7 +38,6 @@ export class Tower extends GameObject { this.definition = definition; this.ticksUntilNextShot = 0; this.parent = Engine.Grid.getCellByRowAndCol(row, column); - console.log(texture); this.sprite = new PIXI.Sprite({ texture: texture, height: Engine.GridCellSize, @@ -97,6 +83,7 @@ export class Tower extends GameObject { public update(elapsedMS: any): void { this.projectiles.forEach((proj) => { if (proj.deleteMe) { + this.damageDealt += this.definition.stats.damage; this.projectiles.splice(this.projectiles.indexOf(proj), 1); proj = null; } else proj.update(elapsedMS); diff --git a/src/classes/game/TowerManager.ts b/src/classes/game/TowerManager.ts index 7267015..d58ff41 100644 --- a/src/classes/game/TowerManager.ts +++ b/src/classes/game/TowerManager.ts @@ -71,7 +71,7 @@ export default class TowerManager { 'TowerManager.selectedTower is null when trying to place tower.', 'danger' ); - throw console.warn('TowerManager.selectedTower is null when trying to place tower.'); + return console.warn('TowerManager.selectedTower is null when trying to place tower.'); } this.PlaceTower(this.selectedTower, row, column, this.selectedTower.behaviour); } diff --git a/src/classes/gui/SelectedTowerPanel.ts b/src/classes/gui/SelectedTowerPanel.ts deleted file mode 100644 index 322f407..0000000 --- a/src/classes/gui/SelectedTowerPanel.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as PIXI from 'pixi.js'; -import GuiObject from '../GuiObject'; -import GameAssets from '../Assets'; -import { Engine } from '../Bastion'; - -// ! TODO NEXT! - -export default class SelectedTowerPanel extends GuiObject { - private bounds: PIXI.Rectangle; - private towerPanel: PIXI.NineSliceSprite; - - constructor(bounds: PIXI.Rectangle) { - super(false); - this.bounds = bounds; - this.container.x = this.bounds.x; - this.container.y = this.bounds.y; - this.towerPanel = new PIXI.NineSliceSprite({ - texture: GameAssets.FrameTowerTab, - leftWidth: 1000, - topHeight: 1000, - rightWidth: 1000, - bottomHeight: 1000, - }); - this.towerPanel.x = -300; - this.towerPanel.y = -300; - this.towerPanel.width = this.bounds.width; - this.towerPanel.height = this.bounds.height; - - this.container.addChild(this.towerPanel); - Engine.GameMaster.currentScene.stage.addChild(this.container); - } -} diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts new file mode 100644 index 0000000..53e2b5a --- /dev/null +++ b/src/classes/gui/TowerPanel.ts @@ -0,0 +1,97 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import GameAssets from '../Assets'; +import { Engine } from '../Bastion'; +import GameUIConstants from '../GameUIConstants'; +import Button, { ButtonTexture } from './Button'; +import { Tower } from '../game/Tower'; + +export default class TowerPanel extends GuiObject { + private bounds: PIXI.Rectangle; + private towerPanel: PIXI.NineSliceSprite; + private closeBtn: Button; + public isShown: boolean = false; + titleText: PIXI.Text; + + constructor(bounds: PIXI.Rectangle) { + super(false); + this.bounds = bounds; + this.towerPanel = new PIXI.NineSliceSprite({ + texture: GameAssets.Frame03Texture, + leftWidth: 100, + topHeight: 100, + rightWidth: 100, + bottomHeight: 100, + }); + this.towerPanel.width = this.bounds.width; + this.towerPanel.height = this.bounds.height - this.bounds.height / 3.5; + this.closeBtn = new Button(new PIXI.Rectangle(-20, -20, 60, 60), '', ButtonTexture.Button01, true); + this.closeBtn.container.removeFromParent(); + // 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.closeBtn.CustomButtonLogic = () => { + this.closeBtn.buttonIcon = new PIXI.Sprite({ + texture: GameAssets.XIconTexture, + x: this.closeBtn.container.width / 2, + y: this.closeBtn.container.height / 2, + scale: 0.2, + }); + this.closeBtn.buttonIcon.anchor.set(0.5, 0.5); + this.closeBtn.container.addChild(this.closeBtn.buttonIcon); + }; + this.closeBtn.onClick = () => { + this.Hide(); + }; + this.Hide(); + this.closeBtn.CustomButtonLogic(); + this.container.y = Engine.app.canvas.height / 2 - Engine.app.canvas.height / 2.7; + this.container.addChild(this.towerPanel); + this.container.addChild(this.closeBtn.container); + Engine.GameMaster.currentScene.stage.addChild(this.container); + + this.titleText = new PIXI.Text({ + x: this.bounds.width / 3, + y: 50, + zIndex: 5, + style: new PIXI.TextStyle({ + fill: 0xffffff, + stroke: { + color: 0x000000, + width: 2, + }, + }), + }); + this.titleText.anchor.set(0.5, 0); + this.container.addChild(this.titleText); + } + public Show(tower: Tower) { + let mouseX = Engine.MouseX; + this.isShown = true; + this.SetContent(tower); + if (mouseX < 900) { + this.ShowRight(); + } else { + this.ShowLeft(); + } + } + private SetContent(tower: Tower) { + this.titleText.text = tower.definition.name; + } + private ShowLeft() { + this.towerPanel.x = -100; + this.container.x = 0; + this.container.alpha = 1; + this.closeBtn.container.x = this.container.width - 150; + } + private ShowRight() { + this.towerPanel.x = -10; + this.container.x = GameUIConstants.SidebarRect.x - 210; + this.closeBtn.container.x = -20; + this.container.alpha = 1; + } + public Hide() { + this.isShown = false; + this.container.alpha = 0; + this.container.x = GameUIConstants.SidebarRect.x + 10; + } +} diff --git a/src/classes/gui/TowerTab.ts b/src/classes/gui/TowerTab.ts index e5b2901..81c2056 100644 --- a/src/classes/gui/TowerTab.ts +++ b/src/classes/gui/TowerTab.ts @@ -76,6 +76,7 @@ class TowerButton extends GuiObject { Engine.GameScene.sidebar.towerTab.resetTint(); Engine.TowerManager.ResetChooseTower(); } + Engine.GameScene.towerPanel.Hide(); Engine.GameScene.tooltip.Hide(); if (this.frameSprite.tint == 0x00ff00) { this.frameSprite.tint = 0xffffff; diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index e313420..edc0708 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -13,6 +13,7 @@ import TowerManager from '../classes/game/TowerManager'; import { MissionPickerScene } from './MissionPicker'; import GameUIConstants from '../classes/GameUIConstants'; import Tooltip from '../classes/gui/Tooltip'; +import TowerPanel from '../classes/gui/TowerPanel'; enum RoundMode { Purchase = 0, @@ -29,6 +30,7 @@ export class GameScene extends Scene { public changeRoundButton: Button; public sidebar: Sidebar; public tooltip: Tooltip; + public towerPanel: TowerPanel; private currentRound: number = 0; private isWaveManagerFinished: boolean = false; private playerWon: boolean = false; @@ -72,6 +74,7 @@ export class GameScene extends Scene { this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => { this.MissionStats.earnGold(playerAward); }); + this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect); this.sidebar = new Sidebar(GameUIConstants.SidebarRect); this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160)); this.changeRoundButton = new Button(GameUIConstants.ChangeRoundButtonRect, '', ButtonTexture.Button01, true); From 2647d8cfd21f8b741b1bcc72316b41c9e28208fa Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:32:57 +0100 Subject: [PATCH 02/10] begin inventory + gems --- public/assets/gems/fire/1.png | Bin 0 -> 10604 bytes public/assets/gems/fire/2.png | Bin 0 -> 7011 bytes public/assets/json/Gems.json | 0 src/classes/Assets.ts | 2 ++ src/classes/Definitions.ts | 13 +++++----- src/classes/game/Gem.ts | 10 +++++++- src/classes/game/Grid.ts | 2 ++ src/classes/game/Inventory.ts | 5 ++++ src/classes/gui/Tooltip.ts | 2 +- src/classes/gui/TowerPanel.ts | 47 ++++++++++++++++++++++++++++++++-- 10 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 public/assets/gems/fire/1.png create mode 100644 public/assets/gems/fire/2.png create mode 100644 public/assets/json/Gems.json create mode 100644 src/classes/game/Inventory.ts diff --git a/public/assets/gems/fire/1.png b/public/assets/gems/fire/1.png new file mode 100644 index 0000000000000000000000000000000000000000..64c4c3e11cbb200b7584c619636dfe7503400210 GIT binary patch literal 10604 zcmV-yDU;TTP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 BrXv6V literal 0 HcmV?d00001 diff --git a/public/assets/json/Gems.json b/public/assets/json/Gems.json new file mode 100644 index 0000000..e69de29 diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index ef06319..2579e29 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -29,6 +29,7 @@ export default class GameAssets { public static HomeIconTexture: PIXI.Texture; public static HammerIconTexture: PIXI.Texture; public static XIconTexture: PIXI.Texture; + public static PlusIconTexture: PIXI.Texture; public static GemAmountIcons: PIXI.Texture[] = []; public static Missions: MissionDefinition[]; @@ -101,6 +102,7 @@ export default class GameAssets { 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)), + this.Load('/assets/gui/icons/plus.png').then((texture) => (this.PlusIconTexture = texture)), this.LoadMissions(), this.LoadTowers(), this.LoadCreeps(), diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index da4701d..e3ab53e 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -72,18 +72,19 @@ export type TowerStatsDefinition = { export type PathDefinition = [[row: number, column: number]]; -export enum CreepType { - Basic = 0, - Quick = 1, - Tank = 2, -} - export enum TerrainType { Restricted = 0, Buildable = 1, Path = 9, } +// Make sure to sync these with the respective JSON files. +export enum CreepType { + Basic = 0, + Quick = 1, + Tank = 2, +} + export enum GemType { Fire = 0, Yeti = 1, diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index 4a51259..ec292ec 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -1 +1,9 @@ -export default class Gem {} +import * as PIXI from 'pixi.js'; +import { GemType } from '../Definitions'; +export default class Gem { + public texture: PIXI.Texture; + public type: GemType; + public level: number = 1; + // TODO: create and load from Gems.json and also load gem textures + constructor(gemType) {} +} diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 3706226..8169204 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -84,6 +84,8 @@ export class Cell extends GameObject { if (this.hasTowerPlaced) { const tower = Engine.TowerManager.GetTowerByRowAndCol(this.row, this.column); Engine.GameScene.towerPanel.Show(tower); + } else { + // TODO: hide the sidepanel somehow } } public checkIfCantPlace() { diff --git a/src/classes/game/Inventory.ts b/src/classes/game/Inventory.ts new file mode 100644 index 0000000..d360f8e --- /dev/null +++ b/src/classes/game/Inventory.ts @@ -0,0 +1,5 @@ +import GameObject from '../GameObject'; + +export default class Inventory extends GameObject { + public update() {} +} diff --git a/src/classes/gui/Tooltip.ts b/src/classes/gui/Tooltip.ts index 763176a..93d6558 100644 --- a/src/classes/gui/Tooltip.ts +++ b/src/classes/gui/Tooltip.ts @@ -105,7 +105,7 @@ export default class Tooltip extends GuiObject { }); this.gemAmount = new PIXI.Text({ x: 54, - y: 108, + y: 105, zIndex: 5, text: 'Something went wrong if you see this.', style: { diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index 53e2b5a..3038fb6 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -5,13 +5,48 @@ import { Engine } from '../Bastion'; import GameUIConstants from '../GameUIConstants'; import Button, { ButtonTexture } from './Button'; import { Tower } from '../game/Tower'; +import Gem from '../game/Gem'; + +class VisualGemSlot extends GuiObject { + private background: PIXI.Sprite; + private iconSprite: PIXI.Sprite; + private i: number = 0; + constructor(index: number, parent: PIXI.Container, gem: Gem | null) { + super(true); + let gtexture; + if (gem == null) { + gtexture = GameAssets.PlusIconTexture; + } else { + gtexture = gem.texture; + } + this.container.x = 10; + this.container.y = index * Engine.GridCellSize + 300; + this.background = new PIXI.Sprite({ + texture: GameAssets.FrameInventory, + }); + this.iconSprite = new PIXI.Sprite({ + texture: gtexture, + }); + this.background.width = Engine.GridCellSize; + this.background.height = Engine.GridCellSize; + this.iconSprite.x = Engine.GridCellSize / 2; + this.iconSprite.y = Engine.GridCellSize / 2; + this.iconSprite.width = Engine.GridCellSize / 2; + this.iconSprite.height = Engine.GridCellSize / 2; + this.iconSprite.anchor.set(0.5, 0.5); + this.container.addChild(this.background); + this.container.addChild(this.iconSprite); + parent.addChild(this.container); + } + public onClick(e: PIXI.FederatedPointerEvent): void {} +} export default class TowerPanel extends GuiObject { private bounds: PIXI.Rectangle; private towerPanel: PIXI.NineSliceSprite; private closeBtn: Button; public isShown: boolean = false; - titleText: PIXI.Text; + public titleText: PIXI.Text; constructor(bounds: PIXI.Rectangle) { super(false); @@ -64,10 +99,18 @@ export default class TowerPanel extends GuiObject { this.titleText.anchor.set(0.5, 0); this.container.addChild(this.titleText); } + private MakeSlots(tower: Tower) { + let amount = tower.definition.stats.gemSlotsAmount; + amount = 6; + for (let i = 0; i < amount; i++) { + const element = new VisualGemSlot(i, this.container, null); + } + } public Show(tower: Tower) { let mouseX = Engine.MouseX; this.isShown = true; this.SetContent(tower); + this.MakeSlots(tower); if (mouseX < 900) { this.ShowRight(); } else { @@ -81,7 +124,7 @@ export default class TowerPanel extends GuiObject { this.towerPanel.x = -100; this.container.x = 0; this.container.alpha = 1; - this.closeBtn.container.x = this.container.width - 150; + this.closeBtn.container.x = this.bounds.width - 150; } private ShowRight() { this.towerPanel.x = -10; From c57e84633b383ed34f7e2d96551e9c0bffc723de Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:38:58 +0100 Subject: [PATCH 03/10] add gems to gameassets --- docs/content.md | 24 +++++++ public/assets/gems/{fire => Fire}/1.png | Bin public/assets/gems/{fire => Fire}/2.png | Bin public/assets/json/Gems.json | 26 +++++++ src/classes/Assets.ts | 86 ++++++++++++++---------- src/classes/Definitions.ts | 18 +++++ src/classes/gui/TowerPanel.ts | 2 +- 7 files changed, 118 insertions(+), 38 deletions(-) create mode 100644 docs/content.md rename public/assets/gems/{fire => Fire}/1.png (100%) rename public/assets/gems/{fire => Fire}/2.png (100%) diff --git a/docs/content.md b/docs/content.md new file mode 100644 index 0000000..25762c4 --- /dev/null +++ b/docs/content.md @@ -0,0 +1,24 @@ +# How to add new content to the game + +A small guide so other people also understand how to add content. + +## Tower + +1. Update Towers.json by adding to the end of the array. +2. Update TowerType in Defintions.ts +3. Based of the Tower.sprite value, add projectile folder with appropriate projectiles as .png. +4. Based of the Tower.sprite value, add the tower sprite into towers folder as a .png. +5. Add appropriate behaviour in Tower.ts (if statement in update). +6. Add way to spawn via TowerTab.ts button. + +## Creep + +1. Update Creeps.json by adding to the end of the array. +2. Update CreepType in Defintions.ts +3. Based of the Creep.name value, add creep's walking animations to the same named subfolder in creeps folder. +4. When using creeps in waves, reference them by their index in the CreepType enum. + +## Gem + +1. Update Gems.json by adding to the end of the array. +2. Update GemType in Defintions.ts and make sure Gem.type is CaSe sensitively the same. diff --git a/public/assets/gems/fire/1.png b/public/assets/gems/Fire/1.png similarity index 100% rename from public/assets/gems/fire/1.png rename to public/assets/gems/Fire/1.png diff --git a/public/assets/gems/fire/2.png b/public/assets/gems/Fire/2.png similarity index 100% rename from public/assets/gems/fire/2.png rename to public/assets/gems/Fire/2.png diff --git a/public/assets/json/Gems.json b/public/assets/json/Gems.json index e69de29..094e1bc 100644 --- a/public/assets/json/Gems.json +++ b/public/assets/json/Gems.json @@ -0,0 +1,26 @@ +[ + { + "name": "Fire Gem", + "type": "Fire", + "totalLevels": 2, + "textures": [], + "cantCombineWith": [], + "specialCombine": ["Yeti"], + "genericImprovements": [ + { + "damageUp": 2, + "attackSpeedUp": 100, + "rangeUp": 0.5, + "timeToLiveUp": 0, + "pierceUp": 1 + }, + { + "damageUp": 2, + "attackSpeedUp": 100, + "rangeUp": 0.5, + "timeToLiveUp": 0, + "pierceUp": 1 + } + ] + } +] diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 2579e29..3085c6e 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -1,5 +1,5 @@ import * as PIXI from 'pixi.js'; -import { CreepDefinition, MissionDefinition, TowerDefinition } from './Definitions'; +import { CreepDefinition, GemDefinition, MissionDefinition, TowerDefinition } from './Definitions'; import { Engine } from './Bastion'; export default class GameAssets { @@ -36,6 +36,7 @@ export default class GameAssets { public static MissionBackgrounds: PIXI.Texture[] = []; public static Towers: TowerDefinition[]; public static Creeps: CreepDefinition[]; + public static Gems: GemDefinition[]; private static text; private static async Load(src) { @@ -76,57 +77,68 @@ export default class GameAssets { Engine.app.stage.addChild(this.text); await Promise.all([ - 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)), - this.Load('/assets/gui/frame_04.png').then((texture) => (this.Frame04Texture = texture)), - this.Load('/assets/gui/frame_inv.png').then((texture) => (this.FrameInventory = texture)), - this.Load('/assets/gui/background_01.png').then((texture) => (this.FrameBackground = texture)), - this.Load('/assets/gui/background_02.png').then((texture) => (this.FrameTowerTab = texture)), - this.Load('/assets/gui/frame_violet.png').then((texture) => (this.VioletBackground = texture)), - this.Load('/assets/gui/frame_red.png').then((texture) => (this.RedBackground = texture)), - this.Load('/assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)), - this.Load('/assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = 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)), - this.Load('/assets/gui/sword_02.png').then((texture) => (this.SwordsTexture = texture)), - 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/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)), - this.Load('/assets/gui/icons/plus.png').then((texture) => (this.PlusIconTexture = texture)), + 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)), + this.Load('./assets/gui/frame_04.png').then((texture) => (this.Frame04Texture = texture)), + this.Load('./assets/gui/frame_inv.png').then((texture) => (this.FrameInventory = texture)), + this.Load('./assets/gui/background_01.png').then((texture) => (this.FrameBackground = texture)), + this.Load('./assets/gui/background_02.png').then((texture) => (this.FrameTowerTab = texture)), + this.Load('./assets/gui/frame_violet.png').then((texture) => (this.VioletBackground = texture)), + this.Load('./assets/gui/frame_red.png').then((texture) => (this.RedBackground = texture)), + this.Load('./assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)), + this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = 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)), + this.Load('./assets/gui/sword_02.png').then((texture) => (this.SwordsTexture = texture)), + 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/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)), + this.Load('./assets/gui/icons/plus.png').then((texture) => (this.PlusIconTexture = texture)), this.LoadMissions(), this.LoadTowers(), this.LoadCreeps(), - this.LoadGemIcons(), + this.LoadGems(), ]); t.destroy(); this.text.destroy(); // Set this.text = true to disallow calling GameAssets.LoadAssets() again this.text = true; } - private static async LoadGemIcons() { + private static async LoadGems() { + const res = await fetch('./assets/json/Gems.json'); + const gems = await res.json(); + this.Gems = gems; + + for (let idx = 0; idx < gems.length; idx++) { + const gem = this.Gems[idx]; + for (let i = 1; i <= gem.totalLevels; i++) { + const texture = await this.Load(`./assets/gems/${gem.type}/${i}.png`); + gem.textures[i] = texture; + } + } for (let i = 0; i < 7; i++) { - this.GemAmountIcons[i] = await this.Load(`/assets/gui/gem_amount_${i}.png`); + this.GemAmountIcons[i] = await this.Load(`./assets/gui/gem_amount_${i}.png`); } } private static async LoadCreeps() { - const res = await fetch('/assets/json/Creeps.json'); + const res = await fetch('./assets/json/Creeps.json'); const creeps = await res.json(); this.Creeps = creeps; for (let idx = 0; idx < this.Creeps.length; idx++) { const creep = this.Creeps[idx]; for (let i = 0; i < creep.textureArrayLength; i++) { - const texture = await this.Load(`/assets/creeps/${creep.name}/${i}.png`); + const texture = await this.Load(`./assets/creeps/${creep.name}/${i}.png`); creep.textures[i] = texture; } } @@ -134,20 +146,20 @@ export default class GameAssets { private static async LoadMissions() { // When adding missions, make sure to keep order. - GameAssets.Missions = [await this.LoadMission('/assets/missions/mission_01.json')]; + GameAssets.Missions = [await this.LoadMission('./assets/missions/mission_01.json')]; } private static async LoadTowers() { - const res = await fetch('/assets/json/Towers.json'); + const res = await fetch('./assets/json/Towers.json'); const towers = await res.json(); this.Towers = towers; for (let idx = 0; idx < this.Towers.length; idx++) { const tower = this.Towers[idx]; for (let i = 0; i < tower.projectileTexturesArrayLength; i++) { - const projTexture = await this.Load(`/assets/projectiles/${tower.sprite}/${i}.png`); + const projTexture = await this.Load(`./assets/projectiles/${tower.sprite}/${i}.png`); tower.projectileTextures[i] = projTexture; } - tower.texture = await this.Load(`/assets/towers/${tower.sprite}.png`); + tower.texture = await this.Load(`./assets/towers/${tower.sprite}.png`); } } diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index e3ab53e..4732c34 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -70,6 +70,24 @@ export type TowerStatsDefinition = { range: number; }; +export type GemDefinition = { + name: string; + type: GemType; + totalLevels: number; + textures: PIXI.Texture[]; + cantCombineWith: GemType[]; + specialCombine: GemType[]; + genericImprovements: GenericGemImprovement[]; +}; + +export type GenericGemImprovement = { + damageUp: number; + attackSpeedUp: number; + rangeUp: number; + timeToLiveUp: number; + pieceUp: number; +}; + export type PathDefinition = [[row: number, column: number]]; export enum TerrainType { diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index 3038fb6..20ac007 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -8,8 +8,8 @@ import { Tower } from '../game/Tower'; import Gem from '../game/Gem'; class VisualGemSlot extends GuiObject { + public iconSprite: PIXI.Sprite; private background: PIXI.Sprite; - private iconSprite: PIXI.Sprite; private i: number = 0; constructor(index: number, parent: PIXI.Container, gem: Gem | null) { super(true); From 2f3bb621c7167d5d379ddbde5116c1eaddb57401 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:10:58 +0100 Subject: [PATCH 04/10] i have to think more about it --- public/assets/gui/{m_frame.png => gem_frame.png} | Bin public/assets/missions/mission_01.json | 2 +- src/classes/Assets.ts | 2 ++ src/classes/Definitions.ts | 2 +- src/classes/game/Gem.ts | 6 ++++-- src/classes/game/Grid.ts | 6 ++++-- src/scenes/Game.ts | 12 ++++++++++-- 7 files changed, 22 insertions(+), 8 deletions(-) rename public/assets/gui/{m_frame.png => gem_frame.png} (100%) diff --git a/public/assets/gui/m_frame.png b/public/assets/gui/gem_frame.png similarity index 100% rename from public/assets/gui/m_frame.png rename to public/assets/gui/gem_frame.png diff --git a/public/assets/missions/mission_01.json b/public/assets/missions/mission_01.json index 496c2b2..13567fd 100644 --- a/public/assets/missions/mission_01.json +++ b/public/assets/missions/mission_01.json @@ -102,7 +102,7 @@ "creeps": [0, 0, 0, 0, 0] } ], - "offeredGems": [0, 1, 2, 3] + "offeredGems": [0, 0, 0, 0] }, { "waves": [ diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 3085c6e..d4bb42a 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -22,6 +22,7 @@ export default class GameAssets { public static WaveTexture: PIXI.Texture; public static SwordsTexture: PIXI.Texture; public static TitleTexture: PIXI.Texture; + public static GemFrame: PIXI.Texture; public static PlayIconTexture: PIXI.Texture; public static PauseIconTexture: PIXI.Texture; @@ -92,6 +93,7 @@ export default class GameAssets { this.Load('./assets/gui/frame_red.png').then((texture) => (this.RedBackground = texture)), this.Load('./assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)), this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)), + this.Load('./assets/gui/gem_frame.png').then((texture) => (this.GemFrame = 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)), diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index 4732c34..4985101 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -85,7 +85,7 @@ export type GenericGemImprovement = { attackSpeedUp: number; rangeUp: number; timeToLiveUp: number; - pieceUp: number; + pierceUp: number; }; export type PathDefinition = [[row: number, column: number]]; diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index ec292ec..b3877ed 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -1,9 +1,11 @@ import * as PIXI from 'pixi.js'; import { GemType } from '../Definitions'; +import GameAssets from '../Assets'; export default class Gem { public texture: PIXI.Texture; public type: GemType; public level: number = 1; - // TODO: create and load from Gems.json and also load gem textures - constructor(gemType) {} + constructor(gemType: GemType) { + this.texture = GameAssets.Gems[gemType].textures[0]; + } } diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 8169204..2cb94f5 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -47,13 +47,16 @@ export class Cell extends GameObject { this.container.addChild(this.clickDetector); this.container.addChild(this.g); this.clickDetector.on('pointerup', (e) => { + if (!Engine.Grid.gridInteractionEnabled) return; if (Engine.TowerManager.isPlacingTower) Engine.Grid.onGridCellClicked(row, column); else this.OpenSelectedTowerPanel(); }); this.clickDetector.on('pointerenter', (e) => { + if (!Engine.Grid.gridInteractionEnabled) return; Engine.GameScene.events.emit(GridEvents.CellMouseOver, this); }); this.clickDetector.on('pointerleave', (e) => { + if (!Engine.Grid.gridInteractionEnabled) return; Engine.GameScene.events.emit(GridEvents.CellMouseLeave, this); Engine.Grid.rangePreview.clear(); }); @@ -84,8 +87,6 @@ export class Cell extends GameObject { if (this.hasTowerPlaced) { const tower = Engine.TowerManager.GetTowerByRowAndCol(this.row, this.column); Engine.GameScene.towerPanel.Show(tower); - } else { - // TODO: hide the sidepanel somehow } } public checkIfCantPlace() { @@ -117,6 +118,7 @@ export class Grid extends GameObject { public rangePreview: PIXI.Graphics; public creeps: Creep[] = []; public gridShown: boolean = false; + public gridInteractionEnabled = true; constructor(map: GameMapDefinition, missionIndex) { super(); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index edc0708..7cf6f4e 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -124,14 +124,17 @@ export class GameScene extends Scene { `Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`, 'info' ); - if (this.currentRound == this.mission.rounds.length) { + if (this.currentRound + 2 == this.mission.rounds.length) { Engine.NotificationManager.Notify(`Final round.`, 'danger'); } if (this.currentRound + 1 == this.mission.rounds.length) { Engine.NotificationManager.Notify(`Mission victory!!`, 'reward'); this.changeRoundButton.buttonIcon.texture = GameAssets.HomeIconTexture; this.playerWon = true; - } else this.currentRound++; + } else { + this.currentRound++; + this.OfferPlayerGems(); + } } if (this.MissionStats.getHP() <= 0) { @@ -142,6 +145,11 @@ export class GameScene extends Scene { this.ShowScoreScreen(false); } } + private OfferPlayerGems() { + Engine.Grid.gridInteractionEnabled = false; + + Engine.Grid.gridInteractionEnabled = true; + } private ShowScoreScreen(lost) { // TODO: show to player for real From 5ea6017547a249c695b379f2e675902f313e2643 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:26:29 +0100 Subject: [PATCH 05/10] get gems when finishing rounds --- docs/todos.md | 6 +- public/assets/json/Creeps.json | 2 +- public/assets/json/Gems.json | 19 +++++- src/classes/Assets.ts | 2 +- src/classes/Definitions.ts | 2 + src/classes/game/Gem.ts | 7 +- src/classes/game/NotificationManager.ts | 4 +- src/classes/game/Tower.ts | 5 +- src/classes/gui/Tooltip.ts | 62 +++++++++++++++--- src/classes/gui/TowerPanel.ts | 32 ++++++---- src/classes/gui/TowerTab.ts | 6 +- src/scenes/Game.ts | 85 ++++++++++++++++++++----- 12 files changed, 182 insertions(+), 50 deletions(-) diff --git a/docs/todos.md b/docs/todos.md index 5db8075..e0941e0 100644 --- a/docs/todos.md +++ b/docs/todos.md @@ -11,16 +11,16 @@ List of things to implement following the "release" of the minimum viable produc ## Towers -- [ ] Extend projectile into seperate defintion + json file +- [x] Extend projectile into seperate defintion + json file - [ ] Make tower react with slotted gems - [ ] Alter damage based on attunement from slotted gems -- [ ] Tower info on click +- [x] Tower info on click - [x] Animate projectiles - [x] Better mouseover tracking when placing tower and showing radius ## Gems -- [ ] Create Gem definitions +- [x] Create Gem definitions - [ ] Make gems affect towers ## Other diff --git a/public/assets/json/Creeps.json b/public/assets/json/Creeps.json index 2a8eff8..0679b07 100644 --- a/public/assets/json/Creeps.json +++ b/public/assets/json/Creeps.json @@ -5,7 +5,7 @@ "textureArrayLength": 12, "stats": { "health": 5, - "speed": 2.4, + "speed": 6, "special": null, "resistance": { "physical": 0, diff --git a/public/assets/json/Gems.json b/public/assets/json/Gems.json index 094e1bc..1177329 100644 --- a/public/assets/json/Gems.json +++ b/public/assets/json/Gems.json @@ -1,11 +1,12 @@ [ { "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.", "type": "Fire", "totalLevels": 2, "textures": [], "cantCombineWith": [], - "specialCombine": ["Yeti"], + "specialCombine": [], "genericImprovements": [ { "damageUp": 2, @@ -21,6 +22,22 @@ "timeToLiveUp": 0, "pierceUp": 1 } + ], + "gemResistanceModifications": [ + { + "physical": 0, + "divine": 0, + "fire": 0.5, + "ice": 0, + "frostfire": 0 + }, + { + "physical": 0, + "divine": 0, + "fire": 0.5, + "ice": 0, + "frostfire": 0 + } ] } ] diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index d4bb42a..9a92fd3 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -125,7 +125,7 @@ export default class GameAssets { const gem = this.Gems[idx]; for (let i = 1; i <= gem.totalLevels; i++) { const texture = await this.Load(`./assets/gems/${gem.type}/${i}.png`); - gem.textures[i] = texture; + gem.textures[i - 1] = texture; } } for (let i = 0; i < 7; i++) { diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index 4985101..4bee02e 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -72,12 +72,14 @@ export type TowerStatsDefinition = { export type GemDefinition = { name: string; + description: string; type: GemType; totalLevels: number; textures: PIXI.Texture[]; cantCombineWith: GemType[]; specialCombine: GemType[]; genericImprovements: GenericGemImprovement[]; + gemResistanceModifications: CreepResistancesDefinition[]; }; export type GenericGemImprovement = { diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index b3877ed..b9ccea7 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -1,11 +1,12 @@ import * as PIXI from 'pixi.js'; -import { GemType } from '../Definitions'; +import { GemType, GemDefinition } from '../Definitions'; import GameAssets from '../Assets'; export default class Gem { public texture: PIXI.Texture; - public type: GemType; public level: number = 1; + public gemDefinition: GemDefinition; constructor(gemType: GemType) { - this.texture = GameAssets.Gems[gemType].textures[0]; + this.gemDefinition = GameAssets.Gems[gemType]; + this.texture = this.gemDefinition.textures[0]; } } diff --git a/src/classes/game/NotificationManager.ts b/src/classes/game/NotificationManager.ts index 1aa5aeb..df86263 100644 --- a/src/classes/game/NotificationManager.ts +++ b/src/classes/game/NotificationManager.ts @@ -3,7 +3,7 @@ import GameObject from '../GameObject'; import * as PIXI from 'pixi.js'; import { FadeInOut } from './AnimationManager'; -export type NotificationType = 'info' | 'warn' | 'danger' | 'reward'; +export type NotificationType = 'info' | 'warn' | 'danger' | 'reward' | 'gemaward'; class Notification { public textObj: PIXI.Text; @@ -20,6 +20,8 @@ class Notification { fill = 0xfc0a0a; } else if (type == 'reward') { fill = 0xd65afc; + } else if (type == 'gemaward') { + fill = 0xffff00; } this.ticksToFadeAway = ticksToFadeAway; this.textObj = new PIXI.Text({ diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index 8e58ed0..dbe1237 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -42,7 +42,7 @@ export class Tower extends GameObject { texture: texture, height: Engine.GridCellSize, width: Engine.GridCellSize, - zIndex: 10, + zIndex: 130, }); this.container.addChild(this.sprite); this.parent.container.addChild(this.container); @@ -53,7 +53,8 @@ export class Tower extends GameObject { } private onParentCellEnter = (e) => { - if (!Engine.TowerManager.isPlacingTower) this.parent.showRangePreview(false, this.definition.stats.range); + if (!Engine.TowerManager.isPlacingTower && Engine.Grid.gridInteractionEnabled) + this.parent.showRangePreview(false, this.definition.stats.range); }; private onParentCellLeave = (e) => { diff --git a/src/classes/gui/Tooltip.ts b/src/classes/gui/Tooltip.ts index 93d6558..32eba83 100644 --- a/src/classes/gui/Tooltip.ts +++ b/src/classes/gui/Tooltip.ts @@ -2,6 +2,7 @@ import * as PIXI from 'pixi.js'; import GuiObject from '../GuiObject'; import GameAssets from '../Assets'; import { Engine } from '../Bastion'; +import Gem from '../game/Gem'; // ! TODO NEXT! @@ -14,12 +15,17 @@ export default class Tooltip extends GuiObject { private damageText: PIXI.Text; private gemAmount: PIXI.Text; private gemAmountSprite: PIXI.Sprite; + private title: PIXI.Sprite; + private costSprite: PIXI.Sprite; + private damageSprite: PIXI.Sprite; + private gemDescriptionText: PIXI.Text; constructor(bounds: PIXI.Rectangle) { super(false); this.bounds = bounds; this.container.x = -500; this.container.y = -500; + this.container.zIndex = 150; this.tooltipSprite = new PIXI.NineSliceSprite({ texture: GameAssets.Frame04Texture, leftWidth: 200, @@ -43,16 +49,16 @@ export default class Tooltip extends GuiObject { }), }); this.titleText.anchor.set(0.5, 0); - let title = new PIXI.Sprite({ + this.title = new PIXI.Sprite({ x: this.tooltipSprite.width / 2, y: -20, width: 250, height: 40, texture: GameAssets.TitleTexture, }); - title.anchor.set(0.5, 0); + this.title.anchor.set(0.5, 0); - const costSprite = new PIXI.Sprite({ + this.costSprite = new PIXI.Sprite({ texture: GameAssets.GoldTexture, x: 10, y: 20, @@ -88,7 +94,7 @@ export default class Tooltip extends GuiObject { }, }, }); - const damageSprite = new PIXI.Sprite({ + this.damageSprite = new PIXI.Sprite({ texture: GameAssets.SwordsTexture, x: 22, y: 70, @@ -117,26 +123,64 @@ export default class Tooltip extends GuiObject { }, }, }); + this.gemDescriptionText = new PIXI.Text({ + x: 10, + y: 20, + text: '', + style: { + fontSize: 18, + wordWrap: true, + wordWrapWidth: this.tooltipSprite.width - 30, + fill: 'white', + fontWeight: 'bold', + fontStyle: 'italic', + stroke: { + color: 0x000000, + width: 5, + }, + }, + }); this.container.addChild(this.tooltipSprite); - this.container.addChild(title); - this.container.addChild(costSprite); - this.container.addChild(damageSprite); + this.container.addChild(this.title); + this.container.addChild(this.costSprite); + this.container.addChild(this.damageSprite); this.container.addChild(this.gemAmountSprite); this.container.addChild(this.costText); this.container.addChild(this.titleText); this.container.addChild(this.damageText); this.container.addChild(this.gemAmount); - Engine.GameMaster.currentScene.stage.addChild(this.container); + this.container.addChild(this.gemDescriptionText); + Engine.app.stage.addChildAt(this.container, 0); } - public SetContent(title, damage: number, cost: number, gemSlotsAmount: number) { + public SetContentTower(title, damage: number, cost: number, gemSlotsAmount: number) { + this.costSprite.alpha = 1; + this.damageSprite.alpha = 1; + this.gemAmountSprite.alpha = 1; + this.costText.alpha = 1; + this.damageText.alpha = 1; + this.gemAmount.alpha = 1; + this.gemDescriptionText.alpha = 0; + this.titleText.text = title; this.gemAmount.text = `Has ${gemSlotsAmount} Gem slots.`; this.gemAmountSprite.texture = GameAssets.GemAmountIcons[gemSlotsAmount]; this.costText.text = `Costs ${cost} gold.`; this.damageText.text = `Deals ${damage} base damage.`; } + public SetContentGem(gem: Gem) { + this.costSprite.alpha = 0; + this.damageSprite.alpha = 0; + this.gemAmountSprite.alpha = 0; + this.costText.alpha = 0; + this.damageText.alpha = 0; + this.gemAmount.alpha = 0; + this.gemDescriptionText.alpha = 1; + + this.titleText.text = `Lv. ${gem.level} ` + gem.gemDefinition.name; + this.gemDescriptionText.text = gem.gemDefinition.description; + } public Show(x, y) { this.container.alpha = 1; if (x + this.container.width > Engine.app.canvas.width) { diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index 20ac007..3921902 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -7,33 +7,43 @@ import Button, { ButtonTexture } from './Button'; import { Tower } from '../game/Tower'; import Gem from '../game/Gem'; -class VisualGemSlot extends GuiObject { +export class VisualGemSlot extends GuiObject { public iconSprite: PIXI.Sprite; private background: PIXI.Sprite; + private gem: Gem; private i: number = 0; constructor(index: number, parent: PIXI.Container, gem: Gem | null) { super(true); let gtexture; - if (gem == null) { - gtexture = GameAssets.PlusIconTexture; - } else { - gtexture = gem.texture; - } this.container.x = 10; this.container.y = index * Engine.GridCellSize + 300; this.background = new PIXI.Sprite({ texture: GameAssets.FrameInventory, }); + this.gem = gem; + if (gem == null) { + gtexture = GameAssets.PlusIconTexture; + } else { + gtexture = gem.texture; + } this.iconSprite = new PIXI.Sprite({ texture: gtexture, + zIndex: 10, }); this.background.width = Engine.GridCellSize; this.background.height = Engine.GridCellSize; - this.iconSprite.x = Engine.GridCellSize / 2; - this.iconSprite.y = Engine.GridCellSize / 2; - this.iconSprite.width = Engine.GridCellSize / 2; - this.iconSprite.height = Engine.GridCellSize / 2; - this.iconSprite.anchor.set(0.5, 0.5); + if (gem == null) { + this.iconSprite.x = Engine.GridCellSize / 2; + this.iconSprite.y = Engine.GridCellSize / 2; + this.iconSprite.width = Engine.GridCellSize / 2; + this.iconSprite.height = Engine.GridCellSize / 2; + this.iconSprite.anchor.set(0.5, 0.5); + } else { + this.iconSprite.x = 4; + this.iconSprite.y = 4; + this.iconSprite.width = Engine.GridCellSize - 8; + this.iconSprite.height = Engine.GridCellSize - 8; + } this.container.addChild(this.background); this.container.addChild(this.iconSprite); parent.addChild(this.container); diff --git a/src/classes/gui/TowerTab.ts b/src/classes/gui/TowerTab.ts index 81c2056..2c131ff 100644 --- a/src/classes/gui/TowerTab.ts +++ b/src/classes/gui/TowerTab.ts @@ -47,7 +47,8 @@ class TowerButton extends GuiObject { Engine.GameScene.events.on(TowerEvents.TowerPlacedEvent, (name) => { this.resetTint(); }); - this.container.onmousemove = (e) => { + this.container.onpointermove = (e) => { + if (Engine.Grid.gridInteractionEnabled == false) return; if (Engine.TowerManager.isPlacingTower) return; this.ShowTooltip(); }; @@ -63,7 +64,7 @@ class TowerButton extends GuiObject { definition = item; } }); - Engine.GameScene.tooltip.SetContent( + Engine.GameScene.tooltip.SetContentTower( this.towerName, definition.stats.damage, definition.stats.cost, @@ -72,6 +73,7 @@ class TowerButton extends GuiObject { Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY); } public onClick(e: PIXI.FederatedPointerEvent): void { + if (Engine.Grid.gridInteractionEnabled == false) return; if (Engine.TowerManager.isPlacingTower && Engine.TowerManager.selectedTower.name != this.towerName) { Engine.GameScene.sidebar.towerTab.resetTint(); Engine.TowerManager.ResetChooseTower(); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 7cf6f4e..f6a3cfb 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -13,11 +13,13 @@ import TowerManager from '../classes/game/TowerManager'; import { MissionPickerScene } from './MissionPicker'; import GameUIConstants from '../classes/GameUIConstants'; import Tooltip from '../classes/gui/Tooltip'; -import TowerPanel from '../classes/gui/TowerPanel'; +import TowerPanel, { VisualGemSlot } from '../classes/gui/TowerPanel'; +import Gem from '../classes/game/Gem'; enum RoundMode { Purchase = 0, Combat = 1, + OfferingGems = 2, } export class GameScene extends Scene { @@ -31,6 +33,13 @@ export class GameScene extends Scene { public sidebar: Sidebar; public tooltip: Tooltip; public towerPanel: TowerPanel; + public dimGraphics: PIXI.Graphics = new PIXI.Graphics({ + x: 0, + y: 0, + zIndex: 120, + }); + private offerGemsSprite: PIXI.NineSliceSprite; + private visualGems: VisualGemSlot[] = []; private currentRound: number = 0; private isWaveManagerFinished: boolean = false; private playerWon: boolean = false; @@ -47,13 +56,6 @@ export class GameScene extends Scene { }); } public init() { - this.ticker = new PIXI.Ticker(); - this.ticker.maxFPS = 60; - this.ticker.minFPS = 30; - this.ticker.add(() => { - if (this.update) this.update(this.ticker.elapsedMS); - }); - this.ticker.start(); new Grid(this.mission.gameMap, this.missionIndex); new TowerManager(); new WaveManager(this.mission.rounds, this.mission.gameMap.paths); @@ -76,10 +78,11 @@ export class GameScene extends Scene { }); this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect); this.sidebar = new Sidebar(GameUIConstants.SidebarRect); - this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160)); this.changeRoundButton = new Button(GameUIConstants.ChangeRoundButtonRect, '', ButtonTexture.Button01, true); this.changeRoundButton.container.removeFromParent(); this.sidebar.container.addChild(this.changeRoundButton.container); + Engine.GameMaster.currentScene.stage.addChildAt(this.dimGraphics, 0); + this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160)); // 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 = () => { @@ -98,12 +101,19 @@ export class GameScene extends Scene { 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'); + if (this.roundMode == RoundMode.OfferingGems) return; this.setRoundMode(RoundMode.Combat); this.changeRoundButton.buttonIcon.texture = GameAssets.ExclamationIconTexture; this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`); }; - this.MissionStats = new MissionStats(100, 200); + this.ticker = new PIXI.Ticker(); + this.ticker.maxFPS = 60; + this.ticker.minFPS = 30; + this.ticker.add(() => { + if (this.update) this.update(this.ticker.elapsedMS); + }); + this.ticker.start(); } public update(elapsedMS) { if (this.isGameOver) { @@ -116,6 +126,7 @@ export class GameScene extends Scene { Engine.WaveManager.update(elapsedMS); Engine.Grid.update(elapsedMS); Engine.TowerManager.update(elapsedMS); + // Means the round is finished. if (this.isWaveManagerFinished && Engine.Grid.creeps.length == 0) { this.isWaveManagerFinished = false; this.setRoundMode(RoundMode.Purchase); @@ -124,16 +135,13 @@ export class GameScene extends Scene { `Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`, 'info' ); - if (this.currentRound + 2 == this.mission.rounds.length) { - Engine.NotificationManager.Notify(`Final round.`, 'danger'); - } if (this.currentRound + 1 == this.mission.rounds.length) { Engine.NotificationManager.Notify(`Mission victory!!`, 'reward'); this.changeRoundButton.buttonIcon.texture = GameAssets.HomeIconTexture; this.playerWon = true; } else { - this.currentRound++; this.OfferPlayerGems(); + this.currentRound++; } } @@ -145,14 +153,59 @@ export class GameScene extends Scene { this.ShowScoreScreen(false); } } + public DarkenScreen() { + this.dimGraphics.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); + this.dimGraphics.fill({ color: 0x000000, alpha: 0.5 }); + } private OfferPlayerGems() { Engine.Grid.gridInteractionEnabled = false; - + Engine.GameScene.sidebar.towerTab.resetTint(); + Engine.TowerManager.ResetChooseTower(); + this.setRoundMode(RoundMode.OfferingGems); + let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems; + this.DarkenScreen(); + this.offerGemsSprite = new PIXI.NineSliceSprite({ + width: 400, + height: 200, + texture: GameAssets.Frame01Texture, + leftWidth: 100, + topHeight: 100, + rightWidth: 100, + bottomHeight: 100, + zIndex: this.dimGraphics.zIndex + 1, + x: Engine.app.canvas.width / 2 - 200, + y: Engine.app.canvas.height / 2 - 100, + }); + Engine.GameMaster.currentScene.stage.addChildAt(this.offerGemsSprite, 0); + gemsToOffer.forEach((gType, index) => { + let _Gem = new Gem(gType); + let vGem = new VisualGemSlot(0, Engine.app.stage, _Gem); + this.visualGems.push(vGem); + vGem.container.x = this.offerGemsSprite.x + 69 * (index + 1); + vGem.container.y = this.offerGemsSprite.y + 50; + vGem.container.onpointermove = () => { + Engine.GameScene.tooltip.SetContentGem(_Gem); + Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY); + }; + vGem.container.onpointerleave = () => { + Engine.GameScene.tooltip.Hide(); + }; + vGem.onClick = () => { + Engine.GameScene.tooltip.Hide(); + this.PlayerPickedGem(_Gem); + }; + }); + } + private PlayerPickedGem(gem: Gem) { + this.offerGemsSprite.destroy(); + this.dimGraphics.clear(); + this.visualGems.forEach((item) => item.destroy()); Engine.Grid.gridInteractionEnabled = true; + Engine.NotificationManager.Notify(gem.gemDefinition.name + ' added to your inventory.', 'gemaward'); } private ShowScoreScreen(lost) { - // TODO: show to player for real + // TODO: show to player for real (see how this.OfferPlayerGems() does it) if (lost) { console.log('LOSE!'); } else { From d12d1e1d3c793800f3fee7c331d87b8efc3640a9 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Sun, 26 Jan 2025 23:16:05 +0100 Subject: [PATCH 06/10] gems are properly awarded, just need to show to the player --- public/assets/json/Gems.json | 2 +- public/maps.tiled-session | 2 +- src/classes/game/Gem.ts | 6 ++--- src/classes/game/MissionStats.ts | 14 +++++++++++ src/classes/gui/Tooltip.ts | 4 ++-- src/scenes/Game.ts | 40 +++++++++++++++++++++++++------- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/public/assets/json/Gems.json b/public/assets/json/Gems.json index 1177329..61d55e3 100644 --- a/public/assets/json/Gems.json +++ b/public/assets/json/Gems.json @@ -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.", + "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.", "type": "Fire", "totalLevels": 2, "textures": [], diff --git a/public/maps.tiled-session b/public/maps.tiled-session index f9cf226..abcd439 100644 --- a/public/maps.tiled-session +++ b/public/maps.tiled-session @@ -36,7 +36,7 @@ "scale": 0.5, "selectedLayer": 0, "viewCenter": { - "x": 1070, + "x": 570, "y": 448 } }, diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index b9ccea7..5ac0400 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -4,9 +4,9 @@ import GameAssets from '../Assets'; export default class Gem { public texture: PIXI.Texture; public level: number = 1; - public gemDefinition: GemDefinition; + public definition: GemDefinition; constructor(gemType: GemType) { - this.gemDefinition = GameAssets.Gems[gemType]; - this.texture = this.gemDefinition.textures[0]; + this.definition = GameAssets.Gems[gemType]; + this.texture = this.definition.textures[0]; } } diff --git a/src/classes/game/MissionStats.ts b/src/classes/game/MissionStats.ts index e8457d9..98ea993 100644 --- a/src/classes/game/MissionStats.ts +++ b/src/classes/game/MissionStats.ts @@ -3,6 +3,7 @@ import { Engine } from '../Bastion'; import GameObject from '../GameObject'; import * as PIXI from 'pixi.js'; import { WaveManagerEvents } from './WaveManager'; +import Gem from './Gem'; export default class MissionStats extends GameObject { private hp: number = 100; @@ -10,6 +11,10 @@ export default class MissionStats extends GameObject { private goldText: PIXI.Text; private healthText: PIXI.Text; private waveText: PIXI.Text; + private inventory: Gem[] = []; + + // TODO: implement score keeping for leaderboards. + private score: number = 0; public getHP() { return this.hp; @@ -44,6 +49,15 @@ export default class MissionStats extends GameObject { this.goldText.text = this.gold; } + public giveGem(gem: Gem) { + this.inventory.push(gem); + Engine.GameScene.events.emit('givegem', gem); + } + + public getInventory() { + return this.inventory; + } + constructor(initialHP: number, initialGold: number) { super(); this.hp = initialHP; diff --git a/src/classes/gui/Tooltip.ts b/src/classes/gui/Tooltip.ts index 32eba83..b6767f3 100644 --- a/src/classes/gui/Tooltip.ts +++ b/src/classes/gui/Tooltip.ts @@ -178,8 +178,8 @@ export default class Tooltip extends GuiObject { this.gemAmount.alpha = 0; this.gemDescriptionText.alpha = 1; - this.titleText.text = `Lv. ${gem.level} ` + gem.gemDefinition.name; - this.gemDescriptionText.text = gem.gemDefinition.description; + this.titleText.text = `Lv. ${gem.level} ` + gem.definition.name; + this.gemDescriptionText.text = gem.definition.description; } public Show(x, y) { this.container.alpha = 1; diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index f6a3cfb..f48a66a 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -85,6 +85,7 @@ export class GameScene extends Scene { this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160)); // 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 + // nevermind, i can't be bothered to do this, and this works fine. this.changeRoundButton.CustomButtonLogic = () => { this.changeRoundButton.buttonIcon = new PIXI.Sprite({ texture: GameAssets.PlayIconTexture, @@ -157,6 +158,9 @@ export class GameScene extends Scene { this.dimGraphics.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); this.dimGraphics.fill({ color: 0x000000, alpha: 0.5 }); } + public UndarkenScreen() { + this.dimGraphics.clear(); + } private OfferPlayerGems() { Engine.Grid.gridInteractionEnabled = false; Engine.GameScene.sidebar.towerTab.resetTint(); @@ -165,24 +169,41 @@ export class GameScene extends Scene { let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems; this.DarkenScreen(); this.offerGemsSprite = new PIXI.NineSliceSprite({ - width: 400, - height: 200, + width: 380, + height: 150, texture: GameAssets.Frame01Texture, leftWidth: 100, topHeight: 100, rightWidth: 100, bottomHeight: 100, zIndex: this.dimGraphics.zIndex + 1, - x: Engine.app.canvas.width / 2 - 200, - y: Engine.app.canvas.height / 2 - 100, + x: Engine.app.canvas.width / 2 - 190, + y: Engine.app.canvas.height / 2 - 75, }); Engine.GameMaster.currentScene.stage.addChildAt(this.offerGemsSprite, 0); + let offerText = new PIXI.Text({ + x: Engine.app.canvas.width / 4, + y: Engine.app.canvas.height / 4, + zIndex: this.dimGraphics.zIndex + 1, + text: 'Choose a Gem as your reward for beating this round!', + style: { + fontSize: 40, + fill: 'orange', + fontWeight: 'bold', + stroke: { + color: 0x000000, + width: 5, + }, + }, + }); + // offerText.x -= offerText.width; + Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0); gemsToOffer.forEach((gType, index) => { let _Gem = new Gem(gType); let vGem = new VisualGemSlot(0, Engine.app.stage, _Gem); this.visualGems.push(vGem); - vGem.container.x = this.offerGemsSprite.x + 69 * (index + 1); - vGem.container.y = this.offerGemsSprite.y + 50; + vGem.container.x = this.offerGemsSprite.x - 15 + 69 * (index + 1); + vGem.container.y = this.offerGemsSprite.y + 40; vGem.container.onpointermove = () => { Engine.GameScene.tooltip.SetContentGem(_Gem); Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY); @@ -192,16 +213,18 @@ export class GameScene extends Scene { }; vGem.onClick = () => { Engine.GameScene.tooltip.Hide(); + offerText.destroy(); this.PlayerPickedGem(_Gem); }; }); } private PlayerPickedGem(gem: Gem) { this.offerGemsSprite.destroy(); - this.dimGraphics.clear(); + this.UndarkenScreen(); this.visualGems.forEach((item) => item.destroy()); Engine.Grid.gridInteractionEnabled = true; - Engine.NotificationManager.Notify(gem.gemDefinition.name + ' added to your inventory.', 'gemaward'); + this.MissionStats.giveGem(gem); + Engine.NotificationManager.Notify(gem.definition.name + ' added to your inventory.', 'gemaward'); } private ShowScoreScreen(lost) { @@ -238,5 +261,4 @@ export class GameScene extends Scene { Engine.GameMaster.currentScene.stage.removeChildren(); Engine.GameMaster.changeScene(new MissionPickerScene()); } - public onTowerPlaced() {} } From 5adab98902e05a8351a547310895edb8a3779332 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:33:26 +0100 Subject: [PATCH 07/10] seperate events into seperate file --- src/classes/Events.ts | 26 ++++++++++++++++++++++++++ src/classes/game/Creep.ts | 8 +------- src/classes/game/Grid.ts | 9 ++------- src/classes/game/Inventory.ts | 5 ----- src/classes/game/MissionStats.ts | 4 ++-- src/classes/game/Projectile.ts | 3 ++- src/classes/game/Tower.ts | 5 ----- src/classes/game/TowerManager.ts | 5 +++-- src/classes/game/WaveManager.ts | 10 ++-------- src/classes/gui/TowerTab.ts | 2 +- src/scenes/Game.ts | 5 +++-- 11 files changed, 42 insertions(+), 40 deletions(-) create mode 100644 src/classes/Events.ts delete mode 100644 src/classes/game/Inventory.ts diff --git a/src/classes/Events.ts b/src/classes/Events.ts new file mode 100644 index 0000000..cafe257 --- /dev/null +++ b/src/classes/Events.ts @@ -0,0 +1,26 @@ +export enum WaveManagerEvents { + CreepSpawned = 'creepSpawned', + Finished = 'finished', + NewWave = 'newwave', +} + +export enum CreepEvents { + Died = 'died', + TakenDamage = 'takenDamage', + Escaped = 'escaped', + Moved = 'moved', +} + +export enum GridEvents { + CellMouseOver = 'cellmouseover', + CellMouseLeave = 'cellmouseleave', +} + +export enum TowerEvents { + TowerPlacedEvent = 'towerPlacedEvent', + TowerSoldEvent = 'towerSoldEvent', +} + +export enum StatsEvents { + GemGivenEvent = 'gemGivenEvent', +} diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts index 6eb0d06..af4f4e1 100644 --- a/src/classes/game/Creep.ts +++ b/src/classes/game/Creep.ts @@ -4,13 +4,7 @@ import { Engine } from '../Bastion'; import { CreepStatsDefinition, CreepType, PathDefinition } from '../Definitions'; import GameObject from '../GameObject'; import * as PIXI from 'pixi.js'; - -export enum CreepEvents { - Died = 'died', - TakenDamage = 'takenDamage', - Escaped = 'escaped', - Moved = 'moved', -} +import { CreepEvents } from '../Events'; export default class Creep extends GameObject { public id: number; diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 2cb94f5..8479512 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -3,13 +3,8 @@ import GameObject from '../GameObject'; 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', - CellMouseLeave = 'cellmouseleave', -} +import Creep from './Creep'; +import { CreepEvents, TowerEvents, GridEvents } from '../Events'; export class Cell extends GameObject { public type: TerrainType; diff --git a/src/classes/game/Inventory.ts b/src/classes/game/Inventory.ts deleted file mode 100644 index d360f8e..0000000 --- a/src/classes/game/Inventory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import GameObject from '../GameObject'; - -export default class Inventory extends GameObject { - public update() {} -} diff --git a/src/classes/game/MissionStats.ts b/src/classes/game/MissionStats.ts index 98ea993..a5a6ee8 100644 --- a/src/classes/game/MissionStats.ts +++ b/src/classes/game/MissionStats.ts @@ -2,7 +2,7 @@ import Assets from '../Assets'; import { Engine } from '../Bastion'; import GameObject from '../GameObject'; import * as PIXI from 'pixi.js'; -import { WaveManagerEvents } from './WaveManager'; +import { WaveManagerEvents, StatsEvents } from '../Events'; import Gem from './Gem'; export default class MissionStats extends GameObject { @@ -51,7 +51,7 @@ export default class MissionStats extends GameObject { public giveGem(gem: Gem) { this.inventory.push(gem); - Engine.GameScene.events.emit('givegem', gem); + Engine.GameScene.events.emit(StatsEvents.GemGivenEvent, gem); } public getInventory() { diff --git a/src/classes/game/Projectile.ts b/src/classes/game/Projectile.ts index b9e6904..9ce0327 100644 --- a/src/classes/game/Projectile.ts +++ b/src/classes/game/Projectile.ts @@ -1,7 +1,8 @@ import * as PIXI from 'pixi.js'; import GameObject from '../GameObject'; import { Engine } from '../Bastion'; -import Creep, { CreepEvents } from './Creep'; +import Creep from './Creep'; +import { CreepEvents } from '../Events'; export function calculateAngleToPoint(x, y, targetX, targetY) { const dx = targetX - x; diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index dbe1237..a8fc19a 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -12,11 +12,6 @@ function distance(x1, y1, x2, y2) { return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); } -export enum TowerEvents { - TowerPlacedEvent = 'towerPlacedEvent', - TowerSoldEvent = 'towerSoldEvent', -} - export class Tower extends GameObject { public row: number; public column: number; diff --git a/src/classes/game/TowerManager.ts b/src/classes/game/TowerManager.ts index d58ff41..1082e62 100644 --- a/src/classes/game/TowerManager.ts +++ b/src/classes/game/TowerManager.ts @@ -2,8 +2,9 @@ import * as PIXI from 'pixi.js'; import { Engine } from '../Bastion'; import { TerrainType, TowerDefinition } from '../Definitions'; import GameAssets from '../Assets'; -import { Tower, TowerEvents } from './Tower'; -import { Cell, GridEvents } from './Grid'; +import { Tower } from './Tower'; +import { Cell } from './Grid'; +import { GridEvents, TowerEvents } from '../Events'; export enum TowerBehaviours { BasicTowerBehaviour = 'BasicTowerBehaviour', diff --git a/src/classes/game/WaveManager.ts b/src/classes/game/WaveManager.ts index 4d61c5b..6bc94b6 100644 --- a/src/classes/game/WaveManager.ts +++ b/src/classes/game/WaveManager.ts @@ -1,15 +1,9 @@ import { CreepType, MissionRoundDefinition, PathDefinition } from '../Definitions'; -import * as PIXI from 'pixi.js'; -import Creep, { CreepEvents } from './Creep'; +import Creep from './Creep'; import { Engine } from '../Bastion'; +import { WaveManagerEvents } from '../Events'; import GameObject from '../GameObject'; -export enum WaveManagerEvents { - CreepSpawned = 'creepSpawned', - Finished = 'finished', - NewWave = 'newwave', -} - type CreepInstance = { creep: Creep; tickToSpawnAt: number; diff --git a/src/classes/gui/TowerTab.ts b/src/classes/gui/TowerTab.ts index 2c131ff..fefd096 100644 --- a/src/classes/gui/TowerTab.ts +++ b/src/classes/gui/TowerTab.ts @@ -2,7 +2,7 @@ import * as PIXI from 'pixi.js'; import GuiObject from '../GuiObject'; import GameAssets from '../Assets'; import { Engine } from '../Bastion'; -import { TowerEvents } from '../game/Tower'; +import { TowerEvents } from '../Events'; import { TowerDefinition } from '../Definitions'; class TowerButton extends GuiObject { diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index f48a66a..ed00434 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -1,9 +1,10 @@ import GameAssets from '../classes/Assets'; import { Engine } from '../classes/Bastion'; import { MissionDefinition } from '../classes/Definitions'; -import Creep, { CreepEvents } from '../classes/game/Creep'; +import Creep from '../classes/game/Creep'; import { Grid } from '../classes/game/Grid'; -import WaveManager, { WaveManagerEvents } from '../classes/game/WaveManager'; +import WaveManager from '../classes/game/WaveManager'; +import { WaveManagerEvents, CreepEvents } from '../classes/Events'; import Sidebar from '../classes/gui/Sidebar'; import Button, { ButtonTexture } from '../classes/gui/Button'; import Scene from './Scene'; From d3988447a6f8adc2b30e49cd0ca7cf287e860f6c Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:42:01 +0100 Subject: [PATCH 08/10] add more visual feedback for inventory --- src/classes/Bastion.ts | 7 +++++++ src/classes/game/MissionStats.ts | 5 +++++ src/classes/gui/GemTab.ts | 29 ++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/classes/Bastion.ts b/src/classes/Bastion.ts index 508e463..94e356b 100644 --- a/src/classes/Bastion.ts +++ b/src/classes/Bastion.ts @@ -8,6 +8,7 @@ import TowerManager from './game/TowerManager'; import { GameScene } from '../scenes/Game'; import { AnimationManager } from './game/AnimationManager'; import NotificationManager from './game/NotificationManager'; +import Gem from './game/Gem'; export class Engine { public static app: PIXI.Application; @@ -25,6 +26,12 @@ export class Engine { public static GridRows: number = 17; public static MouseX: number = 0; public static MouseY: number = 0; + + public static gemTest() { + for (let i = 0; i < 48; i++) { + this.GameScene.MissionStats.giveGem(new Gem(0)); + } + } } export default class GameMaster { diff --git a/src/classes/game/MissionStats.ts b/src/classes/game/MissionStats.ts index a5a6ee8..d7a3347 100644 --- a/src/classes/game/MissionStats.ts +++ b/src/classes/game/MissionStats.ts @@ -50,6 +50,11 @@ export default class MissionStats extends GameObject { } public giveGem(gem: Gem) { + if (this.inventory.length >= 48) + return Engine.NotificationManager.Notify( + "Can't hold more than 48 Gems. Extra Gem was thrown away.", + 'danger' + ); this.inventory.push(gem); Engine.GameScene.events.emit(StatsEvents.GemGivenEvent, gem); } diff --git a/src/classes/gui/GemTab.ts b/src/classes/gui/GemTab.ts index 4084551..fcfe7a1 100644 --- a/src/classes/gui/GemTab.ts +++ b/src/classes/gui/GemTab.ts @@ -1,10 +1,15 @@ import * as PIXI from 'pixi.js'; import GuiObject from '../GuiObject'; import GameAssets from '../Assets'; +import { Engine } from '../Bastion'; +import { StatsEvents } from '../Events'; +import Gem from '../game/Gem'; +import { VisualGemSlot } from './TowerPanel'; export default class GemTab extends GuiObject { private bounds: PIXI.Rectangle; private gemTabSprite: PIXI.NineSliceSprite; + private vGems: VisualGemSlot[] = []; constructor(bounds: PIXI.Rectangle) { super(false); @@ -22,7 +27,29 @@ export default class GemTab extends GuiObject { this.gemTabSprite.y = 0; this.gemTabSprite.width = this.bounds.width; this.gemTabSprite.height = this.bounds.height; - this.container.addChild(this.gemTabSprite); + + Engine.GameScene.events.on(StatsEvents.GemGivenEvent, () => { + this.RebuildInventoryVisual(); + }); + } + public RebuildInventoryVisual() { + this.vGems.forEach((vGem) => vGem.destroy()); + Engine.GameScene.MissionStats.getInventory().forEach((gem, index) => { + let vGem = new VisualGemSlot(0, this.container, gem); + + let vGemYValue = 5; + let vGemXValue = (index % 4) * 64 + 20; + let vGemYIdx = index; + while (true) { + if (vGemYIdx <= 3) break; + vGemYValue += 66; + vGemYIdx -= 4; + } + + vGem.container.x = vGemXValue; + vGem.container.y = vGemYValue; + this.vGems.push(vGem); + }); } } From 5ecec288703da9a47bfb2646cf3904d55908f4b4 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Sat, 1 Feb 2025 22:22:41 +0100 Subject: [PATCH 09/10] propely implement slotting and unslotting gems --- src/classes/Bastion.ts | 2 +- src/classes/Events.ts | 4 ++ src/classes/game/Gem.ts | 6 +++ src/classes/game/MissionStats.ts | 15 ++++++- src/classes/game/NotificationManager.ts | 2 +- src/classes/game/Tower.ts | 16 ++++++- src/classes/gui/GemTab.ts | 59 ++++++++++++++++++++++--- src/classes/gui/Sidebar.ts | 2 +- src/classes/gui/TowerPanel.ts | 29 ++++++++++-- src/scenes/Game.ts | 30 ++++++++----- 10 files changed, 141 insertions(+), 24 deletions(-) diff --git a/src/classes/Bastion.ts b/src/classes/Bastion.ts index 94e356b..db5ded7 100644 --- a/src/classes/Bastion.ts +++ b/src/classes/Bastion.ts @@ -28,7 +28,7 @@ export class Engine { public static MouseY: number = 0; public static gemTest() { - for (let i = 0; i < 48; i++) { + for (let i = 0; i < 2; i++) { this.GameScene.MissionStats.giveGem(new Gem(0)); } } diff --git a/src/classes/Events.ts b/src/classes/Events.ts index cafe257..67be16b 100644 --- a/src/classes/Events.ts +++ b/src/classes/Events.ts @@ -24,3 +24,7 @@ export enum TowerEvents { export enum StatsEvents { GemGivenEvent = 'gemGivenEvent', } + +export enum GemEvents { + TowerPanelSelectGem = 'towerTabSelectGem', +} diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index 5ac0400..185a7ba 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -1,12 +1,18 @@ import * as PIXI from 'pixi.js'; import { GemType, GemDefinition } from '../Definitions'; import GameAssets from '../Assets'; + +let latestGemId = 0; + export default class Gem { public texture: PIXI.Texture; public level: number = 1; public definition: GemDefinition; + private id; constructor(gemType: GemType) { this.definition = GameAssets.Gems[gemType]; this.texture = this.definition.textures[0]; + this.id = latestGemId + 1; + latestGemId++; } } diff --git a/src/classes/game/MissionStats.ts b/src/classes/game/MissionStats.ts index d7a3347..c1c74e2 100644 --- a/src/classes/game/MissionStats.ts +++ b/src/classes/game/MissionStats.ts @@ -49,20 +49,33 @@ export default class MissionStats extends GameObject { this.goldText.text = this.gold; } - public giveGem(gem: Gem) { + public giveGem(gem: Gem, noNotify?) { if (this.inventory.length >= 48) return Engine.NotificationManager.Notify( "Can't hold more than 48 Gems. Extra Gem was thrown away.", 'danger' ); this.inventory.push(gem); + if (!noNotify) + Engine.NotificationManager.Notify( + `Lv. ${gem.level} ${gem.definition.name}` + ' added to your inventory.', + 'gemaward' + ); Engine.GameScene.events.emit(StatsEvents.GemGivenEvent, gem); } + public takeGem(gem) { + return this.inventory.splice(this.inventory.indexOf(gem), 1)[0]; + } + public getInventory() { return this.inventory; } + public checkIfPlayerHasAnyGems() { + return this.inventory.length > 0; + } + constructor(initialHP: number, initialGold: number) { super(); this.hp = initialHP; diff --git a/src/classes/game/NotificationManager.ts b/src/classes/game/NotificationManager.ts index df86263..04f2792 100644 --- a/src/classes/game/NotificationManager.ts +++ b/src/classes/game/NotificationManager.ts @@ -21,7 +21,7 @@ class Notification { } else if (type == 'reward') { fill = 0xd65afc; } else if (type == 'gemaward') { - fill = 0xffff00; + fill = 0xffffff; } this.ticksToFadeAway = ticksToFadeAway; this.textObj = new PIXI.Text({ diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index a8fc19a..95e9448 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -16,7 +16,7 @@ export class Tower extends GameObject { public row: number; public column: number; public definition: TowerDefinition; - public slottedGems: Array; + public slottedGems: Array = []; public damageDealt: number = 0; private projectiles: Projectile[] = []; private behaviour: string; @@ -55,6 +55,20 @@ export class Tower extends GameObject { private onParentCellLeave = (e) => { this.graphics.clear(); }; + public SlotGem(gem: Gem, index: number) { + console.log('ATTEMPTING TO SLOT ', gem, index); + this.slottedGems[index] = gem; + Engine.GameScene.towerPanel.Hide(); + Engine.GameScene.towerPanel.Show(this); + } + public UnslotGem(index) { + const gem = this.slottedGems.splice(index, 1)[0]; + Engine.GameScene.MissionStats.giveGem(gem, true); + Engine.NotificationManager.Notify( + `Gem Lv. ${gem.level} ${gem.definition.name} unslotted from ${this.name} and placed back in your inventory.`, + 'info' + ); + } public GetCreepsInRange() { let creeps = Engine.Grid.creeps; diff --git a/src/classes/gui/GemTab.ts b/src/classes/gui/GemTab.ts index fcfe7a1..50cbf82 100644 --- a/src/classes/gui/GemTab.ts +++ b/src/classes/gui/GemTab.ts @@ -5,11 +5,15 @@ import { Engine } from '../Bastion'; import { StatsEvents } from '../Events'; import Gem from '../game/Gem'; import { VisualGemSlot } from './TowerPanel'; +import { Tower } from '../game/Tower'; export default class GemTab extends GuiObject { private bounds: PIXI.Rectangle; private gemTabSprite: PIXI.NineSliceSprite; private vGems: VisualGemSlot[] = []; + public isSelectingGem: boolean = false; + public selectingGemSlotIndex: number = -1; + public selectingGemTowerObject: Tower = null; constructor(bounds: PIXI.Rectangle) { super(false); @@ -33,22 +37,65 @@ export default class GemTab extends GuiObject { this.RebuildInventoryVisual(); }); } + public TowerPanelSelectingGem(gem: Gem, index: number, tower: Tower) { + console.log(this); + if (this.isSelectingGem) { + this.isSelectingGem = false; + this.selectingGemSlotIndex = -1; + } else { + this.isSelectingGem = true; + if (gem == null) { + // Want to select gem to slot in, already checked if player has a Gem. + Engine.NotificationManager.Notify( + 'Click on any Gem in your inventory to slot it into this Gem slot.', + 'info' + ); + this.selectingGemSlotIndex = index; + this.selectingGemTowerObject = tower; + } else { + // Already have a gem selected + tower.UnslotGem(index); + this.RebuildInventoryVisual(); + Engine.GameScene.towerPanel.Hide(); + Engine.GameScene.towerPanel.Show(tower); + this.selectingGemSlotIndex = -1; + this.selectingGemTowerObject = null; + } + } + } public RebuildInventoryVisual() { this.vGems.forEach((vGem) => vGem.destroy()); + this.vGems = []; Engine.GameScene.MissionStats.getInventory().forEach((gem, index) => { let vGem = new VisualGemSlot(0, this.container, gem); - let vGemYValue = 5; - let vGemXValue = (index % 4) * 64 + 20; + let vGemYCoord = 5; + let vGemXCoord = (index % 4) * 64 + 20; let vGemYIdx = index; while (true) { if (vGemYIdx <= 3) break; - vGemYValue += 66; + vGemYCoord += 66; vGemYIdx -= 4; } - - vGem.container.x = vGemXValue; - vGem.container.y = vGemYValue; + vGem.container.x = vGemXCoord; + vGem.container.y = vGemYCoord; + vGem.container.onpointermove = () => { + if (gem == null) return; + Engine.GameScene.tooltip.SetContentGem(gem); + Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY); + }; + vGem.container.onpointerleave = () => { + Engine.GameScene.tooltip.Hide(); + }; + vGem.onClick = () => { + Engine.GameScene.tooltip.Hide(); + if (this.isSelectingGem) { + this.isSelectingGem = false; + let takenGem = Engine.GameScene.MissionStats.takeGem(gem); + this.selectingGemTowerObject.SlotGem(takenGem, this.selectingGemSlotIndex); + this.RebuildInventoryVisual(); + } + }; this.vGems.push(vGem); }); } diff --git a/src/classes/gui/Sidebar.ts b/src/classes/gui/Sidebar.ts index 060a9b5..8389279 100644 --- a/src/classes/gui/Sidebar.ts +++ b/src/classes/gui/Sidebar.ts @@ -6,9 +6,9 @@ import GemTab from './GemTab'; export default class Sidebar extends GuiObject { public towerTab: TowerTab; + public gemTab: GemTab; private bounds: PIXI.Rectangle; private sidebarSprite: PIXI.NineSliceSprite; - private gemTab: GemTab; constructor(bounds: PIXI.Rectangle) { super(false); diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index 3921902..167b906 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -6,6 +6,7 @@ import GameUIConstants from '../GameUIConstants'; import Button, { ButtonTexture } from './Button'; import { Tower } from '../game/Tower'; import Gem from '../game/Gem'; +import { GemEvents } from '../Events'; export class VisualGemSlot extends GuiObject { public iconSprite: PIXI.Sprite; @@ -55,6 +56,7 @@ export default class TowerPanel extends GuiObject { private bounds: PIXI.Rectangle; private towerPanel: PIXI.NineSliceSprite; private closeBtn: Button; + private vGems: VisualGemSlot[] = []; public isShown: boolean = false; public titleText: PIXI.Text; @@ -110,10 +112,31 @@ export default class TowerPanel extends GuiObject { this.container.addChild(this.titleText); } private MakeSlots(tower: Tower) { + this.vGems.forEach((vGem) => { + vGem.destroy(); + }); + this.vGems = []; let amount = tower.definition.stats.gemSlotsAmount; - amount = 6; + // amount = 6; for (let i = 0; i < amount; i++) { - const element = new VisualGemSlot(i, this.container, null); + let gem = tower.slottedGems[i]; + console.log(gem); + if (!gem) gem = null; + const vGem = new VisualGemSlot(i, this.container, gem); + this.vGems.push(vGem); + vGem.container.onpointermove = () => { + if (!gem) return; + Engine.GameScene.tooltip.SetContentGem(gem); + Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY); + }; + vGem.container.onpointerleave = () => { + Engine.GameScene.tooltip.Hide(); + }; + vGem.onClick = () => { + Engine.GameScene.tooltip.Hide(); + console.log('MAKESLOTS ', gem); + Engine.GameScene.events.emit(GemEvents.TowerPanelSelectGem, gem, i, tower); + }; } } public Show(tower: Tower) { @@ -121,7 +144,7 @@ export default class TowerPanel extends GuiObject { this.isShown = true; this.SetContent(tower); this.MakeSlots(tower); - if (mouseX < 900) { + if (tower.container.x < 900) { this.ShowRight(); } else { this.ShowLeft(); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index ed00434..89a11ff 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -4,7 +4,7 @@ import { MissionDefinition } from '../classes/Definitions'; import Creep from '../classes/game/Creep'; import { Grid } from '../classes/game/Grid'; import WaveManager from '../classes/game/WaveManager'; -import { WaveManagerEvents, CreepEvents } from '../classes/Events'; +import { WaveManagerEvents, CreepEvents, GemEvents } from '../classes/Events'; import Sidebar from '../classes/gui/Sidebar'; import Button, { ButtonTexture } from '../classes/gui/Button'; import Scene from './Scene'; @@ -34,17 +34,17 @@ export class GameScene extends Scene { public sidebar: Sidebar; public tooltip: Tooltip; public towerPanel: TowerPanel; - public dimGraphics: PIXI.Graphics = new PIXI.Graphics({ - x: 0, - y: 0, - zIndex: 120, - }); - private offerGemsSprite: PIXI.NineSliceSprite; private visualGems: VisualGemSlot[] = []; private currentRound: number = 0; private isWaveManagerFinished: boolean = false; private playerWon: boolean = false; private destroyTicker: boolean = false; + private offerGemsSprite: PIXI.NineSliceSprite; + private dimGraphics: PIXI.Graphics = new PIXI.Graphics({ + x: 0, + y: 0, + zIndex: 120, + }); constructor(name: string) { super(); @@ -85,8 +85,6 @@ export class GameScene extends Scene { Engine.GameMaster.currentScene.stage.addChildAt(this.dimGraphics, 0); this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160)); // 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 - // nevermind, i can't be bothered to do this, and this works fine. this.changeRoundButton.CustomButtonLogic = () => { this.changeRoundButton.buttonIcon = new PIXI.Sprite({ texture: GameAssets.PlayIconTexture, @@ -109,6 +107,19 @@ export class GameScene extends Scene { this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`); }; this.MissionStats = new MissionStats(100, 200); + this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => { + if (gem == null) { + if (!this.MissionStats.checkIfPlayerHasAnyGems()) + return Engine.NotificationManager.Notify( + 'You require atleast 1 Gem in your inventory to slot it in a Gem slot.', + 'warn' + ); + console.log(gem); + this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); + } else { + this.sidebar.gemTab.TowerPanelSelectingGem(gem, -1, tower); + } + }); this.ticker = new PIXI.Ticker(); this.ticker.maxFPS = 60; this.ticker.minFPS = 30; @@ -225,7 +236,6 @@ export class GameScene extends Scene { this.visualGems.forEach((item) => item.destroy()); Engine.Grid.gridInteractionEnabled = true; this.MissionStats.giveGem(gem); - Engine.NotificationManager.Notify(gem.definition.name + ' added to your inventory.', 'gemaward'); } private ShowScoreScreen(lost) { From 96454a0df01eacaf9b97147ad0f7d4f8aa5ab209 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:35:26 +0100 Subject: [PATCH 10/10] add more visual feedback to slotting gems --- public/assets/gui/frame_05.png | Bin 0 -> 9167 bytes src/classes/Assets.ts | 2 + src/classes/Bastion.ts | 11 +++-- src/classes/game/Gem.ts | 2 +- src/classes/game/NotificationManager.ts | 4 +- src/classes/game/Tower.ts | 11 ++++- src/classes/game/TowerManager.ts | 4 +- src/classes/gui/GemTab.ts | 21 ++++++---- src/classes/gui/TowerPanel.ts | 52 +++++++++++++++++++----- src/main.ts | 1 + src/scenes/Game.ts | 10 +++-- 11 files changed, 87 insertions(+), 31 deletions(-) create mode 100755 public/assets/gui/frame_05.png diff --git a/public/assets/gui/frame_05.png b/public/assets/gui/frame_05.png new file mode 100755 index 0000000000000000000000000000000000000000..2b24c7e838b9a53a38cc66f64dfd9ac887babb3b GIT binary patch literal 9167 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 bT$Tm^n};_0XgjfgKe5r%x{0jQu#f$J9FKeH literal 0 HcmV?d00001 diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 9a92fd3..528aada 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -7,6 +7,7 @@ export default class GameAssets { public static Frame02Texture: PIXI.Texture; public static Frame03Texture: PIXI.Texture; public static Frame04Texture: PIXI.Texture; + public static Frame05Texture: PIXI.Texture; public static FrameInventory: PIXI.Texture; public static FrameBackground: PIXI.Texture; public static FrameTowerTab: PIXI.Texture; @@ -86,6 +87,7 @@ export default class GameAssets { this.Load('./assets/gui/frame_02.png').then((texture) => (this.Frame02Texture = texture)), this.Load('./assets/gui/frame_03.png').then((texture) => (this.Frame03Texture = texture)), this.Load('./assets/gui/frame_04.png').then((texture) => (this.Frame04Texture = texture)), + this.Load('./assets/gui/frame_05.png').then((texture) => (this.Frame05Texture = texture)), this.Load('./assets/gui/frame_inv.png').then((texture) => (this.FrameInventory = texture)), this.Load('./assets/gui/background_01.png').then((texture) => (this.FrameBackground = texture)), this.Load('./assets/gui/background_02.png').then((texture) => (this.FrameTowerTab = texture)), diff --git a/src/classes/Bastion.ts b/src/classes/Bastion.ts index db5ded7..8821991 100644 --- a/src/classes/Bastion.ts +++ b/src/classes/Bastion.ts @@ -9,6 +9,8 @@ import { GameScene } from '../scenes/Game'; 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; @@ -27,9 +29,12 @@ export class Engine { public static MouseX: number = 0; public static MouseY: number = 0; - public static gemTest() { - for (let i = 0; i < 2; i++) { - this.GameScene.MissionStats.giveGem(new Gem(0)); + public static TestSuite() { + Engine.NotificationManager.Notify('Loaded testing suite.', 'danger'); + Engine.TowerManager.ToggleChoosingTowerLocation('RESET'); + Engine.TowerManager.PlaceTower(GameAssets.Towers[1], 10, 15, GameAssets.Towers[0].behaviour, true); + for (let i = 0; i < 16; i++) { + this.GameScene.MissionStats.giveGem(new Gem(GemType.Fire), true); } } } diff --git a/src/classes/game/Gem.ts b/src/classes/game/Gem.ts index 185a7ba..15c2614 100644 --- a/src/classes/game/Gem.ts +++ b/src/classes/game/Gem.ts @@ -8,7 +8,7 @@ export default class Gem { public texture: PIXI.Texture; public level: number = 1; public definition: GemDefinition; - private id; + public id; constructor(gemType: GemType) { this.definition = GameAssets.Gems[gemType]; this.texture = this.definition.textures[0]; diff --git a/src/classes/game/NotificationManager.ts b/src/classes/game/NotificationManager.ts index 04f2792..737be4e 100644 --- a/src/classes/game/NotificationManager.ts +++ b/src/classes/game/NotificationManager.ts @@ -55,7 +55,7 @@ export default class NotificationManager extends GameObject { this.bb.x = Engine.app.canvas.width / 2; this.bb.y = 40; this.copyBBToContainer(); - this.container.zIndex = 100; + this.container.zIndex = 200; Engine.app.stage.addChild(this.container); } public Notify(text, type: NotificationType) { @@ -74,7 +74,7 @@ export default class NotificationManager extends GameObject { if (this.ticks >= notif.ticksToFadeAway && !notif.animating) { notif.animating = true; Engine.AnimationManager.Animate( - new FadeInOut('out', 240, notif.textObj, () => { + new FadeInOut('out', 300, notif.textObj, () => { notif.destroy(); }) ); diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index 95e9448..3e3615d 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -64,8 +64,15 @@ export class Tower extends GameObject { public UnslotGem(index) { const gem = this.slottedGems.splice(index, 1)[0]; Engine.GameScene.MissionStats.giveGem(gem, true); + for (let i = index; i < this.slottedGems.length - 1; i++) { + if (this.slottedGems[i] == null) { + this.slottedGems[i] = this.slottedGems[i + 1]; + this.slottedGems[i + 1] = null; + } + } + this.slottedGems = this.slottedGems.filter((gem) => gem != null); Engine.NotificationManager.Notify( - `Gem Lv. ${gem.level} ${gem.definition.name} unslotted from ${this.name} and placed back in your inventory.`, + `Lv. ${gem.level} ${gem.definition.name} unslotted and placed back in your inventory.`, 'info' ); } @@ -111,7 +118,7 @@ export class Tower extends GameObject { } } - override destroy(): void { + public destroy(): void { super.destroy(); this.parent.clickDetector.off('pointerenter', this.onParentCellEnter); this.parent.clickDetector.off('pointerleave', this.onParentCellLeave); diff --git a/src/classes/game/TowerManager.ts b/src/classes/game/TowerManager.ts index 1082e62..d63d0e6 100644 --- a/src/classes/game/TowerManager.ts +++ b/src/classes/game/TowerManager.ts @@ -88,7 +88,7 @@ export default class TowerManager { return returnTower; } public PlaceTower(definition: TowerDefinition, row, column, behaviour: string, ignoreCost?) { - const sprite = this.selectedTower.texture; + const sprite = definition.texture; if (!Engine.GameScene.MissionStats.hasEnoughGold(definition.stats.cost) && !ignoreCost) return Engine.NotificationManager.Notify('Not enough gold.', 'warn'); if ( @@ -96,7 +96,7 @@ export default class TowerManager { Engine.Grid.getCellByRowAndCol(row, column).type != TerrainType.Path && Engine.Grid.getCellByRowAndCol(row, column).type != TerrainType.Restricted ) { - Engine.GameScene.MissionStats.spendGold(definition.stats.cost); + if (!ignoreCost) Engine.GameScene.MissionStats.spendGold(definition.stats.cost); let tower = new Tower(row, column, sprite, definition, behaviour); this.towers.push(tower); this.ToggleChoosingTowerLocation('RESET'); diff --git a/src/classes/gui/GemTab.ts b/src/classes/gui/GemTab.ts index 50cbf82..38777c6 100644 --- a/src/classes/gui/GemTab.ts +++ b/src/classes/gui/GemTab.ts @@ -37,12 +37,12 @@ export default class GemTab extends GuiObject { this.RebuildInventoryVisual(); }); } + // TODO: add more visual clarity public TowerPanelSelectingGem(gem: Gem, index: number, tower: Tower) { - console.log(this); - if (this.isSelectingGem) { - this.isSelectingGem = false; - this.selectingGemSlotIndex = -1; - } else { + console.log('TOWER PANEL SELECTING GEM ' + index); + if (index < 0) console.error('TOWER PANEL SELECTING GEM INDEX IS LESS THAN 0, ', index); + // index = Engine.GameScene.towerPanel.vGems.indexOf(gem); + if (!this.isSelectingGem) { this.isSelectingGem = true; if (gem == null) { // Want to select gem to slot in, already checked if player has a Gem. @@ -58,6 +58,13 @@ export default class GemTab extends GuiObject { this.RebuildInventoryVisual(); Engine.GameScene.towerPanel.Hide(); Engine.GameScene.towerPanel.Show(tower); + this.isSelectingGem = false; + this.selectingGemSlotIndex = -1; + this.selectingGemTowerObject = null; + } + } else { + if (gem == null) { + this.isSelectingGem = false; this.selectingGemSlotIndex = -1; this.selectingGemTowerObject = null; } @@ -69,8 +76,8 @@ export default class GemTab extends GuiObject { Engine.GameScene.MissionStats.getInventory().forEach((gem, index) => { let vGem = new VisualGemSlot(0, this.container, gem); - let vGemYCoord = 5; - let vGemXCoord = (index % 4) * 64 + 20; + let vGemYCoord = 10; + let vGemXCoord = (index % 4) * 70 + 10; let vGemYIdx = index; while (true) { if (vGemYIdx <= 3) break; diff --git a/src/classes/gui/TowerPanel.ts b/src/classes/gui/TowerPanel.ts index 167b906..b77e10b 100644 --- a/src/classes/gui/TowerPanel.ts +++ b/src/classes/gui/TowerPanel.ts @@ -11,17 +11,17 @@ import { GemEvents } from '../Events'; export class VisualGemSlot extends GuiObject { public iconSprite: PIXI.Sprite; private background: PIXI.Sprite; - private gem: Gem; - private i: number = 0; + private frame: PIXI.Sprite; + public i: number = 0; constructor(index: number, parent: PIXI.Container, gem: Gem | null) { super(true); let gtexture; + this.i = index; this.container.x = 10; - this.container.y = index * Engine.GridCellSize + 300; + this.container.y = index * (Engine.GridCellSize + 6) + 300; this.background = new PIXI.Sprite({ - texture: GameAssets.FrameInventory, + texture: GameAssets.Frame01Texture, }); - this.gem = gem; if (gem == null) { gtexture = GameAssets.PlusIconTexture; } else { @@ -45,18 +45,47 @@ export class VisualGemSlot extends GuiObject { this.iconSprite.width = Engine.GridCellSize - 8; this.iconSprite.height = Engine.GridCellSize - 8; } + this.frame = new PIXI.Sprite({ + texture: GameAssets.Frame05Texture, + width: 64, + height: 64, + }); + this.container.addChild(this.background); this.container.addChild(this.iconSprite); + this.container.addChild(this.frame); + if (Engine.latestCommit == 'DEVELOPMENT') { + let txt = gem ? gem.id : ''; + let dbgText = new PIXI.Text({ + text: txt, + zIndex: 11, + style: { + fill: 'white', + stroke: { + color: 0x000000, + width: 5, + }, + }, + }); + this.container.addChild(dbgText); + } parent.addChild(this.container); } - public onClick(e: PIXI.FederatedPointerEvent): void {} + + public setTint(color) { + this.frame.tint = color; + } + + public resetTint() { + this.frame.tint = 0xffffff; + } } export default class TowerPanel extends GuiObject { private bounds: PIXI.Rectangle; private towerPanel: PIXI.NineSliceSprite; private closeBtn: Button; - private vGems: VisualGemSlot[] = []; + public vGems: VisualGemSlot[] = []; public isShown: boolean = false; public titleText: PIXI.Text; @@ -119,10 +148,11 @@ export default class TowerPanel extends GuiObject { let amount = tower.definition.stats.gemSlotsAmount; // amount = 6; for (let i = 0; i < amount; i++) { + console.log('BUILDING TOWER PANEL ' + i); let gem = tower.slottedGems[i]; - console.log(gem); if (!gem) gem = null; const vGem = new VisualGemSlot(i, this.container, gem); + vGem.resetTint(); this.vGems.push(vGem); vGem.container.onpointermove = () => { if (!gem) return; @@ -134,8 +164,10 @@ export default class TowerPanel extends GuiObject { }; vGem.onClick = () => { Engine.GameScene.tooltip.Hide(); - console.log('MAKESLOTS ', gem); - Engine.GameScene.events.emit(GemEvents.TowerPanelSelectGem, gem, i, tower); + console.warn('EMITTING TOWER PANEL SELECT GEM', gem, vGem.i, i, tower); + Engine.GameScene.events.emit(GemEvents.TowerPanelSelectGem, gem, vGem.i, tower); + if (!gem && Engine.GameScene.sidebar.gemTab.isSelectingGem) vGem.setTint(0x00ffff); + else vGem.resetTint(); }; } } diff --git a/src/main.ts b/src/main.ts index fac0057..39659c1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -67,4 +67,5 @@ import GameUIConstants from './classes/GameUIConstants'; window.onbeforeunload = () => { return 'You are about to leave.'; }; + else Engine.TestSuite(); })(); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 89a11ff..b6a58d8 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -114,15 +114,17 @@ export class GameScene extends Scene { 'You require atleast 1 Gem in your inventory to slot it in a Gem slot.', 'warn' ); - console.log(gem); - this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); - } else { - this.sidebar.gemTab.TowerPanelSelectingGem(gem, -1, tower); } + this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); }); this.ticker = new PIXI.Ticker(); this.ticker.maxFPS = 60; this.ticker.minFPS = 30; + // fix tooltip behaving weirdly for some reason + this.tooltip.SetContentTower(0, 0, 0, 0); + this.tooltip.Show(Engine.MouseX, Engine.MouseY); + this.tooltip.Hide(); + this.ticker.add(() => { if (this.update) this.update(this.ticker.elapsedMS); });