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
- [ ] Disable player action during combat phase.
- [ ] Add sound effects
- [x] Tutorial image/mission
- [ ] Pause menu
- [x] Pause menu
- [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",
"sprite": "wood",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 5,
"speed": 4,
"health": 2,
"speed": 3,
"special": null,
"resistance": {
"physical": 0,
"physical": 0.05,
"divine": 0,
"fire": 0,
"ice": 0,
@ -20,11 +21,12 @@
{
"name": "quick",
"sprite": "zombie",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 8,
"stats": {
"health": 2,
"speed": 6,
"health": 1,
"speed": 5,
"special": null,
"resistance": {
"physical": 0,
@ -38,10 +40,11 @@
{
"name": "tank",
"sprite": "skeleton",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 12,
"health": 5,
"speed": 2,
"special": null,
"resistance": {
@ -56,29 +59,50 @@
{
"name": "cloaker",
"sprite": "hood",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 12,
"speed": 2,
"health": 7,
"speed": 3,
"special": null,
"resistance": {
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
"divine": 1,
"fire": 1,
"ice": 1,
"frostfire": 1
}
}
},
{
"name": "demon",
"sprite": "demon",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 8,
"stats": {
"health": 12,
"speed": 2,
"health": 5,
"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,
"resistance": {
"physical": 0,
@ -89,17 +113,57 @@
}
}
},
{
"name": "maker",
"sprite": "pumpkin",
"name": "monster",
"sprite": "green",
"tint": "0xffffff",
"textures": [],
"textureArrayLength": 11,
"textureArrayLength": 12,
"stats": {
"health": 11,
"speed": 2,
"health": 30,
"speed": 1,
"special": null,
"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,
"fire": 0,
"ice": 0,

View File

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

View File

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

View File

@ -62,15 +62,30 @@
{
"firstCreepSpawnTick": 500,
"spawnIntervalTicks": 1000,
"creeps": [0, 0, 0]
"creeps": [0, 0, 0, 0, 0]
},
{
"firstCreepSpawnTick": 4000,
"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,
"spawnIntervalTicks": 500,
"creeps": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 2]
"creeps": [0]
}
],
"offeredGems": [0, 0, 0, 0]

View File

@ -49,7 +49,7 @@ export default class GameAssets {
private static text;
private static counter = 0;
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++;
return await PIXI.Assets.load({
src: src,

View File

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

View File

@ -49,6 +49,7 @@ export default class Creep extends GameObject {
this.sprite.scale.x *= -1;
this.sprite.anchor.set(0.5, 0.5);
this.sprite.animationSpeed = 0.3;
this.sprite.tint = Assets.Creeps[this.creepType].tint;
this.sprite.play();
this.id = id;
// 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.maxHealth = this.stats.health;
this.path = path;
// Added + 32 to center them.
this.x = path[0][0] * 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(
CreepEvents.TakenDamage,
(creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => {
if (creepID != this.id) return;
if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) {
damage = damage * 1.5;
console.log('multiplying damage, ' + damage);
}
// 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)
this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0);
if (gemResistanceModifications.ice != 0)
@ -98,7 +99,6 @@ export default class Creep extends GameObject {
CreepEvents.GiveEffect,
(creepID: number, effect: CreepEffects, durationInMS: number) => {
if (creepID != this.id) return;
console.log(' I CAUGHT THE EVENT!');
if (this.effects.find((e) => e.effectEnum == effect) == undefined)
this.effects.push(new Effect(effect, durationInMS));
}
@ -224,13 +224,13 @@ export default class Creep extends GameObject {
this.container.y = this.y;
}
public takeDamage(amount: number) {
this.health -= amount;
if (this.health < 0 && !this.died) {
this.died = true;
this.events.emit(CreepEvents.Died, this);
}
}
// public takeDamage(amount: number) {
// this.health -= amount;
// if (this.health < 0 && !this.died) {
// this.died = true;
// this.events.emit(CreepEvents.Died, this);
// }
// }
public destroy() {
super.destroy();

View File

@ -19,16 +19,15 @@ export default class GamePausedDialog extends ModalDialogBase {
protected override createContent(): PIXI.Container {
const container = new PIXI.Container();
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Main Menu', ButtonTexture.Button01);
this.btnMainMenu.onClick = this.onMainMenuClick.bind(this);
container.addChild(this.btnMainMenu.container);
this.btnContinue = new Button(new PIXI.Rectangle(0, 0, 300, 60), 'Continue', ButtonTexture.Button01);
this.btnContinue.onClick = this.onContinueClick.bind(this);
container.addChild(this.btnContinue.container);
this.btnRetry = new Button(new PIXI.Rectangle(0, 70, 300, 60), 'Retry', ButtonTexture.Button01);
this.btnRetry.onClick = this.onRetryClick.bind(this);
container.addChild(this.btnRetry.container);
this.btnContinue = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Continue', ButtonTexture.Button01);
this.btnContinue.onClick = this.onContinueClick.bind(this);
container.addChild(this.btnContinue.container);
this.btnMainMenu = new Button(new PIXI.Rectangle(0, 140, 300, 60), 'Main Menu', ButtonTexture.Button01);
this.btnMainMenu.onClick = this.onMainMenuClick.bind(this);
container.addChild(this.btnMainMenu.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);
gemsToOffer.forEach((gType, index) => {
let _Gem = new Gem(gType, true);