mirror of
https://github.com/Sheldan/canvas.git
synced 2026-01-25 10:06:09 +00:00
survivors: adding simple drops
moving size to world object adding dying for enemies and projectiles and theoretically for players adding pull range for player adding money stat display made pistol homing
This commit is contained in:
@@ -1,18 +1,19 @@
|
|||||||
import type {Acting, Drawable, Healthy, Moving, Shooting} from "./interfaces.ts";
|
import type {Acting, Drawable, Healthy, Placeable, Shooting} from "./interfaces.ts";
|
||||||
import {drawDot, moveInDirectionOf} from "./utils.ts";
|
import {drawDot, moveInDirectionOf} from "./utils.ts";
|
||||||
import {Vector} from "./base.ts";
|
import {Vector} from "./base.ts";
|
||||||
import {World} from "./World.ts";
|
import {World} from "./World.ts";
|
||||||
import type {Projectile} from "./projectile.ts";
|
import type {Projectile} from "./projectile.ts";
|
||||||
import {HomingProjectile, StraightProjectile} from "./projectile.ts";
|
import {HomingProjectile, ProjectileStats, StraightProjectile} from "./projectile.ts";
|
||||||
|
import {MoneyDrop} from "./drop.ts";
|
||||||
|
|
||||||
export abstract class Enemy implements Moving, Drawable, Acting, Healthy {
|
export abstract class Enemy implements Placeable, Drawable, Acting, Healthy {
|
||||||
protected _position: Vector;
|
protected _position: Vector;
|
||||||
protected speed: number;
|
protected speed: number;
|
||||||
protected world: World;
|
protected world: World;
|
||||||
protected status: EnemyStatus = new EnemyStatus(10);
|
protected status: EnemyStatus = new EnemyStatus(10);
|
||||||
|
|
||||||
constructor(position: Vector) {
|
constructor(position: Vector) {
|
||||||
this._position = position;
|
this._position = position.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(ctx: CanvasRenderingContext2D) {
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
@@ -33,9 +34,22 @@ export abstract class Enemy implements Moving, Drawable, Acting, Healthy {
|
|||||||
takeDamage(damage: number) {
|
takeDamage(damage: number) {
|
||||||
this.status.health -= damage;
|
this.status.health -= damage;
|
||||||
if(this.status.dead) {
|
if(this.status.dead) {
|
||||||
|
this.die()
|
||||||
this.world.removeEnemy(this)
|
this.world.removeEnemy(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
MoneyDrop.createMoneyDrop(this.world, this._position);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
dead() {
|
||||||
|
return this.status.dead
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BasicEnemy extends Enemy {
|
export class BasicEnemy extends Enemy {
|
||||||
@@ -80,6 +94,10 @@ export class BasicEnemy extends Enemy {
|
|||||||
basicEnemy.impactDamage = 2;
|
basicEnemy.impactDamage = 2;
|
||||||
return basicEnemy;
|
return basicEnemy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
return this.size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShootingEnemy extends BasicEnemy implements Shooting {
|
export class ShootingEnemy extends BasicEnemy implements Shooting {
|
||||||
@@ -105,7 +123,8 @@ export class ShootingEnemy extends BasicEnemy implements Shooting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createProjectile() {
|
createProjectile() {
|
||||||
let projectile = StraightProjectile.createStraightProjectile(this.world, this._position, this.world.player.position, this)
|
let stats = new ProjectileStats(0, 1, 5)
|
||||||
|
let projectile = StraightProjectile.createStraightProjectile(this.world, this._position, this.world.player.position, this, stats)
|
||||||
this.projectiles.push(projectile)
|
this.projectiles.push(projectile)
|
||||||
return projectile
|
return projectile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
player._color = 'blue';
|
player._color = 'blue';
|
||||||
player._stats = Stats.defaultPlayerStats();
|
player._stats = Stats.defaultPlayerStats();
|
||||||
player._speed = new Vector(0, 0)
|
player._speed = new Vector(0, 0)
|
||||||
player._status = new Status(10);
|
player._status = new Status(10, 0);
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +59,10 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
return this._stats;
|
return this._stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get status(): Status {
|
||||||
|
return this._status;
|
||||||
|
}
|
||||||
|
|
||||||
get speed(): Vector {
|
get speed(): Vector {
|
||||||
return this._speed;
|
return this._speed;
|
||||||
}
|
}
|
||||||
@@ -66,27 +70,53 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
act() {
|
act() {
|
||||||
this._weapons.forEach(weapon => weapon.act())
|
this._weapons.forEach(weapon => weapon.act())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this._position;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
return this.stats.size
|
||||||
|
}
|
||||||
|
|
||||||
|
dead() {
|
||||||
|
return this.status.dead
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Status {
|
export class Status {
|
||||||
constructor(private _health: number) {
|
constructor(private _health: number, private _wealth: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get health(): number {
|
get health(): number {
|
||||||
return this._health;
|
return this._health;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
set health(value: number) {
|
set health(value: number) {
|
||||||
this._health = value;
|
this._health = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get dead(): boolean {
|
||||||
|
return this._health <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get wealth(): number {
|
||||||
|
return this._wealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
set wealth(value: number) {
|
||||||
|
this._wealth = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Stats {
|
export class Stats {
|
||||||
constructor(private _speed: number,
|
constructor(private _speed: number,
|
||||||
private _size: number,
|
private _size: number,
|
||||||
private _health: number) {
|
private _health: number,
|
||||||
|
private _pullRange: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get speed(): number {
|
get speed(): number {
|
||||||
@@ -105,12 +135,15 @@ export class Stats {
|
|||||||
this._size = value;
|
this._size = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get pullRange(): number {
|
||||||
|
return this._pullRange;
|
||||||
|
}
|
||||||
|
|
||||||
get health(): number {
|
get health(): number {
|
||||||
return this._health;
|
return this._health;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static defaultPlayerStats(): Stats {
|
public static defaultPlayerStats(): Stats {
|
||||||
return new Stats(2, 5, 10);
|
return new Stats(3, 5, 10, 150);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,27 +3,31 @@ import type {Player} from "./Player.ts";
|
|||||||
import {Player} from "./Player.ts";
|
import {Player} from "./Player.ts";
|
||||||
import {Projectile} from "./projectile.ts";
|
import {Projectile} from "./projectile.ts";
|
||||||
import {Vector} from "./base.ts";
|
import {Vector} from "./base.ts";
|
||||||
import type {Moving} from "./interfaces.ts";
|
import type {Drop, Placeable} from "./interfaces.ts";
|
||||||
|
|
||||||
export class World {
|
export class World {
|
||||||
private _enemies: [Enemy] = [];
|
private _enemies: [Enemy] = [];
|
||||||
private _projectiles: [Projectile] = [];
|
private _projectiles: [Projectile] = [];
|
||||||
|
private _drops: [Drop] = [];
|
||||||
private _player: Player;
|
private _player: Player;
|
||||||
private _ctx: CanvasRenderingContext2D;
|
private _ctx: CanvasRenderingContext2D;
|
||||||
|
private _size: Vector
|
||||||
|
|
||||||
|
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
|
||||||
constructor(player: Player, ctx: CanvasRenderingContext2D) {
|
|
||||||
this._player = player;
|
this._player = player;
|
||||||
this._ctx = ctx;
|
this._ctx = ctx;
|
||||||
|
this._size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
enemiesAct() {
|
enemiesAct() {
|
||||||
this._enemies.forEach(enemy => enemy.act())
|
this._enemies.forEach(enemy => enemy.act())
|
||||||
this._projectiles.forEach(projectile => projectile.act())
|
this._projectiles.forEach(projectile => projectile.act())
|
||||||
|
this._drops.forEach(drop => drop.act())
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
this._enemies.forEach(enemy => enemy.draw(this._ctx))
|
this._enemies.forEach(enemy => enemy.draw(this._ctx))
|
||||||
|
this._drops.forEach(drop => drop.draw(this._ctx))
|
||||||
this._projectiles.forEach(projectile => projectile.draw(this._ctx))
|
this._projectiles.forEach(projectile => projectile.draw(this._ctx))
|
||||||
this._player.draw(this._ctx);
|
this._player.draw(this._ctx);
|
||||||
}
|
}
|
||||||
@@ -32,6 +36,14 @@ export class World {
|
|||||||
this._projectiles.push(projectile)
|
this._projectiles.push(projectile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDrop(drop: Drop) {
|
||||||
|
this._drops.push(drop)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDrop(drop: Drop) {
|
||||||
|
this._drops = this._drops.filter(item => item !== drop)
|
||||||
|
}
|
||||||
|
|
||||||
removeProjectile(projectile: Projectile) {
|
removeProjectile(projectile: Projectile) {
|
||||||
this._projectiles = this._projectiles.filter(item => item !== projectile)
|
this._projectiles = this._projectiles.filter(item => item !== projectile)
|
||||||
}
|
}
|
||||||
@@ -45,11 +57,20 @@ export class World {
|
|||||||
return this._enemies;
|
return this._enemies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get size(): Vector {
|
||||||
|
return this._size;
|
||||||
|
}
|
||||||
|
|
||||||
addEnemy(enemy: Enemy) {
|
addEnemy(enemy: Enemy) {
|
||||||
this._enemies.push(enemy)
|
this._enemies.push(enemy)
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosestTargetTo(point: Vector): [number, Moving | undefined] | undefined {
|
randomPlace(): Vector {
|
||||||
|
return new Vector(this.size.x * Math.random(), this.size.y * Math.random())
|
||||||
|
}
|
||||||
|
|
||||||
|
getClosestTargetTo(point: Vector): [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.forEach(enemy => {
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ export class Vector {
|
|||||||
return Math.sqrt(this.x * this.x + this.y * this.y);
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Vector(this.x, this.y)
|
||||||
|
}
|
||||||
|
|
||||||
distanceTo(point: Vector): number {
|
distanceTo(point: Vector): number {
|
||||||
return Math.sqrt(Math.pow(this.x - point.x, 2) + Math.pow(this.y - point.y, 2));
|
return Math.sqrt(Math.pow(this.x - point.x, 2) + Math.pow(this.y - point.y, 2));
|
||||||
}
|
}
|
||||||
@@ -56,4 +60,31 @@ export class Vector {
|
|||||||
set y(value: number) {
|
set y(value: number) {
|
||||||
this._y = value;
|
this._y = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Cooldown {
|
||||||
|
private _currentValue;
|
||||||
|
private _totalValue;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(totalValue) {
|
||||||
|
this._totalValue = totalValue;
|
||||||
|
this._currentValue = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
cooledDown(): boolean {
|
||||||
|
return this.currentValue <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentValue(): number {
|
||||||
|
return this._currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
decreaseCooldown() {
|
||||||
|
this._currentValue -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCooldown() {
|
||||||
|
this._currentValue = this._totalValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
61
absurd-survivors/src/drop.ts
Normal file
61
absurd-survivors/src/drop.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import type {Drop} from "./interfaces.ts";
|
||||||
|
import {World} from "./World.ts";
|
||||||
|
import {drawDot, moveInDirectionOf} from "./utils.ts";
|
||||||
|
import {Vector} from "./base.ts";
|
||||||
|
|
||||||
|
export class MoneyDrop implements Drop {
|
||||||
|
private world: World;
|
||||||
|
private worth: number;
|
||||||
|
private _position: Vector;
|
||||||
|
private _color: string;
|
||||||
|
private _size: number;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(world: World, position: Vector) {
|
||||||
|
this.world = world;
|
||||||
|
this._position = position.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
|
drawDot(this._position, this.getSize(), this._color, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pickup() {
|
||||||
|
this.world.player.status.wealth += this.worth
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this._position;
|
||||||
|
}
|
||||||
|
|
||||||
|
move() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static createMoneyDrop(world: World, position?: Vector): MoneyDrop {
|
||||||
|
if(!position) {
|
||||||
|
position = world.randomPlace()
|
||||||
|
}
|
||||||
|
let drop = new MoneyDrop(world, position)
|
||||||
|
drop.worth = 1;
|
||||||
|
drop._size = 1;
|
||||||
|
drop._color = 'yellow';
|
||||||
|
world.addDrop(drop)
|
||||||
|
return drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
act() {
|
||||||
|
let distanceToPlayer = this._position.distanceTo(this.world.player.position);
|
||||||
|
if(distanceToPlayer < (this.world.player.stats.size + this._size)) {
|
||||||
|
this.pickup()
|
||||||
|
this.world.removeDrop(this)
|
||||||
|
} else if(distanceToPlayer < this.world.player.stats.pullRange) {
|
||||||
|
let speedFactor = 125 / distanceToPlayer;
|
||||||
|
this._position = moveInDirectionOf(this._position, this.world.player.position, speedFactor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
return this._size
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,10 +6,17 @@ export interface Acting {
|
|||||||
|
|
||||||
export interface Healthy {
|
export interface Healthy {
|
||||||
takeDamage(damage: number);
|
takeDamage(damage: number);
|
||||||
|
die();
|
||||||
|
dead();
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Moving {
|
export interface Drop extends Drawable, Acting {
|
||||||
move()
|
pickup()
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Placeable {
|
||||||
|
move(any?: any)
|
||||||
|
getSize();
|
||||||
getPosition(): Vector;
|
getPosition(): Vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +33,10 @@ export interface Shooting {
|
|||||||
removeProjectile(projectile: Projectile)
|
removeProjectile(projectile: Projectile)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Drawable {
|
export interface Drawable extends Placeable {
|
||||||
|
draw(ctx: CanvasRenderingContext2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DrawContainer {
|
||||||
draw(ctx: CanvasRenderingContext2D);
|
draw(ctx: CanvasRenderingContext2D);
|
||||||
}
|
}
|
||||||
@@ -17,14 +17,8 @@ let ctx: CanvasRenderingContext2D;
|
|||||||
let canvas;
|
let canvas;
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private _size: Vector = new Vector(window.innerWidth, window.innerHeight)
|
|
||||||
private _fps: number = 60;
|
private _fps: number = 60;
|
||||||
|
|
||||||
|
|
||||||
get size(): Vector {
|
|
||||||
return this._size;
|
|
||||||
}
|
|
||||||
|
|
||||||
get fps(): number {
|
get fps(): number {
|
||||||
return this._fps;
|
return this._fps;
|
||||||
}
|
}
|
||||||
@@ -41,7 +35,7 @@ export class WorldState {
|
|||||||
|
|
||||||
|
|
||||||
function updateCanvas() {
|
function updateCanvas() {
|
||||||
ctx.clearRect(0, 0, config.size.x, config.size.y);
|
ctx.clearRect(0, 0, world.size.x, world.size.y);
|
||||||
hud.draw(ctx)
|
hud.draw(ctx)
|
||||||
if(!state.ended) {
|
if(!state.ended) {
|
||||||
world.enemiesAct()
|
world.enemiesAct()
|
||||||
@@ -101,23 +95,22 @@ document.onkeydown = keyDown;
|
|||||||
docReady(function () {
|
docReady(function () {
|
||||||
canvas = document.getElementById('canvas');
|
canvas = document.getElementById('canvas');
|
||||||
|
|
||||||
config = new Config();
|
canvas.width = window.innerWidth;
|
||||||
canvas.width = config.size.x;
|
|
||||||
|
|
||||||
canvas.height = config.size.y;
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
|
||||||
ctx = canvas.getContext("2d");
|
ctx = canvas.getContext("2d");
|
||||||
|
config = new Config();
|
||||||
let player = Player.generatePlayer();
|
let player = Player.generatePlayer();
|
||||||
|
|
||||||
world = new World(player, ctx);
|
world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight));
|
||||||
state = new WorldState();
|
state = new WorldState();
|
||||||
|
|
||||||
world.addEnemy(BasicEnemy.generateBasicEnemy(world))
|
world.addEnemy(BasicEnemy.generateBasicEnemy(world))
|
||||||
world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(350, 350)))
|
world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(350, 350)))
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(Math.random() * config.size.x, Math.random() * config.size.y)))
|
world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(Math.random() * world.size.x, Math.random() * world.size.y)))
|
||||||
}, 1000)
|
}, 1000)
|
||||||
player.addWeapon(Pistol.spawnPistol(world))
|
player.addWeapon(Pistol.spawnPistol(world))
|
||||||
let secondPistol = Pistol.spawnPistol(world, new Vector(-5, -5));
|
let secondPistol = Pistol.spawnPistol(world, new Vector(-5, -5));
|
||||||
|
|||||||
@@ -1,55 +1,63 @@
|
|||||||
import type {Acting, Moving, Healthy} from "./interfaces.ts";
|
import type {Acting, Placeable, Healthy} from "./interfaces.ts";
|
||||||
import type {Vector} from "./base.ts";
|
import type {Vector} from "./base.ts";
|
||||||
import {World} from "./World.ts";
|
import {World} from "./World.ts";
|
||||||
import {Vector} from "./base.ts";
|
import {Cooldown, Vector} from "./base.ts";
|
||||||
import {drawDot, moveInDirectionOf, straightMove} from "./utils.ts";
|
import {drawDot, moveInDirectionOf, straightMove} from "./utils.ts";
|
||||||
import {InstanceOfUtils} from "./instance.ts";
|
import {InstanceOfUtils} from "./instance.ts";
|
||||||
|
|
||||||
export abstract class Projectile implements Acting, Moving {
|
export abstract class Projectile implements Acting, Placeable {
|
||||||
|
|
||||||
protected position: Vector;
|
protected position: Vector;
|
||||||
protected speedVec: Vector;
|
protected speedVec: Vector;
|
||||||
protected impact: number;
|
|
||||||
protected world: World;
|
protected world: World;
|
||||||
protected size: number;
|
|
||||||
protected parent: any;
|
protected parent: any;
|
||||||
protected color: string
|
protected color: string
|
||||||
|
private stats: ProjectileStats;
|
||||||
|
private status: ProjectileStatus;
|
||||||
|
|
||||||
|
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any) {
|
||||||
constructor(position: Vector, speedVec: Vector, world: World, parent: any) {
|
this.position = position.clone();
|
||||||
this.position = position;
|
this.speedVec = speedVec.clone();
|
||||||
this.speedVec = speedVec;
|
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.stats = stats;
|
||||||
|
this.status = new ProjectileStatus(stats.piercings)
|
||||||
}
|
}
|
||||||
|
|
||||||
act() {
|
act() {
|
||||||
this.move()
|
this.move()
|
||||||
if(this.parent != this.world.player) {
|
if(this.status.collisionCooldown.cooledDown()) {
|
||||||
if(this.position.distanceTo(this.world.player.position) < (this.size + this.world.player.stats.size)) {
|
if(this.parent !== this.world.player) {
|
||||||
this.impactPlayer()
|
if(this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.stats.size)) {
|
||||||
}
|
this.impactPlayer()
|
||||||
}
|
this.status.collisionCooldown.resetCooldown()
|
||||||
if(this.parent == this.world.player) {
|
}
|
||||||
let closestTargetTo = this.world.getClosestTargetTo(this.position);
|
} else if(this.parent === this.world.player) {
|
||||||
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined && closestTargetTo[1]?.getPosition().distanceTo(this.position) < (this.size + this.world.player.stats.size)) {
|
let closestTargetTo = this.world.getClosestTargetTo(this.position);
|
||||||
let target: Moving = closestTargetTo[1]!;
|
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined && closestTargetTo[1]?.getPosition().distanceTo(this.position) < (this.stats.size + closestTargetTo[1]?.getSize())) {
|
||||||
if(InstanceOfUtils.instanceOfHealthy(target)) {
|
let target: Placeable = closestTargetTo[1]!;
|
||||||
let healthy = target as Healthy;
|
if(InstanceOfUtils.instanceOfHealthy(target)) {
|
||||||
healthy.takeDamage(this.impact)
|
let healthy = target as Healthy;
|
||||||
|
healthy.takeDamage(this.stats.damage)
|
||||||
|
if(this.status.piercingsLeft <= 0) {
|
||||||
|
this.world.removeProjectile(this)
|
||||||
|
}
|
||||||
|
this.status.decreasePiercings()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.status.collisionCooldown.decreaseCooldown();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impactPlayer() {
|
impactPlayer() {
|
||||||
this.world.player.takeDamage(this.impact)
|
this.world.player.takeDamage(this.stats.damage)
|
||||||
this.world.removeProjectile(this)
|
this.world.removeProjectile(this)
|
||||||
};
|
};
|
||||||
|
|
||||||
draw(ctx: CanvasRenderingContext2D) {
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
drawDot(this.position, this.size, this.color, ctx)
|
drawDot(this.position, this.stats.size, this.color, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
move() {
|
move() {
|
||||||
@@ -59,24 +67,27 @@ export abstract class Projectile implements Acting, Moving {
|
|||||||
return this.position;
|
return this.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
return this.stats.size
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StraightProjectile extends Projectile {
|
export class StraightProjectile extends Projectile {
|
||||||
|
|
||||||
|
|
||||||
constructor(position: Vector, dirVector: Vector, world: World, parent: any) {
|
constructor(position: Vector, dirVector: Vector, stats: ProjectileStats, world: World, parent: any) {
|
||||||
super(position, dirVector, world, parent);
|
super(position, dirVector, stats, world, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
move() {
|
move() {
|
||||||
this.position = straightMove(this.position, this.speedVec)
|
this.position = straightMove(this.position, this.speedVec)
|
||||||
}
|
}
|
||||||
|
|
||||||
static createStraightProjectile(world: World, start: Vector, targetPosition: Vector, parent: any) {
|
static createStraightProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, color?: string) {
|
||||||
let projectile = new StraightProjectile(start, Vector.createVector(targetPosition, start).normalize().multiply(5), world, parent)
|
let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(2);
|
||||||
projectile.impact = 1;
|
let projectile = new StraightProjectile(start, dirVector, stats, world, parent)
|
||||||
projectile.size = 1
|
projectile.color = color === undefined ? 'red' : color!;
|
||||||
projectile.color = 'red';
|
|
||||||
world.addProjectile(projectile)
|
world.addProjectile(projectile)
|
||||||
return projectile;
|
return projectile;
|
||||||
}
|
}
|
||||||
@@ -84,17 +95,86 @@ export class StraightProjectile extends Projectile {
|
|||||||
|
|
||||||
export class HomingProjectile extends Projectile {
|
export class HomingProjectile extends Projectile {
|
||||||
|
|
||||||
move() {
|
private target: Placeable;
|
||||||
this.position = moveInDirectionOf(this.position, this.world.player.position, this.speedVec.vecLength())
|
|
||||||
|
|
||||||
|
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, target: Placeable) {
|
||||||
|
super(position, speedVec, stats, world, parent);
|
||||||
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createHomingProjectile(world: World, start: Vector, parent: any) {
|
move() {
|
||||||
let projectile = new HomingProjectile(start, new Vector(5, 1), world, parent)
|
if(InstanceOfUtils.instanceOfHealthy(this.target)) {
|
||||||
projectile.impact = 1;
|
let target = this.target as Healthy
|
||||||
projectile.size = 1
|
if(target.dead()) {
|
||||||
projectile.color = 'red';
|
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)
|
||||||
|
if (closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
|
||||||
|
this.target = closestTargetTo[1]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.position = moveInDirectionOf(this.position, this.target.getPosition(), this.speedVec.vecLength())
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
projectile.color = color === undefined ? 'red' : color!;
|
||||||
world.addProjectile(projectile)
|
world.addProjectile(projectile)
|
||||||
return projectile;
|
return projectile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProjectileStatus {
|
||||||
|
private _piercingsLeft: number;
|
||||||
|
private _collisionCooldown: Cooldown;
|
||||||
|
|
||||||
|
constructor(piercingsLeft: number) {
|
||||||
|
this._piercingsLeft = piercingsLeft;
|
||||||
|
this._collisionCooldown = new Cooldown(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
get piercingsLeft(): number {
|
||||||
|
return this._piercingsLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get collisionCooldown(): Cooldown {
|
||||||
|
return this._collisionCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
decreasePiercings() {
|
||||||
|
this._piercingsLeft -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProjectileStats {
|
||||||
|
private _piercings: number;
|
||||||
|
private _size: number;
|
||||||
|
private _damage: number;
|
||||||
|
|
||||||
|
constructor(piercings: number, size: number, damage: number) {
|
||||||
|
this._piercings = piercings;
|
||||||
|
this._size = size;
|
||||||
|
this._damage = damage
|
||||||
|
}
|
||||||
|
|
||||||
|
get piercings(): number {
|
||||||
|
return this._piercings;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get damage(): number {
|
||||||
|
return this._damage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import type {World} from "./World.ts";
|
import type {Drawable, DrawContainer} from "./interfaces.ts";
|
||||||
import type {Drawable} from "./interfaces.ts";
|
|
||||||
import {World} from "./World.ts";
|
import {World} from "./World.ts";
|
||||||
import type {Vector} from "./base.ts";
|
|
||||||
import {Vector} from "./base.ts";
|
import {Vector} from "./base.ts";
|
||||||
|
|
||||||
export class HUD implements Drawable{
|
export class HUD implements DrawContainer {
|
||||||
private health: HealthInfo;
|
private health: HealthInfo;
|
||||||
private world: World;
|
private world: World;
|
||||||
|
|
||||||
@@ -18,21 +16,59 @@ export class HUD implements Drawable{
|
|||||||
this.health.draw(ctx)
|
this.health.draw(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HealthInfo implements Drawable{
|
export class HealthInfo implements DrawContainer {
|
||||||
private bar: InfoBar;
|
private bar: InfoBar;
|
||||||
|
private statLabels: [StatLabel] = []
|
||||||
private world: World;
|
private world: World;
|
||||||
|
|
||||||
constructor(world: World) {
|
constructor(world: World) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.bar = new InfoBar(new Vector(0, 50), 50, 150, () => 'Health', () => this.world.player.health, () => this.world.player.stats.health)
|
this.bar = new InfoBar(new Vector(0, 50), 50, 150, () => 'Health', () => this.world.player.status.health, () => this.world.player.stats.health)
|
||||||
|
this.statLabels = [
|
||||||
|
new StatLabel(new Vector(0, 150), () => 'Money', () => this.world.player.status.wealth)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(ctx: CanvasRenderingContext2D) {
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
this.bar.draw(ctx)
|
this.bar.draw(ctx)
|
||||||
|
this.statLabels.forEach(label => label.draw(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatLabel implements Drawable {
|
||||||
|
private position: Vector;
|
||||||
|
private borderColor: string = 'white';
|
||||||
|
private textLambda: () => string;
|
||||||
|
private valueLambda: () => number;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(position: Vector, textLambda: () => string, valueLambda: () => number) {
|
||||||
|
this.position = position;
|
||||||
|
this.textLambda = textLambda;
|
||||||
|
this.valueLambda = valueLambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = this.borderColor
|
||||||
|
let value = this.valueLambda();
|
||||||
|
ctx.fillText(`${value}`, this.position.x, this.position.y)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
move(any?: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InfoBar implements Drawable {
|
export class InfoBar implements Drawable {
|
||||||
@@ -40,7 +76,7 @@ export class InfoBar implements Drawable {
|
|||||||
private height: number;
|
private height: number;
|
||||||
private width: number;
|
private width: number;
|
||||||
private fillColor: string = 'green';
|
private fillColor: string = 'green';
|
||||||
private borderColor: string = 'black';
|
private borderColor: string = 'white';
|
||||||
private textLambda: () => string;
|
private textLambda: () => string;
|
||||||
private valueLambda: () => number;
|
private valueLambda: () => number;
|
||||||
private totalValueLambda: () => number;
|
private totalValueLambda: () => number;
|
||||||
@@ -69,4 +105,14 @@ export class InfoBar implements Drawable {
|
|||||||
ctx.fill()
|
ctx.fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
move(any?: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ export function drawDot(position: Vector, size: number, color: string, ctx: Canv
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function moveInDirectionOf(position: Vector, target: Vector, speedFactor): Vector {
|
export function moveInDirectionOf(position: Vector, target: Vector, speedFactor: number): Vector {
|
||||||
let playerVector = Vector.createVector(target, position);
|
let playerVector = Vector.createVector(target, position);
|
||||||
let direction = playerVector.normalize()
|
let direction = playerVector.normalize()
|
||||||
return position.add(direction.multiply(speedFactor))
|
return position.add(direction.multiply(speedFactor))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {Weapon} from "./interfaces.ts";
|
import type {Weapon} from "./interfaces.ts";
|
||||||
import {drawDot} from "./utils.ts";
|
import {drawDot} from "./utils.ts";
|
||||||
import {Player} from "./Player.ts";
|
import {Player} from "./Player.ts";
|
||||||
import {Projectile, StraightProjectile} from "./projectile.ts";
|
import {HomingProjectile, Projectile, ProjectileStats} from "./projectile.ts";
|
||||||
import {World} from "./World.ts";
|
import {World} from "./World.ts";
|
||||||
import {Vector} from "./base.ts";
|
import {Vector} from "./base.ts";
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export class Pistol implements Weapon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw(ctx: CanvasRenderingContext2D) {
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
drawDot(this.player.position.add(this.offset), this.size, this.color, ctx)
|
drawDot(this.getPosition(), this.size, this.color, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
act() {
|
act() {
|
||||||
@@ -37,7 +37,8 @@ export class Pistol implements Weapon {
|
|||||||
private createProjectile(): boolean {
|
private createProjectile(): boolean {
|
||||||
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position);
|
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position);
|
||||||
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
|
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
|
||||||
let projectile = StraightProjectile.createStraightProjectile(this.world, this.player.position.add(this.offset), closestTargetTo[1]!.getPosition(), this.player)
|
let stats = new ProjectileStats(0, 1, 5)
|
||||||
|
let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow')
|
||||||
this.projectiles.push(projectile)
|
this.projectiles.push(projectile)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@@ -53,7 +54,18 @@ export class Pistol implements Weapon {
|
|||||||
pistol.offset = offset;
|
pistol.offset = offset;
|
||||||
pistol.size = 1;
|
pistol.size = 1;
|
||||||
pistol.color = 'yellow';
|
pistol.color = 'yellow';
|
||||||
pistol.shootInterval = 10;
|
pistol.shootInterval = 50;
|
||||||
return pistol;
|
return pistol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this.player.position.add(this.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
move(any?: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
return this.size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user