diff --git a/css/styles.css b/css/styles.css
index 7ffa293..0fb9a57 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -1471,20 +1471,16 @@ input:checked + .slider:before {
background: linear-gradient(135deg, #0d111a 0%, #151a27 50%, #0f131f 100%);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 16px;
- padding: 1.25rem;
+ padding: 1.5rem;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.7), inset 0 0 40px rgba(56, 189, 248, 0.03);
display: grid;
- grid-template-columns: 240px 1.15fr 1.35fr;
+ grid-template-columns: 360px 320px 370px;
gap: 1.25rem;
position: relative;
overflow: hidden;
-}
-
-@media (max-width: 960px) {
- .showcase-card-layout {
- grid-template-columns: 1fr;
- gap: 1.25rem;
- }
+ min-width: 1050px;
+ width: 1050px;
+ box-sizing: border-box;
}
.showcase-splash-col {
@@ -1495,7 +1491,8 @@ input:checked + .slider:before {
display: flex;
flex-direction: column;
justify-content: flex-end;
- min-height: 380px;
+ min-height: 520px;
+ width: 360px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
@@ -2058,26 +2055,58 @@ input:checked + .slider:before {
/* Relics 6 Slots Layout */
.relics-slots-grid {
display: grid;
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
gap: 0.85rem;
}
.relic-slot-box {
- background: rgba(18, 24, 38, 0.65);
+ background: rgba(18, 24, 38, 0.7);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
- padding: 0.85rem;
+ padding: 0.85rem 1rem;
display: flex;
gap: 0.85rem;
align-items: center;
position: relative;
+ cursor: pointer;
transition: all 0.2s ease;
}
+.relic-slot-box.empty {
+ border: 1px dashed rgba(255, 255, 255, 0.18);
+ background: rgba(14, 19, 31, 0.5);
+}
+
.relic-slot-box:hover {
- border-color: rgba(56, 189, 248, 0.5);
- background: rgba(18, 24, 38, 0.85);
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
+ border-color: rgba(56, 189, 248, 0.6);
+ background: rgba(22, 30, 48, 0.9);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
+}
+
+.relic-slot-box .btn-slot-unequip-corner {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: rgba(239, 68, 68, 0.15);
+ border: 1px solid rgba(239, 68, 68, 0.35);
+ color: #f87171;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.75rem;
+ cursor: pointer;
+ transition: all 0.15s;
+ z-index: 5;
+}
+
+.relic-slot-box .btn-slot-unequip-corner:hover {
+ background: #ef4444;
+ color: #fff;
+ transform: scale(1.1);
}
.relic-slot-icon-wrap {
@@ -2100,7 +2129,7 @@ input:checked + .slider:before {
bottom: 0;
left: 0;
right: 0;
- background: rgba(0, 0, 0, 0.8);
+ background: rgba(0, 0, 0, 0.85);
font-size: 0.6rem;
text-align: center;
font-weight: 800;
@@ -2114,33 +2143,70 @@ input:checked + .slider:before {
}
.relic-slot-title {
- font-size: 0.85rem;
- font-weight: 700;
+ font-size: 0.875rem;
+ font-weight: 800;
color: var(--text-highlight);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+.relic-slot-lvl {
+ font-size: 0.7rem;
+ font-weight: 800;
+ color: #000;
+ background: var(--accent-gold);
+ padding: 0.05rem 0.4rem;
+ border-radius: 4px;
+}
+
.relic-slot-main {
- font-size: 0.75rem;
- font-weight: 700;
- color: var(--accent-gold);
+ font-size: 0.8rem;
+ font-weight: 800;
+ color: var(--accent-cyan);
margin-top: 0.2rem;
}
.relic-slot-subs {
- font-size: 0.7rem;
+ font-size: 0.725rem;
color: var(--text-muted);
margin-top: 0.2rem;
line-height: 1.35;
}
-.relic-slot-actions {
+/* Lightcone Picker Card */
+.lc-picker-card {
+ background: rgba(18, 24, 38, 0.7);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ padding: 0.65rem;
display: flex;
- flex-direction: column;
- gap: 0.35rem;
+ gap: 0.65rem;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.2s;
+ position: relative;
+}
+
+.lc-picker-card:hover {
+ background: rgba(28, 38, 60, 0.95);
+ border-color: rgba(56, 189, 248, 0.6);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
+}
+
+.lc-picker-card.active-equipped {
+ border-color: var(--accent-gold);
+ background: rgba(251, 191, 36, 0.1);
+}
+
+.lc-picker-thumb {
+ width: 44px;
+ height: 60px;
+ border-radius: 4px;
+ object-fit: cover;
flex-shrink: 0;
+ border: 1px solid var(--border-color);
}
/* Rich Skills & Traces */
diff --git a/index.html b/index.html
index bc3af57..26780fc 100644
--- a/index.html
+++ b/index.html
@@ -232,7 +232,8 @@
-
+
+
@@ -633,10 +634,7 @@
📋 ข้อมูลภาพรวม
-
@@ -731,6 +729,31 @@
+
+
+
+ 🗡️ กรวยแสงที่สวมใส่ (คลิกเพื่อเลือกเปลี่ยน)
+ เปลี่ยนกรวยแสง ➔
+
+
+
![]()
+
+
+ -
+ ★★★★★
+
+
+ S5
+ •
+ Lv. 80 / 80
+ •
+ HP 952 / ATK 582 / DEF 529
+
+
+
+
+
+
@@ -1062,9 +1085,11 @@
-
-
@@ -1235,6 +1260,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
⭐ กรวยแสงประจำตัวแนะนำ (Signature)
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/app.js b/js/app.js
index eda0411..3532eb5 100644
--- a/js/app.js
+++ b/js/app.js
@@ -546,7 +546,7 @@ function applyTranslations() {
document.querySelectorAll('[data-i18n]').forEach(elem => {
const key = elem.getAttribute('data-i18n');
const val = t(key);
- if (val) elem.textContent = val;
+ if (val && val !== key) elem.textContent = val;
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(elem => {
@@ -1129,37 +1129,9 @@ function renderAvatarsGrid() {
Lv. ${item.level}
E${item.rank}
-
-
-
-
-
`;
- // Quick Showcase Card Button
- const btnShowcase = card.querySelector('.btn-card-showcase');
- btnShowcase?.addEventListener('click', (e) => {
- e.stopPropagation();
- openAvatarDetailModal(item);
- const showcaseTabBtn = document.querySelector('.modal-subtab-btn[data-modaltab="modaltab-showcase"]');
- if (showcaseTabBtn) showcaseTabBtn.click();
- });
-
- // Quick E0S1 Button on Card Outside
- const btnE0S1 = card.querySelector('.btn-card-e0s1');
- btnE0S1?.addEventListener('click', async (e) => {
- e.stopPropagation();
- await setAvatarE0S1(item.avatar_id);
- });
-
- // Quick E6S5 Button on Card Outside
- const btnE6S5 = card.querySelector('.btn-card-e6s5');
- btnE6S5?.addEventListener('click', async (e) => {
- e.stopPropagation();
- await setAvatarE6S5(item.avatar_id);
- });
-
// Clicking card opens full character build modal
card.addEventListener('click', () => {
openAvatarDetailModal(item);
@@ -1899,13 +1871,52 @@ function renderModalOverviewTab(avatarId) {
}
}
-// Render Relics 6 Slots Tab
+// Render Relics & Gear Tab (6 Slots + Equipped Lightcone)
function renderModalRelicsTab(avatarId) {
const aid = parseInt(avatarId, 10);
const meta = getAvatarInfo(aid);
if (!el.modalRelicsSlotsGrid) return;
el.modalRelicsSlotsGrid.innerHTML = '';
+ // 1. Equipped Lightcone in Relics Tab
+ const lc = (state.fullFreesrData?.lightcones || []).find(l => l.equip_avatar === aid);
+ const lcId = lc?.item_id || SIGNATURE_LIGHTCONES[aid] || 21001;
+ const lcInfo = getLightconeInfo(lcId);
+ const lcRank = lc?.rank || 5;
+ const lcLevel = lc?.level || 80;
+ const lcMeta = LIGHTCONE_METADATA[lcId] || {};
+
+ const lcThumbEl = document.getElementById('relics-tab-lc-thumb');
+ const lcNameEl = document.getElementById('relics-tab-lc-name');
+ const lcStarsEl = document.getElementById('relics-tab-lc-stars');
+ const lcRankEl = document.getElementById('relics-tab-lc-rank');
+ const lcLvlEl = document.getElementById('relics-tab-lc-level');
+ const lcStatsEl = document.getElementById('relics-tab-lc-stats');
+ const lcBox = document.getElementById('relics-equipped-lc-box');
+
+ if (lcThumbEl) {
+ lcThumbEl.src = getLightconeArtSrc(lcId);
+ lcThumbEl.onerror = function() {
+ const fb = getLightconeArtFallback(lcId);
+ if (this.src !== fb) this.src = fb;
+ else this.src = './assets/logo.png';
+ };
+ }
+ if (lcNameEl) lcNameEl.textContent = lcInfo.name || `Light Cone ${lcId}`;
+ if (lcStarsEl) lcStarsEl.textContent = '★'.repeat(lcInfo.rarity || 5);
+ if (lcRankEl) lcRankEl.textContent = `S${lcRank}`;
+ if (lcLvlEl) lcLvlEl.textContent = `Lv. ${lcLevel} / 80`;
+ if (lcStatsEl) {
+ const hp = lcMeta.hp || (lcMeta.rarity === 5 ? 1164 : (lcMeta.rarity === 4 ? 953 : 846));
+ const atk = lcMeta.atk || (lcMeta.rarity === 5 ? 582 : (lcMeta.rarity === 4 ? 423 : 317));
+ const def = lcMeta.def || (lcMeta.rarity === 5 ? 529 : (lcMeta.rarity === 4 ? 396 : 264));
+ lcStatsEl.textContent = `HP ${hp} / ATK ${atk} / DEF ${def}`;
+ }
+ if (lcBox) {
+ lcBox.onclick = () => openLightconePicker(aid);
+ }
+
+ // 2. Relic Slots
const allRelics = state.fullFreesrData?.relics || [];
const equippedRelics = allRelics.filter(r => r.equip_avatar === aid);
@@ -1920,34 +1931,36 @@ function renderModalRelicsTab(avatarId) {
const equipped = equippedRelics.find(r => (r.relic_id % 10) === slot);
const box = document.createElement('div');
- box.className = 'relic-slot-box';
if (equipped) {
const setId = Math.floor((equipped.relic_id % 10000) / 10);
const rInfo = getRelicSetInfo(setId);
const mainAffixName = getRelicMainAffixName(slot, equipped.main_affix_id);
+ box.className = 'relic-slot-box equipped';
box.innerHTML = `

-
Slot ${slot}
+
${slotInfo.icon} ${slotInfo.name.split(' ')[0]}
-
${slotInfo.icon} ${rInfo.name}
-
✦ ${mainAffixName} (+${equipped.level})
-
${equipped.sub_affixes?.length || 4} ออปชั่นรอง (Max Level 15)
-
-
-
-
+
+ ${rInfo.name}
+ +15
+
+
✦ ${mainAffixName}
+
${equipped.sub_affixes?.length || 4} ออปชั่นรอง (คลิกเพื่อเลือกเปลี่ยน)
+
`;
- box.querySelector('.btn-slot-change').addEventListener('click', () => {
+ box.addEventListener('click', () => {
openSlotPicker(aid, slot);
});
- box.querySelector('.btn-slot-unequip').addEventListener('click', async () => {
+
+ box.querySelector('.btn-slot-unequip-corner')?.addEventListener('click', async (e) => {
+ e.stopPropagation();
equipped.equip_avatar = 0;
if (state.isConnected) await saveFreesrChangesToServer();
renderModalRelicsTab(aid);
@@ -1956,20 +1969,20 @@ function renderModalRelicsTab(avatarId) {
showToast(`ถอดรีลิกส์ ${slotInfo.name} เรียบร้อย`, 'info');
});
} else {
+ box.className = 'relic-slot-box empty';
box.innerHTML = `
-
+
${slotInfo.icon}
- Slot ${slot}
+ ${slotInfo.name.split(' ')[0]}
${slotInfo.name}
-
ยังไม่ได้ติดตั้ง (แนะนำ: ${slotInfo.defaultStat})
-
-
-
+
ยังไม่ได้สวมใส่
+
+ คลิกเพื่อเลือกหรือสร้าง (แนะนำ: ${slotInfo.defaultStat})
`;
- box.querySelector('.btn-slot-equip').addEventListener('click', () => {
+
+ box.addEventListener('click', () => {
openSlotPicker(aid, slot);
});
}
@@ -2137,6 +2150,305 @@ async function quickGenSlotRelic(avatarId, slotNum) {
showToast(`สร้างและติดตั้งรีลิกส์แนะนำช่อง ${slotNum} สำเร็จ!`, 'success');
}
+// Open Lightcone Picker Modal
+function openLightconePicker(avatarId) {
+ state.lcPickerAvatarId = parseInt(avatarId, 10);
+ const aid = state.lcPickerAvatarId;
+ const meta = getAvatarInfo(aid);
+ const modal = document.getElementById('modal-select-lightcone');
+ if (!modal) return;
+
+ const titleEl = document.getElementById('modal-lc-picker-title');
+ if (titleEl) {
+ titleEl.textContent = `🗡️ เลือกสวมใส่กรวยแสง: ${meta.name}`;
+ }
+
+ const sigLcId = SIGNATURE_LIGHTCONES[aid];
+ const sigBox = document.getElementById('lc-picker-signature-box');
+ const sigThumb = document.getElementById('lc-picker-sig-thumb');
+ const sigName = document.getElementById('lc-picker-sig-name');
+ const btnQuickSig = document.getElementById('btn-lc-picker-quick-sig');
+
+ if (sigLcId && LIGHTCONE_METADATA[sigLcId]) {
+ const sInfo = getLightconeInfo(sigLcId);
+ if (sigBox) sigBox.style.display = 'flex';
+ if (sigThumb) {
+ sigThumb.src = getLightconeArtSrc(sigLcId);
+ sigThumb.onerror = function() { this.src = getLightconeArtFallback(sigLcId); };
+ }
+ if (sigName) sigName.textContent = `${sInfo.name} (★${sInfo.rarity} ${sInfo.path})`;
+ if (btnQuickSig) {
+ btnQuickSig.onclick = async () => {
+ const rank = parseInt(document.getElementById('lc-picker-rank')?.value || '5', 10);
+ await equipLightconeToAvatar(aid, sigLcId, rank, 80);
+ modal.classList.remove('active');
+ };
+ }
+ } else if (sigBox) {
+ sigBox.style.display = 'none';
+ }
+
+ const pathSelect = document.getElementById('lc-picker-filter-path');
+ if (pathSelect) pathSelect.value = 'same';
+
+ const searchInput = document.getElementById('lc-picker-search');
+ if (searchInput) searchInput.value = '';
+
+ renderLightconePickerGrid();
+ modal.classList.add('active');
+}
+
+// Render Lightcone Picker Grid
+function renderLightconePickerGrid() {
+ const listEl = document.getElementById('lc-picker-list');
+ if (!listEl) return;
+ listEl.innerHTML = '';
+
+ const aid = state.lcPickerAvatarId;
+ const avatarMeta = getAvatarInfo(aid);
+ const search = document.getElementById('lc-picker-search')?.value.trim().toLowerCase() || '';
+ const pathFilter = document.getElementById('lc-picker-filter-path')?.value || 'same';
+ const rank = parseInt(document.getElementById('lc-picker-rank')?.value || '5', 10);
+
+ const currentLc = (state.fullFreesrData?.lightcones || []).find(l => l.equip_avatar === aid);
+
+ const allIds = Object.keys(LIGHTCONE_METADATA).map(id => parseInt(id, 10)).sort((a, b) => {
+ const metaA = LIGHTCONE_METADATA[a];
+ const metaB = LIGHTCONE_METADATA[b];
+ if (metaB.rarity !== metaA.rarity) return metaB.rarity - metaA.rarity;
+ return b - a;
+ });
+
+ const filtered = allIds.filter(id => {
+ const meta = LIGHTCONE_METADATA[id];
+ const info = getLightconeInfo(id);
+
+ // Path match
+ if (pathFilter === 'same' && meta.path !== avatarMeta.path) return false;
+ if (pathFilter !== 'all' && pathFilter !== 'same' && meta.path !== pathFilter) return false;
+
+ // Search match
+ if (search) {
+ const matchName = info.name.toLowerCase().includes(search);
+ const matchId = String(id).includes(search);
+ if (!matchName && !matchId) return false;
+ }
+ return true;
+ });
+
+ if (filtered.length === 0) {
+ listEl.innerHTML = `
ไม่พบกรวยแสงตามเงื่อนไขที่ค้นหา
`;
+ return;
+ }
+
+ filtered.forEach(id => {
+ const meta = LIGHTCONE_METADATA[id];
+ const info = getLightconeInfo(id);
+ const isCurrent = currentLc?.item_id === id;
+ const isSig = SIGNATURE_LIGHTCONES[aid] === id;
+
+ const card = document.createElement('div');
+ card.className = `lc-picker-card rarity-${info.rarity} ${isCurrent ? 'active-equipped' : ''}`;
+
+ let badgeHtml = '';
+ if (isCurrent) {
+ badgeHtml = `
สวมใส่อยู่`;
+ } else if (isSig) {
+ badgeHtml = `
ประจำตัว`;
+ }
+
+ card.innerHTML = `
+
})
+
+
+ ${info.name}
+ ${badgeHtml}
+
+
${'★'.repeat(info.rarity || 5)}
+
+ ${getPathIconHtml(meta.path, 14)} ${info.path}
+
+
+ `;
+
+ card.addEventListener('click', async () => {
+ await equipLightconeToAvatar(aid, id, rank, 80);
+ document.getElementById('modal-select-lightcone')?.classList.remove('active');
+ });
+
+ listEl.appendChild(card);
+ });
+}
+
+// Equip Lightcone directly to Avatar
+async function equipLightconeToAvatar(avatarId, lcId, rank = 5, level = 80) {
+ const aid = parseInt(avatarId, 10);
+ const id = parseInt(lcId, 10);
+ const meta = getAvatarInfo(aid);
+ const lcInfo = getLightconeInfo(id);
+
+ if (!state.fullFreesrData) state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
+ state.fullFreesrData.lightcones = state.fullFreesrData.lightcones || [];
+
+ // 1. Unassign other lightcones from this avatar
+ state.fullFreesrData.lightcones.forEach(lc => {
+ if (lc.equip_avatar === aid) lc.equip_avatar = 0;
+ });
+
+ // 2. Find or create lightcone entry
+ let targetLc = state.fullFreesrData.lightcones.find(lc => lc.item_id === id && (!lc.equip_avatar || lc.equip_avatar === 0));
+ if (!targetLc) {
+ targetLc = state.fullFreesrData.lightcones.find(lc => lc.item_id === id);
+ if (targetLc) {
+ targetLc.equip_avatar = aid;
+ targetLc.rank = rank;
+ targetLc.level = level;
+ targetLc.promotion = 6;
+ } else {
+ let maxUid = 1000;
+ state.fullFreesrData.lightcones.forEach(l => {
+ if (l.unique_id >= maxUid) maxUid = l.unique_id + 1;
+ });
+ targetLc = {
+ unique_id: maxUid,
+ internal_uid: state.fullFreesrData.lightcones.length,
+ item_id: id,
+ level: level,
+ rank: rank,
+ promotion: 6,
+ equip_avatar: aid
+ };
+ state.fullFreesrData.lightcones.push(targetLc);
+ }
+ } else {
+ targetLc.equip_avatar = aid;
+ targetLc.rank = rank;
+ targetLc.level = level;
+ targetLc.promotion = 6;
+ }
+
+ if (state.isConnected) {
+ await saveFreesrChangesToServer().catch(() => {});
+ }
+
+ renderModalOverviewTab(aid);
+ renderModalRelicsTab(aid);
+ renderShowcaseCard(aid);
+ renderLightconesGrid();
+
+ showToast(`🗡️ สวมใส่ ${lcInfo.name} (S${rank}) ให้กับ ${meta.name} เรียบร้อย!`, 'success');
+}
+
+// Unequip Lightcone from Avatar
+async function unequipLightconeFromAvatar(avatarId) {
+ const aid = parseInt(avatarId, 10);
+ const meta = getAvatarInfo(aid);
+ if (state.fullFreesrData?.lightcones) {
+ state.fullFreesrData.lightcones.forEach(lc => {
+ if (lc.equip_avatar === aid) lc.equip_avatar = 0;
+ });
+ if (state.isConnected) await saveFreesrChangesToServer().catch(() => {});
+ renderModalOverviewTab(aid);
+ renderModalRelicsTab(aid);
+ renderShowcaseCard(aid);
+ renderLightconesGrid();
+ showToast(`ถอดกรวยแสงออกจาก ${meta.name} เรียบร้อย`, 'info');
+ }
+}
+
+// Quick Set All Avatars to E0S1 Standard (Lv.80, Prom 6, E0, S1 Signature LC)
+async function quickSetAllAvatarsE0S1() {
+ try {
+ if (!state.fullFreesrData) {
+ state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
+ }
+ state.fullFreesrData.avatars = state.fullFreesrData.avatars || {};
+ state.fullFreesrData.lightcones = state.fullFreesrData.lightcones || [];
+
+ const allAvatarIds = Object.keys(AVATARS_DATA).map(id => parseInt(id, 10));
+
+ let nextUniqueId = 1000;
+ state.fullFreesrData.lightcones.forEach(lc => {
+ if (lc.unique_id >= nextUniqueId) nextUniqueId = lc.unique_id + 1;
+ });
+
+ for (const aid of allAvatarIds) {
+ if (!state.fullFreesrData.avatars[aid]) {
+ state.fullFreesrData.avatars[aid] = {
+ owner_uid: 0,
+ avatar_id: aid,
+ level: 80,
+ promotion: 6,
+ techniques: [],
+ sp_max: 140,
+ sp_value: 70,
+ data: {
+ rank: 0,
+ skills: {
+ [`${aid}001`]: 6,
+ [`${aid}002`]: 10,
+ [`${aid}003`]: 10,
+ [`${aid}004`]: 10,
+ [`${aid}007`]: 1,
+ [`${aid}101`]: 1,
+ [`${aid}102`]: 1,
+ [`${aid}103`]: 1,
+ [`${aid}201`]: 1
+ },
+ skills_by_anchor_type: {
+ "1": 6, "2": 10, "3": 10, "4": 10, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1
+ }
+ }
+ };
+ } else {
+ const av = state.fullFreesrData.avatars[aid];
+ av.level = 80;
+ av.promotion = 6;
+ av.data = av.data || {};
+ av.data.rank = 0;
+ if (!av.sp_value) av.sp_value = 70;
+ }
+
+ // Equip S1 Signature Lightcone
+ const sigLcId = SIGNATURE_LIGHTCONES[aid] || 21001;
+ let existingLc = state.fullFreesrData.lightcones.find(l => l.equip_avatar === aid);
+ if (existingLc) {
+ existingLc.item_id = sigLcId;
+ existingLc.rank = 1;
+ existingLc.level = 80;
+ existingLc.promotion = 6;
+ } else {
+ state.fullFreesrData.lightcones.push({
+ unique_id: nextUniqueId++,
+ internal_uid: state.fullFreesrData.lightcones.length,
+ item_id: sigLcId,
+ level: 80,
+ promotion: 6,
+ rank: 1,
+ equip_avatar: aid
+ });
+ }
+ }
+
+ initAvatarsList();
+ state.avatarsList.forEach(a => {
+ a.level = 80;
+ a.promotion = 6;
+ a.rank = 0;
+ });
+
+ if (state.isConnected) {
+ await saveFreesrChangesToServer().catch(() => {});
+ }
+
+ renderAvatarsGrid();
+ if (el.statAvatars) el.statAvatars.textContent = Object.keys(state.fullFreesrData.avatars).length;
+ showToast('⚡ ปรับตัวละครทุกตัวเป็น E0S1 พร้อมกรวยแสงประจำตัวเรียบร้อยแล้ว!', 'success');
+ } catch (err) {
+ showToast(err.message, 'error');
+ }
+}
+
// Render Skills Tab with real StarRailRes descriptions & icons
async function renderModalSkillsTab(avatarId) {
const aid = String(avatarId);
@@ -2733,7 +3045,7 @@ function renderShowcaseCard(avatarId) {
activeSetBadges.forEach(sb => {
setsHtml += `
- ${sb.name}
+ ${sb.name}
${sb.count}-PC
`;
@@ -2769,8 +3081,8 @@ function renderShowcaseCard(avatarId) {
- ${rInfo.name}
- ${slotInfo.stat}
+ ${rInfo.name}
+ ${slotInfo.stat}
CRIT Rate +${(3.2 + slot * 1.5).toFixed(1)}%${rollBadge(slot % 2 === 0 ? 3 : 2)}
@@ -2989,12 +3301,22 @@ async function exportShowcaseImage(avatarId) {
if (typeof html2canvas !== 'undefined') {
try {
+ const origWidth = showcaseEl.style.width;
+ const origMinWidth = showcaseEl.style.minWidth;
+ showcaseEl.style.width = '1050px';
+ showcaseEl.style.minWidth = '1050px';
+
const canvas = await html2canvas(showcaseEl, {
useCORS: true,
allowTaint: true,
scale: 2,
- backgroundColor: '#0c1222'
+ backgroundColor: '#0c1222',
+ windowWidth: 1280
});
+
+ showcaseEl.style.width = origWidth;
+ showcaseEl.style.minWidth = origMinWidth;
+
const dataUrl = canvas.toDataURL('image/png');
const a = document.createElement('a');
a.href = dataUrl;
@@ -4344,9 +4666,37 @@ function setupEvents() {
setupSkillStepper(el.rangeSkillTalent, el.valSkillTalent, el.btnStepDecTalent, el.btnStepIncTalent, el.btnSkillMaxTalent, 10);
// Overview Tab Quick Navigation Cards
- el.overviewLcCard?.addEventListener('click', () => switchModalSubtab('modaltab-lightcone'));
+ el.overviewLcCard?.addEventListener('click', () => {
+ if (state.selectedAvatarDetail) openLightconePicker(state.selectedAvatarDetail.avatar_id);
+ });
el.overviewRelicsCard?.addEventListener('click', () => switchModalSubtab('modaltab-relics'));
+ // Lightcone Picker Modal Controls
+ document.getElementById('btn-close-lc-picker')?.addEventListener('click', () => {
+ document.getElementById('modal-select-lightcone')?.classList.remove('active');
+ });
+ document.getElementById('btn-lc-picker-close')?.addEventListener('click', () => {
+ document.getElementById('modal-select-lightcone')?.classList.remove('active');
+ });
+ document.getElementById('lc-picker-search')?.addEventListener('input', () => {
+ renderLightconePickerGrid();
+ });
+ document.getElementById('lc-picker-filter-path')?.addEventListener('change', () => {
+ renderLightconePickerGrid();
+ });
+ document.getElementById('lc-picker-rank')?.addEventListener('change', () => {
+ renderLightconePickerGrid();
+ });
+ document.getElementById('btn-lc-picker-unequip')?.addEventListener('click', async () => {
+ if (state.lcPickerAvatarId) {
+ await unequipLightconeFromAvatar(state.lcPickerAvatarId);
+ }
+ document.getElementById('modal-select-lightcone')?.classList.remove('active');
+ });
+
+ // Top Bar Characters Tab Quick Actions
+ document.getElementById('btn-all-avatars-e0s1')?.addEventListener('click', quickSetAllAvatarsE0S1);
+
// Relic Tab Controls in Modal
el.btnModalRelicsAuto?.addEventListener('click', async () => {
if (!state.selectedAvatarDetail) return;
diff --git a/js/i18n.js b/js/i18n.js
index c699d8c..7a61d4a 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -268,7 +268,31 @@ export const TRANSLATIONS = {
"Elation": "ปีติ"
},
"settingsSaved": "บันทึกการตั้งค่าสำเร็จ!",
- "settingsReset": "คืนค่าเริ่มต้นเรียบร้อย"
+ "settingsReset": "คืนค่าเริ่มต้นเรียบร้อย",
+ "qaLineupE0S1Title": "ปรับทีมเป็น E0S1",
+ "qaLineupE0S1Desc": "ตัวละครในทีมเป็น E0 พร้อม LC S1",
+ "qaLineupRelicsTitle": "ใส่อุปกรณ์ทีมครบชุด",
+ "qaLineupRelicsDesc": "ใส่รีลิกส์ +15 ครบ 4 คนในทีม",
+ "lblPersistenceInfo": "ระบบจัดการไฟล์เซฟและข้อมูลฝั่งเซิร์ฟเวอร์",
+ "modalTabOverview": "ภาพรวม",
+ "modalTabRelics": "รีลิกส์ & อุปกรณ์",
+ "overviewStatsTitle": "⚔️ ค่าพลังการต่อสู้ (Combat Stats)",
+ "overviewStatsNote": "รวมโบนัสกรวยแสงและรีลิกส์",
+ "overviewEquippedLc": "🗡️ กรวยแสงที่สวมใส่",
+ "overviewEquippedRelics": "🛡️ รีลิกส์และเซ็ต",
+ "modalRelicsTitle": "🛡️ สวมใส่รีลิกส์ (Relics 6 ชิ้น)",
+ "modalRelicsSubtitle": "คลิกแต่ละช่องเพื่อเลือกเปลี่ยน หรือสวมใส่เซ็ตแนะนำ",
+ "btnAutoEquipRelics": "⚡ สวมใส่เซ็ตแนะนำครบชุด",
+ "btnClearAllRelics": "🧹 ถอดทั้งหมด",
+ "activeSetEffects": "✨ เอฟเฟกต์เซ็ตที่เปิดใช้งาน",
+ "modalColLevel": "เลเวล",
+ "lblPromotion": "ขั้นเลื่อนขั้น",
+ "slotPickerTitle": "🛡️ เลือกสวมใส่รีลิกส์",
+ "quickGenRecTitle": "⚡ สร้างชิ้นแนะนำสำหรับช่องนี้",
+ "quickGenRecDesc": "สร้างรีลิกส์ 5★ +15 ตรงเซ็ตและสเตตัสหลักที่ดีที่สุด",
+ "btnGenAndEquip": "สร้าง & สวมใส่",
+ "selectFromInventory": "เลือกจากคลังเก็บของ (ตรงช่อง)",
+ "btnAllE0S1": "⚡ ปรับ E0S1 ทุกตัว"
},
"en": {
"ahaGlobalDesc": "Enable server-wide Aeon ★ Aha special trait",
@@ -520,7 +544,31 @@ export const TRANSLATIONS = {
"Elation": "Elation"
},
"settingsSaved": "Settings saved successfully!",
- "settingsReset": "Reset to default settings"
+ "settingsReset": "Reset to default settings",
+ "qaLineupE0S1Title": "Set Team to E0S1",
+ "qaLineupE0S1Desc": "Team characters to E0 with S1 LC",
+ "qaLineupRelicsTitle": "Auto-Equip Team Relics",
+ "qaLineupRelicsDesc": "Equip +15 relics for 4 teammates",
+ "lblPersistenceInfo": "Server save file & data management",
+ "modalTabOverview": "Overview",
+ "modalTabRelics": "Relics & Gear",
+ "overviewStatsTitle": "⚔️ Combat Stats",
+ "overviewStatsNote": "Includes Lightcone & Relic bonuses",
+ "overviewEquippedLc": "🗡️ Equipped Lightcone",
+ "overviewEquippedRelics": "🛡️ Relics & Set Bonuses",
+ "modalRelicsTitle": "🛡️ Equip Relics (6 Slots)",
+ "modalRelicsSubtitle": "Click each slot to change or equip recommended sets",
+ "btnAutoEquipRelics": "⚡ Equip Recommended Sets",
+ "btnClearAllRelics": "🧹 Unequip All",
+ "activeSetEffects": "✨ Active Set Bonuses",
+ "modalColLevel": "Level",
+ "lblPromotion": "Ascension Rank",
+ "slotPickerTitle": "🛡️ Select Relic to Equip",
+ "quickGenRecTitle": "⚡ Generate Recommended Piece",
+ "quickGenRecDesc": "Generate 5★ +15 with best set & main stat",
+ "btnGenAndEquip": "Generate & Equip",
+ "selectFromInventory": "Select from Inventory",
+ "btnAllE0S1": "⚡ All E0S1"
},
"zh-CN": {
"ahaGlobalDesc": "全服启用星神·阿哈特殊被动",