survivors: fixing drops being duplicated (sometimes)

adding item container system to not edit the list while iterating over
This commit is contained in:
Sheldan
2025-08-30 20:24:35 +02:00
parent e44355bf21
commit b591fc2dee
3 changed files with 65 additions and 33 deletions

View File

@@ -1,16 +1,16 @@
import {Enemy} from "./Enemies.ts"; import {Enemy} from "./Enemies.ts";
import {Player} from "./Player.ts"; import {Player} from "./Player.ts";
import {Projectile, ProjectileStats} from "./projectile.ts"; import {Projectile } from "./projectile.ts";
import {Vector} from "./base.ts"; import {Vector} from "./base.ts";
import type {Drop, Placeable} from "./interfaces.ts"; import type {Drop, Placeable} from "./interfaces.ts";
export class World { export class World {
private _enemies: Enemy[] = []; private _enemies: ObjectContainer<Enemy> = new ObjectContainer<Enemy>()
private _projectiles: Projectile[] = []; private _projectiles: ObjectContainer<Projectile> = new ObjectContainer<Projectile>();
private _drops: Drop[] = []; private _drops: ObjectContainer<Drop> = new ObjectContainer<Drop>();
private _player: Player; private _player: Player;
private _ctx: CanvasRenderingContext2D; private readonly _ctx: CanvasRenderingContext2D;
private _size: Vector private _size: Vector;
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) { constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
this._player = player; this._player = player;
@@ -19,26 +19,42 @@ export class World {
} }
enemiesAct() { enemiesAct() {
this._enemies.forEach(enemy => enemy.act()) this._enemies.items.forEach(enemy => enemy.act())
this._projectiles.forEach(projectile => projectile.act()) this._enemies.clean()
this._drops.forEach(drop => drop.act()) this._projectiles.items.forEach(projectile => projectile.act())
this._projectiles.clean()
this._drops.items.forEach(drop => drop.act())
this._drops.clean()
} }
draw() { draw() {
this._enemies.forEach(enemy => enemy.draw(this._ctx)) this._enemies.items.forEach(enemy => enemy.draw(this._ctx))
this._drops.forEach(drop => drop.draw(this._ctx)) this._drops.items.forEach(drop => drop.draw(this._ctx))
this._projectiles.forEach(projectile => projectile.draw(this._ctx)) this._projectiles.items.forEach(projectile => projectile.draw(this._ctx))
this._player.draw(this._ctx); this._player.draw(this._ctx);
} }
addProjectile(projectile: Projectile) { addProjectile(projectile: Projectile) {
this._projectiles.push(projectile) this._projectiles.add(projectile)
} }
addDrop(drop: Drop) { addDrop(drop: Drop) {
this._drops.push(drop) this._drops.add(drop)
} }
removeDrop(drop: Drop) {
this._drops.scheduleRemoval(drop)
}
removeEnemy(enemy: Enemy) {
this._enemies.scheduleRemoval(enemy)
}
removeProjectile(projectile: Projectile) {
this._projectiles.scheduleRemoval(projectile)
}
movePlayer(vector: Vector) { movePlayer(vector: Vector) {
this._player.position.x += vector.x; this._player.position.x += vector.x;
this._player.position.y += vector.y; this._player.position.y += vector.y;
@@ -48,26 +64,11 @@ export class World {
this._player.position.y = Math.max(this._player.getSize(), this._player.position.y) this._player.position.y = Math.max(this._player.getSize(), this._player.position.y)
} }
removeDrop(drop: Drop) {
this._drops = this._drops.filter(item => item !== drop)
}
removeProjectile(projectile: Projectile) {
this._projectiles = this._projectiles.filter(item => item !== projectile)
}
removeEnemy(enemy: Enemy) {
this._enemies = this._enemies.filter(item => item !== enemy)
}
maxValue() { maxValue() {
return Math.max(this.size.x, this.size.y) return Math.max(this.size.x, this.size.y)
} }
get enemies(): [Enemy] {
return this._enemies;
}
get size(): Vector { get size(): Vector {
return this._size; return this._size;
@@ -78,7 +79,7 @@ export class World {
} }
addEnemy(enemy: Enemy) { addEnemy(enemy: Enemy) {
this._enemies.push(enemy) this._enemies.add(enemy)
} }
randomPlace(): Vector { randomPlace(): Vector {
@@ -96,7 +97,7 @@ export class World {
getClosestTargetToButNotArray(point: Vector, placeAbles?: [Placeable | undefined], range?: number): [number, Placeable | undefined] | undefined { getClosestTargetToButNotArray(point: Vector, placeAbles?: [Placeable | undefined], range?: number): [number, Placeable | undefined] | undefined {
let currentTarget; let currentTarget;
let currentDistance = Number.MAX_SAFE_INTEGER; let currentDistance = Number.MAX_SAFE_INTEGER;
this._enemies.forEach(enemy => { this._enemies.items.forEach(enemy => {
if(placeAbles && placeAbles.indexOf(enemy) !== -1) { if(placeAbles && placeAbles.indexOf(enemy) !== -1) {
return; return;
} }
@@ -117,4 +118,36 @@ export class World {
get player(): Player { get player(): Player {
return this._player; return this._player;
} }
}
class ObjectContainer<T> {
private _items: T[] = [];
private _itemsToRemove: T[] = [];
constructor() {
this._items = []
this._itemsToRemove = []
}
scheduleRemoval(item: T) {
this._itemsToRemove.push(item)
}
clean() {
this._itemsToRemove.forEach(value => this.remove(value))
this._itemsToRemove = []
}
private remove(itemToRemove: T) {
this._items = this._items.filter(item => item !== itemToRemove)
}
add(item: T) {
this._items.push(item)
}
get items(): T[] {
return this._items;
}
} }

View File

@@ -67,7 +67,6 @@ export class MoneyDrop extends BasicDrop {
drop.worth = 1; drop.worth = 1;
drop.size = 1; drop.size = 1;
drop._color = 'orange'; drop._color = 'orange';
world.addDrop(drop)
return drop; return drop;
} }
} }

View File

@@ -7,6 +7,7 @@ import {Vector} from "./base.ts";
import {BasicEnemy, ContainerEnemy, Enemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts"; import {BasicEnemy, ContainerEnemy, Enemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts";
import {HUD} from "./ui.ts"; import {HUD} from "./ui.ts";
import {HomingPistol, Pistol} from "./weapons.ts"; import {HomingPistol, Pistol} from "./weapons.ts";
import {MoneyDrop} from "./drop.ts";
let hud: HUD; let hud: HUD;
@@ -107,7 +108,6 @@ docReady(function () {
world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight)); world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight));
state = new WorldState(); state = new WorldState();
ShootingEnemy.spawnShootingEnemy(world, new Vector(350, 350))
setInterval(() => { setInterval(() => {
BasicEnemy.spawnBasicEnemy(world) BasicEnemy.spawnBasicEnemy(world)
ShootingEnemy.spawnShootingEnemy(world) ShootingEnemy.spawnShootingEnemy(world)