survivors: adding enemy health scaling

adding dmg bonus items and dmg scaling stat for player
This commit is contained in:
Sheldan
2025-09-15 01:14:43 +02:00
parent 26ad150b59
commit ef162a7dc2
7 changed files with 164 additions and 25 deletions

View File

@@ -6,7 +6,7 @@ import type {Projectile} from "./projectile.ts";
import {StraightProjectile} from "./projectile.ts";
import {HealthPack, ItemDrop, LevelDrop, MoneyDrop} from "./drop.ts";
import {ItemManagement} from "./items.ts";
import {ProjectileStats} from "./stats.ts";
import {EnemyStats, ProjectileStats} from "./stats.ts";
import {EnemyStatus} from "./status.ts";
import {NumberDisplayParticle} from "./particles.ts";
@@ -15,13 +15,15 @@ export abstract class Enemy implements Placeable, Drawable, Acting, Healthy {
protected speed: number;
protected world: World;
protected size: number
protected status: EnemyStatus = new EnemyStatus(10);
protected status: EnemyStatus;
protected drops: KillChanceTable;
constructor(position: Vector) {
constructor(position: Vector, enemyStats: EnemyStats) {
this.drops = new KillChanceTable();
this.drops.addDrop( {chance: 10, creationMethod: this.spawnMoney})
this._position = position.clone();
let health = enemyStats.getEffectiveHealth()
this.status = new EnemyStatus(health);
}
draw(ctx: CanvasRenderingContext2D) {
@@ -74,8 +76,8 @@ export abstract class Enemy implements Placeable, Drawable, Acting, Healthy {
export class BasicEnemy extends Enemy {
constructor(position: Vector) {
super(position);
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
protected color: string;
@@ -109,7 +111,8 @@ export class BasicEnemy extends Enemy {
if(position === undefined) {
position = world.randomPlace()
}
let basicEnemy = new BasicEnemy(position);
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());
let basicEnemy = new BasicEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0.5;
@@ -125,8 +128,8 @@ export class ShootingEnemy extends BasicEnemy implements Shooting {
private shootInterval: number;
private projectiles: Projectile[] = []
constructor(position: Vector) {
super(position);
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
removeProjectile(projectile: Projectile) {
@@ -161,7 +164,8 @@ export class ShootingEnemy extends BasicEnemy implements Shooting {
if(position === undefined) {
position = world.randomPlace()
}
let shootingEnemy = new ShootingEnemy(position);
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let shootingEnemy = new ShootingEnemy(position, enemyStats);
shootingEnemy.size = 5;
shootingEnemy.world = world;
shootingEnemy.speed = 0.5;
@@ -174,8 +178,8 @@ export class ShootingEnemy extends BasicEnemy implements Shooting {
export class HealthEnemy extends Enemy {
constructor(position: Vector) {
super(position);
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
protected color: string;
@@ -204,7 +208,8 @@ export class HealthEnemy extends Enemy {
if(position === undefined) {
position = world.randomPlace()
}
let basicEnemy = new HealthEnemy(position);
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let basicEnemy = new HealthEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0;
@@ -221,8 +226,8 @@ export class HealthEnemy extends Enemy {
export class ContainerEnemy extends Enemy {
private drops: KillChanceTable;
constructor(position: Vector) {
super(position);
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
this.status.health = 5;
this.drops = new KillChanceTable();
ItemManagement.getItemsWithRarityFactor().forEach(drop => {
@@ -272,7 +277,8 @@ export class ContainerEnemy extends Enemy {
if(position === undefined) {
position = world.randomPlace()
}
let basicEnemy = new ContainerEnemy(position);
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let basicEnemy = new ContainerEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0;

View File

@@ -15,12 +15,14 @@ export class World {
private _tick: number = 0;
private static readonly TICK_INTERVAL = 10;
private timeStamp: Date;
private startTimeStamp: Date;
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
this._player = player;
this._ctx = ctx;
this._size = size;
this.timeStamp = new Date();
this.startTimeStamp = new Date();
}
enemiesAct() {
@@ -80,7 +82,6 @@ export class World {
this._player.position.y = Math.max(this._player.getSize(), this._player.position.y)
}
maxValue() {
return Math.max(this.size.x, this.size.y)
}
@@ -89,7 +90,19 @@ export class World {
return this._size;
}
tick() {
get tick(): number {
return this._tick;
}
getSecondsPassed() {
return (this.timeStamp.getTime() - this.startTimeStamp.getTime()) / 1000
}
getEnemyHealthFactor() {
return this.getSecondsPassed() / 30;
}
triggerTick() {
this._tick += 1;
if((this._tick % World.TICK_INTERVAL) == 0) {
let currentTimeStamp = new Date();

View File

@@ -44,6 +44,10 @@ export class ItemManagement {
this.ITEMS.push(new ChainBallWeaponItem())
this.ITEMS.push(new PullRangeUp())
this.ITEMS.push(new SpearWeaponItem())
this.ITEMS.push(new DamageUpFactor())
this.ITEMS.push(new DamageFactorUp())
this.ITEMS.push(new DamageFactorUpFactor())
this.ITEMS.push(new DamageUp())
}
}
@@ -206,5 +210,62 @@ export class SpearWeaponItem extends BaseItem {
}
}
export class DamageFactorUpFactor extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.factorDamageFactor)
super.pickup(player, world)
}
name() {
return 'damageFactorUpFactor'
}
getRarity(): Rarity {
return Rarity.LEGENDARY;
}
}
export class DamageUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.increaseDamage)
super.pickup(player, world)
}
name() {
return 'damageUp'
}
getRarity(): Rarity {
return Rarity.COMMON;
}
}
export class DamageUpFactor extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.factorDamage)
super.pickup(player, world)
}
name() {
return 'damageUpFactor'
}
getRarity(): Rarity {
return Rarity.LEGENDARY;
}
}
export class DamageFactorUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.increaseDamageFactor)
super.pickup(player, world)
}
name() {
return 'damageFactorUp'
}
getRarity(): Rarity {
return Rarity.UNCOMMON;
}
}

View File

@@ -38,7 +38,7 @@ function updateCanvas() {
ctx.clearRect(0, 0, world.size.x, world.size.y);
hud.draw(ctx)
if(!state.ended) {
world.tick()
world.triggerTick()
world.enemiesAct()
world.player.act()
world.draw()

View File

@@ -8,6 +8,8 @@ export class PlayerStats {
private _weaponRange: number;
private _weaponRangeFactor: number;
private _healthRegen: number;
private _damage: number;
private _damageFactor: number;
constructor() {
this._speed = 3;
@@ -17,6 +19,8 @@ export class PlayerStats {
this._weaponRange = 250;
this._weaponRangeFactor = 1;
this._healthRegen = 0.001;
this._damage = 0;
this._damageFactor = 0;
}
resetToBasic() {
@@ -25,7 +29,9 @@ export class PlayerStats {
this._pullRange = 0;
this._weaponRange = 0;
this._weaponRangeFactor = 1;
this._healthRegen = 0.1;
this._healthRegen = 0.001;
this._damage = 0;
this._damageFactor = 0;
}
increaseLevel() {
@@ -35,6 +41,8 @@ export class PlayerStats {
this._weaponRange *= 1.25
this._weaponRangeFactor += 0.1
this._healthRegen += 0.1
this._damage += 1;
this._damageFactor += 0.1;
}
mergeStats(otherStats: PlayerStats) {
@@ -44,6 +52,8 @@ export class PlayerStats {
this._weaponRange += otherStats._weaponRange
this._weaponRangeFactor += otherStats._weaponRangeFactor;
this._healthRegen += otherStats._healthRegen;
this._damage += otherStats._damage;
this._damageFactor += otherStats._damageFactor
}
clone() {
@@ -64,6 +74,22 @@ export class PlayerStats {
stats._speed *= value
}
static factorDamage(stats: PlayerStats, value: number) {
stats._damage *= value
}
static increaseDamage(stats: PlayerStats, value: number) {
stats._damage += value
}
static factorDamageFactor(stats: PlayerStats, value: number) {
stats._damageFactor *= value
}
static increaseDamageFactor(stats: PlayerStats, value: number) {
stats._damageFactor += value
}
static increasePullRange(stats: PlayerStats, value: number) {
stats._pullRange += value
}
@@ -104,6 +130,10 @@ export class PlayerStats {
return this._weaponRange * this._weaponRangeFactor;
}
get effectiveDamage(): number {
return this._damage * this._damageFactor;
}
public static defaultPlayerStats(): PlayerStats {
return new PlayerStats();
}
@@ -178,4 +208,28 @@ export class ProjectileStats {
get deathSplit(): number {
return this._deathSplit;
}
}
export class EnemyStats {
private _healthFactor: number;
private _baseHealth: number;
constructor() {
this._healthFactor = 1;
this._baseHealth = 10
}
withHealthFactor(healthFactor: number) {
this._healthFactor = healthFactor;
return this
}
withBaseHealth(baseHealth: number) {
this._baseHealth = baseHealth;
return this
}
getEffectiveHealth() {
return this._baseHealth * this._healthFactor
}
}

View File

@@ -127,7 +127,8 @@ export class PlayerInfo implements DrawContainer {
new StatLabel(() => 'Level', () => this.world.player.status.level),
new StatLabel(() => 'Speed', () => Math.floor(this.world.player.effectiveStats.speed)),
new StatLabel(() => 'Pull range', () => Math.floor(this.world.player.effectiveStats.pullRange)),
new StatLabel(() => 'Weapon range', () => Math.floor(this.world.player.effectiveStats.effectiveWeaponRange))
new StatLabel(() => 'Weapon range', () => Math.floor(this.world.player.effectiveStats.effectiveWeaponRange)),
new StatLabel(() => 'Damage', () => Math.floor(this.world.player.effectiveStats.effectiveDamage))
]
}

View File

@@ -50,6 +50,10 @@ export abstract class BasicWeapon implements Weapon {
return this.size;
}
getDamage() {
return this.stats.damage + this.player.effectiveStats.effectiveDamage;
}
getOffset(): Vector {
return this.offset;
}
@@ -120,7 +124,7 @@ export class Spear extends MeleeWeapon {
let stats = new ProjectileStats()
.withPiercings(1000)
.withSize(3)
.withDamage(this.stats.damage)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let offsetVector = Vector.createVector(closestTargetTo[1]!.getPosition(), this.player.position).multiply(1.1);
if(offsetVector.vecLength() < 15) {
@@ -161,7 +165,7 @@ export class ChainBall extends MeleeWeapon {
let stats = new ProjectileStats()
.withPiercings(1000)
.withSize(3)
.withDamage(this.stats.damage)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = ChainBallProjectile.createChainBallProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, this)
this.projectiles.push(projectile)
@@ -201,7 +205,7 @@ export class HomingPistol extends RangeWeapon {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.stats.damage)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow')
this.projectiles.push(projectile)
@@ -240,7 +244,7 @@ export class Pistol extends RangeWeapon {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.stats.damage)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = StraightProjectile.createStraightProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, 'pink')
this.projectiles.push(projectile)
@@ -278,7 +282,7 @@ export class SpreadWeapon extends RangeWeapon {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.stats.damage)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let targetPosition = closestTargetTo[1]!.getPosition();
let weaponPosition = this.getPosition();