Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d60baa6b29 |
No files matched your search
+22
-22
@@ -6,32 +6,32 @@
|
||||
<meta name="theme-color" content="#0a0305">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22><text y=%2252%22 x=%2250%22 text-anchor=%22middle%22 dominant-baseline=%22middle%22 font-size=%2252%22>🔪</text></svg>">
|
||||
<title>MEATSLICER</title>
|
||||
<link rel="stylesheet" href="css/style.css?v=56">
|
||||
<link rel="stylesheet" href="css/style.css?v=57">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<canvas id="game" width="960" height="640"></canvas>
|
||||
</div>
|
||||
<script src="js/utils.js?v=56"></script>
|
||||
<script src="js/state.js?v=56"></script>
|
||||
<script src="js/input.js?v=56"></script>
|
||||
<script src="js/sfx.js?v=56"></script>
|
||||
<script src="js/sfxbank.js?v=56"></script>
|
||||
<script src="js/audio.js?v=56"></script>
|
||||
<script src="js/sprites.js?v=56"></script>
|
||||
<script src="js/particles.js?v=56"></script>
|
||||
<script src="js/weapons.js?v=56"></script>
|
||||
<script src="js/bullets.js?v=56"></script>
|
||||
<script src="js/pickups.js?v=56"></script>
|
||||
<script src="js/items.js?v=56"></script>
|
||||
<script src="js/perks.js?v=56"></script>
|
||||
<script src="js/enemies.js?v=56"></script>
|
||||
<script src="js/bosses.js?v=56"></script>
|
||||
<script src="js/rooms.js?v=56"></script>
|
||||
<script src="js/player.js?v=56"></script>
|
||||
<script src="js/hud.js?v=56"></script>
|
||||
<script src="js/help.js?v=56"></script>
|
||||
<script src="js/debug.js?v=56"></script>
|
||||
<script src="js/main.js?v=56"></script>
|
||||
<script src="js/utils.js?v=57"></script>
|
||||
<script src="js/state.js?v=57"></script>
|
||||
<script src="js/input.js?v=57"></script>
|
||||
<script src="js/sfx.js?v=57"></script>
|
||||
<script src="js/sfxbank.js?v=57"></script>
|
||||
<script src="js/audio.js?v=57"></script>
|
||||
<script src="js/sprites.js?v=57"></script>
|
||||
<script src="js/particles.js?v=57"></script>
|
||||
<script src="js/weapons.js?v=57"></script>
|
||||
<script src="js/bullets.js?v=57"></script>
|
||||
<script src="js/pickups.js?v=57"></script>
|
||||
<script src="js/items.js?v=57"></script>
|
||||
<script src="js/perks.js?v=57"></script>
|
||||
<script src="js/enemies.js?v=57"></script>
|
||||
<script src="js/bosses.js?v=57"></script>
|
||||
<script src="js/rooms.js?v=57"></script>
|
||||
<script src="js/player.js?v=57"></script>
|
||||
<script src="js/hud.js?v=57"></script>
|
||||
<script src="js/help.js?v=57"></script>
|
||||
<script src="js/debug.js?v=57"></script>
|
||||
<script src="js/main.js?v=57"></script>
|
||||
</body>
|
||||
</html>
|
||||
+5
-1
@@ -9,6 +9,10 @@ const Input = {
|
||||
anyKey: false,
|
||||
};
|
||||
|
||||
let _rect = null;
|
||||
|
||||
Input.updateRect = function () { _rect = canvas ? canvas.getBoundingClientRect() : null; };
|
||||
|
||||
function initInput(canvas) {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
const k = e.key.toLowerCase();
|
||||
@@ -21,7 +25,7 @@ function initInput(canvas) {
|
||||
window.addEventListener('blur', () => { Input.keys = {}; Input.mdown = false; });
|
||||
|
||||
function setMouse(e) {
|
||||
const r = canvas.getBoundingClientRect();
|
||||
const r = _rect || (_rect = canvas.getBoundingClientRect());
|
||||
Input.mx = (e.clientX - r.left) * (W / r.width);
|
||||
Input.my = (e.clientY - r.top) * (H / r.height);
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ function fitCanvas() {
|
||||
const k = Math.min(window.innerWidth / W, window.innerHeight / H) * 0.97;
|
||||
canvas.style.width = Math.floor(W * k) + 'px';
|
||||
canvas.style.height = Math.floor(H * k) + 'px';
|
||||
if (Input.updateRect) Input.updateRect();
|
||||
}
|
||||
|
||||
function resetRun() {
|
||||
|
||||
+45
-18
@@ -94,6 +94,8 @@ function enterRoom(gx, gy) {
|
||||
G.boss = null;
|
||||
G.roomLayer = null;
|
||||
G.roomLayerKey = '';
|
||||
G.wallLayer = null;
|
||||
G.wallLayerKey = '';
|
||||
G.roomDamaged = false;
|
||||
G.roomEnterT = G.time;
|
||||
G.entryFresh = false;
|
||||
@@ -435,8 +437,7 @@ function drawRoom(ctx) {
|
||||
drawWalls(ctx, r);
|
||||
}
|
||||
|
||||
function drawWalls(ctx, r) {
|
||||
const locked = roomLocked();
|
||||
function drawWallBands(target, r) {
|
||||
const a = roomBounds(r);
|
||||
const theme = ROOM_THEMES[r.theme] || ROOM_THEMES.abattoir;
|
||||
const wallPool = theme.wall;
|
||||
@@ -444,43 +445,69 @@ function drawWalls(ctx, r) {
|
||||
const top = a.y0 - WALL, bottom = a.y1 + WALL;
|
||||
|
||||
function clipBand(dir) {
|
||||
ctx.beginPath();
|
||||
target.beginPath();
|
||||
if (dir === 'n' || dir === 's') {
|
||||
const y = dir === 'n' ? top : a.y1;
|
||||
if (r.doors[dir]) {
|
||||
ctx.rect(left, y, a.cx - DOOR_HALF - left, WALL);
|
||||
ctx.rect(a.cx + DOOR_HALF, y, right - a.cx - DOOR_HALF, WALL);
|
||||
} else ctx.rect(left, y, right - left, WALL);
|
||||
target.rect(left, y, a.cx - DOOR_HALF - left, WALL);
|
||||
target.rect(a.cx + DOOR_HALF, y, right - a.cx - DOOR_HALF, WALL);
|
||||
} else target.rect(left, y, right - left, WALL);
|
||||
} else {
|
||||
const x = dir === 'w' ? left : a.x1;
|
||||
if (r.doors[dir]) {
|
||||
ctx.rect(x, top, WALL, a.cy - DOOR_HALF - top);
|
||||
ctx.rect(x, a.cy + DOOR_HALF, WALL, bottom - a.cy - DOOR_HALF);
|
||||
} else ctx.rect(x, top, WALL, bottom - top);
|
||||
target.rect(x, top, WALL, a.cy - DOOR_HALF - top);
|
||||
target.rect(x, a.cy + DOOR_HALF, WALL, bottom - a.cy - DOOR_HALF);
|
||||
} else target.rect(x, top, WALL, bottom - top);
|
||||
}
|
||||
ctx.clip();
|
||||
target.clip();
|
||||
}
|
||||
|
||||
for (const dir of ['n', 's']) {
|
||||
ctx.save(); clipBand(dir);
|
||||
target.save(); clipBand(dir);
|
||||
const y = dir === 'n' ? top + TILE / 2 : a.y1 + TILE / 2;
|
||||
for (let x = left; x < right; x += TILE) {
|
||||
const h = hashTile(Math.floor(x / TILE) + r.gx * 17, (dir === 'n' ? -1 : 11) + r.gy * 19);
|
||||
Sprites.draw(ctx, wallPool[h % wallPool.length], x + TILE / 2, y, 0, TILE);
|
||||
Sprites.draw(target, wallPool[h % wallPool.length], x + TILE / 2, y, 0, TILE);
|
||||
}
|
||||
ctx.restore();
|
||||
target.restore();
|
||||
}
|
||||
for (const dir of ['w', 'e']) {
|
||||
ctx.save(); clipBand(dir);
|
||||
target.save(); clipBand(dir);
|
||||
const x = dir === 'w' ? left + TILE / 2 : a.x1 + TILE / 2;
|
||||
for (let y = top; y < bottom; y += TILE) {
|
||||
const h = hashTile((dir === 'w' ? -1 : 16) + r.gx * 17, Math.floor(y / TILE) + r.gy * 19);
|
||||
Sprites.draw(ctx, wallPool[h % wallPool.length], x, y + TILE / 2, Math.PI / 2, TILE);
|
||||
Sprites.draw(target, wallPool[h % wallPool.length], x, y + TILE / 2, Math.PI / 2, TILE);
|
||||
}
|
||||
ctx.restore();
|
||||
target.restore();
|
||||
}
|
||||
target.strokeStyle = 'rgba(0,0,0,0.5)'; target.lineWidth = 3;
|
||||
target.strokeRect(a.x0, a.y0, a.w, a.h);
|
||||
}
|
||||
|
||||
function drawWalls(ctx, r) {
|
||||
const locked = roomLocked();
|
||||
const a = roomBounds(r);
|
||||
|
||||
// static wall tiles are cached; door sprites are dynamic per frame
|
||||
const canCache = typeof document !== 'undefined' && document.createElement && G.imagesLoaded;
|
||||
if (canCache) {
|
||||
const key = r.gx + ',' + r.gy + ':' + G.floor + ':' + r.shape + ':' + r.theme + ':' + hdScale();
|
||||
if (!G.wallLayer || G.wallLayerKey !== key) {
|
||||
const rs = hdScale();
|
||||
const layer = document.createElement('canvas');
|
||||
layer.width = (a.x1 + WALL) * rs;
|
||||
layer.height = (a.y1 + WALL) * rs;
|
||||
const lctx = layer.getContext('2d');
|
||||
lctx.setTransform(rs, 0, 0, rs, 0, 0);
|
||||
lctx.imageSmoothingEnabled = !G.hdRemaster;
|
||||
drawWallBands(lctx, r);
|
||||
G.wallLayer = layer;
|
||||
G.wallLayerKey = key;
|
||||
}
|
||||
ctx.drawImage(G.wallLayer, 0, 0, G.wallLayer.width / hdScale(), G.wallLayer.height / hdScale());
|
||||
} else {
|
||||
drawWallBands(ctx, r);
|
||||
}
|
||||
ctx.strokeStyle = 'rgba(0,0,0,0.5)'; ctx.lineWidth = 3;
|
||||
ctx.strokeRect(a.x0, a.y0, a.w, a.h);
|
||||
|
||||
for (const [dir, has] of Object.entries(r.doors)) {
|
||||
if (!has) continue;
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ const SfxBank = {
|
||||
if (this.buffers.has(name) || this.pending.has(name) || this.failed.has(name)) return;
|
||||
const file = this.FILES[name];
|
||||
if (!file || !this.ctx || typeof fetch !== 'function') return;
|
||||
const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=56')
|
||||
const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=57')
|
||||
.then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.arrayBuffer(); })
|
||||
.then(bytes => this.ctx.decodeAudioData(bytes))
|
||||
.then(buffer => this.buffers.set(name, this.trim(buffer)))
|
||||
|
||||
+7
-2
@@ -88,10 +88,10 @@ const Sprites = {
|
||||
const finish = () => { if (--pending === 0) G.imagesLoaded = true; };
|
||||
img.onload = () => { this.imgs[name] = img; if (typeof img.decode === 'function') img.decode().then(finish, finish); else finish(); };
|
||||
img.onerror = () => {
|
||||
if (hd && !img._sdRetry) { img._sdRetry = true; img.src = 'assets/' + name + '.png?v=56'; return; }
|
||||
if (hd && !img._sdRetry) { img._sdRetry = true; img.src = 'assets/' + name + '.png?v=57'; return; }
|
||||
finish();
|
||||
};
|
||||
img.src = hd ? 'assets/hd/' + name + '.webp?v=56' : 'assets/' + name + '.png?v=56';
|
||||
img.src = hd ? 'assets/hd/' + name + '.webp?v=57' : 'assets/' + name + '.png?v=57';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -161,6 +161,11 @@ const Sprites = {
|
||||
draw(ctx, name, x, y, rot, targetW, flash, alpha, scaleX, scaleY) {
|
||||
const img = this.imgs[name];
|
||||
const tw = targetW || 32;
|
||||
if (img && !rot && scaleX === undefined && scaleY === undefined && alpha === undefined && !flash) {
|
||||
const dh = img.height * (tw / img.width);
|
||||
ctx.drawImage(img, x - tw / 2, y - dh / 2, tw, dh);
|
||||
return;
|
||||
}
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
if (rot) ctx.rotate(rot);
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meatslicer",
|
||||
"productName": "MeatSlicer",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"private": true,
|
||||
"description": "A Butcher's Descent - clear, harvest, descend.",
|
||||
"main": "desktop/main.js",
|
||||
|
||||
+2
-2
@@ -216,7 +216,7 @@ check('HD loader loads all images (fallbacks)', ctx.G.imagesLoaded === true);
|
||||
let hdFallbackOk = 0;
|
||||
for (const name of ctx.SPRITE_MANIFEST) {
|
||||
const urls = attemptedUrls[name];
|
||||
const ok = urls && urls.length === 2 && urls[0] === 'assets/hd/' + name + '.webp?v=56' && urls[1] === 'assets/' + name + '.png?v=56';
|
||||
const ok = urls && urls.length === 2 && urls[0] === 'assets/hd/' + name + '.webp?v=57' && urls[1] === 'assets/' + name + '.png?v=57';
|
||||
if (ok) hdFallbackOk++;
|
||||
}
|
||||
check('HD loader tries WebP then PNG per sprite', hdFallbackOk === ctx.SPRITE_MANIFEST.length);
|
||||
@@ -241,7 +241,7 @@ check('SD loader loads all images', ctx.G.imagesLoaded === true);
|
||||
let sdUrlOk = 0;
|
||||
for (const name of ctx.SPRITE_MANIFEST) {
|
||||
const urls = attemptedUrls[name];
|
||||
const ok = urls && urls.length === 1 && urls[0] === 'assets/' + name + '.png?v=56';
|
||||
const ok = urls && urls.length === 1 && urls[0] === 'assets/' + name + '.png?v=57';
|
||||
if (ok) sdUrlOk++;
|
||||
}
|
||||
check('SD loader requests one PNG per sprite', sdUrlOk === ctx.SPRITE_MANIFEST.length);
|
||||
|
||||
Reference in new issue
Block a user