add resistances, needs more balance

This commit is contained in:
koneko 2025-02-08 18:28:15 +01:00
parent 18fe4d7c20
commit 2fcca0f21c
8 changed files with 190 additions and 40 deletions

View File

@ -4,7 +4,7 @@ List of things to implement following the "release" of the minimum viable produc
## Creeps
- [ ] Elemental resistances/attunement
- [x] Elemental resistances/attunement
- [x] Proper animation via PNG sequence
- [x] More variety in Creeps
- [x] Health bar
@ -29,4 +29,4 @@ List of things to implement following the "release" of the minimum viable produc
- [ ] Add sound effects
- [ ] Tutorial image/mission
- [ ] Pause menu
- [ ] Score screen when winning/losing map
- [x] Score screen when winning/losing map

View File

@ -31,14 +31,14 @@
{
"physical": 0,
"divine": 0,
"fire": 0.5,
"fire": 0.25,
"ice": 0,
"frostfire": 0
},
{
"physical": 0,
"divine": 0,
"fire": 0.5,
"fire": 0.25,
"ice": 0,
"frostfire": 0
}
@ -47,7 +47,7 @@
{
"name": "Yeti Gem",
"description": "Yeti gem description. Something something, write this while drunk or something.",
"color": "dodgerblue",
"color": "#32e4fc",
"type": "Yeti",
"totalLevels": 2,
"textures": [],

View File

@ -3,11 +3,11 @@
"height": 4300,
"width": 2
},
"activeFile": "tiled/Mission01.tmx",
"activeFile": "tiled/07_final_stretch.tmx",
"expandedProjectPaths": [
".",
"tiled",
"assets/missions",
"."
"assets/missions"
],
"file.lastUsedOpenFilter": "All Files (*)",
"fileStates": {
@ -102,6 +102,14 @@
"y": 474.6666666666666
}
},
"tiled/07_final_stretch.tmx": {
"scale": 0.5,
"selectedLayer": 0,
"viewCenter": {
"x": 971,
"y": 270
}
},
"tiled/Mission01.tmx": {
"scale": 1,
"selectedLayer": 0,
@ -122,26 +130,26 @@
"openFiles": [
"tiled/04_crossroads.tmx",
"tiled/05_the_maze.tmx",
"tiled/Mission01.tmx",
"tiled/01_first_steps.tmx",
"tiled/02_the_turn.tmx",
"tiled/03_fork_in_the_road.tmx",
"tiled/06_multiple_fronts.tmx"
"tiled/06_multiple_fronts.tmx",
"tiled/07_final_stretch.tmx"
],
"project": "maps.tiled-project",
"recentFiles": [
"tiled/Mission01.tmx",
"tiled/04_crossroads.tmx",
"tiled/05_the_maze.tmx",
"tiled/06_multiple_fronts.tmx",
"tiled/03_fork_in_the_road.tmx",
"tiled/02_the_turn.tmx",
"tiled/01_first_steps.tmx",
"tiled/02_the_turn.tmx",
"tiled/03_fork_in_the_road.tmx",
"tiled/06_multiple_fronts.tmx",
"tiled/07_final_stretch.tmx",
"tiled/Mission01.tmx",
"tiled/01_first_steps..tmx",
"Mission011.tmx",
"Tileset.tsx",
"Mission01.tmx",
"C:/home/koneko/dumping/tiles/TiledTDThree64.tmx"
"Mission01.tmx"
],
"tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)"
}

View File

@ -1,7 +1,7 @@
import GameAssets from '../Assets';
import Assets from '../Assets';
import { Engine } from '../Bastion';
import { CreepStatsDefinition, CreepType, PathDefinition } from '../Definitions';
import { CreepResistancesDefinition, CreepStatsDefinition, CreepType, PathDefinition } from '../Definitions';
import GameObject from '../GameObject';
import * as PIXI from 'pixi.js';
import { CreepEvents } from '../Events';
@ -52,11 +52,30 @@ export default class Creep extends GameObject {
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) => {
if (creepID != this.id) return;
this.health -= damage;
this.UpdateHealthbar();
});
Engine.GameScene.events.on(
CreepEvents.TakenDamage,
(creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => {
if (creepID != this.id) return;
// Apply resistances.
this.health -= damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical);
if (gemResistanceModifications.fire != 0)
this.health -= Math.max(damage * (gemResistanceModifications.fire - this.stats.resistance.fire), 0);
if (gemResistanceModifications.ice != 0)
this.health -= Math.max(damage * (gemResistanceModifications.ice - this.stats.resistance.ice), 0);
if (gemResistanceModifications.frostfire != 0)
this.health -= Math.max(
damage * (gemResistanceModifications.frostfire - this.stats.resistance.frostfire),
0
);
if (gemResistanceModifications.divine != 0)
this.health -= Math.max(
damage * (gemResistanceModifications.divine - this.stats.resistance.divine),
0
);
this.UpdateHealthbar();
}
);
Engine.Grid.container.addChild(this.container);
this.container.addChild(this.healthBarGraphics);
this.container.addChild(this.sprite);

View File

@ -4,6 +4,7 @@ import { Engine } from '../Bastion';
import Creep from './Creep';
import { CreepEvents } from '../Events';
import { Tower } from './Tower';
import { CreepResistancesDefinition } from '../Definitions';
export function calculateAngleToPoint(x, y, targetX, targetY) {
const dx = targetX - x;
@ -22,15 +23,26 @@ export default class Projectile extends GameObject {
public pierce: number = 1;
public timeToLive: number;
public parent: Tower;
private collidedCreepIDs = [];
constructor(x, y, textures, angle, damage, tint, tower: Tower) {
public gemResistanceModifications: CreepResistancesDefinition;
public collidedCreepIDs = [];
constructor(
x,
y,
textures,
angle,
damage,
tint,
timeToLive,
pierce,
gemResistanceModifications: CreepResistancesDefinition
) {
super();
this.x = x;
this.y = y;
this.timeToLive = tower.computedTimeToLive;
this.pierce = tower.computedPierce;
this.parent = tower;
this.timeToLive = timeToLive;
this.pierce = pierce;
this.damage = damage;
this.gemResistanceModifications = gemResistanceModifications;
this.sprite = new PIXI.AnimatedSprite({ textures: textures, scale: 0.25, rotation: angle });
this.sprite.anchor.set(0.5, 0.5);
this.sprite.play();
@ -73,7 +85,7 @@ export default class Projectile extends GameObject {
}
public onCollide(creep) {
Engine.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, this.damage);
Engine.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, this.damage, this.gemResistanceModifications);
}
public checkCollision(creep: Creep) {

View File

@ -1,7 +1,7 @@
import { Engine } from '../Bastion';
import * as PIXI from 'pixi.js';
import GameObject from '../GameObject';
import { GemType, TowerDefinition } from '../Definitions';
import { CreepResistancesDefinition, GemType, TowerDefinition } from '../Definitions';
import { Cell } from './Grid';
import { TowerBehaviours } from './TowerManager';
import Projectile, { calculateAngleToPoint } from './Projectile';
@ -29,6 +29,7 @@ export class Tower extends GameObject {
public computedRange: number;
public computedTimeToLive: number;
public computedPierce: number;
public totalGemResistanceModifications: CreepResistancesDefinition;
public parent: Cell;
constructor(row, column, texture, definition, behaviour) {
@ -103,14 +104,23 @@ export class Tower extends GameObject {
public Shoot(angle) {
let x = this.column * Engine.GridCellSize + Engine.GridCellSize / 2;
let y = this.row * Engine.GridCellSize + Engine.GridCellSize / 2;
let combinedTint = 0xffffff;
this.slottedGems.forEach((gem) => {
let rgb = new PIXI.Color(gem.definition.color).toRgb();
combinedTint =
((combinedTint & 0xff0000) + (rgb.r << 16)) |
((combinedTint & 0x00ff00) + (rgb.g << 8)) |
((combinedTint & 0x0000ff) + rgb.b);
});
let combinedTint = new PIXI.Color('white');
if (this.slottedGems.length > 0) {
let color = new PIXI.Color(this.slottedGems[0].definition.color);
for (let i = 1; i < this.slottedGems.length; i++) {
const element = this.slottedGems[i];
color.multiply(element.definition.color);
}
combinedTint = color;
}
// this.slottedGems.forEach((gem) => {
// let rgb = new PIXI.Color(gem.definition.color).toRgb();
// combinedTint =
// ((combinedTint & 0xff0000) + (rgb.r << 16)) |
// ((combinedTint & 0x00ff00) + (rgb.g << 8)) |
// ((combinedTint & 0x0000ff) + rgb.b);
// });
// combinedTint = new PIXI.Color(this.slottedGems[0].definition.color).
let proj = new Projectile(
x,
y,
@ -118,7 +128,9 @@ export class Tower extends GameObject {
angle,
this.computedDamageToDeal,
combinedTint,
this
this.computedTimeToLive,
this.computedPierce,
this.totalGemResistanceModifications
);
this.projectiles.push(proj);
return proj;

View File

@ -13,7 +13,9 @@ import { Tower } from './Tower';
function projectileCheck(tower: Tower, elapsedMS: number) {
tower.projectiles.forEach((proj) => {
if (proj.deleteMe) {
tower.damageDealt += tower.computedDamageToDeal;
proj.collidedCreepIDs.forEach(() => {
tower.damageDealt += tower.computedDamageToDeal;
});
tower.projectiles.splice(tower.projectiles.indexOf(proj), 1);
proj = null;
} else proj.update(elapsedMS);
@ -32,7 +34,13 @@ export function computeGemImprovements(tower: Tower) {
let gemRangeUp = 0;
let gemTimeToLiveUp = 0;
let gemPierceUp = 0;
tower.totalGemResistanceModifications = {
fire: 0,
frostfire: 0,
divine: 0,
ice: 0,
physical: 0,
};
tower.slottedGems.forEach((gem) => {
let ccurrentGemImprovements = gem.currentGemImprovement();
gemDamage += ccurrentGemImprovements.damageUp;
@ -40,6 +48,13 @@ export function computeGemImprovements(tower: Tower) {
gemRangeUp += ccurrentGemImprovements.rangeUp;
gemTimeToLiveUp += ccurrentGemImprovements.timeToLiveUp;
gemPierceUp += ccurrentGemImprovements.pierceUp;
let gemResMod = gem.currentGemResistanceModifications();
tower.totalGemResistanceModifications.physical += gemResMod.physical;
tower.totalGemResistanceModifications.ice += gemResMod.ice;
tower.totalGemResistanceModifications.fire += gemResMod.fire;
tower.totalGemResistanceModifications.divine += gemResMod.divine;
tower.totalGemResistanceModifications.frostfire += gemResMod.frostfire;
});
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
tower.computedAttackSpeed = tower.definition.stats.cooldown - gemAttackSpeedUp;

View File

@ -109,6 +109,11 @@ export default class TowerPanel extends GuiObject {
public damageText: PIXI.Text;
public totalDamage: PIXI.Text;
public attackSpeedText: PIXI.Text;
public fireResDamage: PIXI.Text;
public iceResDamage: PIXI.Text;
public frostFireResDamage: PIXI.Text;
public divineResDamage: PIXI.Text;
public physicalResDamage: PIXI.Text;
constructor(bounds: PIXI.Rectangle) {
super(false);
@ -202,6 +207,79 @@ export default class TowerPanel extends GuiObject {
}),
});
this.container.addChild(this.totalDamage);
this.fireResDamage = new PIXI.Text({
x: 10,
y: 170,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xfc5353,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.fireResDamage);
this.iceResDamage = new PIXI.Text({
x: 10,
y: 190,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0x32e4fc,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.iceResDamage);
this.frostFireResDamage = new PIXI.Text({
x: 10,
y: 210,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xd753fc,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.frostFireResDamage);
this.divineResDamage = new PIXI.Text({
x: 10,
y: 230,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xfcee53,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.divineResDamage);
this.physicalResDamage = new PIXI.Text({
x: 10,
y: 250,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xffffff,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.physicalResDamage);
}
private MakeSlots(tower: Tower) {
this.vGems.forEach((vGem) => {
@ -254,6 +332,12 @@ export default class TowerPanel extends GuiObject {
this.totalDamage.text = 'Damage dealt: ' + tower.damageDealt + ' damage';
this.attackSpeedText.x = this.damageText.width + 10;
this.attackSpeedText.text = ` every ${Math.floor((tower.computedAttackSpeed / 60) * 100) / 100}s`;
this.fireResDamage.text = `+${tower.totalGemResistanceModifications.fire * 100}% Fire damage`;
this.iceResDamage.text = `+${tower.totalGemResistanceModifications.ice * 100}% Ice damage`;
this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`;
this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`;
this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`;
}
private ShowLeft() {
this.towerPanel.x = -100;