survivors: restricting player to world bounds

reworking how repeated collisions are handled for projectiles for the purpose of piercing projectiles
This commit is contained in:
Sheldan
2025-08-21 20:54:56 +02:00
parent 8ca64a19b7
commit 603bf3addc
6 changed files with 73 additions and 39 deletions

View File

@@ -6,7 +6,7 @@ export class Player implements Drawable, Acting, Healthy {
private _position: Vector;
private _stats: Stats;
private _color: string;
private _status: Status;
private _status: PlayerStatus;
private _weapons: [Weapon] = []
// temp
@@ -27,7 +27,7 @@ export class Player implements Drawable, Acting, Healthy {
player._color = 'blue';
player._stats = Stats.defaultPlayerStats();
player._speed = new Vector(0, 0)
player._status = new Status(10, 0);
player._status = new PlayerStatus(10, 0);
return player;
}
@@ -64,7 +64,7 @@ export class Player implements Drawable, Acting, Healthy {
return this._stats;
}
get status(): Status {
get status(): PlayerStatus {
return this._status;
}
@@ -92,7 +92,7 @@ export class Player implements Drawable, Acting, Healthy {
}
}
export class Status {
export class PlayerStatus {
constructor(private _health: number, private _wealth: number) {
}

View File

@@ -1,5 +1,4 @@
import {Enemy} from "./Enemies.ts";
import type {Player} from "./Player.ts";
import {Player} from "./Player.ts";
import {Projectile} from "./projectile.ts";
import {Vector} from "./base.ts";
@@ -40,6 +39,15 @@ export class World {
this._drops.push(drop)
}
movePlayer(vector: Vector) {
this._player.position.x += vector.x;
this._player.position.y += vector.y;
this._player.position.x = Math.min(this.size.x - this._player.getSize(), this._player.position.x)
this._player.position.x = Math.max(this._player.getSize(), this._player.position.x)
this._player.position.y = Math.min(this.size.y -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)
}
@@ -75,9 +83,16 @@ export class World {
}
getClosestTargetTo(point: Vector): [number, Placeable | undefined] | undefined {
return this.getClosestTargetToButNot(point, undefined)
}
getClosestTargetToButNot(point: Vector, placeAble?: Placeable): [number, Placeable | undefined] | undefined {
let currentTarget;
let currentDistance = Number.MAX_SAFE_INTEGER;
this._enemies.forEach(enemy => {
if(placeAble && enemy === placeAble) {
return;
}
let distance = point.distanceTo(enemy.getPosition());
if(distance < currentDistance) {
currentDistance = distance;

View File

@@ -63,7 +63,7 @@ export class MoneyDrop extends BasicDrop {
let drop = new MoneyDrop(world, position)
drop.worth = 1;
drop._size = 1;
drop._color = 'yellow';
drop._color = 'orange';
world.addDrop(drop)
return drop;
}

View File

@@ -65,16 +65,16 @@ function makeKey(char, fun) {
let keys = {};
makeKey('w', function () {
world.player.position.y += -world.player.stats.speed
world.movePlayer(new Vector(0, -world.player.stats.speed))
})
makeKey('s', function () {
world.player.position.y += world.player.stats.speed
world.movePlayer(new Vector(0, world.player.stats.speed))
})
makeKey('a', function () {
world.player.position.x += -world.player.stats.speed
world.movePlayer(new Vector(-world.player.stats.speed, 0))
})
makeKey('d', function () {
world.player.position.x += world.player.stats.speed
world.movePlayer(new Vector(world.player.stats.speed, 0))
})

View File

@@ -12,8 +12,10 @@ export abstract class Projectile implements Acting, Placeable {
protected world: World;
protected parent: any;
protected color: string
private stats: ProjectileStats;
private status: ProjectileStatus;
protected stats: ProjectileStats;
protected status: ProjectileStatus;
protected lastPosition: Vector;
protected lastColliding?: Placeable;
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any) {
this.position = position.clone();
@@ -26,20 +28,20 @@ export abstract class Projectile implements Acting, Placeable {
act() {
this.move()
if(this.status.collisionCooldown.cooledDown()) {
if(this.parent !== this.world.player) {
if(this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.stats.size)) {
this.impactPlayer()
this.status.collisionCooldown.resetCooldown()
}
} else if(this.parent === this.world.player) {
let closestTargetTo = this.world.getClosestTargetTo(this.position);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined && closestTargetTo[1]?.getPosition().distanceTo(this.position) < (this.stats.size + closestTargetTo[1]?.getSize())) {
let target: Placeable = closestTargetTo[1]!;
if(this.parent !== this.world.player) {
if(this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.stats.size) && this.status.collisionCooldown.cooledDown()) {
this.impactPlayer()
this.status.collisionCooldown.resetCooldown()
}
} else if(this.parent === this.world.player) {
let closestTargetTo = this.world.getClosestTargetToButNot(this.position, this.lastColliding);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined && closestTargetTo[1]?.getPosition().distanceTo(this.position) < (this.stats.size + closestTargetTo[1]?.getSize())) {
let target: Placeable = closestTargetTo[1]!;
if(target !== this.lastColliding) {
if(InstanceOfUtils.instanceOfHealthy(target)) {
let healthy = target as Healthy;
healthy.takeDamage(this.stats.damage)
if(this.status.piercingsLeft <= 0) {
if(!this.status.hasPiercingLeft()) {
this.world.removeProjectile(this)
}
this.status.decreasePiercings()
@@ -47,9 +49,12 @@ export abstract class Projectile implements Acting, Placeable {
this.world.removeProjectile(this)
}
}
this.lastColliding = target;
} else {
this.lastColliding = undefined;
}
}
this.status.collisionCooldown.decreaseCooldown();
this.status.collisionCooldown.decreaseCooldown()
this.checkWorldBorder()
}
@@ -61,7 +66,9 @@ export abstract class Projectile implements Acting, Placeable {
impactPlayer() {
this.world.player.takeDamage(this.stats.damage)
this.world.removeProjectile(this)
if(!this.status.hasPiercingLeft()) {
this.world.removeProjectile(this)
}
};
draw(ctx: CanvasRenderingContext2D) {
@@ -69,6 +76,7 @@ export abstract class Projectile implements Acting, Placeable {
}
move() {
this.lastPosition = this.position.clone()
}
getPosition(): Vector {
@@ -89,6 +97,7 @@ export class StraightProjectile extends Projectile {
}
move() {
super.move()
this.position = straightMove(this.position, this.speedVec)
}
@@ -111,35 +120,35 @@ export class HomingProjectile extends Projectile {
}
move() {
super.move()
this.position = moveInDirectionOf(this.position, this.target.getPosition(), this.speedVec.vecLength())
if(InstanceOfUtils.instanceOfHealthy(this.target)) {
let target = this.target as Healthy
if(target.dead()) {
if(this.position.distanceTo(this.target.getPosition()) < (this.target.getSize() + this.getSize())) {
this.world.removeProjectile(this)
return;
}
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position)
let closestTargetTo = this.world.getClosestTargetTo(this.position)
let dir = Vector.createVector(this.target.getPosition(), this.position).normalize()
let oldDir = Vector.createVector(this.position, this.lastPosition).normalize()
if (closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let dir = Vector.createVector(this.target.getPosition(), this.position).normalize()
let newTargetPosition = closestTargetTo[1]!.getPosition();
let newDir = Vector.createVector(newTargetPosition, this.position).normalize()
let newDirAngle = newDir.angleTo(dir);
if(Math.abs(newDirAngle) < toRad(60)) {
if(Math.abs(newDirAngle) >= toRad(150)) {
this.target = closestTargetTo[1]!;
} else {
this.target = new Point(this.target.getPosition().add(dir.normalize().multiply(Math.max(this.world.size.x, this.world.size.y))))
this.target = new Point(this.position.add(oldDir.multiply(Math.max(this.world.size.x, this.world.size.y))))
}
} else {
this.target = new Point(this.position.add(oldDir.multiply(Math.max(this.world.size.x, this.world.size.y))))
}
}
}
this.position = moveInDirectionOf(this.position, this.target.getPosition(), this.speedVec.vecLength())
this.checkWorldBorder()
}
static createHomingProjectile(world: World, start: Vector, parent: any, target: Placeable, stats: ProjectileStats, color?: string) {
let projectile = new HomingProjectile(start, new Vector(5, 1), stats, world, parent, target)
let projectile = new HomingProjectile(start, new Vector(0, stats.speed), stats, world, parent, target)
projectile.color = color === undefined ? 'red' : color!;
world.addProjectile(projectile)
return projectile;
@@ -160,6 +169,10 @@ export class ProjectileStatus {
return this._piercingsLeft;
}
hasPiercingLeft(): boolean {
return this.piercingsLeft >= 0;
}
get collisionCooldown(): Cooldown {
return this._collisionCooldown;
@@ -175,11 +188,13 @@ export class ProjectileStats {
private _piercings: number;
private _size: number;
private _damage: number;
private _speed: number;
constructor(piercings: number, size: number, damage: number) {
constructor(piercings: number, size: number, damage: number, _speed: number) {
this._piercings = piercings;
this._size = size;
this._damage = damage
this._speed = _speed;
}
get piercings(): number {
@@ -191,6 +206,10 @@ export class ProjectileStats {
}
get speed(): number {
return this._speed;
}
get damage(): number {
return this._damage;
}

View File

@@ -7,10 +7,10 @@ import {Vector} from "./base.ts";
export class Pistol implements Weapon {
private player: Player
private readonly player: Player
private shootInterval: number;
private shootCooldown: number = 0;
private world: World;
private readonly world: World;
private offset: Vector;
private projectiles: [Projectile] = []
private color: string;
@@ -37,7 +37,7 @@ export class Pistol implements Weapon {
private createProjectile(): boolean {
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats(0, 1, 5)
let stats = new ProjectileStats(2, 1, 5, 1)
let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow')
this.projectiles.push(projectile)
return true