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 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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)) }));