diff --git a/js/main.js b/js/main.js index 4162b01..3613ae7 100644 --- a/js/main.js +++ b/js/main.js @@ -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) { diff --git a/js/rooms.js b/js/rooms.js index a915d89..96c323e 100644 --- a/js/rooms.js +++ b/js/rooms.js @@ -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) { diff --git a/js/state.js b/js/state.js index 54107f1..6e79ce5 100644 --- a/js/state.js +++ b/js/state.js @@ -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);