simplification
This commit is contained in:
parent
1605fdfc99
commit
905946da06
8 changed files with 138 additions and 608 deletions
|
|
@ -1,78 +0,0 @@
|
||||||
import BoundingRectangle from './BoundingRectangle.js'
|
|
||||||
|
|
||||||
export default class Bitmap {
|
|
||||||
constructor(width, height, rgba = null) {
|
|
||||||
this._w = width
|
|
||||||
this._h = height
|
|
||||||
this._com = 4 // number of components per pixel (RGBA)
|
|
||||||
this._boundingRect = new BoundingRectangle(0, this._w - 1, 0, this._h - 1)
|
|
||||||
const len = this._w * this._h * this._com
|
|
||||||
this._data = new Uint8ClampedArray(len)
|
|
||||||
|
|
||||||
if (rgba) {
|
|
||||||
for (let i = 0; i < len; i += 4) {
|
|
||||||
this._data[i] = rgba[0]
|
|
||||||
this._data[i + 1] = rgba[1]
|
|
||||||
this._data[i + 2] = rgba[2]
|
|
||||||
this._data[i + 3] = rgba[3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public
|
|
||||||
this.width = this._w
|
|
||||||
this.height = this._h
|
|
||||||
}
|
|
||||||
|
|
||||||
toImage() {
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
canvas.width = this._w;
|
|
||||||
canvas.height = this._h;
|
|
||||||
|
|
||||||
const imgData = ctx.createImageData(canvas.width, canvas.height);
|
|
||||||
imgData.data.set(this._data);
|
|
||||||
ctx.putImageData(imgData, 0, 0);
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
|
|
||||||
const img = document.createElement('img')
|
|
||||||
img.onload = () => {
|
|
||||||
resolve(img)
|
|
||||||
}
|
|
||||||
img.src = canvas.toDataURL()
|
|
||||||
return img
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getPix(x, y, out) {
|
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * 4 * this._w) + (x * 4)
|
|
||||||
out[0] = this._data[idx]
|
|
||||||
out[1] = this._data[idx + 1]
|
|
||||||
out[2] = this._data[idx + 2]
|
|
||||||
out[3] = this._data[idx + 3]
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
putPix(x, y, rgba) {
|
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * this._com * this._w) + (x * this._com)
|
|
||||||
this._data[idx] = rgba[0]
|
|
||||||
this._data[idx + 1] = rgba[1]
|
|
||||||
this._data[idx + 2] = rgba[2]
|
|
||||||
this._data[idx + 3] = rgba[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
getBoundingRect() {
|
|
||||||
return this._boundingRect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import Point from './Point.js'
|
|
||||||
|
|
||||||
export default class BoundingRectangle {
|
|
||||||
constructor(x0, x1, y0, y1) {
|
|
||||||
this.x0 = x0
|
|
||||||
this.x1 = x1
|
|
||||||
this.y0 = y0
|
|
||||||
this.y1 = y1
|
|
||||||
this.width = (x1 - x0) + 1
|
|
||||||
this.height = (y1 - y0) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
div(d) {
|
|
||||||
this.x0 /= d
|
|
||||||
this.x1 /= d
|
|
||||||
this.y0 /= d
|
|
||||||
this.y1 /= d
|
|
||||||
}
|
|
||||||
|
|
||||||
move(x, y) {
|
|
||||||
this.x0 += x
|
|
||||||
this.x1 += x
|
|
||||||
this.y0 += y
|
|
||||||
this.y1 += y
|
|
||||||
}
|
|
||||||
|
|
||||||
moved(x, y) {
|
|
||||||
return new BoundingRectangle(
|
|
||||||
this.x0 + x,
|
|
||||||
this.x1 + x,
|
|
||||||
this.y0 + y,
|
|
||||||
this.y1 + y
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
center() {
|
|
||||||
return new Point(
|
|
||||||
this.x0 + this.width / 2,
|
|
||||||
this.y0 + this.height / 2,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
centerDistance(other) {
|
|
||||||
return this.center().distance(other.center())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import BoundingRectangle from "./BoundingRectangle.js"
|
|
||||||
|
|
||||||
export default class Camera {
|
export default class Camera {
|
||||||
constructor(canvas) {
|
constructor(canvas) {
|
||||||
this.x = 0
|
this.x = 0
|
||||||
|
|
@ -17,12 +15,12 @@ export default class Camera {
|
||||||
}
|
}
|
||||||
|
|
||||||
rect() {
|
rect() {
|
||||||
return new BoundingRectangle(
|
return {
|
||||||
- this.x,
|
x: -this.x,
|
||||||
- this.x + (this.width / this.zoom),
|
y: -this.y,
|
||||||
- this.y,
|
w: this.width / this.zoom,
|
||||||
- this.y + (this.height / this.zoom),
|
h: this.height / this.zoom,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
move(x, y) {
|
move(x, y) {
|
||||||
|
|
@ -79,4 +77,11 @@ export default class Camera {
|
||||||
y: (coord.y + this.y) * this.zoom,
|
y: (coord.y + this.y) * this.zoom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
worldDimToViewport(dim) {
|
||||||
|
return {
|
||||||
|
w: dim.w * this.zoom,
|
||||||
|
h: dim.h * this.zoom,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
import BoundingRectangle from './BoundingRectangle.js'
|
|
||||||
|
|
||||||
export default class CanvasAdapter {
|
|
||||||
constructor(canvas) {
|
|
||||||
this._canvas = canvas
|
|
||||||
/** @type {CanvasRenderingContext2D} */
|
|
||||||
this._ctx = this._canvas.getContext('2d')
|
|
||||||
this._w = this._canvas.width
|
|
||||||
this._h = this._canvas.height
|
|
||||||
this._boundingRect = new BoundingRectangle(0, this._w - 1, 0, this._h - 1)
|
|
||||||
|
|
||||||
this._imageData = this._ctx.createImageData(this._w, this._h)
|
|
||||||
this._data = this._imageData.data
|
|
||||||
|
|
||||||
this._dirty = false
|
|
||||||
this._dirtyRect = {x0: 0, x1: 0, y0: 0, y1: 0}
|
|
||||||
this.width = this._w
|
|
||||||
this.height = this._h
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._imageData = this._ctx.createImageData(this._w, this._h)
|
|
||||||
this._data = this._imageData.data
|
|
||||||
this._dirty = false
|
|
||||||
}
|
|
||||||
clearRect(rects) {
|
|
||||||
for (let rect of rects) {
|
|
||||||
for (let x = rect.x0; x< rect.x1; x++) {
|
|
||||||
for (let y = rect.y0; y< rect.y1; y++) {
|
|
||||||
this.putPix(x, y, [0,0,0,0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPix(x, y, out) {
|
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * 4 * this._w) + (x * 4)
|
|
||||||
out[0] = this._data[idx]
|
|
||||||
out[1] = this._data[idx + 1]
|
|
||||||
out[2] = this._data[idx + 2]
|
|
||||||
out[3] = this._data[idx + 3]
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
putPix(x, y, rgba) {
|
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * 4 * this._w) + (x * 4)
|
|
||||||
this._data[idx] = rgba[0]
|
|
||||||
this._data[idx + 1] = rgba[1]
|
|
||||||
this._data[idx + 2] = rgba[2]
|
|
||||||
this._data[idx + 3] = rgba[3]
|
|
||||||
|
|
||||||
if (this._dirty) {
|
|
||||||
// merge
|
|
||||||
this._dirtyRect.x0 = Math.min(this._dirtyRect.x0, x)
|
|
||||||
this._dirtyRect.x1 = Math.max(this._dirtyRect.x1, x)
|
|
||||||
this._dirtyRect.y0 = Math.min(this._dirtyRect.y0, y)
|
|
||||||
this._dirtyRect.y1 = Math.max(this._dirtyRect.y1, y)
|
|
||||||
} else {
|
|
||||||
// set
|
|
||||||
this._dirty = true
|
|
||||||
this._dirtyRect.x0 = x
|
|
||||||
this._dirtyRect.x1 = x
|
|
||||||
this._dirtyRect.y0 = y
|
|
||||||
this._dirtyRect.y1 = y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getBoundingRect() {
|
|
||||||
return this._boundingRect
|
|
||||||
}
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
if (this._dirty) {
|
|
||||||
this._ctx.putImageData(
|
|
||||||
this._imageData,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
this._dirtyRect.x0,
|
|
||||||
this._dirtyRect.y0,
|
|
||||||
this._dirtyRect.x1 - this._dirtyRect.x0,
|
|
||||||
this._dirtyRect.y1 - this._dirtyRect.y0
|
|
||||||
)
|
|
||||||
this._dirty = null
|
|
||||||
} else {
|
|
||||||
this._ctx.putImageData(this._imageData, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
export function tint(c, f) {
|
|
||||||
return [
|
|
||||||
Math.max(0, Math.min(255, Math.round((255 - c[0]) * f))),
|
|
||||||
Math.max(0, Math.min(255, Math.round((255 - c[1]) * f))),
|
|
||||||
Math.max(0, Math.min(255, Math.round((255 - c[2]) * f))),
|
|
||||||
c[3]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shade(c, f) {
|
|
||||||
return [
|
|
||||||
Math.max(0, Math.min(255, Math.round(c[0] * f))),
|
|
||||||
Math.max(0, Math.min(255, Math.round(c[1] * f))),
|
|
||||||
Math.max(0, Math.min(255, Math.round(c[2] * f))),
|
|
||||||
c[3]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
tint,
|
|
||||||
shade
|
|
||||||
}
|
|
||||||
228
game/Graphics.js
228
game/Graphics.js
|
|
@ -1,10 +1,4 @@
|
||||||
import Bitmap from './Bitmap.js'
|
// import Bitmap from './Bitmap.js'
|
||||||
|
|
||||||
function copyUint8ClampedArray(src) {
|
|
||||||
const arr = new Uint8ClampedArray(src.length)
|
|
||||||
arr.set(new Uint8ClampedArray(src));
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCanvas(width = 0, height = 0) {
|
function createCanvas(width = 0, height = 0) {
|
||||||
const c = document.createElement('canvas')
|
const c = document.createElement('canvas')
|
||||||
|
|
@ -13,227 +7,33 @@ function createCanvas(width = 0, height = 0) {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
function dataToBitmap(w, h, data) {
|
|
||||||
const bitmap = new Bitmap(w, h)
|
|
||||||
bitmap._data = copyUint8ClampedArray(data)
|
|
||||||
return bitmap
|
|
||||||
}
|
|
||||||
|
|
||||||
function canvasToBitmap(
|
|
||||||
/** @type {HTMLCanvasElement} */ c,
|
|
||||||
/** @type {CanvasRenderingContext2D} */ ctx
|
|
||||||
) {
|
|
||||||
const data = ctx.getImageData(0, 0, c.width, c.height).data
|
|
||||||
return dataToBitmap(c.width, c.height, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
function imageToBitmap(img) {
|
|
||||||
const c = createCanvas(img.width, img.height)
|
|
||||||
const ctx = c.getContext('2d')
|
|
||||||
ctx.drawImage(img, 0, 0)
|
|
||||||
return canvasToBitmap(c, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadImageToBitmap(imagePath) {
|
async function loadImageToBitmap(imagePath) {
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
img.onload = resolve
|
img.onload = resolve
|
||||||
img.src = imagePath
|
img.src = imagePath
|
||||||
});
|
});
|
||||||
return imageToBitmap(img)
|
return await createImageBitmap(img, 0, 0, img.width, img.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeBitmap (bitmap, width, height) {
|
async function resizeBitmap (bitmap, width, height) {
|
||||||
const tmp = new Bitmap(width, height)
|
const c = createCanvas(width, height)
|
||||||
mapBitmapToBitmap(
|
const ctx = c.getContext('2d')
|
||||||
bitmap,
|
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height)
|
||||||
bitmap.getBoundingRect(),
|
return await createImageBitmap(c)
|
||||||
tmp,
|
|
||||||
tmp.getBoundingRect()
|
|
||||||
)
|
|
||||||
return tmp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapBitmapToBitmap(
|
async function createBitmap(width, height, color) {
|
||||||
/** @type {Bitmap} */src,
|
const c = createCanvas(width, height)
|
||||||
/** @type {BoundingRectangle} */ rect_src,
|
const ctx = c.getContext('2d')
|
||||||
/** @type {Bitmap} */ dst,
|
ctx.fillStyle = color
|
||||||
/** @type {BoundingRectangle} */ rect_dst
|
ctx.fillRect(0, 0, width, height)
|
||||||
) {
|
return await createImageBitmap(c)
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = rect_src.width / rect_dst.width
|
|
||||||
const h_f = rect_src.height / rect_dst.height
|
|
||||||
|
|
||||||
const startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
const startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
const endX = Math.min(rect_dst.x1, Math.ceil(((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
const endY = Math.min(rect_dst.y1, Math.ceil(((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBitmapToBitmapCapped(
|
|
||||||
/** @type {Bitmap} */ src,
|
|
||||||
/** @type {BoundingRectangle} */ rect_src,
|
|
||||||
/** @type {Bitmap} */ dst,
|
|
||||||
/** @type {BoundingRectangle} */ rect_dst,
|
|
||||||
rects_cap
|
|
||||||
) {
|
|
||||||
if (!rects_cap) {
|
|
||||||
return mapBitmapToBitmap(src, rect_src, dst, rect_dst)
|
|
||||||
}
|
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = rect_src.width / rect_dst.width
|
|
||||||
const h_f = rect_src.height / rect_dst.height
|
|
||||||
|
|
||||||
for (let rect_cap of rects_cap) {
|
|
||||||
const startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
const startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
const endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
const endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillBitmap (bitmap, rgba) {
|
|
||||||
const len = bitmap.width * bitmap.height * 4
|
|
||||||
bitmap._data = new Uint8ClampedArray(len)
|
|
||||||
for (let i = 0; i < len; i+=4) {
|
|
||||||
bitmap._data[i] = rgba[0]
|
|
||||||
bitmap._data[i + 1] = rgba[1]
|
|
||||||
bitmap._data[i + 2] = rgba[2]
|
|
||||||
bitmap._data[i + 3] = rgba[3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillBitmapCapped(bitmap, rgba, rects_cap) {
|
|
||||||
if (!rects_cap) {
|
|
||||||
return fillBitmap(bitmap, rgba)
|
|
||||||
}
|
|
||||||
for (let rect_cap of rects_cap) {
|
|
||||||
let startX = Math.floor(rect_cap.x0)
|
|
||||||
let startY = Math.floor(rect_cap.y0)
|
|
||||||
|
|
||||||
let endX = Math.ceil(rect_cap.x1)
|
|
||||||
let endY = Math.ceil(rect_cap.y1)
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
bitmap.putPix(x, y, rgba)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBitmapToAdapterCapped (
|
|
||||||
/** @type {Bitmap} */ src,
|
|
||||||
/** @type {BoundingRectangle} */ rect_src,
|
|
||||||
/** @type {CanvasAdapter} */ dst,
|
|
||||||
/** @type {BoundingRectangle} */ rect_dst,
|
|
||||||
rects_cap
|
|
||||||
) {
|
|
||||||
if (!rects_cap) {
|
|
||||||
return mapBitmapToAdapter(src, rect_src, dst, rect_dst)
|
|
||||||
}
|
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = rect_src.width / rect_dst.width
|
|
||||||
const h_f = rect_src.height / rect_dst.height
|
|
||||||
|
|
||||||
for (let rect_cap of rects_cap) {
|
|
||||||
let startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
let startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
let endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
let endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBitmapToAdapter(
|
|
||||||
/** @type {Bitmap} */ src,
|
|
||||||
/** @type {BoundingRectangle} */ rect_src,
|
|
||||||
/** @type {CanvasAdapter} */ dst,
|
|
||||||
/** @type {BoundingRectangle} */ rect_dst
|
|
||||||
) {
|
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = rect_src.width / rect_dst.width
|
|
||||||
const h_f = rect_src.height / rect_dst.height
|
|
||||||
|
|
||||||
let startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
let startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
let endX = Math.min(rect_dst.x1, Math.ceil(((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
let endY = Math.min(rect_dst.y1, Math.ceil(((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBitmap(adapter, bitmap, pos) {
|
|
||||||
const rect = bitmap.getBoundingRect()
|
|
||||||
mapBitmapToAdapter(
|
|
||||||
bitmap,
|
|
||||||
rect,
|
|
||||||
adapter,
|
|
||||||
rect.moved(pos.x, pos.y)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
createBitmap,
|
||||||
createCanvas,
|
createCanvas,
|
||||||
dataToBitmap,
|
|
||||||
canvasToBitmap,
|
|
||||||
imageToBitmap,
|
|
||||||
loadImageToBitmap,
|
loadImageToBitmap,
|
||||||
resizeBitmap,
|
resizeBitmap,
|
||||||
mapBitmapToBitmap,
|
|
||||||
mapBitmapToBitmapCapped,
|
|
||||||
fillBitmap,
|
|
||||||
fillBitmapCapped,
|
|
||||||
mapBitmapToAdapter,
|
|
||||||
mapBitmapToAdapterCapped,
|
|
||||||
drawBitmap,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,4 @@ export default class Point {
|
||||||
this.y - other.y
|
this.y - other.y
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
distance(other) {
|
|
||||||
const diffX = this.x - other.x
|
|
||||||
const diffY = this.y - other.y
|
|
||||||
return Math.sqrt(diffX * diffX + diffY * diffY)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
233
game/index.js
233
game/index.js
|
|
@ -1,7 +1,4 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
import CanvasAdapter from './CanvasAdapter.js'
|
|
||||||
import BoundingRectangle from './BoundingRectangle.js'
|
|
||||||
import Bitmap from './Bitmap.js'
|
|
||||||
import {run} from './gameloop.js'
|
import {run} from './gameloop.js'
|
||||||
import Camera from './Camera.js'
|
import Camera from './Camera.js'
|
||||||
import EventAdapter from './EventAdapter.js'
|
import EventAdapter from './EventAdapter.js'
|
||||||
|
|
@ -12,18 +9,44 @@ import Communication from './Communication.js'
|
||||||
if (typeof GAME_ID === 'undefined') throw '[ GAME_ID not set ]'
|
if (typeof GAME_ID === 'undefined') throw '[ GAME_ID not set ]'
|
||||||
if (typeof WS_ADDRESS === 'undefined') throw '[ WS_ADDRESS not set ]'
|
if (typeof WS_ADDRESS === 'undefined') throw '[ WS_ADDRESS not set ]'
|
||||||
|
|
||||||
if (typeof DEBUG === 'undefined') window.DEBUG = false
|
if (typeof DEBUG === 'undefined') window.DEBUG = true
|
||||||
|
|
||||||
function addCanvasToDom(canvas) {
|
function addCanvasToDom(canvas) {
|
||||||
document.body.append(canvas)
|
document.body.append(canvas)
|
||||||
return canvas
|
return canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pointDistance(a, b) {
|
||||||
|
const diffX = a.x - b.x
|
||||||
|
const diffY = a.y - b.y
|
||||||
|
return Math.sqrt(diffX * diffX + diffY * diffY)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rectMoved(rect, x, y) {
|
||||||
|
return {
|
||||||
|
x: rect.x + x,
|
||||||
|
y: rect.y + y,
|
||||||
|
w: rect.w,
|
||||||
|
h: rect.h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectCenter = (rect) => {
|
||||||
|
return {
|
||||||
|
x: rect.x + (rect.w / 2),
|
||||||
|
y: rect.y + (rect.h / 2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rectCenterDistance(a, b) {
|
||||||
|
return pointDistance(rectCenter(a), rectCenter(b))
|
||||||
|
}
|
||||||
|
|
||||||
function pointInBounds(pt, rect) {
|
function pointInBounds(pt, rect) {
|
||||||
return pt.x >= rect.x0
|
return pt.x >= rect.x
|
||||||
&& pt.x <= rect.x1
|
&& pt.x <= rect.x + rect.w
|
||||||
&& pt.y >= rect.y0
|
&& pt.y >= rect.y
|
||||||
&& pt.y <= rect.y1
|
&& pt.y <= rect.y + rect.h
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSurroundingTilesByIdx(puzzle, idx) {
|
function getSurroundingTilesByIdx(puzzle, idx) {
|
||||||
|
|
@ -42,8 +65,7 @@ function getSurroundingTilesByIdx(puzzle, idx) {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPuzzleTileBitmaps(bitmap, tiles, info) {
|
async function createPuzzleTileBitmaps(img, tiles, info) {
|
||||||
let img = await bitmap.toImage()
|
|
||||||
var tileSize = info.tileSize
|
var tileSize = info.tileSize
|
||||||
var tileMarginWidth = info.tileMarginWidth
|
var tileMarginWidth = info.tileMarginWidth
|
||||||
var tileDrawSize = info.tileDrawSize
|
var tileDrawSize = info.tileDrawSize
|
||||||
|
|
@ -105,7 +127,6 @@ async function createPuzzleTileBitmaps(bitmap, tiles, info) {
|
||||||
const srcRect = srcRectByIdx(info, tile.idx)
|
const srcRect = srcRectByIdx(info, tile.idx)
|
||||||
const path = pathForShape(info.shapes[tile.idx])
|
const path = pathForShape(info.shapes[tile.idx])
|
||||||
|
|
||||||
|
|
||||||
const c = Graphics.createCanvas(tileDrawSize, tileDrawSize)
|
const c = Graphics.createCanvas(tileDrawSize, tileDrawSize)
|
||||||
const ctx = c.getContext('2d')
|
const ctx = c.getContext('2d')
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
|
|
@ -118,8 +139,8 @@ async function createPuzzleTileBitmaps(bitmap, tiles, info) {
|
||||||
ctx.clip(path)
|
ctx.clip(path)
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
img,
|
img,
|
||||||
srcRect.x0 - tileMarginWidth,
|
srcRect.x - tileMarginWidth,
|
||||||
srcRect.y0 - tileMarginWidth,
|
srcRect.y - tileMarginWidth,
|
||||||
tileDrawSize,
|
tileDrawSize,
|
||||||
tileDrawSize,
|
tileDrawSize,
|
||||||
0,
|
0,
|
||||||
|
|
@ -130,9 +151,7 @@ async function createPuzzleTileBitmaps(bitmap, tiles, info) {
|
||||||
ctx.stroke(path)
|
ctx.stroke(path)
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
const bitmap = Graphics.canvasToBitmap(c, ctx)
|
bitmaps[tile.idx] = await createImageBitmap(c)
|
||||||
|
|
||||||
bitmaps[tile.idx] = bitmap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmaps
|
return bitmaps
|
||||||
|
|
@ -142,12 +161,12 @@ function srcRectByIdx(puzzleInfo, idx) {
|
||||||
let c = puzzleInfo.coords[idx]
|
let c = puzzleInfo.coords[idx]
|
||||||
let cx = c.x * puzzleInfo.tileSize
|
let cx = c.x * puzzleInfo.tileSize
|
||||||
let cy = c.y * puzzleInfo.tileSize
|
let cy = c.y * puzzleInfo.tileSize
|
||||||
return new BoundingRectangle(
|
return {
|
||||||
cx,
|
x: cx,
|
||||||
cx + puzzleInfo.tileSize,
|
y: cy,
|
||||||
cy,
|
w: puzzleInfo.tileSize,
|
||||||
cy + puzzleInfo.tileSize
|
h: puzzleInfo.tileSize,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointSub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y })
|
const pointSub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y })
|
||||||
|
|
@ -165,12 +184,12 @@ const unfinishedTileByPos = (puzzle, pos) => {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const collisionRect = new BoundingRectangle(
|
const collisionRect = {
|
||||||
tile.pos.x,
|
x: tile.pos.x,
|
||||||
tile.pos.x + puzzle.info.tileSize - 1,
|
y: tile.pos.y,
|
||||||
tile.pos.y,
|
w: puzzle.info.tileSize,
|
||||||
tile.pos.y + puzzle.info.tileSize - 1,
|
h: puzzle.info.tileSize,
|
||||||
)
|
}
|
||||||
if (pointInBounds(pos, collisionRect)) {
|
if (pointInBounds(pos, collisionRect)) {
|
||||||
if (maxZ === -1 || tile.z > maxZ) {
|
if (maxZ === -1 || tile.z > maxZ) {
|
||||||
maxZ = tile.z
|
maxZ = tile.z
|
||||||
|
|
@ -215,7 +234,7 @@ async function loadPuzzleBitmaps(puzzle) {
|
||||||
// creation of tile bitmaps
|
// creation of tile bitmaps
|
||||||
// then create the final puzzle bitmap
|
// then create the final puzzle bitmap
|
||||||
// NOTE: this can decrease OR increase in size!
|
// NOTE: this can decrease OR increase in size!
|
||||||
const bmpResized = Graphics.resizeBitmap(bmp, puzzle.info.width, puzzle.info.height)
|
const bmpResized = await Graphics.resizeBitmap(bmp, puzzle.info.width, puzzle.info.height)
|
||||||
return await createPuzzleTileBitmaps(bmpResized, puzzle.tiles, puzzle.info)
|
return await createPuzzleTileBitmaps(bmpResized, puzzle.tiles, puzzle.info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +293,7 @@ async function main () {
|
||||||
|
|
||||||
// Create a dom and attach adapters to it so we can work with it
|
// Create a dom and attach adapters to it so we can work with it
|
||||||
const canvas = addCanvasToDom(Graphics.createCanvas())
|
const canvas = addCanvasToDom(Graphics.createCanvas())
|
||||||
const adapter = new CanvasAdapter(canvas)
|
const ctx = canvas.getContext('2d')
|
||||||
const evts = new EventAdapter(canvas)
|
const evts = new EventAdapter(canvas)
|
||||||
|
|
||||||
// initialize some view data
|
// initialize some view data
|
||||||
|
|
@ -314,8 +333,8 @@ async function main () {
|
||||||
// The actual place for the puzzle. The tiles may
|
// The actual place for the puzzle. The tiles may
|
||||||
// not be moved around infinitely, just on the (invisible)
|
// not be moved around infinitely, just on the (invisible)
|
||||||
// puzzle table. however, the camera may move away from the table
|
// puzzle table. however, the camera may move away from the table
|
||||||
const puzzleTableColor = [40, 40, 40, 0]
|
const puzzleTableColor = '#222'
|
||||||
const puzzleTable = new Bitmap(
|
const puzzleTable = await Graphics.createBitmap(
|
||||||
puzzle.info.table.width,
|
puzzle.info.table.width,
|
||||||
puzzle.info.table.height,
|
puzzle.info.table.height,
|
||||||
puzzleTableColor
|
puzzleTableColor
|
||||||
|
|
@ -323,8 +342,8 @@ async function main () {
|
||||||
|
|
||||||
// In the middle of the table, there is a board. this is to
|
// In the middle of the table, there is a board. this is to
|
||||||
// tell the player where to place the final puzzle
|
// tell the player where to place the final puzzle
|
||||||
const boardColor = [80, 80, 80, 255]
|
const boardColor = '#505050'
|
||||||
const board = new Bitmap(
|
const board = await Graphics.createBitmap(
|
||||||
puzzle.info.width,
|
puzzle.info.width,
|
||||||
puzzle.info.height,
|
puzzle.info.height,
|
||||||
boardColor
|
boardColor
|
||||||
|
|
@ -391,18 +410,19 @@ async function main () {
|
||||||
|
|
||||||
// get the center position of a tile
|
// get the center position of a tile
|
||||||
const tileCenterPos = (tile) => {
|
const tileCenterPos = (tile) => {
|
||||||
return tileRectByTile(tile).center()
|
|
||||||
|
return rectCenter(tileRectByTile(tile))
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the would-be visible bounding rect if a tile was
|
// get the would-be visible bounding rect if a tile was
|
||||||
// in given position
|
// in given position
|
||||||
const tileRectByPos = (pos) => {
|
const tileRectByPos = (pos) => {
|
||||||
return new BoundingRectangle(
|
return {
|
||||||
pos.x,
|
x: pos.x,
|
||||||
pos.x + puzzle.info.tileSize,
|
y: pos.y,
|
||||||
pos.y,
|
w: puzzle.info.tileSize,
|
||||||
pos.y + puzzle.info.tileSize
|
h: puzzle.info.tileSize,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current visible bounding rect for a tile
|
// get the current visible bounding rect for a tile
|
||||||
|
|
@ -540,11 +560,11 @@ async function main () {
|
||||||
let pt = pointSub(tile.pos, boardPos)
|
let pt = pointSub(tile.pos, boardPos)
|
||||||
let dst = tileRectByPos(pt)
|
let dst = tileRectByPos(pt)
|
||||||
let srcRect = srcRectByIdx(puzzle.info, grabbingTileIdx)
|
let srcRect = srcRectByIdx(puzzle.info, grabbingTileIdx)
|
||||||
if (srcRect.centerDistance(dst) < puzzle.info.snapDistance) {
|
if (rectCenterDistance(srcRect, dst) < puzzle.info.snapDistance) {
|
||||||
// Snap the tile to the final destination
|
// Snap the tile to the final destination
|
||||||
moveGroupedTiles(tile, {
|
moveGroupedTiles(tile, {
|
||||||
x: srcRect.x0 + boardPos.x,
|
x: srcRect.x + boardPos.x,
|
||||||
y: srcRect.y0 + boardPos.y,
|
y: srcRect.y + boardPos.y,
|
||||||
})
|
})
|
||||||
finishGroupedTiles(tile)
|
finishGroupedTiles(tile)
|
||||||
rectTable.add(tp, puzzle.info.tileDrawSize)
|
rectTable.add(tp, puzzle.info.tileDrawSize)
|
||||||
|
|
@ -554,13 +574,14 @@ async function main () {
|
||||||
if (!other || (other.owner === -1) || areGrouped(t, other)) {
|
if (!other || (other.owner === -1) || areGrouped(t, other)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let trec_ = tileRectByTile(t)
|
const trec_ = tileRectByTile(t)
|
||||||
let otrec = tileRectByTile(other).moved(
|
const otrec = rectMoved(
|
||||||
|
tileRectByTile(other),
|
||||||
off[0] * puzzle.info.tileSize,
|
off[0] * puzzle.info.tileSize,
|
||||||
off[1] * puzzle.info.tileSize
|
off[1] * puzzle.info.tileSize
|
||||||
)
|
)
|
||||||
if (trec_.centerDistance(otrec) < puzzle.info.snapDistance) {
|
if (rectCenterDistance(trec_, otrec) < puzzle.info.snapDistance) {
|
||||||
moveGroupedTiles(t, { x: otrec.x0, y: otrec.y0 })
|
moveGroupedTiles(t, { x: otrec.x, y: otrec.y })
|
||||||
groupTiles(t, other)
|
groupTiles(t, other)
|
||||||
setGroupedZIndex(t, t.z)
|
setGroupedZIndex(t, t.z)
|
||||||
rectTable.add(tileCenterPos(t), puzzle.info.tileDrawSize)
|
rectTable.add(tileCenterPos(t), puzzle.info.tileDrawSize)
|
||||||
|
|
@ -614,7 +635,7 @@ async function main () {
|
||||||
// if it improves performance:
|
// if it improves performance:
|
||||||
// 1. background
|
// 1. background
|
||||||
// 2. tiles
|
// 2. tiles
|
||||||
// 3. (moving tiles)
|
// 3. (moving tiles
|
||||||
// 4. (players)
|
// 4. (players)
|
||||||
// (currently, if a player moves, everthing needs to be
|
// (currently, if a player moves, everthing needs to be
|
||||||
// rerendered at that position manually, maybe it is faster
|
// rerendered at that position manually, maybe it is faster
|
||||||
|
|
@ -624,103 +645,57 @@ async function main () {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint_start(20)
|
let pos
|
||||||
|
let dim
|
||||||
|
|
||||||
// draw the puzzle table
|
if (DEBUG) Debug.checkpoint_start(0)
|
||||||
if (rerenderTable) {
|
|
||||||
|
|
||||||
Graphics.fillBitmapCapped(puzzleTable, puzzleTableColor, rectTable.get())
|
ctx.fillStyle = puzzleTableColor
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
if (DEBUG) Debug.checkpoint('clear done')
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('after fill')
|
// DRAW BOARD
|
||||||
|
// ---------------------------------------------------------------
|
||||||
// draw the puzzle board on the table
|
pos = viewport.worldToViewport(boardPos)
|
||||||
Graphics.mapBitmapToBitmapCapped(
|
dim = viewport.worldDimToViewport({w: board.width, h: board.height})
|
||||||
board,
|
ctx.drawImage(board,
|
||||||
board.getBoundingRect(),
|
0, 0, board.width, board.height,
|
||||||
puzzleTable,
|
pos.x, pos.y, dim.w, dim.h
|
||||||
new BoundingRectangle(
|
|
||||||
boardPos.x,
|
|
||||||
boardPos.x + board.width - 1,
|
|
||||||
boardPos.y,
|
|
||||||
boardPos.y + board.height - 1,
|
|
||||||
),
|
|
||||||
rectTable.get()
|
|
||||||
)
|
)
|
||||||
|
if (DEBUG) Debug.checkpoint('board done')
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('imgtoimg')
|
// DRAW TILES
|
||||||
|
// ---------------------------------------------------------------
|
||||||
// draw all the tiles on the table
|
|
||||||
|
|
||||||
for (let tile of tilesSortedByZIndex()) {
|
for (let tile of tilesSortedByZIndex()) {
|
||||||
let rect = new BoundingRectangle(
|
|
||||||
puzzle.info.tileDrawOffset + tile.pos.x,
|
|
||||||
puzzle.info.tileDrawOffset + tile.pos.x + puzzle.info.tileDrawSize,
|
|
||||||
puzzle.info.tileDrawOffset + tile.pos.y,
|
|
||||||
puzzle.info.tileDrawOffset + tile.pos.y + puzzle.info.tileDrawSize,
|
|
||||||
)
|
|
||||||
let bmp = bitmaps[tile.idx]
|
let bmp = bitmaps[tile.idx]
|
||||||
Graphics.mapBitmapToBitmapCapped(
|
pos = viewport.worldToViewport({
|
||||||
bmp,
|
x: puzzle.info.tileDrawOffset + tile.pos.x,
|
||||||
bmp.getBoundingRect(),
|
y: puzzle.info.tileDrawOffset + tile.pos.y,
|
||||||
puzzleTable,
|
})
|
||||||
rect,
|
dim = viewport.worldDimToViewport({
|
||||||
rectTable.get()
|
w: puzzle.info.tileDrawSize,
|
||||||
|
h: puzzle.info.tileDrawSize,
|
||||||
|
})
|
||||||
|
ctx.drawImage(bmp,
|
||||||
|
0, 0, bmp.width, bmp.height,
|
||||||
|
pos.x, pos.y, dim.w, dim.h
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (DEBUG) Debug.checkpoint('tiles done')
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('tiles')
|
// DRAW PLAYERS
|
||||||
}
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
if (rerenderTable || rerender) {
|
|
||||||
// finally draw the finished table onto the canvas
|
|
||||||
// only part of the table may be visible, depending on the
|
|
||||||
// camera
|
|
||||||
adapter.clear()
|
|
||||||
adapter.apply()
|
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('afterclear_1')
|
|
||||||
|
|
||||||
// TODO: improve the rendering
|
|
||||||
// atm it is pretty slow (~40-50ms)
|
|
||||||
Graphics.mapBitmapToAdapter(
|
|
||||||
puzzleTable,
|
|
||||||
viewport.rect(),
|
|
||||||
adapter,
|
|
||||||
adapter.getBoundingRect()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('to_adapter_1')
|
|
||||||
|
|
||||||
} else if (rerenderPlayer) {
|
|
||||||
adapter.clearRect(rectPlayer.get())
|
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('afterclear_2')
|
|
||||||
|
|
||||||
Graphics.mapBitmapToAdapterCapped(
|
|
||||||
puzzleTable,
|
|
||||||
viewport.rect(),
|
|
||||||
adapter,
|
|
||||||
adapter.getBoundingRect(),
|
|
||||||
rectPlayer.get()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('to_adapter_2')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rerenderPlayer) {
|
|
||||||
for (let id of Object.keys(players)) {
|
for (let id of Object.keys(players)) {
|
||||||
const p = players[id]
|
const p = players[id]
|
||||||
const cursor = p.down ? cursorGrab : cursorHand
|
const cursor = p.down ? cursorGrab : cursorHand
|
||||||
const pos = viewport.worldToViewport(p)
|
const pos = viewport.worldToViewport(p)
|
||||||
Graphics.drawBitmap(adapter, cursor, pos)
|
ctx.drawImage(cursor, pos.x, pos.y)
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
if (DEBUG) Debug.checkpoint('players done')
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('after_players')
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter.apply()
|
if (DEBUG) Debug.checkpoint('all done')
|
||||||
|
|
||||||
if (DEBUG) Debug.checkpoint('finals')
|
|
||||||
|
|
||||||
rerenderTable = false
|
rerenderTable = false
|
||||||
rerenderPlayer = false
|
rerenderPlayer = false
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue