minigame 4 colour question

This commit is contained in:
2026-04-27 07:45:39 +00:00
parent 8597578a28
commit 9745d91016
14 changed files with 990 additions and 46 deletions
+246 -7
View File
@@ -352,8 +352,194 @@
});
}
function loadQuizCarryPanel() {
gameQuizFetch('GET').then(function (data) {
/** ค่าเริ่มต้น RGBA สำหรับแผงคำถามบนแผนที่ (quiz_carry) */
var QUIZ_CARRY_THEME_DEFAULTS = {
bg: { r: 12, g: 14, b: 28, a: 0.88 },
border: { r: 255, g: 214, b: 102, a: 0.7 },
text: { r: 241, g: 245, b: 249, a: 1 },
};
function quizCarryClampByte(n) {
return Math.max(0, Math.min(255, Math.round(Number(n)) || 0));
}
function quizCarryHexFromRgb(r, g, b) {
return '#' + [r, g, b].map(function (x) {
var h = quizCarryClampByte(x).toString(16);
return h.length === 1 ? '0' + h : h;
}).join('');
}
function quizCarryParseHexToRgb(hex) {
var h = String(hex || '').trim().replace(/^#/, '');
if (!h) return null;
if (h.length === 3) {
return {
r: parseInt(h[0] + h[0], 16),
g: parseInt(h[1] + h[1], 16),
b: parseInt(h[2] + h[2], 16),
alpha01: null,
};
}
if (h.length === 6) {
return {
r: parseInt(h.slice(0, 2), 16),
g: parseInt(h.slice(2, 4), 16),
b: parseInt(h.slice(4, 6), 16),
alpha01: null,
};
}
if (h.length === 8) {
return {
r: parseInt(h.slice(0, 2), 16),
g: parseInt(h.slice(2, 4), 16),
b: parseInt(h.slice(4, 6), 16),
alpha01: parseInt(h.slice(6, 8), 16) / 255,
};
}
return null;
}
function quizCarryParseCssColor(str, fallback) {
var s = String(str == null ? '' : str).trim();
if (!s) return fallback;
var m = /^rgba?\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*(?:,\s*([0-9.]+)\s*)?\)$/i.exec(s);
if (m) {
var a = m[4] != null && m[4] !== '' ? parseFloat(m[4]) : 1;
if (!Number.isFinite(a)) a = 1;
return {
r: quizCarryClampByte(parseInt(m[1], 10)),
g: quizCarryClampByte(parseInt(m[2], 10)),
b: quizCarryClampByte(parseInt(m[3], 10)),
a: Math.max(0, Math.min(1, a)),
};
}
if (/^#/.test(s)) {
var pr = quizCarryParseHexToRgb(s);
if (!pr) return fallback;
var a = pr.alpha01 != null ? pr.alpha01 : 1;
return { r: pr.r, g: pr.g, b: pr.b, a: Math.max(0, Math.min(1, a)) };
}
return fallback;
}
function quizCarryRgbToRgbaString(o) {
var a = Math.max(0, Math.min(1, Number(o.a)));
var t = Math.round(a * 1000) / 1000;
return 'rgba(' + quizCarryClampByte(o.r) + ', ' + quizCarryClampByte(o.g) + ', ' + quizCarryClampByte(o.b) + ', ' + t + ')';
}
function quizCarryRowPrefix(kind) {
return 'quiz-carry-theme-' + kind;
}
function quizCarryThemeHiddenId(kind) {
if (kind === 'bg') return 'quiz-carry-theme-bg';
if (kind === 'border') return 'quiz-carry-theme-border';
return 'quiz-carry-theme-text';
}
function quizCarrySetPickersFromColorString(kind, cssStr) {
var fb = QUIZ_CARRY_THEME_DEFAULTS[kind];
var o = quizCarryParseCssColor(cssStr, fb);
var pref = quizCarryRowPrefix(kind);
var sw = el(pref + '-swatch');
var ra = el(pref + '-alpha');
var out = el(pref + '-alpha-out');
var hi = el(quizCarryThemeHiddenId(kind));
var pr = el(pref + '-preview');
if (!sw || !ra || !hi) return;
sw.value = quizCarryHexFromRgb(o.r, o.g, o.b);
var pct = Math.max(0, Math.min(100, Math.round(Number(o.a) * 100)));
ra.value = String(pct);
if (out) out.textContent = pct + '%';
hi.value = quizCarryRgbToRgbaString(o);
if (pr) pr.textContent = hi.value;
}
function quizCarryComposeFromPickers(kind) {
var pref = quizCarryRowPrefix(kind);
var sw = el(pref + '-swatch');
var ra = el(pref + '-alpha');
if (!sw || !ra) return '';
var pr = quizCarryParseHexToRgb(sw.value);
if (!pr) return '';
var pct = parseInt(String(ra.value), 10);
if (!Number.isFinite(pct)) pct = 100;
pct = Math.max(0, Math.min(100, pct));
return quizCarryRgbToRgbaString({ r: pr.r, g: pr.g, b: pr.b, a: pct / 100 });
}
function quizCarrySyncRowOutput(kind) {
var s = quizCarryComposeFromPickers(kind);
var hi = el(quizCarryThemeHiddenId(kind));
var pref = quizCarryRowPrefix(kind);
var ra = el(pref + '-alpha');
var out = el(pref + '-alpha-out');
var pr = el(pref + '-preview');
if (hi) hi.value = s;
if (out && ra) out.textContent = String(Math.max(0, Math.min(100, parseInt(ra.value, 10) || 0))) + '%';
if (pr && hi) pr.textContent = hi.value;
}
/** อ่านค่าจาก color + range โดยตรง (ไม่พึ่ง hidden ที่อาจว่าง) ก่อน PUT */
function quizCarryBuildThemeForSave() {
quizCarryBindThemePickersOnce();
['bg', 'border', 'text'].forEach(function (k) {
quizCarrySyncRowOutput(k);
});
var fbBg = QUIZ_CARRY_THEME_DEFAULTS.bg;
var fbBr = QUIZ_CARRY_THEME_DEFAULTS.border;
var fbTx = QUIZ_CARRY_THEME_DEFAULTS.text;
var sBg = quizCarryComposeFromPickers('bg');
var sBr = quizCarryComposeFromPickers('border');
var sTx = quizCarryComposeFromPickers('text');
var bwEl = el('quiz-carry-theme-border-w');
var bw = bwEl ? parseInt(String(bwEl.value || '2'), 10) : 2;
if (!Number.isFinite(bw)) bw = 2;
bw = Math.max(0, Math.min(12, Math.round(bw)));
return {
panelBg: (sBg && sBg.trim()) ? sBg.trim().slice(0, 120) : quizCarryRgbToRgbaString(fbBg),
panelBorder: (sBr && sBr.trim()) ? sBr.trim().slice(0, 120) : quizCarryRgbToRgbaString(fbBr),
textColor: (sTx && sTx.trim()) ? sTx.trim().slice(0, 120) : quizCarryRgbToRgbaString(fbTx),
borderWidthPx: bw,
};
}
function quizCarryBindThemePickersOnce() {
if (quizCarryBindThemePickersOnce._done) return;
['bg', 'border', 'text'].forEach(function (kind) {
var pref = quizCarryRowPrefix(kind);
var sw = el(pref + '-swatch');
var ra = el(pref + '-alpha');
if (!sw || !ra) return;
if (ra.getAttribute('data-bound') === '1') return;
ra.setAttribute('data-bound', '1');
function sync() {
quizCarrySyncRowOutput(kind);
}
sw.addEventListener('input', sync);
sw.addEventListener('change', sync);
ra.addEventListener('input', sync);
ra.addEventListener('change', sync);
});
quizCarryBindThemePickersOnce._done = true;
}
function loadQuizCarryPanel(opts) {
opts = opts || {};
var delay = Number(opts.fetchDelayMs) || 0;
var runLoad = function () {
gameQuizFetch('GET').then(function (data) {
if (opts.themeOverride && typeof opts.themeOverride === 'object') {
data.carryMapPanelTheme = opts.themeOverride;
}
if (opts.timingOverride && typeof opts.timingOverride === 'object') {
var t = opts.timingOverride;
if (t.carryReadMs != null) data.carryReadMs = t.carryReadMs;
if (t.carryAnswerMs != null) data.carryAnswerMs = t.carryAnswerMs;
if (t.carrySessionLength != null) data.carrySessionLength = t.carrySessionLength;
}
renderQuizCarryAdminList(data.carryQuestions || []);
var readSec = el('quiz-carry-read-sec');
var ansSec = el('quiz-carry-answer-sec');
@@ -370,10 +556,23 @@
var sl = parseInt(String(data.carrySessionLength), 10);
sessLen.value = String(Number.isFinite(sl) && sl >= 0 ? sl : 0);
}
setMsg('quiz-carry-settings-msg', '', '');
quizCarryBindThemePickersOnce();
var th = data.carryMapPanelTheme && typeof data.carryMapPanelTheme === 'object' ? data.carryMapPanelTheme : {};
quizCarrySetPickersFromColorString('bg', th.panelBg);
quizCarrySetPickersFromColorString('border', th.panelBorder);
quizCarrySetPickersFromColorString('text', th.textColor);
var bwInp = el('quiz-carry-theme-border-w');
if (bwInp) {
var bw = parseInt(String(th.borderWidthPx), 10);
bwInp.value = String(Number.isFinite(bw) && bw >= 0 ? Math.min(12, bw) : 2);
}
if (opts.clearMsg !== false) setMsg('quiz-carry-settings-msg', '', '');
}).catch(function (e) {
setMsg('quiz-carry-settings-msg', e.message || 'โหลดไม่ได้', 'error');
});
};
if (delay > 0) setTimeout(runLoad, delay);
else runLoad();
}
function saveQuizCarryPanel() {
@@ -426,13 +625,52 @@
if (!Number.isFinite(carrySessionLength) || carrySessionLength < 0) carrySessionLength = 0;
if (carrySessionLength > 500) carrySessionLength = 500;
gameQuizFetch('PUT', {
carryQuestions: carryQuestions,
var carryMapPanelTheme = quizCarryBuildThemeForSave();
var blockEls = list.querySelectorAll('.quiz-carry-admin-block');
var hasAnyQuestionText = false;
for (var bi = 0; bi < blockEls.length; bi++) {
var qn = blockEls[bi].querySelector('.quiz-carry-q-text');
if (qn && String(qn.value || '').trim()) {
hasAnyQuestionText = true;
break;
}
}
if (carryQuestions.length === 0 && hasAnyQuestionText) {
setMsg(
'quiz-carry-settings-msg',
'ยังบันทึกชุดคำถามไม่ได้: แต่ละข้อต้องมีคำถาม ตัวเลือกอย่างน้อย 2 ช่องที่กรอกแล้ว และเลือก «ข้อที่ถูกต้อง» — ตรวจแถวที่มีข้อความค้าง',
'error',
);
return;
}
var putBody = {
carryReadMs: carryReadMs,
carryAnswerMs: carryAnswerMs,
carrySessionLength: carrySessionLength,
}).then(function () {
setMsg('quiz-carry-settings-msg', 'บันทึกแล้ว', 'ok');
carryMapPanelTheme: carryMapPanelTheme,
};
if (carryQuestions.length > 0) {
putBody.carryQuestions = carryQuestions;
}
gameQuizFetch('PUT', putBody).then(function (res) {
var tail = carryQuestions.length ? ' · คำถาม ' + carryQuestions.length + ' ข้อ' : ' · คงชุดคำถามเดิม (อัปเดตเวลา/ธีมอย่างเดียว)';
var themeFromRes = res && res.carryMapPanelTheme && typeof res.carryMapPanelTheme === 'object' ? res.carryMapPanelTheme : null;
var themeOk = !!themeFromRes;
var themeTail = themeOk ? ' · ธีมแผงบันทึกแล้ว' : '';
setMsg('quiz-carry-settings-msg', 'บันทึกแล้ว' + tail + themeTail, 'ok');
loadQuizCarryPanel({
clearMsg: false,
themeOverride: themeFromRes || carryMapPanelTheme,
timingOverride: {
carryReadMs: carryReadMs,
carryAnswerMs: carryAnswerMs,
carrySessionLength: carrySessionLength,
},
fetchDelayMs: 120,
});
}).catch(function (e) {
setMsg('quiz-carry-settings-msg', e.message || 'บันทึกไม่ได้', 'error');
});
@@ -1578,6 +1816,7 @@
});
}
if (btnQuizCarrySave) btnQuizCarrySave.addEventListener('click', saveQuizCarryPanel);
quizCarryBindThemePickersOnce();
var btnQbBattleAdd = el('btn-qb-battle-add');
var btnQbBattleSave = el('btn-qb-battle-save');