Skip to content
Merged
2 changes: 1 addition & 1 deletion src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 13 additions & 10 deletions src/Const/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/Const/TankDefinitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6289,6 +6289,7 @@
"absorbtionFactor": 1,
"speed": 1,
"maxHealth": 50,
"bodyDamage": 2,
"preAddon": null,
"postAddon": "spike",
"sides": 1,
Expand Down
2 changes: 2 additions & 0 deletions src/Const/TankDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
4 changes: 1 addition & 3 deletions src/Entity/Boss/Defender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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))
}));
Expand Down
9 changes: 3 additions & 6 deletions src/Entity/Boss/Summoner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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))
}));
Expand Down
23 changes: 7 additions & 16 deletions src/Entity/Misc/ArenaCloser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,21 @@ 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 = 80;

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;
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);

// 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) {
Expand All @@ -91,6 +83,5 @@ export default class ArenaCloser extends TankBody {
}

super.tick(tick);
this.ai.movementSpeed = this.cameraEntity.cameraData.movementSpeed = 80;
}
}
7 changes: 1 addition & 6 deletions src/Entity/Misc/Dominator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 5 additions & 6 deletions src/Entity/Misc/Mothership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,10 @@ 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;

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;
for (let i = Stat.MovementSpeed; i < Stat.HealthRegen; ++i) camera.setStat(i as Stat, 7);
camera.setStat(Stat.HealthRegen, 1);

this.healthData.values.health = this.healthData.values.maxHealth = 7000;
}

public onDeath(killer: Live): void {
Expand Down Expand Up @@ -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;
Expand Down
26 changes: 16 additions & 10 deletions src/Entity/Tank/Barrel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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){
Expand Down
59 changes: 31 additions & 28 deletions src/Entity/Tank/TankBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -181,10 +183,11 @@ export default class TankBody extends LivingEntity implements BarrelBase {
camera.setFieldFactor(tank.fieldFactor);

this.scale(1); // Update addons and etc
this.calculateStatData(); // Re-calculate everything once this is done
}
/** 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();
Expand Down Expand Up @@ -244,6 +247,31 @@ export default class TankBody extends LivingEntity implements BarrelBase {
super.receiveDamage(source, amount);

}

public calculateStatData() {
// Body 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);

for (const barrel of this.barrels) barrel.calculateStatData();
}

/** See LivingEntity.onDeath */
public onDeath(killer: LivingEntity) {
Expand Down Expand Up @@ -326,31 +354,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)
Expand Down
3 changes: 2 additions & 1 deletion src/Gamemodes/Survival.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.2;

/**
* Manage shape count
Expand Down Expand Up @@ -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);
}
Expand Down
Loading
Loading