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.id : Ability.meleeAttack.id, null, null, null], height: options.ranged ? 40 : 38, logic: this.#minionLogic(options.route), maxHealth: options.ranged ? 300 : 450, position: team == Team.blue ? new Vector2(200, 200) : new Vector2(1800, 1800), radius: 48, speed: 325, team, visualRadius: options.ranged ? 36 : 38, } } static player(overrides) { return { abilities: [ Ability.rangedAttack.id, Ability.straightShot.id, Ability.shieldThrow.id, Ability.blink.id, ], height: 80, logic: this.#playerLogic, maxHealth: 600, spawnPosition: new Vector2(500, 150), radius: 65, visualRadius: 40, ...overrides, } } // TODO: minion aggro // TODO: incremental pathfinding stuck in thicker than recalculateDestRadius walls static #minionLogic(route = []) { const checkpointSize = 300 const maxDestDistance = 100 const recalculateDestRadius = 50 return function builtMinionLogic() { const entity = this if (entity.dead) { entity.despawn() } if (route.length > 0) { const routeIndex = entity.memory.routeCheckpoint ?? 0 const goal = route[routeIndex].clone() if (goal instanceof Vector2) { if (entity.distanceTo(goal) < checkpointSize) { if (routeIndex + 1 < route.length) { entity.memory.routeCheckpoint = routeIndex + 1 } } if ((entity.destination?.distanceTo(entity.position) ?? 0) < recalculateDestRadius) { const distanceToGoal = entity.distanceTo(goal) if (distanceToGoal > maxDestDistance) { const direction = goal.clone().sub(entity.position).normalize().multiplyScalar(maxDestDistance) goal.copy(entity.position.clone().add(direction)) } entity.attackAction(goal) } } if (entity.position.equals(route.at(-1))) { entity.despawn() } } } } // TODO: proper respawn static #playerLogic() { const entity = this if (entity.dead) { entity.respawn() } } }