214 lines
5.0 KiB
JavaScript
214 lines
5.0 KiB
JavaScript
import { ctx } from './canvas'
|
|
import { ItemRegistry, ItemStack } from './items'
|
|
|
|
class PhysicsEntity {
|
|
constructor (x, y, w, h) {
|
|
this.x = x
|
|
this.y = y
|
|
|
|
this.width = w
|
|
this.height = h
|
|
|
|
this.mX = 0
|
|
this.mY = 0
|
|
|
|
this.grounded = false
|
|
|
|
this.speed = 8
|
|
this.gravity = 1
|
|
this.jumpPower = 20
|
|
|
|
this.dead = false
|
|
}
|
|
|
|
moveAndSlide (collider) {
|
|
// y collision
|
|
if (this.mY !== 0) {
|
|
let oldY = this.y
|
|
this.y += this.mY
|
|
if (oldY !== this.y && collider.collide(this)) {
|
|
if (this.y > oldY) this.grounded = true
|
|
this.y = oldY
|
|
this.mY = 0
|
|
} else {
|
|
this.grounded = false
|
|
}
|
|
}
|
|
|
|
// x collision
|
|
if (this.mX !== 0) {
|
|
let oldX = this.x
|
|
this.x += this.mX
|
|
if (oldX !== this.x && collider.collide(this)) {
|
|
this.mX = this.mX < 0 ? -1 : 1
|
|
this.x = oldX + this.mX
|
|
if (collider.collide(this)) {
|
|
this.x = oldX
|
|
this.mX = 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
update (dt, vp, world) {
|
|
this.mY += this.gravity
|
|
|
|
this.moveAndSlide(world)
|
|
}
|
|
|
|
draw (vp) {
|
|
ctx.fillStyle = '#f00'
|
|
ctx.fillRect(this.x - vp.x, this.y - vp.y, this.width, this.height)
|
|
}
|
|
|
|
collide (ent) {
|
|
if (!(ent instanceof PhysicsEntity)) return null
|
|
|
|
// Intersection check
|
|
if (this.x > ent.x + ent.width || this.x + this.width < ent.x ||
|
|
this.y > ent.y + ent.height || this.y + this.height < ent.y) return false
|
|
|
|
return true
|
|
}
|
|
|
|
distance (ent) {
|
|
if (!(ent instanceof PhysicsEntity)) return null
|
|
let d1 = { x: ent.x + ent.width / 2, y: ent.y + ent.height / 2 }
|
|
let d2 = { x: this.x + this.width / 2, y: this.y + this.height / 2 }
|
|
let dist = Math.floor(Math.sqrt(Math.pow(d1.x - d2.x, 2) + Math.pow(d1.y - d2.y, 2)))
|
|
return dist
|
|
}
|
|
}
|
|
|
|
const ITEM_LIFE = 120
|
|
const ITEM_MERGE_DISTANCE = 60
|
|
const ITEM_BOB_FACTOR = 5
|
|
|
|
class ItemEntity extends PhysicsEntity {
|
|
constructor (istr, x, y) {
|
|
super(x, y, 16, 16)
|
|
this.istr = istr
|
|
|
|
this._mergeTick = 0
|
|
this._life = 0
|
|
}
|
|
|
|
get name () {
|
|
if (this.istr === '') return ''
|
|
return this.istr.split(' ')[0]
|
|
}
|
|
|
|
get count () {
|
|
if (this.istr === '') return ''
|
|
let c = parseInt(this.istr.split(' ')[1])
|
|
return isNaN(c) ? 0 : c
|
|
}
|
|
|
|
get item () {
|
|
let itm = ItemRegistry.get(this.name)
|
|
return itm
|
|
}
|
|
|
|
static new (itemStack, x, y) {
|
|
if (!(itemStack instanceof ItemStack)) return null
|
|
return new ItemEntity(itemStack.toString(), x, y)
|
|
}
|
|
|
|
mergeNearby (vp, world) {
|
|
let entLayer = world.getLayer('ents')
|
|
if (!entLayer) return
|
|
let active = entLayer.getActiveEntities(vp, world)
|
|
for (let i in active) {
|
|
let ent = active[i]
|
|
if (ent.dead) continue
|
|
if (ent === this) continue
|
|
if (!(ent instanceof ItemEntity)) continue
|
|
if (ent.name !== this.name) continue
|
|
let dist = Math.floor(Math.sqrt(Math.pow(ent.x - this.x, 2) + Math.pow(ent.y - this.y, 2)))
|
|
if (dist > ITEM_MERGE_DISTANCE) continue
|
|
this.istr = this.name + ' ' + (this.count + ent.count)
|
|
this._life = 0
|
|
ent.dead = true
|
|
}
|
|
}
|
|
|
|
update (dt, vp, world) {
|
|
if (this.dead) return
|
|
this._mergeTick++
|
|
this._life += 1 * dt
|
|
if (this._mergeTick >= 60) {
|
|
this._mergeTick = 0
|
|
this.mergeNearby(vp, world)
|
|
}
|
|
if (this._life > ITEM_LIFE) {
|
|
this.dead = true
|
|
return
|
|
}
|
|
|
|
super.update(dt, vp, world)
|
|
}
|
|
|
|
draw (vp, world) {
|
|
let i = this.item
|
|
let b = Math.sin((this._life / ITEM_BOB_FACTOR) / Math.PI * 180)
|
|
if (!i) return
|
|
ctx.drawImage(i.image, this.x - vp.x, this.y - vp.y + b, this.width, this.height)
|
|
}
|
|
}
|
|
|
|
class EntityLayer {
|
|
constructor (name) {
|
|
this.name = name
|
|
this.entities = []
|
|
}
|
|
|
|
getActiveEntities (vp, world, cleanup = false) {
|
|
let active = []
|
|
let alive = []
|
|
for (let i in this.entities) {
|
|
let ent = this.entities[i]
|
|
if (ent.dead) {
|
|
continue
|
|
} else if (cleanup) {
|
|
alive.push(ent)
|
|
}
|
|
let entInChunk = world.gridPosition(vp, ent)
|
|
if (!entInChunk || !entInChunk.chunk) continue
|
|
let entChunk
|
|
for (let i in world._active) {
|
|
let chunk = world._active[i]
|
|
if (chunk.x === entInChunk.chunk.x && chunk.y === entInChunk.chunk.y) {
|
|
entChunk = chunk
|
|
break
|
|
}
|
|
}
|
|
if (!entChunk) continue
|
|
ent._chunk = entChunk
|
|
active.push(ent)
|
|
}
|
|
if (cleanup) this.entities = alive
|
|
return active
|
|
}
|
|
|
|
// Update active entities and clean up dead ones
|
|
update (dt, vp, world) {
|
|
let active = this.getActiveEntities(vp, world, true)
|
|
for (let i in active) {
|
|
let ent = active[i]
|
|
ent.update(dt, vp, world, ent._chunk)
|
|
}
|
|
}
|
|
|
|
draw (vp, world) {
|
|
let active = this.getActiveEntities(vp, world)
|
|
for (let i in active) {
|
|
let ent = active[i]
|
|
if (ent.x > vp.x + vp.width + ent.width || ent.x + ent._chunk.fullSize < vp.x - ent.width ||
|
|
ent.y > vp.y + vp.height + ent.height || ent.y + ent._chunk.fullSize < vp.y - ent.height) continue
|
|
ent.draw(vp, world)
|
|
}
|
|
}
|
|
}
|
|
|
|
export { ItemEntity, PhysicsEntity, EntityLayer }
|