mirror of
https://github.com/Sheldan/canvas.git
synced 2026-01-02 23:30:10 +00:00
survivors: adding health regen and simple particle system
This commit is contained in:
@@ -3,6 +3,8 @@ import {Vector} from "./base.ts";
|
|||||||
import {fillDot, getCoordinatesSplit} from "./utils.ts";
|
import {fillDot, getCoordinatesSplit} from "./utils.ts";
|
||||||
import {PlayerStats} from "./stats.ts";
|
import {PlayerStats} from "./stats.ts";
|
||||||
import {PlayerStatus} from "./status.ts";
|
import {PlayerStatus} from "./status.ts";
|
||||||
|
import {World} from "./World.ts";
|
||||||
|
import {HealingParticle} from "./particles.ts";
|
||||||
|
|
||||||
export class Player implements Drawable, Acting, Healthy {
|
export class Player implements Drawable, Acting, Healthy {
|
||||||
private _position: Vector;
|
private _position: Vector;
|
||||||
@@ -14,10 +16,13 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
private _status: PlayerStatus;
|
private _status: PlayerStatus;
|
||||||
private _weapons: Weapon[] = []
|
private _weapons: Weapon[] = []
|
||||||
private _items: Item[] = []
|
private _items: Item[] = []
|
||||||
|
private _healTick: number = 0;
|
||||||
|
private static readonly HEAL_TICK_INTERVAL = 20;
|
||||||
|
private _world: World;
|
||||||
|
|
||||||
// temp
|
// temp
|
||||||
private _speed: Vector;
|
private _speed: Vector;
|
||||||
|
private _toHeal: number = 0;
|
||||||
|
|
||||||
constructor(position: Vector) {
|
constructor(position: Vector) {
|
||||||
this._position = position;
|
this._position = position;
|
||||||
@@ -61,6 +66,10 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
this._items.push(item)
|
this._items.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set world(value: World) {
|
||||||
|
this._world = value;
|
||||||
|
}
|
||||||
|
|
||||||
move(direction: Vector) {
|
move(direction: Vector) {
|
||||||
this._position = this.position.add(direction)
|
this._position = this.position.add(direction)
|
||||||
}
|
}
|
||||||
@@ -145,5 +154,23 @@ export class Player implements Drawable, Acting, Healthy {
|
|||||||
level() {
|
level() {
|
||||||
return this.status.level
|
return this.status.level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isHurt() {
|
||||||
|
return this.health < this._effectiveStats.health
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(seconds: number, tick: number) {
|
||||||
|
this._healTick += 1;
|
||||||
|
if((this._healTick % Player.HEAL_TICK_INTERVAL) == 0 && this.isHurt()) {
|
||||||
|
let healed = this._effectiveStats.healthRegen / seconds
|
||||||
|
this._toHeal += healed;
|
||||||
|
if(this._toHeal >= 1) {
|
||||||
|
let toHealNow = this._toHeal - (this._toHeal % 1);
|
||||||
|
this._toHeal -= toHealNow;
|
||||||
|
this.heal(toHealNow);
|
||||||
|
HealingParticle.spawnHealingParticle(this._world, toHealNow, this.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,25 @@ import {Enemy} from "./Enemies.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 {Drop, Placeable} from "./interfaces.ts";
|
import type {Drop, Particle, Placeable} from "./interfaces.ts";
|
||||||
|
|
||||||
export class World {
|
export class World {
|
||||||
private _enemies: ObjectContainer<Enemy> = new ObjectContainer<Enemy>()
|
private _enemies: ObjectContainer<Enemy> = new ObjectContainer<Enemy>()
|
||||||
private _projectiles: ObjectContainer<Projectile> = new ObjectContainer<Projectile>();
|
private _projectiles: ObjectContainer<Projectile> = new ObjectContainer<Projectile>();
|
||||||
private _drops: ObjectContainer<Drop> = new ObjectContainer<Drop>();
|
private _drops: ObjectContainer<Drop> = new ObjectContainer<Drop>();
|
||||||
|
private _particles: ObjectContainer<Particle> = new ObjectContainer();
|
||||||
private _player: Player;
|
private _player: Player;
|
||||||
private readonly _ctx: CanvasRenderingContext2D;
|
private readonly _ctx: CanvasRenderingContext2D;
|
||||||
private _size: Vector;
|
private _size: Vector;
|
||||||
|
private _tick: number = 0;
|
||||||
|
private static readonly TICK_INTERVAL = 10;
|
||||||
|
private timeStamp: Date;
|
||||||
|
|
||||||
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
|
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
|
||||||
this._player = player;
|
this._player = player;
|
||||||
this._ctx = ctx;
|
this._ctx = ctx;
|
||||||
this._size = size;
|
this._size = size;
|
||||||
|
this.timeStamp = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
enemiesAct() {
|
enemiesAct() {
|
||||||
@@ -25,12 +30,15 @@ export class World {
|
|||||||
this._projectiles.clean()
|
this._projectiles.clean()
|
||||||
this._drops.items.forEach(drop => drop.act())
|
this._drops.items.forEach(drop => drop.act())
|
||||||
this._drops.clean()
|
this._drops.clean()
|
||||||
|
this._particles.items.forEach(particle => particle.act())
|
||||||
|
this._particles.clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
this._enemies.items.forEach(enemy => enemy.draw(this._ctx))
|
this._enemies.items.forEach(enemy => enemy.draw(this._ctx))
|
||||||
this._drops.items.forEach(drop => drop.draw(this._ctx))
|
this._drops.items.forEach(drop => drop.draw(this._ctx))
|
||||||
this._projectiles.items.forEach(projectile => projectile.draw(this._ctx))
|
this._projectiles.items.forEach(projectile => projectile.draw(this._ctx))
|
||||||
|
this._particles.items.forEach(particle => particle.draw(this._ctx))
|
||||||
this._player.draw(this._ctx);
|
this._player.draw(this._ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +46,10 @@ export class World {
|
|||||||
this._projectiles.add(projectile)
|
this._projectiles.add(projectile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addParticle(particle: Particle) {
|
||||||
|
this._particles.add(particle)
|
||||||
|
}
|
||||||
|
|
||||||
addDrop(drop: Drop) {
|
addDrop(drop: Drop) {
|
||||||
this._drops.add(drop)
|
this._drops.add(drop)
|
||||||
}
|
}
|
||||||
@@ -46,6 +58,10 @@ export class World {
|
|||||||
this._drops.scheduleRemoval(drop)
|
this._drops.scheduleRemoval(drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeParticle(particle: Particle) {
|
||||||
|
this._particles.scheduleRemoval(particle)
|
||||||
|
}
|
||||||
|
|
||||||
removeEnemy(enemy: Enemy) {
|
removeEnemy(enemy: Enemy) {
|
||||||
this._enemies.scheduleRemoval(enemy)
|
this._enemies.scheduleRemoval(enemy)
|
||||||
}
|
}
|
||||||
@@ -69,11 +85,21 @@ export class World {
|
|||||||
return Math.max(this.size.x, this.size.y)
|
return Math.max(this.size.x, this.size.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get size(): Vector {
|
get size(): Vector {
|
||||||
return this._size;
|
return this._size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
this._tick += 1;
|
||||||
|
if((this._tick % World.TICK_INTERVAL) == 0) {
|
||||||
|
let currentTimeStamp = new Date();
|
||||||
|
let seconds = (currentTimeStamp.getTime() - this.timeStamp.getTime()) / 1000;
|
||||||
|
this._player.tick(seconds, this._tick);
|
||||||
|
this._particles.items.forEach(particle => particle.tick(seconds, this._tick))
|
||||||
|
this.timeStamp = currentTimeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outside(position: Vector): boolean {
|
outside(position: Vector): boolean {
|
||||||
return position.x > this.size.x || position.y > this.size.y || position.x < 0 || position.y < 0
|
return position.x > this.size.x || position.y > this.size.y || position.x < 0 || position.y < 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ export abstract class BasicDrop implements Drop {
|
|||||||
return this.size
|
return this.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(seconds: number, tick: number) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MoneyDrop extends BasicDrop {
|
export class MoneyDrop extends BasicDrop {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type {World} from "./World.ts";
|
|||||||
|
|
||||||
export interface Acting {
|
export interface Acting {
|
||||||
act()
|
act()
|
||||||
|
tick(seconds: number, tick: number)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Healthy {
|
export interface Healthy {
|
||||||
@@ -33,6 +34,10 @@ export interface Drop extends Drawable, Acting {
|
|||||||
pickup()
|
pickup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Particle extends Drawable, Placeable, Acting {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export interface Placeable {
|
export interface Placeable {
|
||||||
move(any?: any)
|
move(any?: any)
|
||||||
getSize();
|
getSize();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {HUD} from "./ui.ts";
|
|||||||
import {Pistol} from "./weapons.ts";
|
import {Pistol} from "./weapons.ts";
|
||||||
import {ItemManagement} from "./items.ts";
|
import {ItemManagement} from "./items.ts";
|
||||||
|
|
||||||
|
|
||||||
let hud: HUD;
|
let hud: HUD;
|
||||||
let world: World;
|
let world: World;
|
||||||
let config: Config;
|
let config: Config;
|
||||||
@@ -39,6 +38,7 @@ function updateCanvas() {
|
|||||||
ctx.clearRect(0, 0, world.size.x, world.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.tick()
|
||||||
world.enemiesAct()
|
world.enemiesAct()
|
||||||
world.player.act()
|
world.player.act()
|
||||||
world.draw()
|
world.draw()
|
||||||
@@ -108,6 +108,7 @@ docReady(function () {
|
|||||||
let player = Player.generatePlayer(new Vector(window.innerWidth /2, window.innerHeight / 2));
|
let player = Player.generatePlayer(new Vector(window.innerWidth /2, window.innerHeight / 2));
|
||||||
|
|
||||||
world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight));
|
world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight));
|
||||||
|
player.world = world; // not sure if this is great design
|
||||||
state = new WorldState();
|
state = new WorldState();
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
|
|||||||
69
absurd-survivors/src/particles.ts
Normal file
69
absurd-survivors/src/particles.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import type {Particle} from "./interfaces.ts";
|
||||||
|
import {Vector} from "./base.ts";
|
||||||
|
import {World} from "./World.ts";
|
||||||
|
|
||||||
|
abstract class BaseParticle implements Particle {
|
||||||
|
protected _position: Vector;
|
||||||
|
protected world: World;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(position: Vector, world: World) {
|
||||||
|
this._position = position.clone();
|
||||||
|
this.world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): Vector {
|
||||||
|
return this._position;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
move(any?: any) {
|
||||||
|
this._position = this._position.add(new Vector(0, -0.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
|
}
|
||||||
|
|
||||||
|
act() {
|
||||||
|
this.move()
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(seconds: number, tick: number) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HealingParticle extends BaseParticle {
|
||||||
|
private healthAmount: number;
|
||||||
|
private secondsToDisplay: number = 2;
|
||||||
|
private alreadyDisplayed: number = 0;
|
||||||
|
|
||||||
|
constructor(position: Vector, world: World, healthAmount: number) {
|
||||||
|
super(position, world);
|
||||||
|
this.healthAmount = healthAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.fillText(this.healthAmount + '', this._position.x, this._position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static spawnHealingParticle(world: World, health: number, position: Vector) {
|
||||||
|
world.addParticle(this.createHealingParticle(world, health, position))
|
||||||
|
}
|
||||||
|
|
||||||
|
static createHealingParticle(world: World, health: number, position: Vector) {
|
||||||
|
let healingParticle = new HealingParticle(position, world, health)
|
||||||
|
return healingParticle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tick(seconds: number, tick: number) {
|
||||||
|
this.alreadyDisplayed += seconds;
|
||||||
|
if(this.alreadyDisplayed > this.secondsToDisplay) {
|
||||||
|
this.world.removeParticle(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ export class PlayerStats {
|
|||||||
private _pullRange: number;
|
private _pullRange: number;
|
||||||
private _weaponRange: number;
|
private _weaponRange: number;
|
||||||
private _weaponRangeFactor: number;
|
private _weaponRangeFactor: number;
|
||||||
|
private _healthRegen: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._speed = 3;
|
this._speed = 3;
|
||||||
@@ -15,6 +16,7 @@ export class PlayerStats {
|
|||||||
this._pullRange = 150;
|
this._pullRange = 150;
|
||||||
this._weaponRange = 250;
|
this._weaponRange = 250;
|
||||||
this._weaponRangeFactor = 1;
|
this._weaponRangeFactor = 1;
|
||||||
|
this._healthRegen = 0.001;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetToBasic() {
|
resetToBasic() {
|
||||||
@@ -22,7 +24,8 @@ export class PlayerStats {
|
|||||||
this._health = 0;
|
this._health = 0;
|
||||||
this._pullRange = 0;
|
this._pullRange = 0;
|
||||||
this._weaponRange = 0;
|
this._weaponRange = 0;
|
||||||
this._weaponRangeFactor = 1
|
this._weaponRangeFactor = 1;
|
||||||
|
this._healthRegen = 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
increaseLevel() {
|
increaseLevel() {
|
||||||
@@ -31,6 +34,7 @@ export class PlayerStats {
|
|||||||
this._pullRange *= 1.1;
|
this._pullRange *= 1.1;
|
||||||
this._weaponRange *= 1.25
|
this._weaponRange *= 1.25
|
||||||
this._weaponRangeFactor += 0.1
|
this._weaponRangeFactor += 0.1
|
||||||
|
this._healthRegen += 0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeStats(otherStats: PlayerStats) {
|
mergeStats(otherStats: PlayerStats) {
|
||||||
@@ -39,6 +43,7 @@ export class PlayerStats {
|
|||||||
this._pullRange += otherStats._pullRange;
|
this._pullRange += otherStats._pullRange;
|
||||||
this._weaponRange += otherStats._weaponRange
|
this._weaponRange += otherStats._weaponRange
|
||||||
this._weaponRangeFactor += otherStats._weaponRangeFactor;
|
this._weaponRangeFactor += otherStats._weaponRangeFactor;
|
||||||
|
this._healthRegen += otherStats._healthRegen;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
@@ -91,6 +96,10 @@ export class PlayerStats {
|
|||||||
return this._weaponRange
|
return this._weaponRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get healthRegen(): number {
|
||||||
|
return this._healthRegen;
|
||||||
|
}
|
||||||
|
|
||||||
get effectiveWeaponRange(): number {
|
get effectiveWeaponRange(): number {
|
||||||
return this._weaponRange * this._weaponRangeFactor;
|
return this._weaponRange * this._weaponRangeFactor;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user