From 9d3fbda494bfcd959f3b2207cc7f05132488ce98 Mon Sep 17 00:00:00 2001 From: Thayol Date: Mon, 13 Jan 2025 16:53:12 +0900 Subject: [PATCH] move entity definitions to templates --- src/entity.js | 11 +--- src/game.js | 2 +- src/index.js | 145 +++++++--------------------------------------- src/pathfind.js | 1 - src/projectile.js | 2 +- src/satx.js | 2 +- src/template.js | 65 +++++++++++++++++++++ src/terrain.js | 2 +- 8 files changed, 93 insertions(+), 137 deletions(-) create mode 100644 src/template.js diff --git a/src/entity.js b/src/entity.js index 5d3545b..b4f9f1f 100644 --- a/src/entity.js +++ b/src/entity.js @@ -1,18 +1,12 @@ import { Vector2 } from 'three' +import Pathfind from './pathfind.js' import SAT from 'sat' import SATX from './satx.js' -import Pathfind from './pathfind.js' -import Ability from './ability.js' import Team from './team.js' export default class Entity { id = crypto.randomUUID() - abilities = [ - Ability.rangedAttack, - Ability.straightShot, - Ability.shieldThrow, - Ability.blink, - ] + abilities = [null, null, null, null] // TODO: do something about this being an array... casting = null cooldowns = {} dead = false @@ -22,6 +16,7 @@ export default class Entity { radius = 0 speed = 400 team = Team.neutral + memory = {} // TODO: WARNING: currently only used for minions (code smell?) #attacking = false #dest = null diff --git a/src/game.js b/src/game.js index 3fbc641..1f47b0e 100644 --- a/src/game.js +++ b/src/game.js @@ -1,7 +1,7 @@ import { EventEmitter } from 'node:events' import Entity from './entity.js' -import Terrain from './terrain.js' import Projectile from './projectile.js' +import Terrain from './terrain.js' export default class Game { tickRate = 30 diff --git a/src/index.js b/src/index.js index de50c8b..8f4b798 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,12 @@ -import express from 'express' -import { WebSocketExpress } from 'websocket-express' -import Game from './game.js' -import Entity from './entity.js' -import Terrain from './terrain.js' import { Vector2 } from 'three' -import Team from './team.js' +import { WebSocketExpress } from 'websocket-express' import Ability from './ability.js' +import Entity from './entity.js' +import express from 'express' +import Game from './game.js' +import Team from './team.js' +import Template from './template.js' +import Terrain from './terrain.js' const app = new WebSocketExpress() const port = 1280 @@ -58,39 +59,22 @@ app.ws('/ws', async (req, res) => { }) function laneScenario() { - // TODO: proper respawn - const playerLogic = function playerLogic() { - const entity = this - if (entity.dead) { - entity.respawn() - } - } - - const playerTemplate = { - height: 80, - logic: playerLogic, - maxHealth: 600, - spawnPosition: new Vector2(500, 150), - radius: 65, - } - - const entity1 = new Entity({ - ...playerTemplate, + const player1 = new Entity(Template.player({ id: '1', spawnPosition: new Vector2(500, 150), team: Team.blue, - }) + })) + game.spawnEntity(player1) - game.spawnEntity(entity1) - - const entity2 = new Entity({ - ...playerTemplate, + const player2 = new Entity(Template.player({ id: '2', spawnPosition: new Vector2(1600, 1800), team: Team.red, - }) + })) + game.spawnEntity(player2) - game.spawnEntity(entity2) + player1.attackAction(500, 150) + player2.attackAction(1600, 1800) const midWallStart = new Vector2(400, 400) const midWallEnd = new Vector2(1600, 1600) @@ -117,106 +101,19 @@ function laneScenario() { midSouthWall.id = 'midSouthWall' game.addTerrain(midSouthWall) - const minionLogic = (team) => { - const finalGoal = team == Team.blue ? new Vector2(1900, 1900) : new Vector2(100, 100) - const subGoal = new Vector2(850, 1150) - const subGoalCheck = team == Team.blue ? ((entity) => entity.position.x < 800 || entity.position.y < 1100) : ((entity) => entity.position.x > 900 || entity.position.y > 1200) - - return function builtMinionLogic() { - const entity = this - if (entity.dead) { entity.despawn() } - - let goal = finalGoal - if (subGoalCheck(entity)) { - goal = subGoal - } - - const direction = goal.clone().sub(entity.position).normalize().multiplyScalar(100) - const fakeDestination = entity.position.clone().add(direction) - entity.attackAction(fakeDestination.x, fakeDestination.y) - } - } - - const minionTemplate = { - height: 40, - maxHealth: 300, - radius: 48, - speed: 325, - } - - const meleeMinionTemplate = { - ...minionTemplate, - height: 38, - radius: 46, - maxHealth: 450, - } - const gameLogic = function gameLogic() { const game = this - - const blueMinion = new Entity({ - ...minionTemplate, - logic: minionLogic(Team.blue), - team: Team.blue, - position: new Vector2(200, 200), - }) - - const blueMeleeMinion = new Entity({ - ...meleeMinionTemplate, - logic: minionLogic(Team.blue), - team: Team.blue, - position: new Vector2(200, 200), - }) - blueMeleeMinion.abilities[0] = Ability.meleeAttack - const redMinion = new Entity({ - ...minionTemplate, - logic: minionLogic(Team.red), - team: Team.red, - position: new Vector2(1800, 1800), - }) - - const redMeleeMinion = new Entity({ - ...meleeMinionTemplate, - logic: minionLogic(Team.red), - team: Team.red, - position: new Vector2(1800, 1800), - }) - redMeleeMinion.abilities[0] = Ability.meleeAttack - - if (game.currentTick % (30 * game.tickRate) == (0 * game.tickRate)) { - game.spawnEntity(blueMeleeMinion) - game.spawnEntity(redMeleeMinion) + if ([(0 * game.tickRate), (1 * game.tickRate), (2 * game.tickRate)].includes(game.currentTick % (30 * game.tickRate))) { + game.spawnEntity(new Entity(Template.minion(Team.blue, { ranged: false }))) + game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: false }))) } - if (game.currentTick % (30 * game.tickRate) == (1 * game.tickRate)) { - game.spawnEntity(blueMeleeMinion) - game.spawnEntity(redMeleeMinion) - } - - if (game.currentTick % (30 * game.tickRate) == (2 * game.tickRate)) { - game.spawnEntity(blueMeleeMinion) - game.spawnEntity(redMeleeMinion) - } - - if (game.currentTick % (30 * game.tickRate) == (3 * game.tickRate)) { - game.spawnEntity(blueMinion) - game.spawnEntity(redMinion) - } - - if (game.currentTick % (30 * game.tickRate) == (4 * game.tickRate)) { - game.spawnEntity(blueMinion) - game.spawnEntity(redMinion) - } - - if (game.currentTick % (30 * game.tickRate) == (5 * game.tickRate)) { - game.spawnEntity(blueMinion) - game.spawnEntity(redMinion) + if ([(3 * game.tickRate), (4 * game.tickRate), (5 * game.tickRate)].includes(game.currentTick % (30 * game.tickRate))) { + game.spawnEntity(new Entity(Template.minion(Team.blue, { ranged: true }))) + game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true }))) } } - - entity2.attackAction(1600, 1800) - entity1.attackAction(500, 150) game.logic = gameLogic } diff --git a/src/pathfind.js b/src/pathfind.js index 6312be1..9c3f861 100644 --- a/src/pathfind.js +++ b/src/pathfind.js @@ -1,4 +1,3 @@ -import { Path, Vector2 } from 'three' import Entity from './entity.js' import PriorityQueue from './priority-queue.js' import SATX from './satx.js' diff --git a/src/projectile.js b/src/projectile.js index 23132e0..a51eedc 100644 --- a/src/projectile.js +++ b/src/projectile.js @@ -1,6 +1,6 @@ +import { Vector2 } from 'three' import SAT from 'sat' import SATX from './satx.js' -import { Vector2 } from 'three' export default class Projectile { id = crypto.randomUUID() diff --git a/src/satx.js b/src/satx.js index 94a06f6..2b75e5d 100644 --- a/src/satx.js +++ b/src/satx.js @@ -1,6 +1,6 @@ -import SAT from 'sat' import { Vector2 } from 'three' import Entity from './entity.js' +import SAT from 'sat' export default class SATX { static clamp(vectorOrObject, maxX = Infinity, maxY = Infinity, radius = 0) { diff --git a/src/template.js b/src/template.js new file mode 100644 index 0000000..dd03e0e --- /dev/null +++ b/src/template.js @@ -0,0 +1,65 @@ +import { Vector2 } from 'three' +import Ability from './ability.js' +import Team from './team.js' + +export default class Template { + static minion(team, options = {}) { + return { + abilities: [options.ranged ? Ability.rangedAttack : Ability.meleeAttack, null, null, null], + height: options.ranged ? 40 : 38, + logic: this.#minionLogic(team), + maxHealth: options.ranged ? 300 : 450, + position: team == Team.blue ? new Vector2(200, 200) : new Vector2(1800, 1800), + radius: options.ranged ? 46 : 48, + speed: 325, + team, + } + } + + static player(overrides) { + return { + abilities: [ + Ability.rangedAttack, + Ability.straightShot, + Ability.shieldThrow, + Ability.blink, + ], + height: 80, + logic: this.#playerLogic, + maxHealth: 600, + spawnPosition: new Vector2(500, 150), + radius: 65, + ...overrides, + } + } + + static #minionLogic(team) { + const finalGoal = team == Team.blue ? new Vector2(1900, 1900) : new Vector2(100, 100) + const subGoal = new Vector2(850, 1150) + const subGoalCheck = team == Team.blue ? ((entity) => entity.position.x < 800 || entity.position.y < 1100) : ((entity) => entity.position.x > 900 || entity.position.y > 1200) + + return function builtMinionLogic() { + const entity = this + if (entity.dead) { entity.despawn() } + + let goal = finalGoal + if (subGoalCheck(entity)) { + goal = subGoal + } + + const direction = goal.clone().sub(entity.position).normalize().multiplyScalar(100) + const fakeDestination = entity.position.clone().add(direction) + entity.attackAction(fakeDestination.x, fakeDestination.y) + } + } + + // TODO: proper respawn + static #playerLogic() { + return function playerLogic() { + const entity = this + if (entity.dead) { + entity.respawn() + } + } + } +} diff --git a/src/terrain.js b/src/terrain.js index e119aa4..af97d0b 100644 --- a/src/terrain.js +++ b/src/terrain.js @@ -1,5 +1,5 @@ -import SAT from 'sat' import { Shape, ShapeUtils, Vector2 } from 'three' +import SAT from 'sat' export default class Terrain { id = crypto.randomUUID()