minigame 3add more design 1.65
This commit is contained in:
@@ -25,7 +25,7 @@ English: Upload Stack / tower assets under **`TowerBlock`** (no spaces). **`Towe
|
||||
| `sling.png` | เชือกเหวี่ยงบล็อกในเกม (ยืดจากจุดแขวนถึงบล็อก) — แนวตั้งในไฟล์ = แนวสาย; ไม่มีไฟล์จะใช้เส้นประแทน |
|
||||
| `heart-1.png` | โชว์กลางเหนือกองเมื่อ **พลาด** (Tower live) — ไม่มีจะ fallback ข้อความ `♥ −1` |
|
||||
| `life-full.png` | HUD มุมขวา: หัวใจ **เต็ม** ในแถบชีวิต (คู่ `life-hit.png`) — ไม่มีจะ fallback เป็น `heart-1.png` |
|
||||
| `life-bar.png` | HUD: สไตล์ข้อความ **SYSTEM INTEGRITY** + วงเล็บ — วาง **ซ้ายของอวตาร** ในแผง cyan (mock รูป2) |
|
||||
| `life-bar.png` | HUD: ใช้เฉพาะ **ครึ่งบน** (~38%) เป็นหัวข้อ SYSTEM INTEGRITY — แถวหัวใจวาดแยกใต้ + วงเล็บ `[` `]` จากโค้ด (ไม่ทับขอบใน PNG) · ปรับ `STACK_TOWER_LIFE_BAR_TITLE_FRAC` ใน `play.js` ถ้า art เปลี่ยน |
|
||||
| `life-hit.png` | หัวใจ **หาย / โดนตี** ในแถบชีวิต (คู่ `life-full.png`) |
|
||||
| `btn-drop.png` | ปุ่ม **DROP** มุมขวาล่างใน `play.html` (เทียบ Space / Enter) |
|
||||
| `score+10.png` | Tower live: โชว์เหนือบล็อกเมื่อได้ **+10** |
|
||||
|
||||
@@ -4211,13 +4211,12 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* พื้นที่ระหว่างวงเล็บบน life-bar.png (normalized 0–1) — ปรับถ้า asset เปลี่ยนเลย์เอาต์
|
||||
* UV rect on life-bar.png for heart row between brackets (tune if art changes).
|
||||
* สัดส่วนสูงของ life-bar.png ที่เป็นข้อความ "SYSTEM INTEGRITY" (ที่เหลือ = แถวหัวใจแยกวาดเอง ไม่ทับขอบ `[` `]` ใน PNG)
|
||||
*/
|
||||
const STACK_TOWER_LIFE_BAR_HEART_SLOT_UV = { u0: 0.11, u1: 0.89, v0: 0.40, v1: 0.86 };
|
||||
const STACK_TOWER_LIFE_BAR_TITLE_FRAC = 0.38;
|
||||
|
||||
/**
|
||||
* วาด life-bar + หัวใจ life-full (เต็ม) / life-hit (หาย) ในกรอบ dest — คืนความสูงจริงที่วาด
|
||||
* วาดหัวข้อจาก life-bar.png (ครอปบน) + แถวหัวใจ life-full / life-hit กึ่งกลาง — คืนความสูงที่ใช้จริง
|
||||
*/
|
||||
function drawStackTowerLifeIntegrityBarPlay(ctx, destX, destY, maxW, maxH, lifeSlots, livesRem) {
|
||||
ensureStackTowerLifeHudImagesPlay();
|
||||
@@ -4246,35 +4245,69 @@
|
||||
|
||||
const bw0 = bar.naturalWidth;
|
||||
const bh0 = bar.naturalHeight;
|
||||
const scale = Math.min(maxW / bw0, maxH / bh0, 1.35);
|
||||
const titleSrcH = Math.max(2, Math.floor(bh0 * STACK_TOWER_LIFE_BAR_TITLE_FRAC));
|
||||
const scale = Math.min(maxW / bw0, 1.45);
|
||||
const bw = bw0 * scale;
|
||||
const bh = bh0 * scale;
|
||||
const titleDh = titleSrcH * scale;
|
||||
const gap = 3;
|
||||
const bx = destX;
|
||||
const by = destY + Math.max(0, (maxH - bh) * 0.5);
|
||||
const by0 = destY + Math.max(0, (maxH - titleDh - gap - Math.min(40, maxH * 0.45)) * 0.35);
|
||||
const heartsTop = by0 + titleDh + gap;
|
||||
const roomHearts = Math.max(10, destY + maxH - heartsTop - 2);
|
||||
const heartsH = Math.max(18, Math.min(roomHearts, 40, Math.floor(maxH * 0.5)));
|
||||
|
||||
ctx.save();
|
||||
try {
|
||||
ctx.drawImage(bar, 0, 0, bw0, bh0, bx, by, bw, bh);
|
||||
ctx.drawImage(bar, 0, 0, bw0, titleSrcH, bx, by0, bw, titleDh);
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
const uv = STACK_TOWER_LIFE_BAR_HEART_SLOT_UV;
|
||||
const ix = bx + uv.u0 * bw;
|
||||
const iy = by + uv.v0 * bh;
|
||||
const iwSlot = Math.max(4, (uv.u1 - uv.u0) * bw);
|
||||
const ihSlot = Math.max(4, (uv.v1 - uv.v0) * bh);
|
||||
const slotW = iwSlot / slots;
|
||||
const iconMax = Math.min(slotW * 0.82, ihSlot * 0.88, 36);
|
||||
const cy = heartsTop + heartsH * 0.5;
|
||||
const bracketW = Math.min(14, Math.max(9, bw * 0.07));
|
||||
ctx.fillStyle = 'rgba(94, 239, 255, 0.88)';
|
||||
ctx.font = '600 ' + Math.round(Math.min(17, heartsH * 0.55)) + 'px ui-monospace, "Cascadia Mono", Consolas, monospace';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.textAlign = 'left';
|
||||
ctx.shadowColor = 'rgba(0, 234, 255, 0.45)';
|
||||
ctx.shadowBlur = 4;
|
||||
ctx.fillText('[', bx + 1, cy);
|
||||
ctx.textAlign = 'right';
|
||||
ctx.fillText(']', bx + bw - 1, cy);
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
const ix = bx + bracketW + 4;
|
||||
const iwInner = Math.max(24, bw - (bracketW + 4) * 2);
|
||||
const slotW = iwInner / slots;
|
||||
const iconMax = Math.min(heartsH * 0.78, slotW * 0.72, 34);
|
||||
|
||||
for (let i = 0; i < slots; i++) {
|
||||
const cx = ix + slotW * (i + 0.5);
|
||||
const cy = iy + ihSlot * 0.5;
|
||||
const useFull = i < lives;
|
||||
const im = useFull ? fullIm : hitIm;
|
||||
if (im && im.complete && im.naturalWidth > 0 && im.naturalHeight > 0) {
|
||||
const ih = iconMax;
|
||||
const iw = (ih * im.naturalWidth) / im.naturalHeight;
|
||||
const nw = im.naturalWidth;
|
||||
const nh = im.naturalHeight;
|
||||
let ih = iconMax;
|
||||
let iw = (ih * nw) / nh;
|
||||
const capW = slotW * 0.82;
|
||||
const capH = heartsH * 0.88;
|
||||
if (iw > capW) {
|
||||
iw = capW;
|
||||
ih = (iw * nh) / nw;
|
||||
}
|
||||
if (ih > capH) {
|
||||
ih = capH;
|
||||
iw = (ih * nw) / nh;
|
||||
}
|
||||
if (iw < 6 && nh > 0) {
|
||||
iw = 6;
|
||||
ih = (iw * nh) / nw;
|
||||
if (ih > capH) {
|
||||
ih = capH;
|
||||
iw = (ih * nw) / nh;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ctx.drawImage(im, 0, 0, im.naturalWidth, im.naturalHeight, cx - iw / 2, cy - ih / 2, iw, ih);
|
||||
ctx.drawImage(im, 0, 0, nw, nh, cx - iw / 2, cy - ih / 2, iw, ih);
|
||||
} catch (e2) { /* ignore */ }
|
||||
} else {
|
||||
ctx.fillStyle = useFull ? '#9ece6a' : '#565f89';
|
||||
@@ -4285,7 +4318,8 @@
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
return bh;
|
||||
const usedH = heartsTop + heartsH - by0 + 2;
|
||||
return Math.min(maxH, Math.max(titleDh, usedH));
|
||||
}
|
||||
|
||||
function triggerStackTowerHeartMinusFxPlay() {
|
||||
|
||||
@@ -2753,7 +2753,7 @@
|
||||
</div>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0258"></script>
|
||||
<script src="js/play.js?v=0.0259"></script>
|
||||
<script src="js/play.js?v=0.0260"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user