move entity definitions to templates
This commit is contained in:
+3
-8
@@ -1,18 +1,12 @@
|
|||||||
import { Vector2 } from 'three'
|
import { Vector2 } from 'three'
|
||||||
|
import Pathfind from './pathfind.js'
|
||||||
import SAT from 'sat'
|
import SAT from 'sat'
|
||||||
import SATX from './satx.js'
|
import SATX from './satx.js'
|
||||||
import Pathfind from './pathfind.js'
|
|
||||||
import Ability from './ability.js'
|
|
||||||
import Team from './team.js'
|
import Team from './team.js'
|
||||||
|
|
||||||
export default class Entity {
|
export default class Entity {
|
||||||
id = crypto.randomUUID()
|
id = crypto.randomUUID()
|
||||||
abilities = [
|
abilities = [null, null, null, null] // TODO: do something about this being an array...
|
||||||
Ability.rangedAttack,
|
|
||||||
Ability.straightShot,
|
|
||||||
Ability.shieldThrow,
|
|
||||||
Ability.blink,
|
|
||||||
]
|
|
||||||
casting = null
|
casting = null
|
||||||
cooldowns = {}
|
cooldowns = {}
|
||||||
dead = false
|
dead = false
|
||||||
@@ -22,6 +16,7 @@ export default class Entity {
|
|||||||
radius = 0
|
radius = 0
|
||||||
speed = 400
|
speed = 400
|
||||||
team = Team.neutral
|
team = Team.neutral
|
||||||
|
memory = {} // TODO: WARNING: currently only used for minions (code smell?)
|
||||||
|
|
||||||
#attacking = false
|
#attacking = false
|
||||||
#dest = null
|
#dest = null
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { EventEmitter } from 'node:events'
|
import { EventEmitter } from 'node:events'
|
||||||
import Entity from './entity.js'
|
import Entity from './entity.js'
|
||||||
import Terrain from './terrain.js'
|
|
||||||
import Projectile from './projectile.js'
|
import Projectile from './projectile.js'
|
||||||
|
import Terrain from './terrain.js'
|
||||||
|
|
||||||
export default class Game {
|
export default class Game {
|
||||||
tickRate = 30
|
tickRate = 30
|
||||||
|
|||||||
+21
-124
@@ -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 { Vector2 } from 'three'
|
||||||
import Team from './team.js'
|
import { WebSocketExpress } from 'websocket-express'
|
||||||
import Ability from './ability.js'
|
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 app = new WebSocketExpress()
|
||||||
const port = 1280
|
const port = 1280
|
||||||
@@ -58,39 +59,22 @@ app.ws('/ws', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function laneScenario() {
|
function laneScenario() {
|
||||||
// TODO: proper respawn
|
const player1 = new Entity(Template.player({
|
||||||
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,
|
|
||||||
id: '1',
|
id: '1',
|
||||||
spawnPosition: new Vector2(500, 150),
|
spawnPosition: new Vector2(500, 150),
|
||||||
team: Team.blue,
|
team: Team.blue,
|
||||||
})
|
}))
|
||||||
|
game.spawnEntity(player1)
|
||||||
|
|
||||||
game.spawnEntity(entity1)
|
const player2 = new Entity(Template.player({
|
||||||
|
|
||||||
const entity2 = new Entity({
|
|
||||||
...playerTemplate,
|
|
||||||
id: '2',
|
id: '2',
|
||||||
spawnPosition: new Vector2(1600, 1800),
|
spawnPosition: new Vector2(1600, 1800),
|
||||||
team: Team.red,
|
team: Team.red,
|
||||||
})
|
}))
|
||||||
|
game.spawnEntity(player2)
|
||||||
|
|
||||||
game.spawnEntity(entity2)
|
player1.attackAction(500, 150)
|
||||||
|
player2.attackAction(1600, 1800)
|
||||||
|
|
||||||
const midWallStart = new Vector2(400, 400)
|
const midWallStart = new Vector2(400, 400)
|
||||||
const midWallEnd = new Vector2(1600, 1600)
|
const midWallEnd = new Vector2(1600, 1600)
|
||||||
@@ -117,106 +101,19 @@ function laneScenario() {
|
|||||||
midSouthWall.id = 'midSouthWall'
|
midSouthWall.id = 'midSouthWall'
|
||||||
game.addTerrain(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 gameLogic = function gameLogic() {
|
||||||
const game = this
|
const game = this
|
||||||
|
|
||||||
const blueMinion = new Entity({
|
if ([(0 * game.tickRate), (1 * game.tickRate), (2 * game.tickRate)].includes(game.currentTick % (30 * game.tickRate))) {
|
||||||
...minionTemplate,
|
game.spawnEntity(new Entity(Template.minion(Team.blue, { ranged: false })))
|
||||||
logic: minionLogic(Team.blue),
|
game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: false })))
|
||||||
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 (game.currentTick % (30 * game.tickRate) == (1 * game.tickRate)) {
|
if ([(3 * game.tickRate), (4 * game.tickRate), (5 * game.tickRate)].includes(game.currentTick % (30 * game.tickRate))) {
|
||||||
game.spawnEntity(blueMeleeMinion)
|
game.spawnEntity(new Entity(Template.minion(Team.blue, { ranged: true })))
|
||||||
game.spawnEntity(redMeleeMinion)
|
game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true })))
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entity2.attackAction(1600, 1800)
|
|
||||||
entity1.attackAction(500, 150)
|
|
||||||
game.logic = gameLogic
|
game.logic = gameLogic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Path, Vector2 } from 'three'
|
|
||||||
import Entity from './entity.js'
|
import Entity from './entity.js'
|
||||||
import PriorityQueue from './priority-queue.js'
|
import PriorityQueue from './priority-queue.js'
|
||||||
import SATX from './satx.js'
|
import SATX from './satx.js'
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
|
import { Vector2 } from 'three'
|
||||||
import SAT from 'sat'
|
import SAT from 'sat'
|
||||||
import SATX from './satx.js'
|
import SATX from './satx.js'
|
||||||
import { Vector2 } from 'three'
|
|
||||||
|
|
||||||
export default class Projectile {
|
export default class Projectile {
|
||||||
id = crypto.randomUUID()
|
id = crypto.randomUUID()
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import SAT from 'sat'
|
|
||||||
import { Vector2 } from 'three'
|
import { Vector2 } from 'three'
|
||||||
import Entity from './entity.js'
|
import Entity from './entity.js'
|
||||||
|
import SAT from 'sat'
|
||||||
|
|
||||||
export default class SATX {
|
export default class SATX {
|
||||||
static clamp(vectorOrObject, maxX = Infinity, maxY = Infinity, radius = 0) {
|
static clamp(vectorOrObject, maxX = Infinity, maxY = Infinity, radius = 0) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import SAT from 'sat'
|
|
||||||
import { Shape, ShapeUtils, Vector2 } from 'three'
|
import { Shape, ShapeUtils, Vector2 } from 'three'
|
||||||
|
import SAT from 'sat'
|
||||||
|
|
||||||
export default class Terrain {
|
export default class Terrain {
|
||||||
id = crypto.randomUUID()
|
id = crypto.randomUUID()
|
||||||
|
|||||||
Reference in New Issue
Block a user