more balance

This commit is contained in:
koneko 2025-03-08 21:01:26 +01:00
parent a3e8be3c18
commit 185faa42e2
14 changed files with 142 additions and 62 deletions

View File

@ -26,8 +26,7 @@ List of things to implement following the "release" of the minimum viable produc
## Other ## Other
- [ ] Disable player action during combat phase.
- [ ] Add sound effects - [ ] Add sound effects
- [x] Tutorial image/mission - [x] Tutorial image/mission
- [ ] Pause menu - [x] Pause menu
- [x] Score screen when winning/losing map - [x] Score screen when winning/losing map

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -2,14 +2,15 @@
{ {
"name": "basic", "name": "basic",
"sprite": "wood", "sprite": "wood",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 5, "health": 2,
"speed": 4, "speed": 3,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0.05,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0,
@ -20,11 +21,12 @@
{ {
"name": "quick", "name": "quick",
"sprite": "zombie", "sprite": "zombie",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 8, "textureArrayLength": 8,
"stats": { "stats": {
"health": 2, "health": 1,
"speed": 6, "speed": 5,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
@ -38,10 +40,11 @@
{ {
"name": "tank", "name": "tank",
"sprite": "skeleton", "sprite": "skeleton",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 12, "health": 5,
"speed": 2, "speed": 2,
"special": null, "special": null,
"resistance": { "resistance": {
@ -56,29 +59,50 @@
{ {
"name": "cloaker", "name": "cloaker",
"sprite": "hood", "sprite": "hood",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 12, "textureArrayLength": 12,
"stats": { "stats": {
"health": 12, "health": 7,
"speed": 2, "speed": 3,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
"divine": 0, "divine": 1,
"fire": 0, "fire": 1,
"ice": 0, "ice": 1,
"frostfire": 0 "frostfire": 1
} }
} }
}, },
{ {
"name": "demon", "name": "demon",
"sprite": "demon", "sprite": "demon",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 8, "textureArrayLength": 8,
"stats": { "stats": {
"health": 12, "health": 5,
"speed": 2, "speed": 3,
"special": null,
"resistance": {
"physical": 1,
"divine": -0.25,
"fire": 1,
"ice": 1,
"frostfire": 1
}
}
},
{
"name": "maker",
"sprite": "pumpkin",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 11,
"stats": {
"health": 5,
"speed": 3,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0,
@ -89,17 +113,57 @@
} }
} }
}, },
{ {
"name": "maker", "name": "monster",
"sprite": "pumpkin", "sprite": "green",
"tint": "0xffffff",
"textures": [], "textures": [],
"textureArrayLength": 11, "textureArrayLength": 12,
"stats": { "stats": {
"health": 11, "health": 30,
"speed": 2, "speed": 1,
"special": null, "special": null,
"resistance": { "resistance": {
"physical": 0, "physical": 0.05,
"divine": 0.05,
"fire": -0.25,
"ice": 0.05,
"frostfire": 0.05
}
}
},
{
"name": "remaker",
"sprite": "orange",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 2,
"speed": 3,
"special": null,
"resistance": {
"physical": 0.05,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
}
},
{
"name": "elite",
"sprite": "phood",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 2,
"speed": 3,
"special": null,
"resistance": {
"physical": 0.05,
"divine": 0, "divine": 0,
"fire": 0, "fire": 0,
"ice": 0, "ice": 0,

View File

@ -584,7 +584,7 @@
"attackSpeedUp": 100, "attackSpeedUp": 100,
"rangeUp": 2, "rangeUp": 2,
"timeToLiveUp": 200, "timeToLiveUp": 200,
"pierceUp": 20, "pierceUp": 3,
"gemValueUp": 0 "gemValueUp": 0
} }
], ],

View File

@ -28,12 +28,12 @@
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Shoots 8 projectiles in a circle, they may miss.", "description": "Shoots 8 projectiles in a circle, they may miss.",
"stats": { "stats": {
"damage": 3, "damage": 1,
"cooldown": 3000, "cooldown": 2000,
"gemSlotsAmount": 2, "gemSlotsAmount": 2,
"cost": 50, "cost": 55,
"range": 3, "range": 3,
"timeToLive": 20, "timeToLive": 18,
"pierce": 5 "pierce": 5
} }
}, },
@ -47,10 +47,10 @@
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Zap zap zap! This towers shots connect to other enemies!", "description": "Zap zap zap! This towers shots connect to other enemies!",
"stats": { "stats": {
"damage": 3, "damage": 2,
"cooldown": 3500, "cooldown": 3500,
"gemSlotsAmount": 2, "gemSlotsAmount": 2,
"cost": 100, "cost": 110,
"range": 3, "range": 3,
"timeToLive": 12, "timeToLive": 12,
"pierce": 1 "pierce": 1
@ -66,7 +66,7 @@
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Doesn't shoot, instead buffs other towers with some of its power.", "description": "Doesn't shoot, instead buffs other towers with some of its power.",
"stats": { "stats": {
"damage": 4, "damage": 3,
"cooldown": 1000, "cooldown": 1000,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 200, "cost": 200,
@ -86,12 +86,12 @@
"description": "Behaves like the Basic Tower, only its shots stop creeps in their tracks!", "description": "Behaves like the Basic Tower, only its shots stop creeps in their tracks!",
"stats": { "stats": {
"damage": 2, "damage": 2,
"cooldown": 2250, "cooldown": 2200,
"gemSlotsAmount": 2, "gemSlotsAmount": 2,
"cost": 75, "cost": 80,
"range": 3.25, "range": 3.25,
"timeToLive": 12, "timeToLive": 12,
"pierce": 1 "pierce": 2
} }
}, },
{ {
@ -104,13 +104,13 @@
"projectileTexturesArrayLength": 4, "projectileTexturesArrayLength": 4,
"description": "Shoots a quick, high pierce rail projectile at creeps.", "description": "Shoots a quick, high pierce rail projectile at creeps.",
"stats": { "stats": {
"damage": 4, "damage": 5,
"cooldown": 3750, "cooldown": 5020,
"gemSlotsAmount": 3, "gemSlotsAmount": 3,
"cost": 125, "cost": 134,
"range": 2.5, "range": 2.5,
"timeToLive": 12, "timeToLive": 12,
"pierce": 30 "pierce": 10
} }
}, },
{ {

View File

@ -62,15 +62,30 @@
{ {
"firstCreepSpawnTick": 500, "firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000, "spawnIntervalTicks": 1000,
"creeps": [0, 0, 0] "creeps": [0, 0, 0, 0, 0]
}, },
{ {
"firstCreepSpawnTick": 4000, "firstCreepSpawnTick": 4000,
"spawnIntervalTicks": 1000, "spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0] "creeps": [0, 0, 0, 0, 0]
} }
], ],
"offeredGems": [0, 1] "offeredGems": [0, 1, 0, 1]
},
{
"waves": [
{
"firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
{
"firstCreepSpawnTick": 1000,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
],
"offeredGems": [0, 1, 0, 1]
} }
] ]
} }

View File

@ -99,7 +99,7 @@
{ {
"firstCreepSpawnTick": 500, "firstCreepSpawnTick": 500,
"spawnIntervalTicks": 500, "spawnIntervalTicks": 500,
"creeps": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 2] "creeps": [0]
} }
], ],
"offeredGems": [0, 0, 0, 0] "offeredGems": [0, 0, 0, 0]

View File

@ -49,7 +49,7 @@ export default class GameAssets {
private static text; private static text;
private static counter = 0; private static counter = 0;
private static async Load(src) { private static async Load(src) {
this.text.text = `Loading asset: ${src} (${this.counter}/99)`; this.text.text = `Loading asset: ${src} (${this.counter}/102)`;
this.counter++; this.counter++;
return await PIXI.Assets.load({ return await PIXI.Assets.load({
src: src, src: src,

View File

@ -32,6 +32,7 @@ export type WaveDefinition = {
export type CreepDefinition = { export type CreepDefinition = {
name: string; name: string;
sprite: string; sprite: string;
tint: PIXI.ColorSource;
textures: PIXI.Texture[]; textures: PIXI.Texture[];
textureArrayLength: number; textureArrayLength: number;
stats: CreepStatsDefinition; stats: CreepStatsDefinition;
@ -113,6 +114,9 @@ export enum CreepType {
Cloaker = 3, Cloaker = 3,
Demon = 4, Demon = 4,
Maker = 5, Maker = 5,
Monster = 6,
Remaker = 7,
Elite = 8,
} }
export enum GemType { export enum GemType {

View File

@ -49,6 +49,7 @@ export default class Creep extends GameObject {
this.sprite.scale.x *= -1; this.sprite.scale.x *= -1;
this.sprite.anchor.set(0.5, 0.5); this.sprite.anchor.set(0.5, 0.5);
this.sprite.animationSpeed = 0.3; this.sprite.animationSpeed = 0.3;
this.sprite.tint = Assets.Creeps[this.creepType].tint;
this.sprite.play(); this.sprite.play();
this.id = id; this.id = id;
// Explanation: WaveManager spawns all creeps instantly, and since I don't want // Explanation: WaveManager spawns all creeps instantly, and since I don't want
@ -63,20 +64,20 @@ export default class Creep extends GameObject {
this.health = this.stats.health; this.health = this.stats.health;
this.maxHealth = this.stats.health; this.maxHealth = this.stats.health;
this.path = path; this.path = path;
// Added + 32 to center them.
this.x = path[0][0] * Engine.GridCellSize + Engine.GridCellSize / 2; this.x = path[0][0] * Engine.GridCellSize + Engine.GridCellSize / 2;
this.y = path[0][1] * Engine.GridCellSize + Engine.GridCellSize / 2; this.y = path[0][1] * Engine.GridCellSize + Engine.GridCellSize / 2;
// TODO: Unsubscribe from events once the scene is destroyed
Engine.GameScene.events.on( Engine.GameScene.events.on(
CreepEvents.TakenDamage, CreepEvents.TakenDamage,
(creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => { (creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => {
if (creepID != this.id) return; if (creepID != this.id) return;
if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) { if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) {
damage = damage * 1.5; damage = damage * 1.5;
console.log('multiplying damage, ' + damage);
} }
// Apply resistances. // Apply resistances.
this.health -= damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical); this.health -= Math.max(
damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical),
0
);
if (gemResistanceModifications.fire != 0) if (gemResistanceModifications.fire != 0)
this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0); this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0);
if (gemResistanceModifications.ice != 0) if (gemResistanceModifications.ice != 0)
@ -98,7 +99,6 @@ export default class Creep extends GameObject {
CreepEvents.GiveEffect, CreepEvents.GiveEffect,
(creepID: number, effect: CreepEffects, durationInMS: number) => { (creepID: number, effect: CreepEffects, durationInMS: number) => {
if (creepID != this.id) return; if (creepID != this.id) return;
console.log(' I CAUGHT THE EVENT!');
if (this.effects.find((e) => e.effectEnum == effect) == undefined) if (this.effects.find((e) => e.effectEnum == effect) == undefined)
this.effects.push(new Effect(effect, durationInMS)); this.effects.push(new Effect(effect, durationInMS));
} }
@ -224,13 +224,13 @@ export default class Creep extends GameObject {
this.container.y = this.y; this.container.y = this.y;
} }
public takeDamage(amount: number) { // public takeDamage(amount: number) {
this.health -= amount; // this.health -= amount;
if (this.health < 0 && !this.died) { // if (this.health < 0 && !this.died) {
this.died = true; // this.died = true;
this.events.emit(CreepEvents.Died, this); // this.events.emit(CreepEvents.Died, this);
} // }
} // }
public destroy() { public destroy() {
super.destroy(); super.destroy();

View File

@ -19,16 +19,15 @@ export default class GamePausedDialog extends ModalDialogBase {
protected override createContent(): PIXI.Container { protected override createContent(): PIXI.Container {
const container = new PIXI.Container(); const container = new PIXI.Container();
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Main Menu', ButtonTexture.Button01); this.btnContinue = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Continue', ButtonTexture.Button01);
this.btnMainMenu.onClick = this.onMainMenuClick.bind(this); this.btnContinue.onClick = this.onContinueClick.bind(this);
container.addChild(this.btnMainMenu.container); container.addChild(this.btnContinue.container);
this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01); this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01);
this.btnRetry.onClick = this.onRetryClick.bind(this); this.btnRetry.onClick = this.onRetryClick.bind(this);
container.addChild(this.btnRetry.container); container.addChild(this.btnRetry.container);
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Main Menu', ButtonTexture.Button01);
this.btnContinue = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Continue', ButtonTexture.Button01); this.btnMainMenu.onClick = this.onMainMenuClick.bind(this);
this.btnContinue.onClick = this.onContinueClick.bind(this); container.addChild(this.btnMainMenu.container);
container.addChild(this.btnContinue.container);
return container; return container;
} }

View File

@ -251,7 +251,6 @@ export class GameScene extends Scene {
}, },
}, },
}); });
// offerText.x -= offerText.width;
Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0); Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0);
gemsToOffer.forEach((gType, index) => { gemsToOffer.forEach((gType, index) => {
let _Gem = new Gem(gType, true); let _Gem = new Gem(gType, true);