From c9c063b47758bcf0eb0e3b6a59887c3ebc0802e9 Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Sun, 7 Sep 2025 19:12:54 +0200 Subject: [PATCH] survivors: adding spear (kinda) changed range calculation for melee weapons --- absurd-survivors/src/items.ts | 19 +++++++- absurd-survivors/src/projectile.ts | 74 +++++++++++++++++++++++++++--- absurd-survivors/src/weapons.ts | 70 ++++++++++++++++++++++++++-- 3 files changed, 151 insertions(+), 12 deletions(-) diff --git a/absurd-survivors/src/items.ts b/absurd-survivors/src/items.ts index 75f88f0..57bf2ba 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 {ChainBall, HomingPistol, Pistol, SpreadWeapon} from "./weapons.ts"; +import {ChainBall, HomingPistol, Pistol, Spear, SpreadWeapon} from "./weapons.ts"; import type {World} from "./World.ts"; export enum Rarity { @@ -42,6 +42,7 @@ export class ItemManagement { this.ITEMS.push(new SpreadWeaponItem()) this.ITEMS.push(new ChainBallWeaponItem()) this.ITEMS.push(new PullRangeUp()) + this.ITEMS.push(new SpearWeaponItem()) } } @@ -187,4 +188,20 @@ export class ChainBallWeaponItem extends BaseItem { } } +export class SpearWeaponItem extends BaseItem { + pickup(player: Player, world: World) { + player.addWeapon(Spear.createSpear(world)) + super.pickup(player, world) + } + + name() { + return 'spear' + } + + getRarity(): Rarity { + return Rarity.EPIC; + } +} + + diff --git a/absurd-survivors/src/projectile.ts b/absurd-survivors/src/projectile.ts index 390ed0d..2d97ac1 100644 --- a/absurd-survivors/src/projectile.ts +++ b/absurd-survivors/src/projectile.ts @@ -12,7 +12,7 @@ import { toRad } from "./utils.ts"; import {InstanceOfUtils} from "./instance.ts"; -import {MeleeWeapon} from "./weapons.ts"; +import {ChainBall, MeleeWeapon} from "./weapons.ts"; import type {Enemy} from "./Enemies.ts"; export abstract class Projectile implements Acting, Placeable { @@ -129,13 +129,13 @@ export class StraightProjectile extends Projectile { } } -export class WeaponProjectile extends Projectile { - private weapon: MeleeWeapon; +export class ChainBallProjectile extends Projectile { + private weapon: ChainBall; 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) { + constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, weapon: ChainBall, target: Vector) { super(position, speedVec, stats, world, parent); this.weapon = weapon; this.target = target.clone() @@ -177,9 +177,71 @@ export class WeaponProjectile extends Projectile { } } - static createWeaponProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, weapon: MeleeWeapon, color?: string) { + static createChainBallProjectile(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) + let projectile = new ChainBallProjectile(start, dirVector, stats, world, parent, weapon, targetPosition) + projectile.color = color === undefined ? 'red' : color!; + world.addProjectile(projectile) + return projectile; + } +} + +export class StraightMeleeWeaponProjectile 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: ChainBall, target: Vector) { + super(position, speedVec, stats, world, parent); + this.weapon = weapon; + this.target = target.clone() + } + + draw(ctx: CanvasRenderingContext2D) { + let position = this.getRealPosition(); + drawDot(position, this.stats.size, 'brown', ctx) // todo render the weapon instead + } + + getRealPosition(): Vector { + return this.world.player.getPosition().add(this.position) + } + + act() { + this.move() + let hitEnemies = this.world.getAllInRange(this.getRealPosition(), 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; + this.speedVec = this.speedVec.multiply(3) + } + } else { + this.position = moveInDirectionOf(this.position, new Vector(0, 0), this.speedVec.vecLength()) + } + if(this.movingBack && this.position.distanceTo(new Vector(0, 0)) < (this.stats.size + this.world.player.stats.size)) { + this.weapon.reset(); + this.die() + } + } + + static createStraightMeleeProjectile(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 StraightMeleeWeaponProjectile(start, dirVector, stats, world, parent, weapon, targetPosition) projectile.color = color === undefined ? 'red' : color!; world.addProjectile(projectile) return projectile; diff --git a/absurd-survivors/src/weapons.ts b/absurd-survivors/src/weapons.ts index 03c9308..63792e5 100644 --- a/absurd-survivors/src/weapons.ts +++ b/absurd-survivors/src/weapons.ts @@ -1,7 +1,14 @@ import type {Weapon} from "./interfaces.ts"; import {drawDot, toRad} from "./utils.ts"; import {Player} from "./Player.ts"; -import {HomingProjectile, Projectile, ProjectileStats, StraightProjectile, WeaponProjectile} from "./projectile.ts"; +import { + HomingProjectile, + Projectile, + ProjectileStats, + StraightProjectile, + ChainBallProjectile, + StraightMeleeWeaponProjectile +} from "./projectile.ts"; import {World} from "./World.ts"; import {Vector} from "./base.ts"; @@ -84,15 +91,67 @@ export abstract class MeleeWeapon extends RangeWeapon { act() { if(this.shootCooldown <= 0) { if(this.createProjectile()) { + this.launched = true; this.shootCooldown = 1; } } + if(!this.launched) { + this.shootCooldown -= 1; + } } reset() { - this.shootCooldown = 0 + this.shootCooldown = this.stats.shootInterval + this.launched = false; + } + + calculateRange(): number { + return this.stats.effectiveWeaponRange; } } + +export class Spear extends MeleeWeapon { + protected launched: boolean; + + createProjectile(): boolean { + let range = this.calculateRange() + let closestTargetTo = this.world.getFarthestTargetButWithin(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 offsetVector = Vector.createVector(closestTargetTo[1]!.getPosition(), this.player.position).multiply(1.1); + if(offsetVector.vecLength() < 15) { + offsetVector = offsetVector.normalize().multiply(40) + } + let projectile = StraightMeleeWeaponProjectile.createStraightMeleeProjectile(this.world, new Vector(0, 0), offsetVector, this.player, stats, this) + this.projectiles.push(projectile) + return true + } else { + return false; + } + } + + static createSpear(world: World, offset?: Vector) { + if(!offset) { + offset = new Vector(5, 5) + } + let stats = new WeaponStats() + .withProjectileSpeed(5) + .withDamage(15) + .withWeaponRange(150) + .withShootInterval(50) + let pistol = new Spear(world, stats) + pistol.offset = offset; + pistol.size = 7; + pistol.color = 'brown'; + return pistol; + } + +} + export class ChainBall extends MeleeWeapon { createProjectile(): boolean { @@ -104,7 +163,7 @@ export class ChainBall extends MeleeWeapon { .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) + let projectile = ChainBallProjectile.createChainBallProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, this) this.projectiles.push(projectile) return true } else { @@ -119,7 +178,8 @@ export class ChainBall extends MeleeWeapon { let stats = new WeaponStats() .withProjectileSpeed(3) .withDamage(15) - .withShootInterval(50) + .withWeaponRange(150) + .withShootInterval(20) let pistol = new ChainBall(world, stats) pistol.offset = offset; pistol.size = 4; @@ -267,7 +327,7 @@ export class WeaponStats { constructor() { this._weaponRangeFactor = 1 - this._weaponRange = 0 + this._weaponRange = 50 this._projectilePiercings = 0 this._projectileSpeed = 100 this._damage = 1