add buffs
This commit is contained in:
@@ -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
@@ -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
@@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user