From 33310100f71e934a603caf3c2eeff41a747a5c7e Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Sat, 6 Sep 2025 00:50:45 +0200 Subject: [PATCH] survivors: adding new "melee" weapon --- absurd-survivors/src/World.ts | 11 ++++++ absurd-survivors/src/items.ts | 21 +++++++++- absurd-survivors/src/projectile.ts | 61 +++++++++++++++++++++++++++++- absurd-survivors/src/weapons.ts | 53 +++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 4 deletions(-) diff --git a/absurd-survivors/src/World.ts b/absurd-survivors/src/World.ts index 7b03ac5..8934e17 100644 --- a/absurd-survivors/src/World.ts +++ b/absurd-survivors/src/World.ts @@ -115,6 +115,17 @@ export class World { } } + getAllInRange(point: Vector, range: number): Enemy[] { + let found = []; + this._enemies.items.forEach(enemy => { + let distance = point.distanceTo(enemy.getPosition()); + if(range && distance < range) { + found.push(enemy) + } + }); + return found; + } + get player(): Player { return this._player; } diff --git a/absurd-survivors/src/items.ts b/absurd-survivors/src/items.ts index 9b4c023..50fdb9a 100644 --- a/absurd-survivors/src/items.ts +++ b/absurd-survivors/src/items.ts @@ -1,7 +1,7 @@ import type {ChanceEntry, Item} from "./interfaces.ts"; import {Player} from "./Player.ts"; import {randomItem} from "./utils.ts"; -import {HomingPistol, Pistol, SpreadWeapon} from "./weapons.ts"; +import {Dagger, HomingPistol, Pistol, SpreadWeapon} from "./weapons.ts"; import type {World} from "./World.ts"; export enum Rarity { @@ -40,6 +40,7 @@ export class ItemManagement { this.ITEMS.push(new HomingPistolItem()) this.ITEMS.push(new PistolItem()) this.ITEMS.push(new SpreadWeaponItem()) + this.ITEMS.push(new DaggerWeaponItem()) } } @@ -91,7 +92,7 @@ export class SpeedUp extends BaseItem { } getRarity(): Rarity { - return Rarity.COMMON; + return Rarity.LEGENDARY; } } @@ -155,3 +156,19 @@ export class SpreadWeaponItem extends BaseItem { } } +export class DaggerWeaponItem extends BaseItem { + pickup(player: Player, world: World) { + player.addWeapon(Dagger.createDagger(world)) + super.pickup(player, world) + } + + name() { + return 'dagger' + } + + getRarity(): Rarity { + return Rarity.EPIC; + } +} + + diff --git a/absurd-survivors/src/projectile.ts b/absurd-survivors/src/projectile.ts index db5f1d7..bfad6b3 100644 --- a/absurd-survivors/src/projectile.ts +++ b/absurd-survivors/src/projectile.ts @@ -1,4 +1,4 @@ -import type {Acting, Placeable, Healthy} from "./interfaces.ts"; +import type {Acting, Placeable, Healthy, Weapon} from "./interfaces.ts"; import type {Vector} from "./base.ts"; import {World} from "./World.ts"; import {Cooldown, DeadPoint, Point, Vector} from "./base.ts"; @@ -12,6 +12,8 @@ import { toRad } from "./utils.ts"; import {InstanceOfUtils} from "./instance.ts"; +import {MeleeWeapon} from "./weapons.ts"; +import type {Enemy} from "./Enemies.ts"; export abstract class Projectile implements Acting, Placeable { @@ -47,6 +49,7 @@ export abstract class Projectile implements Acting, Placeable { this.status.collisionCooldown.resetCooldown() } } else if(this.parent === this.world.player) { + // TODO think why this was done, why do I need to calculate the newest target on _each_ act? let closestTargetTo = this.world.getClosestTargetToButNot(this.position, this.lastColliding); if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { let target: Placeable = closestTargetTo[1]!; @@ -127,6 +130,62 @@ export class StraightProjectile extends Projectile { } } +export class WeaponProjectile extends Projectile { + private weapon: MeleeWeapon; + private movingBack: boolean = false; + private target: Vector; + private lastHit: Enemy[] = [] + + constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, weapon: MeleeWeapon, target: Vector) { + super(position, speedVec, stats, world, parent); + this.weapon = weapon; + this.target = target.clone() + } + + draw(ctx: CanvasRenderingContext2D) { + drawDot(this.position, this.stats.size, 'pink', ctx) // todo render the weapon instead + } + + act() { + this.move() + // TODO It seems that the projectile doesnt _quite_ hit the enemy, but is just getting close to it + let hitEnemies = this.world.getAllInRange(this.position, this.stats.size * 2); + hitEnemies.forEach(value => { + if(this.lastHit.indexOf(value) !== -1) { + if(InstanceOfUtils.instanceOfHealthy(value)) { + let healthy = value as Healthy; + healthy.takeDamage(this.stats.damage) + } + } + }) + this.lastHit = hitEnemies; + } + + move() { + super.move() + if(!this.movingBack) { + this.position = straightMove(this.position, this.speedVec) + if(this.position.distanceTo(this.target) < this.stats.size) { + this.movingBack = true; + } + } else { + this.position = moveInDirectionOf(this.position, this.world.player.position, this.speedVec.vecLength()) + } + if(this.movingBack && this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.stats.size)) { + this.weapon.reset(); + this.die() + } + } + + static createWeaponProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, weapon: MeleeWeapon, color?: string) { + let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(stats.speed); + let projectile = new WeaponProjectile(start, dirVector, stats, world, parent, weapon, targetPosition) + projectile.color = color === undefined ? 'red' : color!; + world.addProjectile(projectile) + return projectile; + } +} + export class HomingProjectile extends Projectile { private target: Placeable; diff --git a/absurd-survivors/src/weapons.ts b/absurd-survivors/src/weapons.ts index 27b5673..2a743c9 100644 --- a/absurd-survivors/src/weapons.ts +++ b/absurd-survivors/src/weapons.ts @@ -1,7 +1,7 @@ import type {Weapon} from "./interfaces.ts"; import {drawDot, toRad} from "./utils.ts"; import {Player} from "./Player.ts"; -import {HomingProjectile, Projectile, ProjectileStats, StraightProjectile} from "./projectile.ts"; +import {HomingProjectile, Projectile, ProjectileStats, StraightProjectile, WeaponProjectile} from "./projectile.ts"; import {World} from "./World.ts"; import {Vector} from "./base.ts"; @@ -77,6 +77,57 @@ export abstract class RangeWeapon extends BasicWeapon { } } + +export abstract class MeleeWeapon extends RangeWeapon { + protected launched: boolean; + + act() { + if(this.shootCooldown <= 0) { + if(this.createProjectile()) { + this.shootCooldown = 1; + } + } + } + + reset() { + this.shootCooldown = 0 + } +} +export class Dagger extends MeleeWeapon { + + createProjectile(): boolean { + let range = this.calculateRange() + let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position, range); + if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { + let stats = new ProjectileStats() + .withPiercings(1000) + .withSize(3) + .withDamage(this.stats.damage) + .withSpeed(this.stats.projectileSpeed); + let projectile = WeaponProjectile.createWeaponProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, this) + this.projectiles.push(projectile) + return true + } else { + return false; + } + } + + static createDagger(world: World, offset?: Vector) { + if(!offset) { + offset = new Vector(5, 5) + } + let stats = new WeaponStats() + .withProjectileSpeed(3) + .withDamage(15) + .withShootInterval(50) + let pistol = new Dagger(world, stats) + pistol.offset = offset; + pistol.size = 4; + pistol.color = 'gray'; + return pistol; + } +} + export class HomingPistol extends RangeWeapon { draw(ctx: CanvasRenderingContext2D) {