feat: HD Remaster toggle with 4x canvas render path

G.hdRemaster persisted in localStorage (meatslicer_hd_remaster), toggled
from the title screen via H key or the bottom-left tag; activation reloads
so the asset tier and canvas resolution swap at boot. draw() applies a
uniform hdScale() transform, the room-layer cache renders at render scale,
and image smoothing follows the tier.
This commit is contained in:
Quintine committed 2026-08-12 10:04:49 +10:00
1 parent 24c1958f2b
commit ea354d0153
3 files changed
+35 -7

No files matched your search

+25 -2
View File
@@ -6,6 +6,7 @@ const PRESSURE_SLIDER_RECT = { x: 300, y: 470, w: 360, h: 46 };
const MENU_EXIT_BUTTON = { x: W - 208, y: H - 48, w: 180, h: 28 };
const PAUSE_MENU_BUTTON = { x: W / 2 - 75, y: 526, w: 150, h: 28 };
const PAUSE_EXIT_BUTTON = { x: W / 2 + 87, y: 526, w: 150, h: 28 };
const HD_TOGGLE_BUTTON = { x: 28, y: H - 48, w: 220, h: 28 };
const CONFIRM_WINDOW = 3;
function pauseBars() {
@@ -20,7 +21,11 @@ function pauseBars() {
function init() {
canvas = document.getElementById('game');
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
try { G.hdRemaster = localStorage.getItem('meatslicer_hd_remaster') === '1'; } catch (e) {}
canvas.width = W * hdScale();
canvas.height = H * hdScale();
canvas.style.imageRendering = G.hdRemaster ? 'auto' : 'pixelated';
ctx.imageSmoothingEnabled = !G.hdRemaster;
G.devMode = debugEnabled();
initInput(canvas);
Sprites.load();
@@ -67,6 +72,16 @@ function setAutoPerk(enabled) {
Sfx.menu();
}
function setHdRemaster(enabled) {
G.hdRemaster = !!enabled;
try { localStorage.setItem('meatslicer_hd_remaster', G.hdRemaster ? '1' : '0'); } catch (e) {}
}
function toggleHdRemaster() {
setHdRemaster(!G.hdRemaster);
Sfx.menu();
location.reload();
}
function buildAtmosphereLayer() {
if (!document.createElement) return;
const layer = document.createElement('canvas');
@@ -199,7 +214,8 @@ function update(dt) {
setPressureDial(Math.round(((Input.mx - sl.x) / sl.w) * 20) - 10);
}
if (!Input.mdown) G.menuDialDrag = false;
if (keyPressed('enter', ' ') || (Input.mpressed && !inRect(Input.mx, Input.my, sl) && !exitClick)) startRun();
if (keyPressed('h') || (Input.mpressed && inRect(Input.mx, Input.my, HD_TOGGLE_BUTTON))) { toggleHdRemaster(); break; }
if (keyPressed('enter', ' ') || (Input.mpressed && !inRect(Input.mx, Input.my, sl) && !inRect(Input.mx, Input.my, HD_TOGGLE_BUTTON) && !exitClick)) startRun();
break;
}
case 'play':
@@ -296,6 +312,7 @@ function update(dt) {
}
function draw() {
ctx.setTransform(hdScale(), 0, 0, hdScale(), 0, 0);
ctx.fillStyle = '#0a0506';
ctx.fillRect(0, 0, W, H);
@@ -429,6 +446,12 @@ function drawMenu(ctx) {
color: !desktopExit ? '#65595b' : (exitConfirm ? '#ffd36a' : (exitHover ? '#f5e9d6' : '#ad8f8c')),
accent: !desktopExit ? '#3a2d31' : (exitConfirm ? '#d69a22' : (exitHover ? '#e22b46' : '#6e3843')),
});
const hdHover = inRect(Input.mx, Input.my, HD_TOGGLE_BUTTON);
drawPixelTag(ctx, '[H] HD REMASTER // ' + (G.hdRemaster ? 'ON' : 'OFF'), HD_TOGGLE_BUTTON.x, HD_TOGGLE_BUTTON.y, {
width: HD_TOGGLE_BUTTON.w, height: HD_TOGGLE_BUTTON.h,
color: G.hdRemaster ? '#e0b94e' : (hdHover ? '#f5e9d6' : '#ad8f8c'),
accent: G.hdRemaster ? '#a47e25' : (hdHover ? '#e22b46' : '#6e3843'),
});
}
function drawPressureDial(ctx) {
+7 -5
View File
@@ -393,17 +393,19 @@ function drawFloor(ctx, r) {
// browser; headless tests keep the simple direct path.
const canCache = typeof document !== 'undefined' && document.createElement && G.imagesLoaded;
if (!canCache) { drawFloorTiles(ctx, r); return; }
const key = r.gx + ',' + r.gy + ':' + G.floor + ':' + r.shape + ':' + r.theme;
const key = r.gx + ',' + r.gy + ':' + G.floor + ':' + r.shape + ':' + r.theme + ':' + hdScale();
if (!G.roomLayer || G.roomLayerKey !== key) {
const a = roomBounds(r);
const rs = hdScale();
const layer = document.createElement('canvas');
layer.width = Math.max(W, a.x1); layer.height = Math.max(H, a.y1);
layer.width = Math.max(W, a.x1) * rs; layer.height = Math.max(H, a.y1) * rs;
const lctx = layer.getContext('2d');
lctx.imageSmoothingEnabled = false;
drawFloorTiles(lctx, r, layer.width, layer.height);
lctx.setTransform(rs, 0, 0, rs, 0, 0);
lctx.imageSmoothingEnabled = !G.hdRemaster;
drawFloorTiles(lctx, r, layer.width / rs, layer.height / rs);
G.roomLayer = layer; G.roomLayerKey = key;
}
ctx.drawImage(G.roomLayer, 0, 0);
ctx.drawImage(G.roomLayer, 0, 0, G.roomLayer.width / hdScale(), G.roomLayer.height / hdScale());
}
function drawRoom(ctx) {
+3
View File
@@ -49,6 +49,7 @@ const G = {
sfxVol: 0.45,
musicVol: 0.55,
hudAlpha: 1,
hdRemaster: false,
imagesLoaded: false,
roomLayer: null,
roomLayerKey: '',
@@ -70,6 +71,8 @@ const PRESSURE_DROP_BASE = 3; // dial 0 == 0.03 relief base
const DEATH_LOCK = 3;
const PRESSURE_DIAL_MIN = -10, PRESSURE_DIAL_MAX = 10;
function hdScale() { return G.hdRemaster ? 4 : 1; }
// Preserve the old -5 / 0 / +5 balance while adding genuinely wider extremes.
function pressureGainUnits(dial) {
const d = clamp(dial, PRESSURE_DIAL_MIN, PRESSURE_DIAL_MAX);