From 60d691c187f48f7997cb3995f0139b2d99edd9a8 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:56:14 +0200 Subject: [PATCH 01/11] stuff --- src/Client.ts | 2 +- src/Const/Commands.ts | 23 +++++----- src/Const/TankDefinitions.json | 1 + src/Const/TankDefinitions.ts | 2 + src/Native/Camera.ts | 79 ++++++++++++++++++++++++---------- 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index 1ab9bfd7..d8b6a458 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -358,7 +358,7 @@ export default class Client { if (camera.cameraData.values.statLevels.values[statId] >= statLimit) return; - camera.cameraData.statLevels[statId] += 1; + camera.addStat(statId, 1); camera.cameraData.statsAvailable -= 1; return; diff --git a/src/Const/Commands.ts b/src/Const/Commands.ts index f4a780bd..586f397b 100644 --- a/src/Const/Commands.ts +++ b/src/Const/Commands.ts @@ -215,10 +215,11 @@ export const commandCallbacks = { }, game_set_score: (client: Client, scoreArg: string) => { const score = parseInt(scoreArg); - const camera = client.camera?.cameraData; - const player = client.camera?.cameraData.player; + const camera = client.camera; + const cameraData = camera?.cameraData; + const player = cameraData?.player; if (!isFinite(score) || score > Number.MAX_SAFE_INTEGER || score < Number.MIN_SAFE_INTEGER || !Entity.exists(player) || !TankBody.isTank(player) || !camera) return; - camera.score = score; + camera.setScore(score); }, game_set_stat_max: (client: Client, statIdArg: string, statMaxArg: string) => { const statId = StatCount - parseInt(statIdArg); @@ -233,17 +234,19 @@ export const commandCallbacks = { game_set_stat: (client: Client, statIdArg: string, statPointsArg: string) => { const statId = StatCount - parseInt(statIdArg); const statPoints = parseInt(statPointsArg); - const camera = client.camera?.cameraData; - const player = client.camera?.cameraData.player; + const camera = client.camera; + const cameraData = camera?.cameraData; + const player = camera?.cameraData.player; if (statId < 0 || statId >= StatCount || !isFinite(statId) || !isFinite(statPoints) || !Entity.exists(player) || !TankBody.isTank(player) || !camera) return; - camera.statLevels[statId as Stat] = statPoints; + camera.setStat(statId as Stat, statPoints); }, game_add_upgrade_points: (client: Client, pointsArg: string) => { const points = parseInt(pointsArg); - const camera = client.camera?.cameraData; - const player = client.camera?.cameraData.player; - if (!isFinite(points) || points > Number.MAX_SAFE_INTEGER || points < Number.MIN_SAFE_INTEGER || !Entity.exists(player) || !TankBody.isTank(player) || !camera) return; - camera.statsAvailable += points; + const camera = client.camera; + const cameraData = camera?.cameraData; + const player = cameraData?.player; + if (!isFinite(points) || points > Number.MAX_SAFE_INTEGER || points < Number.MIN_SAFE_INTEGER || !Entity.exists(player) || !TankBody.isTank(player) || !camera || !cameraData) return; + cameraData.statsAvailable += points; }, game_teleport: (client: Client, xArg: string, yArg: string) => { const player = client.camera?.cameraData.player; diff --git a/src/Const/TankDefinitions.json b/src/Const/TankDefinitions.json index 45b18bc2..5350f193 100644 --- a/src/Const/TankDefinitions.json +++ b/src/Const/TankDefinitions.json @@ -6289,6 +6289,7 @@ "absorbtionFactor": 1, "speed": 1, "maxHealth": 50, + "bodyDamage": 2, "preAddon": null, "postAddon": "spike", "sides": 1, diff --git a/src/Const/TankDefinitions.ts b/src/Const/TankDefinitions.ts index 617182d4..884536d7 100644 --- a/src/Const/TankDefinitions.ts +++ b/src/Const/TankDefinitions.ts @@ -151,6 +151,8 @@ export interface TankDefinition { absorbtionFactor: number; /** The base max health of the tank. */ maxHealth: number; + /** Extra body damage addition, such as spike. */ + bodyDamage?: number; /** The addon, if not empty, which is built before the barrels. */ preAddon: addonId | null; /** The addon, if not empty, which is built after the barrels. */ diff --git a/src/Native/Camera.ts b/src/Native/Camera.ts index bedff207..887ec748 100644 --- a/src/Native/Camera.ts +++ b/src/Native/Camera.ts @@ -62,6 +62,7 @@ export class CameraEntity extends Entity { if (TankBody.isTank(player)) { const scaleFactor = Math.pow(1.01, level - previousLevel); player.scale(scaleFactor); + player.calculateStatData(); if (isMaxLevel) { player.scoreData.score = levelScore; @@ -73,7 +74,7 @@ export class CameraEntity extends Entity { const statIncrease = ClientCamera.calculateStatCount(level) - ClientCamera.calculateStatCount(previousLevel); this.cameraData.statsAvailable += statIncrease; - this.setFieldFactor(getTankById(this.cameraData.values.tank)?.fieldFactor || 1); + this.setFieldFactor(getTankById(this.cameraData.values.tank)?.fieldFactor ?? 1); } /** Returns the camera's client if it exists */ public getClient(): Client | null { @@ -84,6 +85,60 @@ export class CameraEntity extends Entity { public setFieldFactor(fieldFactor: number) { this.cameraData.FOV = (.55 * fieldFactor) / Math.pow(1.01, (this.cameraData.values.level - 1) / 2); } + + public addScore(score: number) { + this.cameraData.score += score; + + const player = this.cameraData.values.player; + if (player?.scoreData) player.scoreData.score += score; + + this.calculateLevelData(); + } + + public setScore(score: number) { + this.cameraData.score = score; + + const player = this.cameraData.values.player; + if (player?.scoreData) player.scoreData.score = score; + + this.calculateLevelData(); + } + + public addStat(statId: Stat, amount: number) { + this.cameraData.statLevels[statId] += amount; + + const player = this.cameraData.values.player; + + if (TankBody.isTank(player)) player.calculateStatData(); + } + + public setStat(statId: Stat, amount: number) { + this.cameraData.statLevels[statId] = amount; + + const player = this.cameraData.values.player; + + if (TankBody.isTank(player)) player.calculateStatData(); + } + + public calculateLevelData() { + const player = this.cameraData.values.player; + if (!TankBody.isTank(player)) return; + + const score = this.cameraData.values.score; + let newLevel = this.cameraData.values.level; + while (newLevel < levelToScoreTable.length && score - levelToScore(newLevel + 1) >= 0) newLevel += 1 + + if (newLevel !== this.cameraData.values.level) { + this.setLevel(newLevel); + this.cameraData.score = score; + } + + if (newLevel < levelToScoreTable.length) { + const levelScore = levelToScore(this.cameraData.values.level) + this.cameraData.levelbarMax = levelToScore(this.cameraData.values.level + 1) - levelScore; + this.cameraData.levelbarProgress = score - levelScore; + } + } public tick(tick: number) { if (Entity.exists(this.cameraData.values.player)) { @@ -92,28 +147,6 @@ export class CameraEntity extends Entity { this.cameraData.cameraX = focus.rootParent.positionData.values.x; this.cameraData.cameraY = focus.rootParent.positionData.values.y; } - - if (TankBody.isTank(this.cameraData.values.player)) { - // Update player related data - const player = this.cameraData.values.player as TankBody; - - const score = this.cameraData.values.score; - let newLevel = this.cameraData.values.level; - while (newLevel < levelToScoreTable.length && score - levelToScore(newLevel + 1) >= 0) newLevel += 1 - - if (newLevel !== this.cameraData.values.level) { - this.setLevel(newLevel); - this.cameraData.score = score; - } - - if (newLevel < levelToScoreTable.length) { - const levelScore = levelToScore(this.cameraData.values.level) - this.cameraData.levelbarMax = levelToScore(this.cameraData.values.level + 1) - levelScore; - this.cameraData.levelbarProgress = score - levelScore; - } - - this.cameraData.movementSpeed = player.definition.speed * 2.55 * Math.pow(1.07, this.cameraData.values.statLevels.values[Stat.MovementSpeed]) / Math.pow(1.015, this.cameraData.values.level - 1) - } } else { this.cameraData.flags |= CameraFlags.usesCameraCoords; } From 25c6509ef2f01ab4d9ba8ac7d9654078565e16b9 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:57:31 +0200 Subject: [PATCH 02/11] Update TankBody.ts --- src/Entity/Tank/TankBody.ts | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Entity/Tank/TankBody.ts b/src/Entity/Tank/TankBody.ts index a963c1bd..cce00aaa 100644 --- a/src/Entity/Tank/TankBody.ts +++ b/src/Entity/Tank/TankBody.ts @@ -181,10 +181,11 @@ export default class TankBody extends LivingEntity implements BarrelBase { camera.setFieldFactor(tank.fieldFactor); this.scale(1); // Update addons and etc + this.calculateStatData(); } /** See LivingEntity.onKill */ public onKill(entity: LivingEntity) { - if (Entity.exists(this.cameraEntity.cameraData.values.player) && entity !== this) this.scoreData.score = this.cameraEntity.cameraData.score += entity.scoreReward; + if (Entity.exists(this.cameraEntity.cameraData.values.player) && entity !== this) this.cameraEntity.addScore(entity.scoreReward); if ((entity.nameData && !(entity.nameData.values.flags & NameFlags.hiddenName))) { const client = this.cameraEntity.getClient(); @@ -244,6 +245,30 @@ export default class TankBody extends LivingEntity implements BarrelBase { super.receiveDamage(source, amount); } + + public calculateStatData() { + // Damage + this.damagePerTick = this.cameraEntity.cameraData.statLevels[Stat.BodyDamage] + 5 + (this.definition.bodyDamage ?? 0); + + // Max Health + const maxHealthCache = this.healthData.values.maxHealth; + + this.healthData.maxHealth = this.definition.maxHealth + 2 * (this.cameraEntity.cameraData.values.level - 1) + this.cameraEntity.cameraData.values.statLevels.values[Stat.MaxHealth] * 20; + if (this.healthData.values.health === maxHealthCache) this.healthData.health = this.healthData.maxHealth; // just in case + else if (this.healthData.values.maxHealth !== maxHealthCache) { + this.healthData.health *= this.healthData.values.maxHealth / maxHealthCache + } + + // Regen + this.regenPerTick = (this.healthData.values.maxHealth * 4 * this.cameraEntity.cameraData.values.statLevels.values[Stat.HealthRegen] + this.healthData.values.maxHealth) / 25000; + + // Reload + this.reloadTime = 15 * Math.pow(0.914, this.cameraEntity.cameraData.values.statLevels.values[Stat.Reload]); + + // Movement speed + this.cameraEntity.cameraData.movementSpeed = + this.definition.speed * 2.55 * Math.pow(1.07, this.cameraEntity.cameraData.values.statLevels.values[Stat.MovementSpeed]) / Math.pow(1.015, this.cameraEntity.cameraData.values.level - 1); + } /** See LivingEntity.onDeath */ public onDeath(killer: LivingEntity) { @@ -326,31 +351,6 @@ export default class TankBody extends LivingEntity implements BarrelBase { this.styleData.opacity = util.constrain(this.styleData.values.opacity, 0, 1); } - - // Update stat related - updateStats: { - // Damage - this.damagePerTick = this.cameraEntity.cameraData.statLevels[Stat.BodyDamage] + 5; - if (this._currentTank === Tank.Spike) this.damagePerTick += 2; - - // Max Health - const maxHealthCache = this.healthData.values.maxHealth; - - this.healthData.maxHealth = this.definition.maxHealth + 2 * (this.cameraEntity.cameraData.values.level - 1) + this.cameraEntity.cameraData.values.statLevels.values[Stat.MaxHealth] * 20; - if (this.healthData.values.health === maxHealthCache) this.healthData.health = this.healthData.maxHealth; // just in case - else if (this.healthData.values.maxHealth !== maxHealthCache) { - this.healthData.health *= this.healthData.values.maxHealth / maxHealthCache - } - - // Regen - this.regenPerTick = (this.healthData.values.maxHealth * 4 * this.cameraEntity.cameraData.values.statLevels.values[Stat.HealthRegen] + this.healthData.values.maxHealth) / 25000; - - // Reload - this.reloadTime = 15 * Math.pow(0.914, this.cameraEntity.cameraData.values.statLevels.values[Stat.Reload]); - } - - this.scoreData.score = this.cameraEntity.cameraData.values.score; - if ((this.styleData.values.flags & StyleFlags.isFlashing) && (this.game.tick >= this.cameraEntity.cameraData.values.spawnTick + 374 || this.inputs.attemptingShot() || this.inputs.movement.magnitude > 0)) { this.styleData.flags ^= StyleFlags.isFlashing; // Dont worry about invulnerability here - not gonna be invulnerable while flashing ever (see setInvulnerability) From e985af0842d2e77fef22dbd3174a30265b586471 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:59:08 +0200 Subject: [PATCH 03/11] Fix survival --- src/Gamemodes/Survival.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Gamemodes/Survival.ts b/src/Gamemodes/Survival.ts index b96a672f..3bbe60b6 100644 --- a/src/Gamemodes/Survival.ts +++ b/src/Gamemodes/Survival.ts @@ -27,6 +27,7 @@ import { ArenaFlags, ClientBound } from "../Const/Enums"; import { countdownDuration, scoreboardUpdateInterval } from "../config"; const MIN_PLAYERS = 4; // 6 in Diep.io +const SCORE_PER_TICK = 0.25; /** * Manage shape count @@ -121,7 +122,7 @@ export default class SurvivalArena extends ArenaEntity { public tick(tick: number) { for (const client of this.game.clients) { const camera = client.camera; - if (camera && Entity.exists(camera.cameraData.values.player)) camera.cameraData.score += 0.2; + if (camera && Entity.exists(camera.cameraData.values.player)) camera.addScore(SCORE_PER_TICK); } super.tick(tick); } From 1993e64b48822b768dcbca59716da67ca858e61b Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:18:10 +0200 Subject: [PATCH 04/11] fix AC and mothership --- src/Entity/Misc/ArenaCloser.ts | 6 +++--- src/Entity/Misc/Mothership.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Entity/Misc/ArenaCloser.ts b/src/Entity/Misc/ArenaCloser.ts index a30fbc03..5f52f725 100644 --- a/src/Entity/Misc/ArenaCloser.ts +++ b/src/Entity/Misc/ArenaCloser.ts @@ -58,7 +58,7 @@ export default class ArenaCloser extends TankBody { def.maxHealth = 10000 - 598; // TODO(ABC): // Fix all the stats - def.speed = this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 80; + def.speed = this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 10; Object.defineProperty(this, "damagePerTick", { get() { @@ -72,7 +72,7 @@ export default class ArenaCloser extends TankBody { this.positionData.values.flags |= PositionFlags.canMoveThroughWalls; this.physicsData.values.flags |= PhysicsFlags.canEscapeArena; - for (let i = Stat.MovementSpeed; i < Stat.BodyDamage; ++i) camera.cameraData.values.statLevels.values[i] = 7; + for (let i = Stat.MovementSpeed; i < Stat.BodyDamage; ++i) camera.setStat(i as Stat, 7); this.ai.aimSpeed = this.barrels[0].bulletAccel * 1.6; this.setInvulnerability(true); @@ -91,6 +91,6 @@ export default class ArenaCloser extends TankBody { } super.tick(tick); - this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 80; + this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 10; } } diff --git a/src/Entity/Misc/Mothership.ts b/src/Entity/Misc/Mothership.ts index ecac3940..dd5b63ee 100644 --- a/src/Entity/Misc/Mothership.ts +++ b/src/Entity/Misc/Mothership.ts @@ -63,8 +63,8 @@ export default class Mothership extends TankBody { camera.cameraData.values.player = this; - for (let i = Stat.MovementSpeed; i < Stat.HealthRegen; ++i) camera.cameraData.values.statLevels.values[i] = 7; - camera.cameraData.values.statLevels.values[Stat.HealthRegen] = 1; + for (let i = Stat.MovementSpeed; i < Stat.HealthRegen; ++i) camera.setStat(i as Stat, 7); + camera.setStat(Stat.HealthRegen, 1); const def = (this.definition = Object.assign({}, this.definition)); // 418 is what the normal health increase for stat/level would be, so we just subtract it and force it 7k From f75b66fc46644fc274a67b130919a454668983f2 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:23:10 +0200 Subject: [PATCH 05/11] Update ArenaCloser.ts --- src/Entity/Misc/ArenaCloser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entity/Misc/ArenaCloser.ts b/src/Entity/Misc/ArenaCloser.ts index 5f52f725..4fe3b999 100644 --- a/src/Entity/Misc/ArenaCloser.ts +++ b/src/Entity/Misc/ArenaCloser.ts @@ -58,7 +58,7 @@ export default class ArenaCloser extends TankBody { def.maxHealth = 10000 - 598; // TODO(ABC): // Fix all the stats - def.speed = this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 10; + def.speed = this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 5; Object.defineProperty(this, "damagePerTick", { get() { @@ -91,6 +91,6 @@ export default class ArenaCloser extends TankBody { } super.tick(tick); - this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 10; + this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 5; } } From 69c96028dfd701999288a798468b6253f2ba5eda Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:50:23 +0200 Subject: [PATCH 06/11] Update Survival.ts --- src/Gamemodes/Survival.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gamemodes/Survival.ts b/src/Gamemodes/Survival.ts index 3bbe60b6..bdbdac48 100644 --- a/src/Gamemodes/Survival.ts +++ b/src/Gamemodes/Survival.ts @@ -27,7 +27,7 @@ import { ArenaFlags, ClientBound } from "../Const/Enums"; import { countdownDuration, scoreboardUpdateInterval } from "../config"; const MIN_PLAYERS = 4; // 6 in Diep.io -const SCORE_PER_TICK = 0.25; +const SCORE_PER_TICK = 0.2; /** * Manage shape count From b0cd0997cae4e73a76328b7793391cd0c6af66e5 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:22:08 +0200 Subject: [PATCH 07/11] Remove useless array --- src/Entity/Boss/Defender.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Entity/Boss/Defender.ts b/src/Entity/Boss/Defender.ts index 5512c0da..ad0109f9 100644 --- a/src/Entity/Boss/Defender.ts +++ b/src/Entity/Boss/Defender.ts @@ -78,8 +78,6 @@ const DEFENDER_SIZE = 150; * Class which represents the boss "Defender" */ export default class Defender extends AbstractBoss { - /** Defender's trap launchers */ - private trappers: Barrel[] = []; /** See AbstractBoss.movementSpeed */ public movementSpeed = 0.2; @@ -99,7 +97,7 @@ export default class Defender extends AbstractBoss { const offset = 60 / (DEFENDER_SIZE * Math.SQRT1_2); for (let i = 0; i < count; ++i) { // Add trap launcher - this.trappers.push(new Barrel(this, { + this.barrels.push(new Barrel(this, { ...TrapperDefinition, angle: PI2 * ((i / count) + 1 / (count * 2)) })); From 189376203900fa2490058bcc0aa0bd5691ec90e4 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:23:04 +0200 Subject: [PATCH 08/11] Remove useless array --- src/Entity/Boss/Summoner.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Entity/Boss/Summoner.ts b/src/Entity/Boss/Summoner.ts index 56332f64..89d282dd 100644 --- a/src/Entity/Boss/Summoner.ts +++ b/src/Entity/Boss/Summoner.ts @@ -58,10 +58,6 @@ const SUMMONER_SIZE = 150; * Class which represents the boss "Summoner" */ export default class Summoner extends AbstractBoss { - - /** Summoner spawners */ - private spawners: Barrel[] = []; - public constructor(game: GameServer) { super(game); @@ -71,8 +67,9 @@ export default class Summoner extends AbstractBoss { this.physicsData.values.size = SUMMONER_SIZE * Math.SQRT1_2; this.physicsData.values.sides = 4; - for (let i = 0; i < 4; ++i) { - this.spawners.push(new Barrel(this, { + const count = this.physicsData.values.sides; + for (let i = 0; i < count; ++i) { + this.barrels.push(new Barrel(this, { ...SummonerSpawnerDefinition, angle: PI2 * ((i / 4)) })); From 63a3f4b2c8d7e4ee5a3de1ceb23d18e174826ac2 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:05:51 +0200 Subject: [PATCH 09/11] Barrel stat function --- src/Entity/Tank/Barrel.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Entity/Tank/Barrel.ts b/src/Entity/Tank/Barrel.ts index 630576f7..62ee18fa 100644 --- a/src/Entity/Tank/Barrel.ts +++ b/src/Entity/Tank/Barrel.ts @@ -37,16 +37,17 @@ import CrocSkimmer from "./Projectile/CrocSkimmer"; import { BarrelAddon, BarrelAddonById } from "./BarrelAddons"; import { Swarm } from "./Projectile/Swarm"; import NecromancerSquare from "./Projectile/NecromancerSquare"; + /** * Class that determines when barrels can shoot, and when they can't. */ export class ShootCycle { /** The barrel this cycle is keeping track of. */ - private barrelEntity: Barrel; + public barrelEntity: Barrel; /** The current position in the cycle. */ - private pos: number; + public pos: number; /** The last known reload time of the barrel. */ - private reloadTime: number; + public reloadTime: number; public constructor(barrel: Barrel) { this.barrelEntity = barrel; @@ -56,11 +57,6 @@ export class ShootCycle { public tick() { const reloadTime = this.barrelEntity.tank.reloadTime * this.barrelEntity.definition.reload; - if (reloadTime !== this.reloadTime) { - this.pos *= reloadTime / this.reloadTime; - this.reloadTime = this.barrelEntity.barrelData.reloadTime = reloadTime; - } - const alwaysShoot = (this.barrelEntity.definition.forceFire) || (this.barrelEntity.definition.bullet.type === "drone") || (this.barrelEntity.definition.bullet.type === "minion"); if (this.pos >= reloadTime) { @@ -143,7 +139,7 @@ export default class Barrel extends ObjectEntity { this.barrelData.values.trapezoidDirection = barrelDefinition.trapezoidDirection; this.shootCycle = new ShootCycle(this); - this.bulletAccel = (20 + (owner.cameraEntity.cameraData?.values.statLevels.values[Stat.BulletSpeed] || 0) * 3) * barrelDefinition.bullet.speed; + this.calculateStatData(); } /** Shoots a bullet from the barrel. */ @@ -214,9 +210,19 @@ export default class Barrel extends ObjectEntity { if (this.definition.bullet.color) projectile.styleData.values.color = this.definition.bullet.color; } } + + public calculateStatData() { + const reloadTime = this.tank.reloadTime * this.definition.reload; + + if (reloadTime !== this.shootCycle.reloadTime) { + this.shootCycle.pos *= reloadTime / this.shootCycle.reloadTime; + this.shootCycle.reloadTime = this.barrelData.reloadTime = reloadTime; + } + + this.bulletAccel = (20 + (this.tank.cameraEntity.cameraData?.values.statLevels.values[Stat.BulletSpeed] || 0) * 3) * this.definition.bullet.speed; + } public tick(tick: number) { - this.bulletAccel = (20 + (this.tank.cameraEntity.cameraData?.values.statLevels.values[Stat.BulletSpeed] || 0) * 3) * this.definition.bullet.speed; this.relationsData.values.team = this.tank.relationsData.values.team; if (!this.tank.rootParent.deletionAnimation){ From 595196b192dd79dec9d88bc2d6b7b3e5908f8806 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:06:42 +0200 Subject: [PATCH 10/11] Barrel stats --- src/Entity/Tank/TankBody.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Entity/Tank/TankBody.ts b/src/Entity/Tank/TankBody.ts index cce00aaa..bdf2e4bf 100644 --- a/src/Entity/Tank/TankBody.ts +++ b/src/Entity/Tank/TankBody.ts @@ -121,6 +121,7 @@ export default class TankBody extends LivingEntity implements BarrelBase { this.children[i].isChild = false; this.children[i].delete(); } + this.children = []; this.barrels = []; this.addons = []; @@ -134,10 +135,10 @@ export default class TankBody extends LivingEntity implements BarrelBase { if (!Entity.exists(camera)) throw new Error("No camera"); this.physicsData.sides = tank.sides; - this.styleData.opacity = 1; + this.styleData.opacity = 1.0; for (let i: Stat = 0; i < StatCount; ++i) { - const {name, max} = tank.stats[i]; + const { name, max } = tank.stats[i]; camera.cameraData.statLimits[i] = max; camera.cameraData.statNames[i] = name; @@ -150,6 +151,7 @@ export default class TankBody extends LivingEntity implements BarrelBase { this.baseSize = tank.baseSizeOverride ?? tank.sides === 4 ? Math.SQRT2 * 32.5 : tank.sides === 16 ? Math.SQRT2 * 25 : 50; this.physicsData.size = this.baseSize * this.scaleFactor; this.physicsData.absorbtionFactor = this.isInvulnerable ? 0 : tank.absorbtionFactor; + if (tank.absorbtionFactor === 0) this.positionData.flags |= PositionFlags.canMoveThroughWalls; else if (this.positionData.flags & PositionFlags.canMoveThroughWalls) this.positionData.flags ^= PositionFlags.canMoveThroughWalls; @@ -181,7 +183,7 @@ export default class TankBody extends LivingEntity implements BarrelBase { camera.setFieldFactor(tank.fieldFactor); this.scale(1); // Update addons and etc - this.calculateStatData(); + this.calculateStatData(); // Re-calculate everything once this is done } /** See LivingEntity.onKill */ public onKill(entity: LivingEntity) { @@ -247,12 +249,11 @@ export default class TankBody extends LivingEntity implements BarrelBase { } public calculateStatData() { - // Damage + // Body damage this.damagePerTick = this.cameraEntity.cameraData.statLevels[Stat.BodyDamage] + 5 + (this.definition.bodyDamage ?? 0); - // Max Health + // Max health const maxHealthCache = this.healthData.values.maxHealth; - this.healthData.maxHealth = this.definition.maxHealth + 2 * (this.cameraEntity.cameraData.values.level - 1) + this.cameraEntity.cameraData.values.statLevels.values[Stat.MaxHealth] * 20; if (this.healthData.values.health === maxHealthCache) this.healthData.health = this.healthData.maxHealth; // just in case else if (this.healthData.values.maxHealth !== maxHealthCache) { @@ -268,6 +269,8 @@ export default class TankBody extends LivingEntity implements BarrelBase { // Movement speed this.cameraEntity.cameraData.movementSpeed = this.definition.speed * 2.55 * Math.pow(1.07, this.cameraEntity.cameraData.values.statLevels.values[Stat.MovementSpeed]) / Math.pow(1.015, this.cameraEntity.cameraData.values.level - 1); + + for (const barrel of this.barrels) barrel.calculateStatData(); } /** See LivingEntity.onDeath */ From 34766ac8d5eeac3ebabbf4f8ad6824932f952497 Mon Sep 17 00:00:00 2001 From: c86ec23b-fef1-4979-b2fa-b9adc351b8cc <87239823+c86ec23b-fef1-4979-b2fa-b9adc351b8cc@users.noreply.github.com> Date: Wed, 25 Mar 2026 04:15:11 +0200 Subject: [PATCH 11/11] Stat fixes --- src/Entity/Misc/ArenaCloser.ts | 21 ++++++--------------- src/Entity/Misc/Dominator.ts | 7 +------ src/Entity/Misc/Mothership.ts | 7 +++---- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/Entity/Misc/ArenaCloser.ts b/src/Entity/Misc/ArenaCloser.ts index 4fe3b999..c52edb42 100644 --- a/src/Entity/Misc/ArenaCloser.ts +++ b/src/Entity/Misc/ArenaCloser.ts @@ -53,20 +53,6 @@ export default class ArenaCloser extends TankBody { this.setTank(Tank.ArenaCloser); - const def = (this.definition = Object.assign({}, this.definition)); - // 598 is what the normal health increase for stat/level would be, so we just subtract it. - def.maxHealth = 10000 - 598; - // TODO(ABC): - // Fix all the stats - def.speed = this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 5; - - Object.defineProperty(this, "damagePerTick", { - get() { - return 45; - }, - set() {} - }); - this.nameData.values.name = "Arena Closer"; this.styleData.values.color = Color.Neutral; this.positionData.values.flags |= PositionFlags.canMoveThroughWalls; @@ -76,6 +62,12 @@ export default class ArenaCloser extends TankBody { this.ai.aimSpeed = this.barrels[0].bulletAccel * 1.6; this.setInvulnerability(true); + + // TODO(ABC): + // Fix all the stats + this.ai.movementSpeed = this.cameraEntity.cameraData.values.movementSpeed = 5; + this.healthData.values.health = this.healthData.values.maxHealth = 10000; + this.damagePerTick = 45; } public tick(tick: number) { @@ -91,6 +83,5 @@ export default class ArenaCloser extends TankBody { } super.tick(tick); - this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 5; } } diff --git a/src/Entity/Misc/Dominator.ts b/src/Entity/Misc/Dominator.ts index 6e164a93..9a1da95e 100644 --- a/src/Entity/Misc/Dominator.ts +++ b/src/Entity/Misc/Dominator.ts @@ -92,12 +92,7 @@ export default class Dominator extends TankBody { this.base = base; - Object.defineProperty(this, "damagePerTick", { - get() { - return 10; - }, - set() {} - }); + this.damagePerTick = 10; if (this.styleData.values.flags & StyleFlags.isFlashing) { // Remove spawn shield this.styleData.values.flags ^= StyleFlags.isFlashing; diff --git a/src/Entity/Misc/Mothership.ts b/src/Entity/Misc/Mothership.ts index dd5b63ee..09ac8c0d 100644 --- a/src/Entity/Misc/Mothership.ts +++ b/src/Entity/Misc/Mothership.ts @@ -65,10 +65,8 @@ export default class Mothership extends TankBody { for (let i = Stat.MovementSpeed; i < Stat.HealthRegen; ++i) camera.setStat(i as Stat, 7); camera.setStat(Stat.HealthRegen, 1); - - const def = (this.definition = Object.assign({}, this.definition)); - // 418 is what the normal health increase for stat/level would be, so we just subtract it and force it 7k - def.maxHealth = 7000 - 418; + + this.healthData.values.health = this.healthData.values.maxHealth = 7000; } public onDeath(killer: Live): void { @@ -129,6 +127,7 @@ export default class Mothership extends TankBody { } } } + const team = this.relationsData.values.team; if (team?.teamData) { team.teamData.mothershipX = this.positionData.values.x;