add buffs

This commit is contained in:
2025-01-18 10:49:38 +09:00
parent ed6394354e
commit 7415475cb0
6 changed files with 112 additions and 7 deletions
+35
View File
@@ -1,3 +1,4 @@
import Buff from './buff.js'
import Projectile from './projectile.js' import Projectile from './projectile.js'
// major damage OR minor self sustain / crowd control // major damage OR minor self sustain / crowd control
@@ -169,6 +170,40 @@ export default class Ability {
}, },
}) })
static expose = new Ability({
id: 'expose',
name: 'Expose',
castTime: 0.25,
cooldown: 8,
radius: 80,
range: 1200,
speed: 1700,
visualRadius: 50,
effect: function exposeEffect(caster, cursor) {
const ability = this
const exposeCollision = function exposeCollision(projectile, collidingEntity) {
if (collidingEntity == null) { return }
if (collidingEntity.team == (projectile.owner?.team ?? 'unknown')) { return }
collidingEntity.applyBuff(Buff.exposed.id)
projectile.despawn()
}
const projectile = new Projectile({
onCollide: exposeCollision,
owner: caster,
position: caster.position.clone(),
radius: ability.radius,
speed: ability.speed,
visualRadius: ability.visualRadius,
})
projectile.destination = caster.position.clone().add(cursor.clone().sub(caster.position).normalize().multiplyScalar(ability.range + caster.radius))
caster.game?.spawnProjectile(projectile)
caster.cooldown(ability.id)
},
})
static control = new Ability({ static control = new Ability({
id: 'control', id: 'control',
name: 'Control', name: 'Control',
+20
View File
@@ -0,0 +1,20 @@
export default class Buff {
id = crypto.randomUUID()
name = 'Buff'
duration = 0
#effect = () => {}
get effect() { return this.#effect }
set effect(value) { this.#effect = value }
constructor(options = {}) {
Object.entries(options).forEach(([key, value]) => this[key] = value)
}
static exposed = new Buff({
id: 'exposed',
name: 'Exposed',
duration: 4,
})
}
+50 -2
View File
@@ -3,17 +3,19 @@ import Pathfind from './pathfind.js'
import SAT from 'sat' import SAT from 'sat'
import SATX from './satx.js' import SATX from './satx.js'
import Team from './team.js' import Team from './team.js'
import Buff from './buff.js'
export default class Entity { export default class Entity {
id = crypto.randomUUID() id = crypto.randomUUID()
abilities = {} abilities = {}
buffs = []
casting = null casting = null
cooldowns = {} cooldowns = {}
dead = false dead = false
health = null health = null
height = 40 height = 40
maxHealth = 1 maxHealth = 1
memory = {} memory = {} // TODO: hide from reports but keep public
position = null position = null
radius = 0 radius = 0
speed = 400 speed = 400
@@ -151,6 +153,17 @@ export default class Entity {
) )
} }
applyBuff(id) {
const index = this.buffs.findIndex((it) => it.id == id)
const timestamp = this.game?.currentTick ?? 0
if (index > -1) {
this.buffs[index].timestamp = timestamp
}
else {
this.buffs.push({ id, timestamp })
}
}
collidables() { collidables() {
const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider()) const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider())
const terrainColliders = (this.game?.terrains ?? []).map((t) => t.colliders()).flat() const terrainColliders = (this.game?.terrains ?? []).map((t) => t.colliders()).flat()
@@ -179,7 +192,13 @@ export default class Entity {
} }
damage(amount) { damage(amount) {
this.health = Math.min(Math.max(0, this.health - amount), this.maxHealth) let damage = amount
if (this.hasBuff(Buff.exposed.id)) {
damage *= 3 // TODO: move to buff, make generic
this.removeBuff(Buff.exposed.id)
}
this.health = Math.min(Math.max(0, this.health - damage), this.maxHealth)
} }
despawn() { despawn() {
@@ -190,6 +209,10 @@ export default class Entity {
return this.position.distanceTo(cursor) return this.position.distanceTo(cursor)
} }
hasBuff(id) {
return this.buffs.some((it) => it.id == id)
}
heal(amount) { heal(amount) {
this.health = Math.min(Math.max(0, this.health + amount), this.maxHealth) this.health = Math.min(Math.max(0, this.health + amount), this.maxHealth)
} }
@@ -202,6 +225,10 @@ export default class Entity {
return SATX.collideObjects(this.collider(), colliders) return SATX.collideObjects(this.collider(), colliders)
} }
removeBuff(id) {
this.buffs = this.buffs.filter((it) => it.id != id)
}
respawn() { respawn() {
this.position = this.#spawnPosition.clone() this.position = this.#spawnPosition.clone()
this.health = this.maxHealth this.health = this.maxHealth
@@ -221,6 +248,7 @@ export default class Entity {
this.#cast() this.#cast()
this.#checkHealth() this.#checkHealth()
this.#move() this.#move()
this.#tickBuffs()
this.fixPosition() this.fixPosition()
} }
@@ -375,4 +403,24 @@ export default class Entity {
} }
} }
} }
#tickBuff(index) {
const entityBuff = this.buffs[index]
if (entityBuff == null) { return }
const buffDefinition = this.game?.buffs.find((it) => it.id == entityBuff.id)
if (buffDefinition == null) { return }
const buff = { ...buffDefinition, ...entityBuff }
const duration = this.game?.secToTick(buff.duration) ?? 0
const currentTick = this.game?.currentTick ?? 0
if (buff.timestamp + duration < currentTick) {
this.removeBuff(buff.id)
}
}
#tickBuffs() {
this.buffs.forEach((_v, i) => this.#tickBuff(i))
}
} }
+2
View File
@@ -1,11 +1,13 @@
import { EventEmitter } from 'node:events' import { EventEmitter } from 'node:events'
import Ability from './ability.js' import Ability from './ability.js'
import Buff from './buff.js'
import Entity from './entity.js' import Entity from './entity.js'
import Projectile from './projectile.js' import Projectile from './projectile.js'
import Terrain from './terrain.js' import Terrain from './terrain.js'
export default class Game { export default class Game {
abilities = Object.values({...Ability}) abilities = Object.values({...Ability})
buffs = Object.values({...Buff})
averageTick = 0 averageTick = 0
currentTick = 0 currentTick = 0
entities = [] entities = []
+4 -4
View File
@@ -4,11 +4,11 @@ import SATX from './satx.js'
export default class Projectile { export default class Projectile {
id = crypto.randomUUID() id = crypto.randomUUID()
after = null after = null // TODO: hide from reports but keep public
height = 50 height = 50
memory = {} memory = {} // TODO: hide from reports but keep public
onCollide = null onCollide = null // TODO: hide from reports but keep public
owner = null owner = null // TODO: only keep an ID
position = new Vector2() position = new Vector2()
radius = 5 radius = 5
speed = 1000 speed = 1000
+1 -1
View File
@@ -22,7 +22,7 @@ export default class Template {
abilities: { abilities: {
a: Ability.rangedAttack.id, a: Ability.rangedAttack.id,
q: Ability.straightShot.id, q: Ability.straightShot.id,
w: Ability.shieldThrow.id, w: Ability.expose.id,
e: Ability.blink.id, e: Ability.blink.id,
}, },
height: 80, height: 80,