diff --git a/docs/assets.md b/docs/assets.md new file mode 100644 index 0000000..8050bb1 --- /dev/null +++ b/docs/assets.md @@ -0,0 +1,4 @@ +# Assets + +List of assets used in the project, all purchased. +https://assetstore.unity.com/packages/2d/gui/icons/gui-megapack-101517 diff --git a/docs/todos.md b/docs/todos.md index 00c510b..a824a87 100644 --- a/docs/todos.md +++ b/docs/todos.md @@ -5,7 +5,7 @@ List of things to implement following the "release" of the minimum viable produc ## Creeps - [ ] Elemental resistances/attunement -- [ ] Proper animation via PNG sequence +- [x] Proper animation via PNG sequence - [ ] More variety in Creeps - [ ] Health bar + statistics on hover over - [ ] +/- on x or y axis to give creeps more variance when walking along path (maybe) @@ -15,7 +15,7 @@ List of things to implement following the "release" of the minimum viable produc - [ ] 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 (replace gem tab temporarily or something) +- [ ] Tower info on click - [ ] Animate projectiles - [ ] Better mouseover tracking when placing tower and showing radius diff --git a/public/aclonica.woff2 b/public/aclonica.woff2 new file mode 100644 index 0000000..ac9c610 Binary files /dev/null and b/public/aclonica.woff2 differ diff --git a/public/assets/CreepStats.json b/public/assets/CreepStats.json deleted file mode 100644 index 2953501..0000000 --- a/public/assets/CreepStats.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "health": 2, - "speed": 0.04, - "special": null, - "resistance": { - "physical": 0, - "divine": 0, - "fire": 0, - "ice": 0, - "frostfire": 0 - } - } -] diff --git a/public/assets/creeps/basic.jpg b/public/assets/creeps/basic.jpg deleted file mode 100644 index c803937..0000000 Binary files a/public/assets/creeps/basic.jpg and /dev/null differ diff --git a/public/assets/creeps/basic/0.png b/public/assets/creeps/basic/0.png new file mode 100644 index 0000000..c51e9a8 Binary files /dev/null and b/public/assets/creeps/basic/0.png differ diff --git a/public/assets/creeps/basic/1.png b/public/assets/creeps/basic/1.png new file mode 100644 index 0000000..992f85e Binary files /dev/null and b/public/assets/creeps/basic/1.png differ diff --git a/public/assets/creeps/basic/10.png b/public/assets/creeps/basic/10.png new file mode 100644 index 0000000..45d24ba Binary files /dev/null and b/public/assets/creeps/basic/10.png differ diff --git a/public/assets/creeps/basic/11.png b/public/assets/creeps/basic/11.png new file mode 100644 index 0000000..992f85e Binary files /dev/null and b/public/assets/creeps/basic/11.png differ diff --git a/public/assets/creeps/basic/2.png b/public/assets/creeps/basic/2.png new file mode 100644 index 0000000..45d24ba Binary files /dev/null and b/public/assets/creeps/basic/2.png differ diff --git a/public/assets/creeps/basic/3.png b/public/assets/creeps/basic/3.png new file mode 100644 index 0000000..3d10052 Binary files /dev/null and b/public/assets/creeps/basic/3.png differ diff --git a/public/assets/creeps/basic/4.png b/public/assets/creeps/basic/4.png new file mode 100644 index 0000000..9618474 Binary files /dev/null and b/public/assets/creeps/basic/4.png differ diff --git a/public/assets/creeps/basic/5.png b/public/assets/creeps/basic/5.png new file mode 100644 index 0000000..238576a Binary files /dev/null and b/public/assets/creeps/basic/5.png differ diff --git a/public/assets/creeps/basic/6.png b/public/assets/creeps/basic/6.png new file mode 100644 index 0000000..f1c37fb Binary files /dev/null and b/public/assets/creeps/basic/6.png differ diff --git a/public/assets/creeps/basic/7.png b/public/assets/creeps/basic/7.png new file mode 100644 index 0000000..238576a Binary files /dev/null and b/public/assets/creeps/basic/7.png differ diff --git a/public/assets/creeps/basic/8.png b/public/assets/creeps/basic/8.png new file mode 100644 index 0000000..9618474 Binary files /dev/null and b/public/assets/creeps/basic/8.png differ diff --git a/public/assets/creeps/basic/9.png b/public/assets/creeps/basic/9.png new file mode 100644 index 0000000..3d10052 Binary files /dev/null and b/public/assets/creeps/basic/9.png differ diff --git a/public/assets/json/Creeps.json b/public/assets/json/Creeps.json new file mode 100644 index 0000000..2f2eaa8 --- /dev/null +++ b/public/assets/json/Creeps.json @@ -0,0 +1,19 @@ +[ + { + "name": "basic", + "textures": [], + "textureArrayLength": 12, + "stats": { + "health": 2, + "speed": 0.04, + "special": null, + "resistance": { + "physical": 0, + "divine": 0, + "fire": 0, + "ice": 0, + "frostfire": 0 + } + } + } +] diff --git a/public/assets/Towers.json b/public/assets/json/Towers.json similarity index 93% rename from public/assets/Towers.json rename to public/assets/json/Towers.json index ba6f616..eac4067 100644 --- a/public/assets/Towers.json +++ b/public/assets/json/Towers.json @@ -3,6 +3,7 @@ "name": "Basic Tower", "behaviour": "BasicTowerBehaviour", "sprite": "basic_tower", + "texture": null, "description": "The building block of society, nothing more basic exists.", "stats": { "damage": 2, diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index 383e0b5..86a1189 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -1,120 +1,8 @@ import * as PIXI from 'pixi.js'; -import { CreepStatsDefinition, MissionDefinition, TowerDefinition } from './Definitions'; +import { CreepDefinition, CreepStatsDefinition, MissionDefinition, TowerDefinition } from './Definitions'; import { Engine } from './Bastion'; export default class GameAssets { - public static async LoadAssets() { - console.log('Loading Texture Assets'); - const text = new PIXI.Text({ - text: 'Loading textures. This might take a while.', - style: new PIXI.TextStyle({ - fill: 0x333333, - fontSize: 50, - }), - }); - text.x = Engine.app.canvas.width / 2; - text.y = Engine.app.canvas.height / 2; - text.anchor.set(0.5, 0.5); - Engine.app.stage.addChild(text); - GameAssets.Button01Texture = await PIXI.Assets.load({ - src: '/assets/gui/button_01.png', - }); - GameAssets.Button02Texture = await PIXI.Assets.load({ - src: '/assets/gui/button_02.png', - }); - GameAssets.Frame01Texture = await PIXI.Assets.load({ - src: '/assets/gui/frame_01.png', - }); - GameAssets.Frame02Texture = await PIXI.Assets.load({ - src: '/assets/gui/frame_02.png', - }); - GameAssets.FrameBackground = await PIXI.Assets.load({ - src: '/assets/gui/background_01.png', - }); - GameAssets.FrameTowerTab = await PIXI.Assets.load({ - src: '/assets/gui/background_02.png', - }); - GameAssets.VioletBackground = await PIXI.Assets.load({ - src: '/assets/gui/frame_violet.png', - }); - GameAssets.RedBackground = await PIXI.Assets.load({ - src: '/assets/gui/frame_red.png', - }); - GameAssets.GreenBackground = await PIXI.Assets.load({ - src: '/assets/gui/frame_green.png', - }); - GameAssets.HealthTexture = await PIXI.Assets.load({ - src: '/assets/gui/heart.png', - }); - GameAssets.GoldTexture = await PIXI.Assets.load({ - src: '/assets/gui/money.png', - }); - GameAssets.WaveTexture = await PIXI.Assets.load({ - src: '/assets/gui/wave.png', - }); - - GameAssets.BasicCreepTexture = await PIXI.Assets.load({ - src: '/assets/creeps/basic.jpg', - }); - - GameAssets.BasicTowerTexture = await PIXI.Assets.load({ - src: '/assets/towers/basic_tower.png', - }); - - GameAssets.BasicProjectileTexture = await PIXI.Assets.load({ - src: '/assets/projectiles/basic_tower.png', - }); - await PIXI.Assets.load({ - src: 'https://fonts.googleapis.com/css?family=Aclonica', - }); - - await this.LoadMissions(); - await this.LoadTowers(); - await this.LoadCreepStats(); - text.destroy(); - } - - public static async LoadCreepStats() { - const res = await fetch('/assets/CreepStats.json'); - const stats = await res.json(); - this.CreepStats = stats; - } - - private static async LoadMissions() { - // When adding missions, make sure to keep order. - GameAssets.Missions = [await this.LoadMission('/assets/missions/mission_01.json')]; - } - - private static async LoadTowers() { - const res = await fetch('/assets/Towers.json'); - const towers = await res.json(); - GameAssets.Towers = towers; - towers.forEach(async (tower) => { - let index = this.TowerSprites.length - 1; - if (index == -1) index = 0; - this.TowerSprites[index] = await PIXI.Assets.load({ - src: `/assets/towers/${tower.sprite}.png`, - }); - }); - } - - private static async LoadMission(missionUrl: string) { - const res = await fetch(missionUrl); - const mission = await res.json(); - await this.LoadBackground(mission.mapImage.url); - return mission; - } - - private static async LoadBackground(backgroundUrl: string) { - let index = this.MissionBackgrounds.length - 1; - if (index == -1) index = 0; - this.MissionBackgrounds[index] = await PIXI.Assets.load({ - src: backgroundUrl, - }); - } - - public static BasicCreepTexture: PIXI.Texture; - public static BasicTowerTexture: PIXI.Texture; public static BasicProjectileTexture: PIXI.Texture; @@ -132,10 +20,112 @@ export default class GameAssets { public static GoldTexture: PIXI.Texture; public static WaveTexture: PIXI.Texture; + public static Missions: MissionDefinition[]; public static MissionBackgrounds: PIXI.Texture[] = []; public static TowerSprites: PIXI.Texture[] = []; - public static Missions: MissionDefinition[]; public static Towers: TowerDefinition[]; - public static CreepStats: CreepStatsDefinition[]; - public static DebuggingEnabled: boolean = false; + public static Creeps: CreepDefinition[]; + + private static text; + private static async Load(src) { + this.text.text = 'Loading asset: ' + src; + return await PIXI.Assets.load({ + src: src, + }); + } + + public static async LoadAssets() { + if (this.text) { + throw 'Do not call GameAssets.LoadAssets() more than once.'; + return; + } + console.log('Loading Texture Assets'); + const t = new PIXI.Text({ + text: 'Loading textures. This might take a while.', + style: new PIXI.TextStyle({ + fill: 0x333333, + fontSize: 50, + }), + }); + t.x = Engine.app.canvas.width / 2; + t.y = Engine.app.canvas.height / 2; + t.anchor.set(0.5, 0.5); + Engine.app.stage.addChild(t); + + this.text = new PIXI.Text({ + text: '', + style: new PIXI.TextStyle({ + fill: 0x333333, + fontSize: 50, + }), + }); + this.text.x = Engine.app.canvas.width / 2; + this.text.y = Engine.app.canvas.height / 2 + 50; + this.text.anchor.set(0.5, 0.5); + Engine.app.stage.addChild(this.text); + this.Load('/aclonica.woff2'); + this.Button01Texture = await this.Load('/assets/gui/button_01.png'); + this.Button02Texture = await this.Load('/assets/gui/button_02.png'); + this.Frame01Texture = await this.Load('/assets/gui/frame_01.png'); + this.Frame02Texture = await this.Load('/assets/gui/frame_02.png'); + this.FrameBackground = await this.Load('/assets/gui/background_01.png'); + this.FrameTowerTab = await this.Load('/assets/gui/background_02.png'); + this.VioletBackground = await this.Load('/assets/gui/frame_violet.png'); + this.RedBackground = await this.Load('/assets/gui/frame_red.png'); + this.GreenBackground = await this.Load('/assets/gui/frame_green.png'); + this.HealthTexture = await this.Load('/assets/gui/heart.png'); + this.GoldTexture = await this.Load('/assets/gui/money.png'); + this.WaveTexture = await this.Load('/assets/gui/wave.png'); + this.BasicTowerTexture = await this.Load('/assets/towers/basic_tower.png'); + this.BasicProjectileTexture = await this.Load('/assets/projectiles/basic_tower.png'); + await this.LoadMissions(); + await this.LoadTowers(); + await this.LoadCreeps(); + t.destroy(); + this.text.destroy(); + // Set this.text = true to disallow calling GameAssets.LoadAssets() again + this.text = true; + } + + private static async LoadCreeps() { + 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`); + creep.textures[i] = texture; + } + } + } + + private static async LoadMissions() { + // When adding missions, make sure to keep order. + GameAssets.Missions = [await this.LoadMission('/assets/missions/mission_01.json')]; + } + + private static async LoadTowers() { + const res = await fetch('/assets/json/Towers.json'); + const towers = await res.json(); + GameAssets.Towers = towers; + towers.forEach(async (tower) => { + let index = this.TowerSprites.length - 1; + if (index == -1) index = 0; + this.TowerSprites[index] = await this.Load(`/assets/towers/${tower.sprite}.png`); + }); + } + + private static async LoadMission(missionUrl: string) { + const res = await fetch(missionUrl); + const mission = await res.json(); + await this.LoadBackground(mission.mapImage.url); + return mission; + } + + private static async LoadBackground(backgroundUrl: string) { + let index = this.MissionBackgrounds.length - 1; + if (index == -1) index = 0; + this.MissionBackgrounds[index] = await this.Load(backgroundUrl); + } } diff --git a/src/classes/Definitions.ts b/src/classes/Definitions.ts index a1328cf..5153276 100644 --- a/src/classes/Definitions.ts +++ b/src/classes/Definitions.ts @@ -1,3 +1,4 @@ +import * as PIXI from 'pixi.js'; export type MissionDefinition = { name: string; description: string; @@ -28,6 +29,13 @@ export type WaveDefinition = { creeps: CreepType[]; }; +export type CreepDefinition = { + name: string; + textures: PIXI.Texture[]; + textureArrayLength: number; + stats: CreepStatsDefinition; +}; + export type CreepStatsDefinition = { health: number; speed: number; @@ -48,6 +56,7 @@ export type TowerDefinition = { behaviour: string; sprite: string; description: string; + texture: PIXI.Texture; stats: TowerStatsDefinition; }; diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts index a7aa9d2..d317946 100644 --- a/src/classes/game/Creep.ts +++ b/src/classes/game/Creep.ts @@ -15,11 +15,12 @@ export enum CreepEvents { export default class Creep extends GameObject { public id: number; public creepType: CreepType; - private sprite: PIXI.Sprite; + private sprite: PIXI.AnimatedSprite; private path: PathDefinition; private stats: CreepStatsDefinition; private pathIndex: number = 0; private speed: number; + private direction: number = 1; public health: number; public maxHealth: number; public escaped: boolean = false; @@ -31,10 +32,12 @@ export default class Creep extends GameObject { super(); this.creepType = creepType; // Structured clone is used just in case, so that 1 creep doesnt alter stats for all creeps. - this.stats = structuredClone(Assets.CreepStats[this.creepType]); - this.sprite = new PIXI.Sprite({ - texture: GameAssets.BasicCreepTexture, - }); + this.stats = structuredClone(Assets.Creeps[this.creepType].stats); + this.sprite = new PIXI.AnimatedSprite(Assets.Creeps[this.creepType].textures); + // Initially flip sprite to the right, since the asset is facing left. + this.sprite.scale.x *= -1; + this.sprite.anchor.set(0.5, 0.5); + this.sprite.play(); this.id = id; // Explanation: WaveManager spawns all creeps instantly, and since I don't want // them to show up on the beginning while they are waiting, I put them outside the visible @@ -64,6 +67,7 @@ export default class Creep extends GameObject { this.destroy(); // The reason for setting this.dead instead of deleting self is because // I need to allow WaveManager/Grid to manage their death and keep array up to date. + // Also I realised that you can't do that from the object itself. this.dead = true; return; } @@ -81,6 +85,19 @@ export default class Creep extends GameObject { const targetY = targetCell[0] * Engine.GridCellSize + Engine.GridCellSize / 2; const directionX = targetCell[1] - currentCell[1]; const directionY = targetCell[0] - currentCell[0]; + if (directionX > 0) { + // Going right + if (this.direction != 1) { + this.direction = 1; + this.sprite.scale.x *= -1; + } + } else if (directionX < 0) { + // Going left + if (this.direction != -1) { + this.direction = -1; + this.sprite.scale.x *= -1; + } + } let deltaX = this.speed * elapsedMS * directionX; let deltaY = this.speed * elapsedMS * directionY; let increaseIndex = false; @@ -108,7 +125,8 @@ export default class Creep extends GameObject { this.x += deltaX; this.y += deltaY; if (increaseIndex) this.pathIndex++; - this.draw(); + this.container.x = this.x; + this.container.y = this.y; } public takeDamage(amount: number) { @@ -123,9 +141,4 @@ export default class Creep extends GameObject { super.destroy(); this.container.removeChildren(); } - protected draw() { - this.sprite.anchor.set(0.5, 0.5); - this.container.x = this.x; - this.container.y = this.y; - } } diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 39b6691..7959cf6 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -38,20 +38,19 @@ export class Cell extends GameObject { Engine.Grid.onGridCellClicked(row, column); }; - if (!GameAssets.DebuggingEnabled) return; - const text = new PIXI.Text({ - text: `${this.row}|${this.column}`, - style: new PIXI.TextStyle({ - fill: 0xffffff, - dropShadow: true, - fontSize: 16, - }), - }); - this.container.addChild(text); - text.anchor.set(0.5, 0.5); - text.x = this.bb.width / 2; - text.y = this.bb.height / 2; - if (isPath) text.text += 'p'; + // const text = new PIXI.Text({ + // text: `${this.row}|${this.column}`, + // style: new PIXI.TextStyle({ + // fill: 0xffffff, + // dropShadow: true, + // fontSize: 16, + // }), + // }); + // this.container.addChild(text); + // text.anchor.set(0.5, 0.5); + // text.x = this.bb.width / 2; + // text.y = this.bb.height / 2; + // if (isPath) text.text += 'p'; } public gDraw() { this.g = new PIXI.Graphics({