diff --git a/absurd-survivors/src/Player.ts b/absurd-survivors/src/Player.ts index 77cfeab..9988cf7 100644 --- a/absurd-survivors/src/Player.ts +++ b/absurd-survivors/src/Player.ts @@ -22,8 +22,11 @@ export class Player implements Drawable, Acting, Healthy { this._weapons.forEach(weapon => weapon.draw(ctx)) } - public static generatePlayer(): Player { - let player = new Player(new Vector(500, 500)); + public static generatePlayer(position?: Vector): Player { + if(position === undefined) { + position = new Vector(500, 500) + } + let player = new Player(position); player._color = 'blue'; player._stats = Stats.defaultPlayerStats(); player._speed = new Vector(0, 0) diff --git a/absurd-survivors/src/base.ts b/absurd-survivors/src/base.ts index f481de5..456df9e 100644 --- a/absurd-survivors/src/base.ts +++ b/absurd-survivors/src/base.ts @@ -9,6 +9,10 @@ export class Vector { return new Vector(tip.x - shaft.x, tip.y - shaft.y); } + static zero() { + return new Vector(0, 0) + } + normalize(): Vector { let length = this.vecLength(); return new Vector(this.x / length, this.y / length) @@ -47,7 +51,7 @@ export class Vector { } dotProduct(vector: Vector): number { - return this._x * vector._x + this._y * vector._y; + return (this._x * vector._x + this._y * vector._y) / Math.pow(vector.vecLength(), 2); } angleTo(vector: Vector): number { diff --git a/absurd-survivors/src/drop.ts b/absurd-survivors/src/drop.ts index 0e83ea4..c1783d3 100644 --- a/absurd-survivors/src/drop.ts +++ b/absurd-survivors/src/drop.ts @@ -5,9 +5,9 @@ import {Vector} from "./base.ts"; export abstract class BasicDrop implements Drop { protected world: World; - protected _position: Vector; - protected _color: string; - protected _size: number; + protected _position: Vector; + protected _color: string; + protected _size: number; constructor(world: World, position: Vector) { this.world = world; diff --git a/absurd-survivors/src/main.ts b/absurd-survivors/src/main.ts index bd03505..5d0f4fb 100644 --- a/absurd-survivors/src/main.ts +++ b/absurd-survivors/src/main.ts @@ -6,7 +6,7 @@ import {Player} from "./Player.ts"; import {Vector} from "./base.ts"; import {BasicEnemy, Enemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts"; import {HUD} from "./ui.ts"; -import {Pistol} from "./weapons.ts"; +import {HomingPistol, Pistol} from "./weapons.ts"; let hud: HUD; @@ -118,7 +118,7 @@ docReady(function () { }, 15_000) player.addWeapon(Pistol.spawnPistol(world)) - player.addWeapon(Pistol.spawnPistol(world)) + player.addWeapon(HomingPistol.spawnPistol(world)) hud = new HUD(world); diff --git a/absurd-survivors/src/projectile.ts b/absurd-survivors/src/projectile.ts index 429f58a..6d7648f 100644 --- a/absurd-survivors/src/projectile.ts +++ b/absurd-survivors/src/projectile.ts @@ -2,7 +2,7 @@ import type {Acting, Placeable, Healthy} from "./interfaces.ts"; import type {Vector} from "./base.ts"; import {World} from "./World.ts"; import {Cooldown, Point, Vector} from "./base.ts"; -import {drawDot, moveInDirectionOf, straightMove, toRad} from "./utils.ts"; +import {circleLineCollision, drawDot, moveInDirectionOf, straightMove, toRad} from "./utils.ts"; import {InstanceOfUtils} from "./instance.ts"; export abstract class Projectile implements Acting, Placeable { @@ -35,18 +35,21 @@ export abstract class Projectile implements Acting, Placeable { } } 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())) { + if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { 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.hasPiercingLeft()) { + if(target.getPosition().distanceTo(this.position) < (this.stats.size + target.getSize()) + || circleLineCollision(target.getPosition(), target.getSize(), this.position, this.lastPosition)) { + if(target !== this.lastColliding) { + if(InstanceOfUtils.instanceOfHealthy(target)) { + let healthy = target as Healthy; + healthy.takeDamage(this.stats.damage) + if(!this.status.hasPiercingLeft()) { + this.world.removeProjectile(this) + } + this.status.decreasePiercings() + } else { this.world.removeProjectile(this) } - this.status.decreasePiercings() - } else { - this.world.removeProjectile(this) } } this.lastColliding = target; @@ -102,7 +105,7 @@ export class StraightProjectile extends Projectile { } static createStraightProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, color?: string) { - let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(2); + let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(stats.speed); let projectile = new StraightProjectile(start, dirVector, stats, world, parent) projectile.color = color === undefined ? 'red' : color!; world.addProjectile(projectile) @@ -127,11 +130,11 @@ export class HomingProjectile extends Projectile { if(target.dead()) { let closestTargetTo = this.world.getClosestTargetTo(this.position) - let dir = Vector.createVector(this.target.getPosition(), this.position).normalize() + let dir = Vector.createVector(this.target.getPosition(), this.position) let oldDir = Vector.createVector(this.position, this.lastPosition).normalize() if (closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { let newTargetPosition = closestTargetTo[1]!.getPosition(); - let newDir = Vector.createVector(newTargetPosition, this.position).normalize() + let newDir = Vector.createVector(newTargetPosition, this.position) let newDirAngle = newDir.angleTo(dir); if(Math.abs(newDirAngle) >= toRad(150)) { this.target = closestTargetTo[1]!; diff --git a/absurd-survivors/src/utils.ts b/absurd-survivors/src/utils.ts index dfde738..eab1494 100644 --- a/absurd-survivors/src/utils.ts +++ b/absurd-survivors/src/utils.ts @@ -23,4 +23,37 @@ export function toRad(angle) { export function toDegrees(angle) { return angle * 180 / Math.PI +} + +export function pointInsideCircle(circleCenter: Vector, radius: number, point: Vector) { + return circleCenter.distanceTo(point) < radius; +} + +export function linePointCollision(point: Vector, lineStart: Vector, lineEnd: Vector) { + let lineLength = Vector.createVector(lineEnd, lineStart).vecLength(); + let distanceStart = Vector.createVector(lineStart, point).vecLength() + let distanceEnd = Vector.createVector(lineEnd, point).vecLength(); + let buffer = 0.001 + if((distanceStart + distanceEnd) >= (lineLength - buffer) && (distanceEnd + distanceStart) <= (lineLength + buffer)) { + return true; + } + return false; +} + +export function circleLineCollision(circleCenter: Vector, radius: number, lineStart: Vector, lineEnd: Vector) { + if(pointInsideCircle(circleCenter, radius, lineStart) || pointInsideCircle(circleCenter, radius, lineEnd)) { + return true; + } + let lineVector = Vector.createVector(lineEnd, lineStart) + let vectorCenterLine = Vector.createVector(circleCenter, lineStart) + let dot = vectorCenterLine.dotProduct(lineVector) + let closestX = lineStart.x + (dot * (lineVector.x)) + let closestY = lineStart.y + (dot * (lineVector.y)) + let closestPoint = new Vector(closestX, closestY); + let onSegment = linePointCollision(closestPoint, lineStart, lineEnd) + if(!onSegment) { + return false; + } + + return pointInsideCircle(circleCenter, radius, closestPoint); } \ No newline at end of file diff --git a/absurd-survivors/src/weapons.ts b/absurd-survivors/src/weapons.ts index 5ce04a9..bfe1704 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} from "./utils.ts"; import {Player} from "./Player.ts"; -import {HomingProjectile, Projectile, ProjectileStats} from "./projectile.ts"; +import {HomingProjectile, Projectile, ProjectileStats, StraightProjectile} from "./projectile.ts"; import {World} from "./World.ts"; import {Vector} from "./base.ts"; @@ -43,7 +43,52 @@ export abstract class BasicWeapon implements Weapon { } } -export class Pistol extends BasicWeapon{ +export class HomingPistol extends BasicWeapon { + + private shootInterval: number; + private shootCooldown: number = 0; + + private projectiles: [Projectile] = [] + + draw(ctx: CanvasRenderingContext2D) { + drawDot(this.getPosition(), 1, this.color, ctx) + } + + act() { + if(this.shootCooldown <= 0) { + if(this.createProjectile()) { + this.shootCooldown = this.shootInterval; + } + } + this.shootCooldown -= 1; + } + + private createProjectile(): boolean { + let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position); + if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { + let stats = new ProjectileStats(5, 1, 5, 5) + let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow') + this.projectiles.push(projectile) + return true + } else { + return false; + } + } + + static spawnPistol(world: World, offset?: Vector) { + if(!offset) { + offset = new Vector(5, 5) + } + let pistol = new HomingPistol(world) + pistol.offset = offset; + pistol.size = 5; + pistol.color = 'yellow'; + pistol.shootInterval = 50; + return pistol; + } +} + +export class Pistol extends BasicWeapon { private shootInterval: number; private shootCooldown: number = 0; @@ -67,7 +112,7 @@ export class Pistol extends BasicWeapon{ let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position); if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) { let stats = new ProjectileStats(2, 1, 5, 5) - let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow') + let projectile = StraightProjectile.createStraightProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, 'pink') this.projectiles.push(projectile) return true } else { @@ -82,9 +127,9 @@ export class Pistol extends BasicWeapon{ let pistol = new Pistol(world) pistol.offset = offset; pistol.size = 5; - pistol.color = 'yellow'; + pistol.color = 'brown'; pistol.shootInterval = 50; return pistol; } +} -} \ No newline at end of file