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 }