import Projectile from './projectile.js' // major damage OR minor self sustain / crowd control // major support OR minor vision / selfless support / creative // major control OR minor mobility export default class Ability { id = crypto.randomUUID() name = 'Ability' castTime = 0 cooldown = 0 damage = 0 radius = 1 range = 0 speed = 1000 #effect = () => {} get effect() { return this.#effect } set effect(value) { this.#effect = value } constructor(options = {}) { 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, cooldown: 1, damage: 10, radius: 7, range: 800, speed: 3000, effect: function straightShotEffect(caster, cursor) { const ability = this const straightShotCollision = function straightShotCollision(projectile, collidingEntity) { collidingEntity.damage(ability.damage) projectile.despawn() } const projectile = new Projectile({ onCollide: straightShotCollision, owner: caster, position: caster.position.clone(), radius: ability.radius, speed: ability.speed, }) projectile.destination = caster.position.clone().add(cursor.clone().sub(caster.position).normalize().multiplyScalar(ability.range)) caster.game?.spawnProjectile(projectile) caster.cooldown(ability.id) }, }) static rangedAttack = new Ability({ id: 'ranged_attack', name: 'Ranged Attack', castTime: 0.25, cooldown: 1.25, damage: 5, radius: 5, range: 500, speed: 600, effect: function rangedAttackEffect(caster, cursor) { const ability = this let closest = null let distance = Infinity caster.game?.entities.filter((e) => e.team != caster.team && e.position.clone().sub(caster.position).length() < ability.range).forEach((e) => { const newDistance = e.position.clone().sub(cursor).length() if (newDistance < distance) { closest = e distance = newDistance } }) if (closest == null) { return } const rangedAttackAfter = function rangedAttackAfter() { closest.damage(ability.damage) } const projectile = new Projectile({ after: rangedAttackAfter, homingTarget: closest, owner: caster, position: caster.position.clone(), radius: ability.radius, speed: ability.speed, }) caster.game?.spawnProjectile(projectile) caster.cooldown(ability.id) }, }) static meleeAttack = new Ability({ id: 'melee_attack', name: 'Melee Attack', castTime: 0.1, cooldown: 1.75, damage: 10, radius: 5, range: 100, effect: function meleeAttackEffect(caster, cursor) { const ability = this let closest = null let distance = Infinity caster.game?.entities.filter((e) => e.team != caster.team && e.position.clone().sub(caster.position).length() < ability.range).forEach((e) => { const newDistance = e.position.clone().sub(cursor).length() < distance if (newDistance < distance) { closest = e distance = newDistance } }) if (closest == null) { return } closest.damage(ability.damage) caster.cooldown(ability.id) }, }) static shieldThrow = new Ability({ id: 'shield_throw', name: 'Shield Throw', castTime: 0.15, cooldown: 5, radius: 20, range: 1000, speed: 2000, effect: function shieldThrowEffect(caster, cursor) { const ability = this const shieldThrowReturn = function shieldThrowReturn(projectile, homingTarget) { const returnProjectile = new Projectile({ owner: caster, position: projectile.position.clone(), radius: ability.radius, speed: ability.speed, homingTarget: caster, }) caster.game?.spawnProjectile(returnProjectile) } const projectile = new Projectile({ after: shieldThrowReturn, owner: caster, position: caster.position.clone(), radius: ability.radius, speed: ability.speed, }) projectile.destination = caster.position.clone().add(cursor.clone().sub(caster.position).normalize().multiplyScalar(ability.range)) caster.game?.spawnProjectile(projectile) caster.cooldown(ability.id) }, }) static blink = new Ability({ id: 'blink', name: 'Blink', castTime: 1, cooldown: 2, range: 400, effect: function blinkEffect(caster, cursor) { const ability = this const direction = cursor.clone().sub(caster.position) if (direction.length() > ability.range) { direction.normalize().multiplyScalar(ability.range) } const destination = caster.position.clone().add(direction) caster.teleport(destination) caster.cooldown(ability.id) }, }) static control = new Ability({ id: 'control', name: 'Control', castTime: 1, cooldown: 5, effect: function controlEffect(caster, cursor) { }, }) }