diff --git a/src/ability.js b/src/ability.js index aafc75b..ff5be6f 100644 --- a/src/ability.js +++ b/src/ability.js @@ -24,21 +24,23 @@ export default class Ability { Object.entries(options).forEach(([key, value]) => this[key] = value) } - // TODO: skill seemingly going right through minions without a registered hit static straightShot = new Ability({ id: 'straight_shot', name: 'Straight Shot', - castTime: 0.1, + castTime: 0.25, cooldown: 1, damage: 10, - radius: 7, - range: 800, - speed: 3000, + radius: 60, + range: 1200, + speed: 2000, effect: function straightShotEffect(caster, cursor) { const ability = this const straightShotCollision = function straightShotCollision(projectile, collidingEntity) { - collidingEntity.damage(ability.damage) - projectile.despawn() + if (!projectile.memory.hit) { + collidingEntity.damage(ability.damage) + projectile.memory.hit = true + projectile.destination = collidingEntity.position.clone() + } } const projectile = new Projectile({ diff --git a/src/entity.js b/src/entity.js index 7222857..64e08ef 100644 --- a/src/entity.js +++ b/src/entity.js @@ -88,7 +88,7 @@ export default class Entity { this.moveAction(cursor, true) } - castAction(slot, cursor, halt = true) { + castAction(slot, cursor, halt = false) { const ability = this.abilities[slot] if (this.casting != null) { @@ -120,6 +120,7 @@ export default class Entity { this.#moving = false } + // TODO: cancelable and uncancelable abilities moveAction(cursor, attack = false) { if (this.casting != null && (!this.#attacking || this.casting.ability.id != this.abilities[0].id)) { this.casting = null @@ -146,7 +147,7 @@ export default class Entity { } collider() { - return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius) + return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius) } colliders() { diff --git a/src/index.js b/src/index.js index 347dee0..8b8e8fb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import { Vector2 } from 'three' import { WebSocketExpress } from 'websocket-express' -import Ability from './ability.js' import Entity from './entity.js' import express from 'express' import Game from './game.js' @@ -69,13 +68,13 @@ function laneScenario() { game.spawnEntity(player1) player1.attackAction(new Vector2(500, 150)) - const player2 = new Entity(Template.player({ - id: '2', - spawnPosition: new Vector2(1600, 1800), - team: Team.red, - })) - game.spawnEntity(player2) - player2.attackAction(new Vector2(1600, 1800)) + // const player2 = new Entity(Template.player({ + // id: '2', + // spawnPosition: new Vector2(1600, 1800), + // team: Team.red, + // })) + // game.spawnEntity(player2) + // player2.attackAction(new Vector2(1600, 1800)) const midWallStart = new Vector2(600, 600) const midWallEnd = new Vector2(1400, 1400) @@ -117,7 +116,7 @@ function laneScenario() { game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true, route: redRoute }))) } } - game.logic = gameLogic + // game.logic = gameLogic // const uBottomPoints = [ // midSouthWallPoints.at(0).clone().sub(midWallThickness), @@ -128,6 +127,13 @@ function laneScenario() { // const uBottom = new Terrain(uBottomPoints) // uBottom.id = 'uBottom' // game.addTerrain(uBottom) + + const minion = new Entity({...Template.minion(Team.red, { ranged: true }), logic: null}) + minion.teleport(new Vector2(850, 250)) + game.spawnEntity(minion) + + player1.stopAction() + player1.castAction(1, new Vector2(850, 250)) } app.listen(port, () => { diff --git a/src/projectile.js b/src/projectile.js index 2355faa..fa0b913 100644 --- a/src/projectile.js +++ b/src/projectile.js @@ -6,6 +6,7 @@ export default class Projectile { id = crypto.randomUUID() after = null height = 50 + memory = {} // TODO: WARNING: currently only used for hit detection (code smell?) onCollide = null owner = null position = new Vector2() @@ -29,18 +30,18 @@ export default class Projectile { Object.entries(options).forEach(([key, value]) => this[key] = value) } - checkCollisions() { + checkCollisions(collider) { (this.game?.entities ?? []).filter((e) => e.id != this.id).forEach((e) => { if (e.id == this.owner?.id) { return } - if (SATX.collideObject(this.collider(), e.collider())) { - this.onCollide(this, e) + if (SATX.collideObject(collider, e.collider())) { + if (this.onCollide != null) { this.onCollide(this, e) } } }) } collider() { - return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius) + return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius) } despawn() { @@ -55,11 +56,12 @@ export default class Projectile { update() { this.#move() - if (this.onCollide != null) { this.checkCollisions() } + if (this.onCollide != null) { this.checkCollisions(this.collider()) } this.#checkIfArrived() } #checkIfArrived() { + if (this.destination == null) { return } if (!this.position.equals(this.destination)) { return } if (this.after != null) { @@ -71,11 +73,16 @@ export default class Projectile { #move() { const speed = (this.speed / (this.game?.tickBudget ?? 1000)) + const prevPos = this.position.clone() if (this.position.distanceTo(this.destination) < speed) { this.position.copy(this.destination) } + else { + const step = this.destination.clone().sub(this.position).normalize().multiplyScalar(speed) + this.position.add(step) + } - const step = this.destination.clone().sub(this.position).normalize().multiplyScalar(speed) - this.position.add(step) + const tunnel = SATX.entityTunnel(prevPos.x, prevPos.y, this.position.x, this.position.y, this.radius) + if (this.onCollide != null) { this.checkCollisions(tunnel) } } }