« get me outta code hell

More coordinate redesign/refactor, more or less - csb-game - Pixelly spin-off of the Command Synergy Battle system used in Final Fantasy XIII
summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2018-08-23 23:11:38 -0300
committerFlorrie <towerofnix@gmail.com>2018-08-23 23:11:38 -0300
commit4c545ecb13785f7bf443ec401f2097af7f4ce555 (patch)
tree831ace16de095805875ab3bfdd904c66e6f83d3b
parentbe012d6f2664d52e47d222877eb22f7ab221d02f (diff)
More coordinate redesign/refactor, more or less
-rw-r--r--index.js153
1 files changed, 111 insertions, 42 deletions
diff --git a/index.js b/index.js
index 30e17cc..f9269a4 100644
--- a/index.js
+++ b/index.js
@@ -47,7 +47,12 @@ const isHealingAction = action => {
 
 class Sprite {
   constructor() {
-    // X is left/right, Z is forwards/backwards, Y is up/down.
+    // Coordinates of the BASE of the sprite. Typically, this is the middle of
+    // the bottom of the sprite's canvas's display rectangle.
+    // X is right/left, Z is forwards/backwards, Y is up/down.
+    // +X: Right, -X: Left
+    // +Z: Closer, -Z: Further
+    // +Y: Higher, -Y: Lower
     this.x = 0
     this.y = 0
     this.z = 0
@@ -70,15 +75,89 @@ class Sprite {
     }
   }
 
+  getBaseCoordinates() {
+    // BASE of the sprite. Typically, this is the middle of the bottom of the
+    // sprite's canvas's display rectangle. This is equivalent to the built-in
+    // x/y/z properties of the sprite, but usually getBaseCoordinates() should
+    // be used instead, so that code is more explicit and easy to reason about.
+
+    return {
+      x: this.x,
+      y: this.y,
+      z: this.z
+    }
+  }
+
+  getCenterCoordinates() {
+    // CENTER of the sprite. Typically, this is the center of the sprite's
+    // canvas's display rectangle.
+
+    const base = this.getBaseCoordinates()
+
+    return {
+      x: base.x,
+      y: base.y + this.canvas.height / 2,
+      z: base.z
+    }
+  }
+
+  getDrawCoordinates(camera) {
+    // DRAW/RENDER position of the sprite. These are the coordinates of where
+    // the sprite's canvas can directly be drawn onto the screen, in 2D space.
+    // Since where a sprite is drawn is based on the position of the camera,
+    // this function requires a camera to be passed.
+    // Also returned are the 2D draw/render coordinates of the center of the
+    // sprite (ala getCenterCoordinates) and the width and height of the canvas
+    // adjusted to the scale ratio (i.e. the actual size at which the canvas
+    // should be drawn).
+
+    const base = this.getBaseCoordinates()
+
+    const cameraOffsetX = base.x - camera.x
+    const cameraOffsetY = camera.y - base.y
+    const cameraOffsetZ = base.z - camera.z
+
+    const scaleRatio = camera.getScaleRatio(cameraOffsetZ)
+    const drawX = scaleRatio * cameraOffsetX + camera.width * 0.5
+    const drawY = scaleRatio * cameraOffsetY + camera.height * 0.5
+
+    const drawW = this.canvas.width * scaleRatio
+    const drawH = this.canvas.height * scaleRatio
+
+    const canvasOffsetDrawX = drawX - drawW * 0.5
+    const canvasOffsetDrawY = drawY - drawH
+
+    return {
+      x: canvasOffsetDrawX,
+      y: canvasOffsetDrawY,
+      centerX: drawX,
+      centerY: drawY - drawH * 0.5,
+      drawW,
+      drawH
+    }
+  }
+
   applySpriteEffects(ctx) {}
 }
 
 class Backdrop extends Sprite {
   constructor() {
     super()
-    // this.image.src = 'img/bg.png'
     this.image.src = 'img/xy-grid.gif'
   }
+
+  getBaseCoordinates() {
+    // Base coordinates of the backdrop are its center.
+    return this.getCenterCoordinates()
+  }
+
+  getCenterCoordinates() {
+    return {
+      x: this.x,
+      y: this.y - this.canvas.height / 2,
+      z: this.z
+    }
+  }
 }
 
 class ATBBar {
@@ -931,37 +1010,21 @@ class Battle {
       return a.z < b.z ? 1 : a.z > b.z ? -1 : 0
     })
 
-    const spriteData = []
-    for (const sprite of allSprites) {
+    const spriteData = allSprites.map(sprite => {
       sprite.draw()
-
-      const cameraOffsetX = sprite.x - camera.x
-      const cameraOffsetY = camera.y - sprite.y
-      const cameraOffsetZ = sprite.z - camera.z
-      const scaleRatio = camera.getScaleRatio(cameraOffsetZ)
-      // const scaleRatio = 1
-      const drawX = scaleRatio * cameraOffsetX + camera.width / 2
-      const drawY = scaleRatio * cameraOffsetY + camera.height / 2
-
-      const drawW = sprite.canvas.width * scaleRatio
-      const drawH = sprite.canvas.height * scaleRatio
-
-      const canvasOffsetDrawX = drawX - drawW / 2
-      const canvasOffsetDrawY = drawY - drawH / 2
-
-      spriteData.push({drawX, drawY, canvasOffsetDrawX, canvasOffsetDrawY, drawW, drawH, scaleRatio, sprite})
-    }
+      return Object.assign({sprite}, sprite.getDrawCoordinates(camera))
+    })
     window.spriteData = spriteData
 
-    for (const { canvasOffsetDrawX, canvasOffsetDrawY, drawW, drawH, sprite } of spriteData) {
-      ctx.drawImage(sprite.canvas, canvasOffsetDrawX, canvasOffsetDrawY, drawW, drawH)
+    for (const { x, y, drawW, drawH, sprite } of spriteData) {
+      ctx.drawImage(sprite.canvas, x, y, drawW, drawH)
     }
 
-    for (const { drawX, canvasOffsetDrawY, sprite } of spriteData) {
+    for (const { centerX, y, sprite } of spriteData) {
       const overlayHPBar = overlayHPBars.find(o => o.battleCharacter === sprite)
       if (overlayHPBar) {
-        overlayHPBar.x = drawX
-        overlayHPBar.y = canvasOffsetDrawY
+        overlayHPBar.x = centerX
+        overlayHPBar.y = y
       }
     }
 
@@ -1061,12 +1124,10 @@ class Battle {
     }
 
     for (const { x, y, battleCharacter } of overlayHPBars) {
-      const { drawX: spriteX, canvasOffsetDrawY: spriteY } = spriteData.find(o => o.sprite === battleCharacter)
-
       const hpBar = battleCharacter.hpBar
       hpBar.draw()
-      const drawX = Math.min(this.canvas.width - 10 - hpBar.canvas.width, Math.max(10 + hpBar.labelCanvas.width, Math.round(spriteX - hpBar.canvas.width / 2)))
-      const drawY = Math.min(this.canvas.height - 20, Math.max(10, Math.round(spriteY - hpBar.canvas.height)))
+      const drawX = Math.min(this.canvas.width - 10 - hpBar.canvas.width, Math.max(10 + hpBar.labelCanvas.width, Math.round(x - hpBar.canvas.width / 2)))
+      const drawY = Math.min(this.canvas.height - 20, Math.max(10, Math.round(y - hpBar.canvas.height)))
       ctx.drawImage(hpBar.canvas, drawX, drawY)
       ctx.drawImage(hpBar.labelCanvas, drawX - hpBar.labelCanvas.width - 2, drawY - 1)
     }
@@ -1299,11 +1360,14 @@ class BattleCamera extends Camera {
   }
 }
 
-class MagicProjectile {
+class MagicProjectile extends Sprite {
   constructor(caster, target, action) {
-    this.x = caster.x
-    this.y = caster.y
-    this.z = caster.z
+    super()
+
+    const center = caster.getCenterCoordinates()
+    this.x = center.x
+    this.y = center.y
+    this.z = center.z
 
     this.target = target
     this.action = action
@@ -1311,18 +1375,21 @@ class MagicProjectile {
     this.canvas = document.createElement('canvas')
     this.canvas.width = 8
     this.canvas.height = 8
+
+    delete this.image
   }
 
   update(dt) {
     // TODO: Good animation, make distance affect acceleration, not velocity
-    const xvel = 5 * (this.target.x - this.x)
-    const yvel = 5 * (this.target.y - this.y)
-    const zvel = 5 * (this.target.z - this.z)
+    const target = this.target.getCenterCoordinates()
+    const xvel = 5 * (target.x - this.x)
+    const yvel = 5 * (target.y - this.y)
+    const zvel = 5 * (target.z - this.z)
     this.x += dt * xvel
     this.y += dt * yvel
     this.z += dt * zvel
 
-    if (Math.abs(this.target.x - this.x) <= 5 && Math.abs(this.target.y - this.y) <= 5 && Math.abs(this.target.z - this.z) <= 5) {
+    if (Math.abs(target.x - this.x) <= 5 && Math.abs(target.y - this.y) <= 5 && Math.abs(target.z - this.z) <= 5) {
       if (this.action.target === 'enemy') {
         if (this.action.damage) this.target.takeDamage(this.action.damage)
         if (this.action.stun) this.target.addStunTime(this.action.stun || 0)
@@ -1457,11 +1524,11 @@ const basicAI = function() {
 const battle = new Battle({
   teamData: [
     {characterData: [
-      {x: -50, y: 30, name: 'Ren', hp: 400, maxHP: 400, knownActions: ['fire', 'fira', 'firaga', 'blizz', 'blizzara', 'cure'], determineChain: basicAI},
-      {x: -75, y: 30, name: 'Fie', hp: 375, maxHP: 375, knownActions: ['fire', 'blizz', 'blizzara', 'blizzaga', 'aqua', 'aquara', 'curasa'], determineChain: basicAI}
+      {x: -50, y: 0, z: 10, name: 'Ren', hp: 400, maxHP: 400, knownActions: ['fire', 'fira', 'firaga', 'blizz', 'blizzara', 'cure'], determineChain: basicAI},
+      {x: -75, y: 0, z: -10, name: 'Fie', hp: 375, maxHP: 375, knownActions: ['fire', 'blizz', 'blizzara', 'blizzaga', 'aqua', 'aquara', 'curasa'], determineChain: basicAI}
     ]},
     {characterData: [
-      {x: 75, y: 40, name: 'Manasvin Warmech', hp: 1200, maxHP: 1200, knownActions: ['aqua', 'aquaga', 'manasvinSwipe', 'manasvinRecover'], determineChain: basicAI, knockbackMultiplier: 0}
+      {x: 75, y: 0, z: 0, name: 'Manasvin Warmech', hp: 1200, maxHP: 1200, knownActions: ['aqua', 'aquaga', 'manasvinSwipe', 'manasvinRecover'], determineChain: basicAI, knockbackMultiplier: 0}
     ]}
   ]
 })
@@ -1475,7 +1542,7 @@ camera.width = canvas.width
 camera.height = canvas.height
 // camera.warpTo(battle.playerCharacter)
 camera.x = 0
-camera.y = 0
+camera.y = 40
 camera.z = 0
 
 for (let i = 0; i < 5 + 10 * Math.random(); i++) {
@@ -1490,6 +1557,8 @@ battle.canvas.width = canvas.width
 battle.canvas.height = canvas.height
 
 battle.backdrop.z = 80
+battle.backdrop.y = 80
+battle.backdrop.image.src = 'img/bg.png'
 
 let lastTime = Date.now()