minigame 2 add more design 2.7

This commit is contained in:
2026-05-06 18:56:42 +00:00
parent 07d53ebe1c
commit 1f9be315ce
3 changed files with 205 additions and 41 deletions
File diff suppressed because one or more lines are too long
+174 -36
View File
@@ -34,9 +34,23 @@
const QUIZ_QUESTION_MISSION_MAP_ID = 'mng8a80o';
/** Stack ซ้อนตึก — ฉากภารกิจ (HOWTO → นับถอยหลัง → เล่น → สรุป) รูปใน `/Game/img/TowerBlock` */
const STACK_TOWER_MISSION_MAP_ID = 'mnn93hpi';
/** Stack Tower: ความละเอียดบัฟเฟอร์วาดคงที่ — ย่อ/ขยายด้วย CSS เท่านั้น กัน BG กับตำแหน่งบล็อกเลื่อนตามขนาดหน้าต่าง */
const STACK_TOWER_FIXED_RENDER_W = 1280;
const STACK_TOWER_FIXED_RENDER_H = 720;
/** Stack Tower: บัฟเฟอร์วาดคงที่ 16:9 — 1920×1080 */
const STACK_TOWER_FIXED_RENDER_W = 1920;
const STACK_TOWER_FIXED_RENDER_H = 1080;
/** คูณ zDraw ให้มุมมองโลกเท่าเดิมเมื่อบัฟเฟอร์กว้างกว่าฐานอ้างอิง 1280w (= 1.5 ที่ 1920w) */
const STACK_TOWER_FIXED_RENDER_Z_MUL = STACK_TOWER_FIXED_RENDER_W / 1280;
/** เชือก Stack Tower: ปลายล่างตามเส้น (1 = ถึงจุดยึดบล็อก) */
const STACK_TOWER_ROPE_DRAW_T1 = 1.0;
/** ปลายบนเชือก (จอ): ยืดเหนือขอบบนแคนวาสเป็นเศษส่วนของความสูง (เช่น 0.25 = 25% นอกจอด้านบน) */
const STACK_TOWER_ROPE_TOP_ABOVE_CANVAS_FRAC = 0.25;
/** progress ≥ นี้: แอนิเมตซูม + เลื่อนแมป (ครั้งเดียวต่อรอบ stack) */
const STACK_TOWER_POST50_PROGRESS_THRESH = 50;
const STACK_TOWER_POST50_ZOOM_MUL = 1.1;
/** เลื่อน BG แนวตั้งเท่าเศษส่วนของความสูงแคนวาส (จอ) */
const STACK_TOWER_POST50_MAP_SHIFT_SCREEN_FRAC = 0.1;
const STACK_TOWER_POST50_ANIM_MS = 680;
/** หลัง 50%: เลื่อนกล้องลงในมุมมองจอเท่าเศษส่วนของความสูงแคนวาส (เช่น 0.25 = 25% ของความสูงจอ) */
const STACK_TOWER_POST50_VIEW_SHIFT_SCREEN_FRAC = 0.25;
/** Mega Virus — balloon_boss ฉากภารกิจ (flow เดียวกับ crown / mno9kb07) รูปใน `/Game/img/MegaVirus` */
const BALLOON_BOSS_MISSION_MAP_ID = 'mnq1eml7';
/** ครบชั้นนี้ขึ้นไป — ยกจุดแกว่ง/เชือกขึ้น (world px) ป้องกันชนกองสูง (เฉพาะ Tower mission) */
@@ -1074,7 +1088,8 @@
const scaleL = cwR / loop.naturalWidth;
const drawHLoop = Math.max(1, Math.round(loop.naturalHeight * scaleL));
const camFollowScreen = isStackTowerMissionUiMapPlay() ? getStackTowerBgScrollHeightBoostPx() * zPan : 0;
const S = stackTowerScrollBgPx + camFollowScreen;
const post50Scroll = isStackTowerMissionUiMapPlay() ? getStackTowerPost50BgScrollExtraPxPlay(chR) : 0;
const S = stackTowerScrollBgPx + camFollowScreen + post50Scroll;
const vp0 = S;
const vp1 = S + chR;
const yLimit = vp1 + drawHLoop * 2;
@@ -1138,7 +1153,8 @@
const scaleL = cwR / loop.naturalWidth;
const drawHLoop = Math.max(1, Math.round(loop.naturalHeight * scaleL));
const skyScreenH = Math.min(chR, Math.ceil(-worldMinY * zDraw) + 4);
const rawScroll = stackTowerScrollBgPx + getStackTowerBgScrollHeightBoostPx() * zDraw;
const rawScroll = stackTowerScrollBgPx + getStackTowerBgScrollHeightBoostPx() * zDraw
+ getStackTowerPost50BgScrollExtraPxPlay(chR);
const scroll = ((rawScroll % drawHLoop) + drawHLoop) % drawHLoop;
let y = -scroll;
ctx.save();
@@ -1158,6 +1174,19 @@
stackTowerScrollBgPx += (stackTowerScrollBgSpeedPxPerSec || 40) * dtSec;
}
/**
* าลบจาก screen Y ใน worldToScreen ใหบลอก/กรดเลอนตามฉาก BG stackTowerScrollBgPx เลอน (งก drawStackTowerScrollBgFullCanvas)
*/
function getStackTowerWorldLayerScrollScreenOffsetYPlay(zPan) {
if (!isStackTowerMissionUiMapPlay() || !stackTowerScrollBgDrawActive() || !stackTowerScrollBgImagesReady()) return 0;
if (stackTowerScrollBgOff) return 0;
const zPn = Number(zPan) > 0 ? zPan : zoom;
const zRef = stackTowerScrollBgZoomRefPlay > 0 ? stackTowerScrollBgZoomRefPlay : zPn;
const bgZm = Math.max(0.28, Math.min(4.25, zPn / zRef));
const raw = stackTowerScrollBgPx / bgZm;
return stackTowerScrollBgFlowDown ? -raw : raw;
}
/** โหลดจาก mapData.gridImageLibrary — ดัชนีตรงกับ gridImageCells[y][x] */
let mapGridImageImgs = [];
let mapGridImageHeldImgs = [];
@@ -2081,6 +2110,8 @@
let stackTowerMissionDeferredPhase = null;
let stackTowerSessionStartAt = 0;
let stackTowerMissionEndedOnce = false;
/** progress ข้าม 50%: เริ่มแอนิเมตซูม+เลื่อนแมป — null ยังไม่ข้าม, ตัวเลข = performance.now() ตอนเริ่ม */
let stackTowerPost50AnimStartMs = null;
/** แฟลชรูปผล Tower (timeup / gameover / complete) ก่อน GCM 5 วิ */
let stackTowerResultFlashTimer = null;
const STACK_TOWER_RESULT_FLASH_MS = 5000;
@@ -4167,8 +4198,8 @@
}
/** Last Light (mno9kb07) ใน iframe เอดิเตอร์: ซูมคงที่ — ผู้เล่นปรับเองไม่ได้ */
const PLAY_EMBED_LAST_LIGHT_FIXED_ZOOM_MUL = 1.49;
/** Stack Tower (mnn93hpi) ใน iframe เอดิเตอร์: ซูมคงที่ 1.22 — ล้อ/ปุ่มไม่ปรับได้ */
const PLAY_EMBED_STACK_TOWER_FIXED_ZOOM_MUL = 1.22;
/** Stack Tower (mnn93hpi) ใน embed พรีวิว: ซูมคงที่ ×1 — ล้อ/ปุ่มไม่ปรับได้ */
const PLAY_EMBED_STACK_TOWER_FIXED_ZOOM_MUL = 1;
function isLastLightEmbedZoomLockedPlay() {
return !!(previewMode && editorEmbedReturn && mapData && isGauntletCrownHeistMapPlay());
}
@@ -4182,6 +4213,39 @@
playEmbedUserZoomMul = PLAY_EMBED_STACK_TOWER_FIXED_ZOOM_MUL;
}
}
/** smoothstep 0..1 */
function stackTowerPost50SmoothstepPlay(t) {
const x = Math.max(0, Math.min(1, t));
return x * x * (3 - 2 * x);
}
/** Tower live + progress ≥50%: 0→1 ระหว่าง STACK_TOWER_POST50_ANIM_MS */
function getStackTowerPost50AnimEasePlay() {
if (!isStackTowerMissionUiMapPlay() || stackTowerMissionPhase !== 'live' || !stackMini) return 0;
const p = Number(stackMini.progressPct) || 0;
if (p < STACK_TOWER_POST50_PROGRESS_THRESH) {
stackTowerPost50AnimStartMs = null;
return 0;
}
if (stackTowerPost50AnimStartMs == null) stackTowerPost50AnimStartMs = performance.now();
const u = Math.min(1, Math.max(0, (performance.now() - stackTowerPost50AnimStartMs) / STACK_TOWER_POST50_ANIM_MS));
return stackTowerPost50SmoothstepPlay(u);
}
function getStackTowerPost50ZMulPlay() {
const e = getStackTowerPost50AnimEasePlay();
if (e <= 0) return 1;
return 1 + (STACK_TOWER_POST50_ZOOM_MUL - 1) * e;
}
function getStackTowerPost50BgScrollExtraPxPlay(ch) {
const e = getStackTowerPost50AnimEasePlay();
if (e <= 0) return 0;
const chR = Math.max(1, Number(ch) || 720);
return STACK_TOWER_POST50_MAP_SHIFT_SCREEN_FRAC * chR * e;
}
/**
* Same zDraw chain as draw() (zoom, special maps, Last Light embed lock, preview embed mul).
* Runway end checks must not rely on lastPlayZDrawForInput alone it can lag map/canvas or default 1.4 vs real zoom.
@@ -4209,6 +4273,10 @@
if (previewMode && editorEmbedReturn && mapData && !isQuizQuestionMissionHudActivePlay()) {
zDraw *= playEmbedUserZoomMul;
}
if (isStackTowerMissionUiMapPlay()) {
zDraw *= STACK_TOWER_FIXED_RENDER_Z_MUL;
zDraw *= getStackTowerPost50ZMulPlay();
}
return zDraw;
}
let gauntletCrownHowtoVisible = false;
@@ -4415,14 +4483,16 @@
const dur = STACK_TOWER_SCORE_POPUP_MS;
const imgs = ensureStackTowerScorePopupImagesPlay();
m.scorePopups = m.scorePopups.filter((p) => p.until > now);
const hidPop = getStackTowerVisualHiddenLayerCountPlay();
for (let pi = 0; pi < m.scorePopups.length; pi++) {
const p = m.scorePopups[pi];
const li = Math.floor(Number(p.layerIndex)) || 0;
if (li < 0 || li >= m.layers.length) continue;
if (li < hidPop) continue;
const L = m.layers[li];
const lw = (L.w != null ? L.w : m.initialWidthTiles) * tileSize;
const cxW = L.cx * tileSize;
const yb = floorY - (li + 1) * layerWorldH;
const yb = floorY - (li + 1 - hidPop) * layerWorldH;
const xl = cxW - lw / 2;
const [sx, sy] = worldToScreen(xl, yb);
const drawW = lw * zoom;
@@ -4614,7 +4684,8 @@
const u = Math.max(0, Math.min(1, (nt - t0) / dur));
const alpha = 1 - u * u;
const worldCx = m.topCenterX * tileSize;
const stackTopY = floorY - nLay * layerWorldH;
const hidHm = getStackTowerVisualHiddenLayerCountPlay();
const stackTopY = floorY - (nLay - hidHm) * layerWorldH;
const worldCy = stackTopY - tileSize * 1.55 - u * tileSize * 0.5;
const [sx, sy] = worldToScreen(worldCx, worldCy);
const im = ensureStackTowerHeartMinusImgPlay();
@@ -4716,6 +4787,7 @@
stackTowerMissionDeferredPhase = null;
stackTowerSessionStartAt = 0;
stackTowerMissionEndedOnce = false;
stackTowerPost50AnimStartMs = null;
hideStackTowerHowtoRulesDom();
setGauntletCrownHowtoStackTowerArtMaskPlay(false);
if (stackTowerMissionCountdownTimer) {
@@ -8580,6 +8652,16 @@
return Math.min(lh * 20, tiers * lh * 0.98);
}
/** progress ≥50% + ชั้นพอ: ไม่วาดครึ่งล่างของกอง — ซ้อนเฉพาะครึ่งบนจากพื้น (เฉพาะภาพ ไม่แก้ logic เกม) */
function getStackTowerVisualHiddenLayerCountPlay() {
if (!isStackTowerMissionHudActivePlay() || !stackMini) return 0;
const p = Number(stackMini.progressPct) || 0;
if (p < STACK_TOWER_POST50_PROGRESS_THRESH) return 0;
const n = stackMini.layers ? stackMini.layers.length : 0;
if (n < 4) return 0;
return Math.floor(n / 2);
}
/** world px ตามความสูงหอ (mnn93hpi) — ใช้เลื่อนกล้องให้บล็อก/ฉากไปกับมุมมอง */
function getStackTowerBgScrollHeightBoostPx() {
if (!isStackTowerMissionUiMapPlay() || !stackMini) return 0;
@@ -8595,10 +8677,10 @@
return Math.min(cfg.maxPx, layerCount * cfg.pxPerLayer);
}
/** กล้อง stack: จุดกลางจาก land / release ของแมปเท่านั้น (Tower ไม่เลื่อน cy ตามหอ / post-50 / ตามบล็อก) */
function getStackCameraCentersPx() {
const w = mapData.width || 20, h = mapData.height || 15;
const ts = tileSize;
const mapHpx = h * ts;
const land = getStackAreaBoundsPlay(mapData.stackLandArea, w, h);
const rel = getStackAreaBoundsPlay(mapData.stackReleaseArea, w, h);
let cx = (w / 2) * ts;
@@ -8613,12 +8695,6 @@
cx = rel.cx * ts;
cy = rel.cy * ts;
}
if (isStackTowerMissionUiMapPlay() && stackMini) {
const shift = getStackTowerBgScrollHeightBoostPx();
if (shift > 0) {
cy = Math.max(ts * 1.5, Math.min(mapHpx - ts * 2, cy - shift));
}
}
return { px: cx, py: cy };
}
@@ -8641,6 +8717,7 @@
function resetStackMinigameState() {
stackMini = null;
stackTowerPost50AnimStartMs = null;
stackTowerHeartMinusFx = null;
if (!mapData || !isStack()) return;
const w = mapData.width || 20, h = mapData.height || 15;
@@ -9158,6 +9235,26 @@
stepPreviewStackTurnBased(Date.now());
}
/**
* ดบนจอทเสนกานบลอกตดแนว y = -(ความสงแคนวาส × STACK_TOWER_ROPE_TOP_ABOVE_CANVAS_FRAC)
* ปลายเชอกอยเหนอขอบบน ~25% ของความสงจอ (มองเหนชวงทเขามาในจอ)
*/
function stackTowerRopeScreenTopAnchorPlay(slingBx, slingBy, tcx, tcy, canvasH) {
const ch = Math.max(80, Number(canvasH) || 1080);
const marginTop = -ch * STACK_TOWER_ROPE_TOP_ABOVE_CANVAS_FRAC;
const ddx = tcx - slingBx;
const ddy = tcy - slingBy;
if (Math.abs(ddy) < 1e-4) {
return { x: tcx, y: Math.min(marginTop, Math.min(tcy, slingBy)) };
}
const vTop = (marginTop - slingBy) / ddy;
if (!Number.isFinite(vTop) || vTop < 0) {
return { x: tcx, y: tcy };
}
const xTop = slingBx + vTop * ddx;
return { x: xTop, y: marginTop };
}
function applyStackPreviewSpawnLayout() {
if (!mapData || mapData.gameType !== 'stack') return;
const w = mapData.width || 20, h = mapData.height || 15;
@@ -9189,13 +9286,15 @@
const floorY = m.floorWorldY;
const swingLiftDraw = getStackTowerSwingLiftWorldPx(m.layers.length, layerWorldH);
const swingDrawY = m.swingWorldY - swingLiftDraw;
const hidLayers = towerHudLive ? getStackTowerVisualHiddenLayerCountPlay() : 0;
const visualLiftWorld = hidLayers * layerWorldH;
const nLay = m.layers.length;
for (let i = 0; i < nLay; i++) {
for (let i = hidLayers; i < nLay; i++) {
const L = m.layers[i];
const lw = (L.w != null ? L.w : m.initialWidthTiles) * tileSize;
const cxW = L.cx * tileSize;
const yb = floorY - (i + 1) * layerWorldH;
const yb = floorY - (i + 1 - hidLayers) * layerWorldH;
const xl = cxW - lw / 2;
const [sx, sy] = worldToScreen(xl, yb);
const drawW = lw * zoom;
@@ -9218,11 +9317,11 @@
m.perfectStackX2Fx = null;
} else {
const ti = fxX2.topLayerIndex;
if (ti >= 0 && ti < m.layers.length) {
if (ti >= hidLayers && ti >= 0 && ti < m.layers.length) {
const Ltop = m.layers[ti];
const lwT = (Ltop.w != null ? Ltop.w : m.initialWidthTiles) * tileSize;
const cxWT = Ltop.cx * tileSize;
const ybT = floorY - (ti + 1) * layerWorldH;
const ybT = floorY - (ti + 1 - hidLayers) * layerWorldH;
const xlT = cxWT - lwT / 2;
const [sxT, syT] = worldToScreen(xlT, ybT);
const drawWT = lwT * zoom;
@@ -9247,7 +9346,8 @@
const xSlide = rotSign * ue * tileSize * 0.62;
const cxW = s.xLeft0 + s.twWorld / 2 + xSlide;
const cyW = s.yTop0 + s.stripH * 0.5 + yFall;
const [cxs, cys] = worldToScreen(cxW, cyW);
const cyWDraw = isStackTowerMissionUiMapPlay() ? cyW + visualLiftWorld : cyW;
const [cxs, cys] = worldToScreen(cxW, cyWDraw);
const drawW = s.twWorld * zoom;
const drawH = s.stripH * zoom;
const hue = (m.layers.length * 47) % 360;
@@ -9261,8 +9361,9 @@
} else {
const twWorld = m.widthTiles * tileSize;
const drawStripPx = stripH * zoom;
const swingDrawYDraw = isStackTowerMissionUiMapPlay() ? swingDrawY + visualLiftWorld : swingDrawY;
const cableTopY = isStackTowerMissionUiMapPlay()
? (swingDrawY - tileSize * 6)
? (swingDrawYDraw - tileSize * 6)
: Math.max(0, swingDrawY - tileSize * 6);
const craneX = m.craneWorldX != null ? m.craneWorldX : m.topCenterX * tileSize;
let blockLeftWorld;
@@ -9274,11 +9375,12 @@
const u = stackEaseDropPhys(t, stackFall.sloppyN);
blockLeftWorld = stackFall.xLeft0 + (stackFall.xLeft1 - stackFall.xLeft0) * u;
blockTopWorld = stackFall.y0 + (stackFall.y1 - stackFall.y0) * u;
if (isStackTowerMissionUiMapPlay()) blockTopWorld += visualLiftWorld;
fallTiltRad = stackFall.tiltMax * Math.sin(Math.PI * u);
} else {
const swingXW = (m.topCenterX + m.swingAmp * Math.sin(m.phase)) * tileSize;
blockLeftWorld = swingXW - twWorld / 2;
blockTopWorld = swingDrawY;
blockTopWorld = isStackTowerMissionUiMapPlay() ? swingDrawYDraw : swingDrawY;
}
const cxMid = blockLeftWorld + twWorld / 2;
const [tcx, tcy] = worldToScreen(craneX, cableTopY);
@@ -9286,14 +9388,32 @@
let slingBx = bcx;
let slingBy = bcy;
if (stackFall && stackFall.releaseAttachWorldX != null) {
const p = worldToScreen(stackFall.releaseAttachWorldX, stackFall.releaseAttachWorldY);
const attY = stackFall.releaseAttachWorldY + (isStackTowerMissionUiMapPlay() ? visualLiftWorld : 0);
const p = worldToScreen(stackFall.releaseAttachWorldX, attY);
slingBx = p[0];
slingBy = p[1];
}
drawStackSlingSegmentPlay(ctx, tcx, tcy, slingBx, slingBy, zoom);
let ropeSx0 = tcx;
let ropeSy0 = tcy;
let ropeSx1 = slingBx;
let ropeSy1 = slingBy;
if (isStackTowerMissionUiMapPlay()) {
const chCv = ctx.canvas && ctx.canvas.height ? ctx.canvas.height : STACK_TOWER_FIXED_RENDER_H;
const topA = stackTowerRopeScreenTopAnchorPlay(slingBx, slingBy, tcx, tcy, chCv);
ropeSx0 = topA.x;
ropeSy0 = topA.y;
ropeSx1 = slingBx;
ropeSy1 = slingBy;
const t1 = Math.max(0, Math.min(1, STACK_TOWER_ROPE_DRAW_T1));
if (t1 < 1) {
ropeSx1 = ropeSx0 + (slingBx - ropeSx0) * t1;
ropeSy1 = ropeSy0 + (slingBy - ropeSy0) * t1;
}
}
drawStackSlingSegmentPlay(ctx, ropeSx0, ropeSy0, ropeSx1, ropeSy1, zoom);
const ropeAng = stackFall && stackFall.releaseRopeAng != null
? stackFall.releaseRopeAng
: Math.atan2(bcy - tcy, bcx - tcx);
: Math.atan2(bcy - ropeSy0, bcx - ropeSx0);
const swingTiltRad = ropeAng - Math.PI / 2;
const hitMissTint = stackFall && stackFall.hit && stackFall.hit.miss;
const seatDraw = stackFall && stackFall.actorSeat != null
@@ -15214,7 +15334,8 @@
ctx.closePath();
}
function drawHudAvatar(cx, baselineY, maxSize, characterId, playTint) {
/** @param {{ headBust?: boolean, headSrcFrac?: number, bustLiftPx?: number }} [avatarOpts] — headBust + bustLiftPx ดันรูปขึ้นในกรอบ */
function drawHudAvatar(cx, baselineY, maxSize, characterId, playTint, avatarOpts) {
const timeMs = now;
const dir = 'down';
const rawImg = getAvatarImg(characterId, dir, timeMs, false);
@@ -15227,16 +15348,31 @@
iw = charImg.naturalWidth;
ih = charImg.naturalHeight;
}
const headBust = !!(avatarOpts && avatarOpts.headBust);
let sx = 0;
let sy = 0;
let sww = iw;
let shh = ih;
if (headBust && iw > 0 && ih > 0) {
const rawF = Number(avatarOpts && avatarOpts.headSrcFrac);
const frac = Number.isFinite(rawF) && rawF > 0.22 && rawF < 0.85 ? rawF : 0.4;
shh = Math.max(8, Math.round(ih * frac));
}
const ax = cx - maxSize / 2;
const ay = baselineY - maxSize;
ctx.save();
roundRectPath(ax, ay, maxSize, maxSize, 5);
ctx.clip();
if (iw > 0 && ih > 0) {
const scale = Math.min(maxSize / iw, maxSize / ih, 1);
const dw = iw * scale;
const dh = ih * scale;
ctx.drawImage(charImg, 0, 0, iw, ih, cx - dw / 2, baselineY - dh, dw, dh);
const maxScale = headBust ? 2.45 : 1;
const scale = Math.min(maxSize / sww, maxSize / shh, maxScale);
const dw = sww * scale;
const dh = shh * scale;
const rawLift = headBust && avatarOpts ? Number(avatarOpts.bustLiftPx) : NaN;
const bustLift = headBust
? (Number.isFinite(rawLift) ? Math.max(0, Math.min(maxSize * 0.45, rawLift)) : 22)
: 0;
ctx.drawImage(charImg, sx, sy, sww, shh, cx - dw / 2, baselineY - dh - bustLift, dw, dh);
} else {
ctx.fillStyle = 'rgba(80, 200, 255, 0.35)';
ctx.fillRect(ax, ay, maxSize, maxSize);
@@ -15397,7 +15533,7 @@
ctx.lineWidth = 1;
roundRectPath(avFrameX - 1, avFrameY - 1, avBox + 2, avBox + 2, 6);
ctx.stroke();
drawHudAvatar(avCx, avFrameY + avBox - 5, bigAv, curV.characterId, curV.playTint);
drawHudAvatar(avCx, avFrameY + avBox - 5, bigAv, curV.characterId, curV.playTint, towerMapHud ? { headBust: true } : undefined);
if (!towerMapHud) {
const footY = innerTop + integrityH + 12;
@@ -16209,6 +16345,7 @@
ctx.setTransform(1, 0, 0, 1, 0, 0);
const w = mapData.width, h = mapData.height;
let zDraw = computePlayCameraZDrawPlay();
const stackCam = isStack() ? getStackCameraCentersPx() : null;
let camX = stackCam ? stackCam.px : me.x * tileSize;
let camY = stackCam ? stackCam.py : me.y * tileSize;
@@ -16216,7 +16353,6 @@
camX = jumpSurviveCamCenterX;
camY = jumpSurviveCamCenterY;
}
let zDraw = computePlayCameraZDrawPlay();
if (isSpaceShooter() || isBalloonBoss()) {
const mwPx = w * tileSize, mhPx = h * tileSize;
camX = mwPx * 0.5;
@@ -16243,12 +16379,14 @@
? Math.max(0.38, Math.min(0.82, frac))
: 0.56;
const floorY0 = stackMini.floorWorldY;
const curScreen = (floorY0 - camY) * zDraw + canvas.height * 0.5;
const stScrollFloor = getStackTowerWorldLayerScrollScreenOffsetYPlay(zDraw);
const curScreen = (floorY0 - camY) * zDraw + canvas.height * 0.5 - stScrollFloor;
const desired = canvas.height * screenFrac;
camY += (curScreen - desired) / zDraw;
}
}
lastPlayZDrawForInput = zDraw;
const stackTowerWorldScrollScreenY = getStackTowerWorldLayerScrollScreenOffsetYPlay(zDraw);
const halfW = canvas.width / (2 * zDraw);
const halfH = canvas.height / (2 * zDraw);
@@ -16350,7 +16488,7 @@
}
function worldToScreen(wx, wy) {
const sx = (wx - camX) * zDraw + canvas.width / 2;
const sy = (wy - camY) * zDraw + canvas.height / 2;
const sy = (wy - camY) * zDraw + canvas.height / 2 - stackTowerWorldScrollScreenY;
return [sx, sy];
}
@@ -16380,7 +16518,7 @@
}
if (!gim || !gim.complete || !gim.naturalWidth) continue;
const sx0 = (wx0 - camX) * zDraw + canvas.width / 2;
const sy0 = (wy0 - camY) * zDraw + canvas.height / 2;
const sy0 = (wy0 - camY) * zDraw + canvas.height / 2 - stackTowerWorldScrollScreenY;
const sw = ww * zDraw;
const sh = wh * zDraw;
ctx.save();
+30 -4
View File
@@ -127,14 +127,40 @@
image-rendering: pixelated;
image-rendering: crisp-edges;
}
/* Stack Tower (mnn93hpi) เท่านั้น — class บน html จาก play.js — แคนวาสคงที่ไม่ย่อเข้า stage (ไม่มีขอบเทารอบพิกเซล) */
/* Stack Tower (mnn93hpi):ต็มพื้นที่ stage ยังคงสัดส่วนบัฟเฟอร์ 16:9 (object-fit) */
html.play-stack-tower-pixel-canvas #play-canvas-stage.play-canvas-stage {
position: relative;
min-height: 0;
}
html.play-stack-tower-pixel-canvas #play-canvas-stage #game-canvas {
position: absolute;
inset: 0;
width: 100% !important;
height: 100% !important;
max-width: none;
max-height: none;
object-fit: contain;
object-position: center center;
touch-action: none;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
html.play-preview-editor-embed.play-stack-tower-pixel-canvas #play-canvas-stage #game-canvas {
max-width: none !important;
max-height: none !important;
align-self: stretch !important;
}
/* Stack Tower: portrait มุมขวา (DOM) — โฟกัสประมาณคอขึ้น */
html.play-stack-tower-pixel-canvas .play-cyber-self-frame {
overflow: hidden;
}
html.play-stack-tower-pixel-canvas .play-cyber-self-frame img#play-cyber-portrait-img {
width: 72px;
height: 96px;
object-fit: cover;
object-position: center 2%;
margin: -22px 0 0 0;
}
html.play-stack-tower-pixel-canvas .play-cyber-self {
top: max(44px, calc(env(safe-area-inset-top) + 30px)) !important;
}
/* หลังกดรับหลักฐานในหน้าสรุป — BG ดำ alpha ทั้งจอ + timeup ติดโซนโต๊ะเหมือน #quiz-map-question-panel */
#quiz-carry-timeup-desk-layer {
@@ -3176,7 +3202,7 @@
</div>
<script src="/Game/socket.io/socket.io.js"></script>
<script src="js/version.js?v=0.0306"></script>
<script src="js/play.js?v=0.0355"></script>
<script src="js/play.js?v=0.0375"></script>
<div class="version-tag">v —</div>
</body>
</html>