feat: upgrade character build modal with skills info, traces, energy, and lightcones
This commit is contained in:
1 parent
edc2843f0c
commit
42b6d6a5a9
4 files changed
+761
-106
No files matched your search
+158
@@ -966,6 +966,164 @@ input:checked + .slider:before {
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* Modal Sub-tabs */
|
||||
.modal-subtabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.modal-subtab-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
padding: 0.45rem 0.95rem;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.modal-subtab-btn:hover {
|
||||
color: var(--text-highlight);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.modal-subtab-btn.active {
|
||||
color: #fff;
|
||||
background: var(--accent-blue);
|
||||
box-shadow: 0 0 12px rgba(2, 132, 199, 0.45);
|
||||
}
|
||||
|
||||
/* Skill Stepper & Row Cards (Inspired by srtools.punklorde.org/skills-info) */
|
||||
.skill-row-card {
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.75rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.skill-row-card:hover {
|
||||
border-color: rgba(56, 189, 248, 0.4);
|
||||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.1);
|
||||
}
|
||||
|
||||
.skill-row-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.skill-name {
|
||||
font-weight: 700;
|
||||
font-size: 0.925rem;
|
||||
color: var(--text-highlight);
|
||||
}
|
||||
|
||||
.skill-lvl-badge {
|
||||
font-size: 0.85rem;
|
||||
font-family: monospace;
|
||||
color: var(--accent-cyan);
|
||||
background: rgba(56, 189, 248, 0.12);
|
||||
padding: 0.2rem 0.55rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(56, 189, 248, 0.35);
|
||||
}
|
||||
|
||||
.skill-stepper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-step {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color);
|
||||
background: var(--bg-card);
|
||||
color: #fff;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.btn-step:hover {
|
||||
border-color: var(--accent-cyan);
|
||||
background: rgba(56, 189, 248, 0.25);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.btn-max-pill {
|
||||
padding: 0.25rem 0.65rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(251, 191, 36, 0.5);
|
||||
background: rgba(251, 191, 36, 0.15);
|
||||
color: var(--accent-gold);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.btn-max-pill:hover {
|
||||
background: rgba(251, 191, 36, 0.35);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.skill-range {
|
||||
flex: 1;
|
||||
accent-color: var(--accent-cyan);
|
||||
cursor: pointer;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.trace-toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-main);
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 0.55rem 0.85rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.trace-toggle-label:hover {
|
||||
border-color: var(--accent-cyan);
|
||||
background: rgba(56, 189, 248, 0.08);
|
||||
}
|
||||
|
||||
.trace-checkbox {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
accent-color: var(--accent-cyan);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lc-super-pip {
|
||||
width: 40px;
|
||||
height: 36px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.lineup-grid {
|
||||
|
||||
+203
-52
@@ -115,7 +115,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="form-label">พิกัดตัวละคร (X, Y, Z)</span>
|
||||
<span class="form-label">พิกัดตัวละคร XYZ</span>
|
||||
<span id="lbl-coordinates" style="color: var(--text-muted); font-size: 0.85rem;">-</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -380,7 +380,8 @@
|
||||
|
||||
<!-- Character Detail / Build Modal -->
|
||||
<div class="modal-overlay" id="modal-avatar-detail">
|
||||
<div class="modal-content" style="max-width: 640px;">
|
||||
<div class="modal-content" style="max-width: 760px;">
|
||||
<!-- Modal Header -->
|
||||
<div class="modal-header">
|
||||
<h3 id="modal-char-title" style="font-weight: 800; font-size: 1.15rem; color: var(--text-highlight);">
|
||||
ปรับแต่งตัวละคร
|
||||
@@ -388,75 +389,225 @@
|
||||
<button class="btn btn-sm btn-secondary" id="btn-close-avatar-modal">✕</button>
|
||||
</div>
|
||||
|
||||
<!-- Character Top Banner & Quick Max All -->
|
||||
<div style="display: flex; gap: 1.25rem; align-items: center; margin-bottom: 1.25rem; flex-wrap: wrap;">
|
||||
<div class="avatar-image-container" style="width: 76px; height: 76px;">
|
||||
<img id="modal-char-img" class="avatar-img" src="" alt="">
|
||||
</div>
|
||||
<div style="flex: 1; min-width: 180px;">
|
||||
<div id="modal-char-name" style="font-size: 1.25rem; font-weight: 800;"></div>
|
||||
<div id="modal-char-meta" style="font-size: 0.85rem; color: var(--text-muted); margin-top: 0.25rem; display: flex; gap: 0.35rem; align-items: center; flex-wrap: wrap;"></div>
|
||||
<div id="modal-char-meta" style="font-size: 0.85rem; color: var(--text-muted); margin-top: 0.25rem; display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap;"></div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-gold btn-sm" id="btn-modal-max-all-char">
|
||||
<button type="button" class="btn btn-gold" id="btn-modal-max-all-char" style="padding: 0.5rem 1rem; font-weight: 800;">
|
||||
⭐ ปรับเต็มทั้งหมด
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid-2" style="gap: 1rem; margin-bottom: 1rem;">
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">ระดับเลเวล (1-80)</label>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="1" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">1</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="20" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">20</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="60" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">60</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="70" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">70</button>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-lvl-preset" data-lvl="80" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">80</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" id="modal-char-level" class="form-input" min="1" max="80" value="80">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">การเลื่อนขั้น (0-6)</label>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-prom-preset" data-prom="0" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">0</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-prom-preset" data-prom="3" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">3</button>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-prom-preset" data-prom="6" style="padding: 0.15rem 0.45rem; font-size: 0.75rem;">6</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" id="modal-char-promotion" class="form-input" min="0" max="6" value="6">
|
||||
</div>
|
||||
<!-- Sub Tabs Navigation (srtools style) -->
|
||||
<div class="modal-subtabs" style="display: flex; gap: 0.5rem; margin-bottom: 1.25rem; border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; overflow-x: auto;">
|
||||
<button type="button" class="modal-subtab-btn active" data-modaltab="modaltab-skills">
|
||||
⚡ สกิลและร่องรอย
|
||||
</button>
|
||||
<button type="button" class="modal-subtab-btn" data-modaltab="modaltab-status">
|
||||
⚙️ เลเวล & Eidolon
|
||||
</button>
|
||||
<button type="button" class="modal-subtab-btn" data-modaltab="modaltab-lightcone">
|
||||
🗡️ กรวยแสง
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">ระดับ Eidolon</label>
|
||||
<div style="display: flex; gap: 0.4rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="btn-modal-quick-e0" style="padding: 0.2rem 0.6rem; font-size: 0.75rem;">
|
||||
รีเซ็ต E0
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-gold" id="btn-modal-quick-e6" style="padding: 0.2rem 0.6rem; font-size: 0.75rem;">
|
||||
⭐ ปลดเต็ม E6
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eidolon-buttons" id="modal-char-eidolon-btns" style="margin-top: 0.5rem; gap: 0.5rem;">
|
||||
<!-- Injected pips -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top: 1rem;">
|
||||
<label class="form-label">สกิลและร่องรอย</label>
|
||||
<div style="background: rgba(0,0,0,0.3); border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: 0.75rem; display: flex; justify-content: space-between; align-items: center;">
|
||||
<div style="font-size: 0.85rem; color: var(--text-muted);">
|
||||
โจมตีปกติเลเวล 6, สกิลต่อสู้เลเวล 10, ท่าไม้ตายเลเวล 10, พรสวรรค์เลเวล 10 และโหนดเสริมทั้งหมด
|
||||
<!-- Sub Tab 1: Skills & Traces (Like https://srtools.punklorde.org/skills-info) -->
|
||||
<div id="modaltab-skills" class="modal-subtab-content active">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<div style="font-weight: 700; font-size: 0.95rem; color: var(--text-highlight);">
|
||||
ระดับสกิลต่อสู้
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-gold" id="btn-modal-max-skills">
|
||||
⭐ อัพสกิล 10/10
|
||||
⭐ ปรับสกิลเต็ม 10/10
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 4 Core Skills -->
|
||||
<div class="skills-grid" style="display: flex; flex-direction: column; gap: 0.75rem;">
|
||||
<!-- 1. Basic Attack -->
|
||||
<div class="skill-row-card">
|
||||
<div class="skill-row-header">
|
||||
<span class="skill-name">โจมตีปกติ</span>
|
||||
<span class="skill-lvl-badge">Lv. <strong id="val-skill-basic">6</strong> / 6</span>
|
||||
</div>
|
||||
<div class="skill-stepper">
|
||||
<button type="button" class="btn-step" id="btn-step-dec-basic">-</button>
|
||||
<input type="range" min="1" max="6" value="6" id="range-skill-basic" class="skill-range">
|
||||
<button type="button" class="btn-step" id="btn-step-inc-basic">+</button>
|
||||
<button type="button" class="btn-max-pill" id="btn-skill-max-basic">MAX</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Skill -->
|
||||
<div class="skill-row-card">
|
||||
<div class="skill-row-header">
|
||||
<span class="skill-name">สกิลต่อสู้</span>
|
||||
<span class="skill-lvl-badge">Lv. <strong id="val-skill-skill">10</strong> / 10</span>
|
||||
</div>
|
||||
<div class="skill-stepper">
|
||||
<button type="button" class="btn-step" id="btn-step-dec-skill">-</button>
|
||||
<input type="range" min="1" max="10" value="10" id="range-skill-skill" class="skill-range">
|
||||
<button type="button" class="btn-step" id="btn-step-inc-skill">+</button>
|
||||
<button type="button" class="btn-max-pill" id="btn-skill-max-skill">MAX</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Ultimate -->
|
||||
<div class="skill-row-card">
|
||||
<div class="skill-row-header">
|
||||
<span class="skill-name">ท่าไม้ตาย</span>
|
||||
<span class="skill-lvl-badge">Lv. <strong id="val-skill-ultra">10</strong> / 10</span>
|
||||
</div>
|
||||
<div class="skill-stepper">
|
||||
<button type="button" class="btn-step" id="btn-step-dec-ultra">-</button>
|
||||
<input type="range" min="1" max="10" value="10" id="range-skill-ultra" class="skill-range">
|
||||
<button type="button" class="btn-step" id="btn-step-inc-ultra">+</button>
|
||||
<button type="button" class="btn-max-pill" id="btn-skill-max-ultra">MAX</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Talent -->
|
||||
<div class="skill-row-card">
|
||||
<div class="skill-row-header">
|
||||
<span class="skill-name">พรสวรรค์</span>
|
||||
<span class="skill-lvl-badge">Lv. <strong id="val-skill-talent">10</strong> / 10</span>
|
||||
</div>
|
||||
<div class="skill-stepper">
|
||||
<button type="button" class="btn-step" id="btn-step-dec-talent">-</button>
|
||||
<input type="range" min="1" max="10" value="10" id="range-skill-talent" class="skill-range">
|
||||
<button type="button" class="btn-step" id="btn-step-inc-talent">+</button>
|
||||
<button type="button" class="btn-max-pill" id="btn-skill-max-talent">MAX</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Traces & Technique Section -->
|
||||
<div style="margin-top: 1.25rem;">
|
||||
<div style="font-weight: 700; font-size: 0.95rem; color: var(--text-highlight); margin-bottom: 0.75rem;">
|
||||
ร่องรอยและเทคนิค
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 0.6rem;">
|
||||
<label class="trace-toggle-label">
|
||||
<input type="checkbox" id="check-trace-technique" checked class="trace-checkbox">
|
||||
<span>⚡ เทคนิค — เปิดใช้งานก่อนเริ่มการต่อสู้</span>
|
||||
</label>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.6rem;">
|
||||
<label class="trace-toggle-label">
|
||||
<input type="checkbox" id="check-trace-a2" checked class="trace-checkbox">
|
||||
<span>🌟 ร่องรอยพิเศษ A2</span>
|
||||
</label>
|
||||
<label class="trace-toggle-label">
|
||||
<input type="checkbox" id="check-trace-a4" checked class="trace-checkbox">
|
||||
<span>🌟 ร่องรอยพิเศษ A4</span>
|
||||
</label>
|
||||
<label class="trace-toggle-label">
|
||||
<input type="checkbox" id="check-trace-a6" checked class="trace-checkbox">
|
||||
<span>🌟 ร่องรอยพิเศษ A6</span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="trace-toggle-label">
|
||||
<input type="checkbox" id="check-trace-stats" checked class="trace-checkbox">
|
||||
<span>💎 โหนดสเตตัสเสริมทั้งหมด (10 โหนด)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sub Tab 2: Status & Eidolon -->
|
||||
<div id="modaltab-status" class="modal-subtab-content" style="display: none;">
|
||||
<div class="grid-2" style="gap: 1rem; margin-bottom: 1rem;">
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">ระดับเลเวล (1-80)</label>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="1">1</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="20">20</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="60">60</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-lvl-preset" data-lvl="70">70</button>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-lvl-preset" data-lvl="80">80</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" id="modal-char-level" class="form-input" min="1" max="80" value="80">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">การเลื่อนขั้น (0-6)</label>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-prom-preset" data-prom="0">0</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary btn-prom-preset" data-prom="3">3</button>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-prom-preset" data-prom="6">6</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="number" id="modal-char-promotion" class="form-input" min="0" max="6" value="6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Eidolon Section -->
|
||||
<div class="form-group">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">ระดับ Eidolon</label>
|
||||
<div style="display: flex; gap: 0.4rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="btn-modal-quick-e0">รีเซ็ต E0</button>
|
||||
<button type="button" class="btn btn-sm btn-gold" id="btn-modal-quick-e6">⭐ ปลดเต็ม E6</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eidolon-buttons" id="modal-char-eidolon-btns" style="margin-top: 0.5rem; gap: 0.5rem;">
|
||||
<!-- Injected pips -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimate Energy Section -->
|
||||
<div class="form-group" style="margin-top: 1.25rem;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem;">
|
||||
<label class="form-label" style="margin: 0;">พลังงานท่าไม้ตาย</label>
|
||||
<div style="display: flex; gap: 0.35rem;">
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="btn-modal-energy-50">ตั้งค่า 50%</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="btn-modal-energy-100">เต็ม 100%</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.75rem; align-items: center;">
|
||||
<input type="range" id="range-char-energy" min="0" max="140" value="140" class="skill-range" style="flex: 1;">
|
||||
<span id="val-char-energy" style="font-weight: 800; font-family: monospace; min-width: 60px; text-align: right;">140/140</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sub Tab 3: Lightcone Equipment -->
|
||||
<div id="modaltab-lightcone" class="modal-subtab-content" style="display: none;">
|
||||
<div class="card" style="padding: 1.25rem; background: rgba(0,0,0,0.3); border: 1px solid var(--border-color);">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<span style="font-weight: 800; font-size: 1rem; color: var(--text-highlight);">กรวยแสงที่สวมใส่</span>
|
||||
<button type="button" class="btn btn-sm btn-gold" id="btn-modal-max-lc">⭐ ปรับกรวยแสงเต็มขั้น</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">ID กรวยแสง</label>
|
||||
<input type="number" id="modal-lc-id" class="form-input" placeholder="เช่น 21002 หรือ 24001">
|
||||
</div>
|
||||
<div class="grid-2" style="gap: 1rem;">
|
||||
<div class="form-group">
|
||||
<label class="form-label">เลเวลกรวยแสง (1-80)</label>
|
||||
<input type="number" id="modal-lc-level" class="form-input" min="1" max="80" value="80">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">ระดับการขัดเกลา</label>
|
||||
<div class="eidolon-buttons" id="modal-lc-rank-btns" style="margin-top: 0.25rem;">
|
||||
<button type="button" class="eidolon-pip lc-super-pip" data-rank="1">S1</button>
|
||||
<button type="button" class="eidolon-pip lc-super-pip" data-rank="2">S2</button>
|
||||
<button type="button" class="eidolon-pip lc-super-pip" data-rank="3">S3</button>
|
||||
<button type="button" class="eidolon-pip lc-super-pip" data-rank="4">S4</button>
|
||||
<button type="button" class="eidolon-pip lc-super-pip active" data-rank="5">S5</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Footer -->
|
||||
<div style="display: flex; gap: 0.75rem; margin-top: 1.5rem; justify-content: flex-end; flex-wrap: wrap;">
|
||||
<button type="button" class="btn btn-secondary" id="btn-modal-cancel-avatar">
|
||||
✕ ปิด
|
||||
|
||||
@@ -106,6 +106,47 @@ const el = {
|
||||
btnModalMaxSkills: document.getElementById('btn-modal-max-skills'),
|
||||
btnModalSaveAvatar: document.getElementById('btn-modal-save-avatar'),
|
||||
|
||||
// Skills Info Steppers (Inspired by srtools.punklorde.org/skills-info)
|
||||
valSkillBasic: document.getElementById('val-skill-basic'),
|
||||
rangeSkillBasic: document.getElementById('range-skill-basic'),
|
||||
btnStepDecBasic: document.getElementById('btn-step-dec-basic'),
|
||||
btnStepIncBasic: document.getElementById('btn-step-inc-basic'),
|
||||
btnSkillMaxBasic: document.getElementById('btn-skill-max-basic'),
|
||||
|
||||
valSkillSkill: document.getElementById('val-skill-skill'),
|
||||
rangeSkillSkill: document.getElementById('range-skill-skill'),
|
||||
btnStepDecSkill: document.getElementById('btn-step-dec-skill'),
|
||||
btnStepIncSkill: document.getElementById('btn-step-inc-skill'),
|
||||
btnSkillMaxSkill: document.getElementById('btn-skill-max-skill'),
|
||||
|
||||
valSkillUltra: document.getElementById('val-skill-ultra'),
|
||||
rangeSkillUltra: document.getElementById('range-skill-ultra'),
|
||||
btnStepDecUltra: document.getElementById('btn-step-dec-ultra'),
|
||||
btnStepIncUltra: document.getElementById('btn-step-inc-ultra'),
|
||||
btnSkillMaxUltra: document.getElementById('btn-skill-max-ultra'),
|
||||
|
||||
valSkillTalent: document.getElementById('val-skill-talent'),
|
||||
rangeSkillTalent: document.getElementById('range-skill-talent'),
|
||||
btnStepDecTalent: document.getElementById('btn-step-dec-talent'),
|
||||
btnStepIncTalent: document.getElementById('btn-step-inc-talent'),
|
||||
btnSkillMaxTalent: document.getElementById('btn-skill-max-talent'),
|
||||
|
||||
checkTraceTechnique: document.getElementById('check-trace-technique'),
|
||||
checkTraceA2: document.getElementById('check-trace-a2'),
|
||||
checkTraceA4: document.getElementById('check-trace-a4'),
|
||||
checkTraceA6: document.getElementById('check-trace-a6'),
|
||||
checkTraceStats: document.getElementById('check-trace-stats'),
|
||||
|
||||
btnModalEnergy50: document.getElementById('btn-modal-energy-50'),
|
||||
btnModalEnergy100: document.getElementById('btn-modal-energy-100'),
|
||||
rangeCharEnergy: document.getElementById('range-char-energy'),
|
||||
valCharEnergy: document.getElementById('val-char-energy'),
|
||||
|
||||
modalLcId: document.getElementById('modal-lc-id'),
|
||||
modalLcLevel: document.getElementById('modal-lc-level'),
|
||||
btnModalMaxLc: document.getElementById('btn-modal-max-lc'),
|
||||
modalLcRankBtns: document.getElementById('modal-lc-rank-btns'),
|
||||
|
||||
// Lineup Picker Modal
|
||||
modalLineupPicker: document.getElementById('modal-lineup-picker'),
|
||||
btnClosePickerModal: document.getElementById('btn-close-picker-modal'),
|
||||
@@ -671,15 +712,52 @@ async function maxSingleAvatar(avatarId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Character Detail / Build Modal
|
||||
// Active Lightcone Superimposition Rank in Modal (1 - 5)
|
||||
let selectedLcRank = 5;
|
||||
|
||||
// Modal Sub-tabs Handler
|
||||
function setupModalSubtabs() {
|
||||
const tabBtns = document.querySelectorAll('.modal-subtab-btn');
|
||||
tabBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
tabBtns.forEach(b => b.classList.remove('active'));
|
||||
document.querySelectorAll('.modal-subtab-content').forEach(c => {
|
||||
c.style.display = 'none';
|
||||
c.classList.remove('active');
|
||||
});
|
||||
btn.classList.add('active');
|
||||
const targetId = btn.getAttribute('data-modaltab');
|
||||
const targetContent = document.getElementById(targetId);
|
||||
if (targetContent) {
|
||||
targetContent.style.display = 'block';
|
||||
targetContent.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateLcRankPips(rank) {
|
||||
selectedLcRank = rank;
|
||||
document.querySelectorAll('.lc-super-pip').forEach(btn => {
|
||||
const r = parseInt(btn.getAttribute('data-rank'), 10);
|
||||
if (r === rank) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Character Detail / Build Modal (Inspired by srtools.punklorde.org/skills-info)
|
||||
function openAvatarDetailModal(avatarItem) {
|
||||
state.selectedAvatarDetail = avatarItem;
|
||||
const meta = getAvatarInfo(avatarItem.avatar_id);
|
||||
const aid = avatarItem.avatar_id;
|
||||
const meta = getAvatarInfo(aid);
|
||||
|
||||
el.modalCharTitle.textContent = `ปรับแต่งตัวละคร: ${meta.name} (ID: ${avatarItem.avatar_id})`;
|
||||
el.modalCharImg.src = `./assets/avatars/${avatarItem.avatar_id}.png`;
|
||||
el.modalCharTitle.textContent = `ปรับแต่งตัวละคร: ${meta.name} (ID: ${aid})`;
|
||||
el.modalCharImg.src = `./assets/avatars/${aid}.png`;
|
||||
el.modalCharImg.onerror = function() {
|
||||
this.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${avatarItem.avatar_id}.png`;
|
||||
this.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${aid}.png`;
|
||||
};
|
||||
el.modalCharName.textContent = meta.name;
|
||||
el.modalCharMeta.innerHTML = `
|
||||
@@ -688,10 +766,97 @@ function openAvatarDetailModal(avatarItem) {
|
||||
<span class="avatar-badge-rarity rarity-${meta.rarity}" style="position: static;">★ ${meta.rarity}</span>
|
||||
`;
|
||||
|
||||
// Reset to first sub-tab (Skills & Traces)
|
||||
document.querySelectorAll('.modal-subtab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelectorAll('.modal-subtab-content').forEach(c => {
|
||||
c.style.display = 'none';
|
||||
c.classList.remove('active');
|
||||
});
|
||||
const firstTabBtn = document.querySelector('.modal-subtab-btn[data-modaltab="modaltab-skills"]');
|
||||
if (firstTabBtn) firstTabBtn.classList.add('active');
|
||||
const firstTabContent = document.getElementById('modaltab-skills');
|
||||
if (firstTabContent) {
|
||||
firstTabContent.style.display = 'block';
|
||||
firstTabContent.classList.add('active');
|
||||
}
|
||||
|
||||
// Basic Status
|
||||
el.modalCharLevel.value = avatarItem.level || 80;
|
||||
el.modalCharPromotion.value = avatarItem.promotion || 6;
|
||||
|
||||
renderModalEidolonPips(avatarItem.rank ?? 6);
|
||||
|
||||
// Load avatar data from fullFreesrData if available
|
||||
const av = state.fullFreesrData?.avatars?.[aid];
|
||||
const skills = av?.data?.skills || {};
|
||||
const anchorSkills = av?.data?.skills_by_anchor_type || {};
|
||||
|
||||
// 1. Basic Attack: anchor 1 or aid + '001' (1-6)
|
||||
const basicVal = anchorSkills['1'] ?? skills[`${aid}001`] ?? 6;
|
||||
if (el.rangeSkillBasic && el.valSkillBasic) {
|
||||
el.rangeSkillBasic.value = Math.min(6, Math.max(1, basicVal));
|
||||
el.valSkillBasic.textContent = el.rangeSkillBasic.value;
|
||||
}
|
||||
|
||||
// 2. Skill: anchor 2 or aid + '002' (1-10)
|
||||
const skillVal = anchorSkills['2'] ?? skills[`${aid}002`] ?? 10;
|
||||
if (el.rangeSkillSkill && el.valSkillSkill) {
|
||||
el.rangeSkillSkill.value = Math.min(10, Math.max(1, skillVal));
|
||||
el.valSkillSkill.textContent = el.rangeSkillSkill.value;
|
||||
}
|
||||
|
||||
// 3. Ultimate: anchor 3 or aid + '003' (1-10)
|
||||
const ultraVal = anchorSkills['3'] ?? skills[`${aid}003`] ?? 10;
|
||||
if (el.rangeSkillUltra && el.valSkillUltra) {
|
||||
el.rangeSkillUltra.value = Math.min(10, Math.max(1, ultraVal));
|
||||
el.valSkillUltra.textContent = el.rangeSkillUltra.value;
|
||||
}
|
||||
|
||||
// 4. Talent: anchor 4 or aid + '004' (1-10)
|
||||
const talentVal = anchorSkills['4'] ?? skills[`${aid}004`] ?? 10;
|
||||
if (el.rangeSkillTalent && el.valSkillTalent) {
|
||||
el.rangeSkillTalent.value = Math.min(10, Math.max(1, talentVal));
|
||||
el.valSkillTalent.textContent = el.rangeSkillTalent.value;
|
||||
}
|
||||
|
||||
// Traces & Technique
|
||||
if (el.checkTraceTechnique) {
|
||||
el.checkTraceTechnique.checked = (anchorSkills['5'] ?? skills[`${aid}007`] ?? 1) > 0;
|
||||
}
|
||||
if (el.checkTraceA2) {
|
||||
el.checkTraceA2.checked = (anchorSkills['6'] ?? skills[`${aid}101`] ?? 1) > 0;
|
||||
}
|
||||
if (el.checkTraceA4) {
|
||||
el.checkTraceA4.checked = (anchorSkills['7'] ?? skills[`${aid}102`] ?? 1) > 0;
|
||||
}
|
||||
if (el.checkTraceA6) {
|
||||
el.checkTraceA6.checked = (anchorSkills['8'] ?? skills[`${aid}103`] ?? 1) > 0;
|
||||
}
|
||||
if (el.checkTraceStats) {
|
||||
el.checkTraceStats.checked = (anchorSkills['9'] ?? skills[`${aid}201`] ?? 1) > 0;
|
||||
}
|
||||
|
||||
// Ultimate Energy
|
||||
const maxSp = av?.sp_max ?? 140;
|
||||
const currentSp = av?.sp_value ?? maxSp;
|
||||
if (el.rangeCharEnergy && el.valCharEnergy) {
|
||||
el.rangeCharEnergy.max = maxSp;
|
||||
el.rangeCharEnergy.value = currentSp;
|
||||
el.valCharEnergy.textContent = `${currentSp}/${maxSp}`;
|
||||
}
|
||||
|
||||
// Lightcone
|
||||
const equippedLc = (state.fullFreesrData?.lightcones || []).find(lc => lc.equip_avatar === aid);
|
||||
if (equippedLc) {
|
||||
if (el.modalLcId) el.modalLcId.value = equippedLc.item_id || '';
|
||||
if (el.modalLcLevel) el.modalLcLevel.value = equippedLc.level || 80;
|
||||
selectedLcRank = equippedLc.rank || 5;
|
||||
} else {
|
||||
if (el.modalLcId) el.modalLcId.value = '';
|
||||
if (el.modalLcLevel) el.modalLcLevel.value = 80;
|
||||
selectedLcRank = 5;
|
||||
}
|
||||
updateLcRankPips(selectedLcRank);
|
||||
|
||||
el.modalAvatarDetail.classList.add('active');
|
||||
}
|
||||
|
||||
@@ -746,6 +911,63 @@ async function setAvatarRank(avatarId, rank) {
|
||||
}
|
||||
}
|
||||
|
||||
// Max Skills & Traces for character
|
||||
function maxSkillsForAvatar() {
|
||||
if (el.rangeSkillBasic && el.valSkillBasic) {
|
||||
el.rangeSkillBasic.value = 6;
|
||||
el.valSkillBasic.textContent = 6;
|
||||
}
|
||||
if (el.rangeSkillSkill && el.valSkillSkill) {
|
||||
el.rangeSkillSkill.value = 10;
|
||||
el.valSkillSkill.textContent = 10;
|
||||
}
|
||||
if (el.rangeSkillUltra && el.valSkillUltra) {
|
||||
el.rangeSkillUltra.value = 10;
|
||||
el.valSkillUltra.textContent = 10;
|
||||
}
|
||||
if (el.rangeSkillTalent && el.valSkillTalent) {
|
||||
el.rangeSkillTalent.value = 10;
|
||||
el.valSkillTalent.textContent = 10;
|
||||
}
|
||||
if (el.checkTraceTechnique) el.checkTraceTechnique.checked = true;
|
||||
if (el.checkTraceA2) el.checkTraceA2.checked = true;
|
||||
if (el.checkTraceA4) el.checkTraceA4.checked = true;
|
||||
if (el.checkTraceA6) el.checkTraceA6.checked = true;
|
||||
if (el.checkTraceStats) el.checkTraceStats.checked = true;
|
||||
|
||||
showToast('⭐ ปรับสกิลและร่องรอยเต็มทั้งหมดแล้ว', 'info');
|
||||
}
|
||||
|
||||
// Max All Build For Current Avatar (Lv.80, Prom 6, E6, Max Skills, Max Traces, 100% Energy, LC Lv.80 S5)
|
||||
async function maxAllBuildForCurrentAvatar() {
|
||||
if (!state.selectedAvatarDetail) return;
|
||||
const aid = state.selectedAvatarDetail.avatar_id;
|
||||
|
||||
// Level & Eidolon
|
||||
el.modalCharLevel.value = 80;
|
||||
el.modalCharPromotion.value = 6;
|
||||
state.selectedAvatarDetail.level = 80;
|
||||
state.selectedAvatarDetail.promotion = 6;
|
||||
state.selectedAvatarDetail.rank = 6;
|
||||
renderModalEidolonPips(6);
|
||||
|
||||
// Skills & Traces
|
||||
maxSkillsForAvatar();
|
||||
|
||||
// Energy 100%
|
||||
if (el.rangeCharEnergy && el.valCharEnergy) {
|
||||
el.rangeCharEnergy.value = el.rangeCharEnergy.max;
|
||||
el.valCharEnergy.textContent = `${el.rangeCharEnergy.max}/${el.rangeCharEnergy.max}`;
|
||||
}
|
||||
|
||||
// Lightcone
|
||||
if (el.modalLcLevel) el.modalLcLevel.value = 80;
|
||||
updateLcRankPips(5);
|
||||
|
||||
// Save changes
|
||||
await saveAvatarDetails();
|
||||
}
|
||||
|
||||
// Save avatar details from modal
|
||||
async function saveAvatarDetails() {
|
||||
if (!state.selectedAvatarDetail) return;
|
||||
@@ -765,55 +987,133 @@ async function saveAvatarDetails() {
|
||||
target.rank = rank;
|
||||
}
|
||||
|
||||
if (state.fullFreesrData?.avatars?.[aid]) {
|
||||
state.fullFreesrData.avatars[aid].level = level;
|
||||
state.fullFreesrData.avatars[aid].promotion = promotion;
|
||||
state.fullFreesrData.avatars[aid].data.rank = rank;
|
||||
if (!state.fullFreesrData) {
|
||||
state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
|
||||
}
|
||||
if (!state.fullFreesrData.avatars) {
|
||||
state.fullFreesrData.avatars = {};
|
||||
}
|
||||
if (!state.fullFreesrData.avatars[aid]) {
|
||||
state.fullFreesrData.avatars[aid] = {
|
||||
owner_uid: 0,
|
||||
avatar_id: aid,
|
||||
level,
|
||||
promotion,
|
||||
techniques: [],
|
||||
sp_value: parseInt(el.rangeCharEnergy?.value, 10) || 140,
|
||||
sp_max: parseInt(el.rangeCharEnergy?.max, 10) || 140,
|
||||
data: {
|
||||
rank,
|
||||
skills: {},
|
||||
skills_by_anchor_type: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const av = state.fullFreesrData.avatars[aid];
|
||||
av.level = level;
|
||||
av.promotion = promotion;
|
||||
av.data = av.data || {};
|
||||
av.data.rank = rank;
|
||||
if (el.rangeCharEnergy) {
|
||||
av.sp_value = parseInt(el.rangeCharEnergy.value, 10) || av.sp_max || 140;
|
||||
}
|
||||
|
||||
// Extract skills values
|
||||
const basicVal = parseInt(el.rangeSkillBasic?.value, 10) || 6;
|
||||
const skillVal = parseInt(el.rangeSkillSkill?.value, 10) || 10;
|
||||
const ultraVal = parseInt(el.rangeSkillUltra?.value, 10) || 10;
|
||||
const talentVal = parseInt(el.rangeSkillTalent?.value, 10) || 10;
|
||||
const techVal = el.checkTraceTechnique?.checked ? 1 : 0;
|
||||
const a2Val = el.checkTraceA2?.checked ? 1 : 0;
|
||||
const a4Val = el.checkTraceA4?.checked ? 1 : 0;
|
||||
const a6Val = el.checkTraceA6?.checked ? 1 : 0;
|
||||
const statVal = el.checkTraceStats?.checked ? 1 : 0;
|
||||
|
||||
av.data.skills = av.data.skills || {};
|
||||
av.data.skills[`${aid}001`] = basicVal;
|
||||
av.data.skills[`${aid}002`] = skillVal;
|
||||
av.data.skills[`${aid}003`] = ultraVal;
|
||||
av.data.skills[`${aid}004`] = talentVal;
|
||||
av.data.skills[`${aid}007`] = techVal;
|
||||
av.data.skills[`${aid}101`] = a2Val;
|
||||
av.data.skills[`${aid}102`] = a4Val;
|
||||
av.data.skills[`${aid}103`] = a6Val;
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
const pad = i < 10 ? `0${i}` : `${i}`;
|
||||
av.data.skills[`${aid}2${pad}`] = statVal;
|
||||
}
|
||||
|
||||
av.data.skills_by_anchor_type = {
|
||||
"1": basicVal,
|
||||
"2": skillVal,
|
||||
"3": ultraVal,
|
||||
"4": talentVal,
|
||||
"5": techVal,
|
||||
"6": a2Val,
|
||||
"7": a4Val,
|
||||
"8": a6Val,
|
||||
"9": statVal,
|
||||
"10": statVal,
|
||||
"11": statVal,
|
||||
"12": statVal,
|
||||
"13": statVal,
|
||||
"14": statVal,
|
||||
"15": statVal,
|
||||
"16": statVal,
|
||||
"17": statVal,
|
||||
"18": statVal
|
||||
};
|
||||
|
||||
// Lightcone update
|
||||
state.fullFreesrData.lightcones = state.fullFreesrData.lightcones || [];
|
||||
const lcId = parseInt(el.modalLcId?.value, 10);
|
||||
const lcLevel = parseInt(el.modalLcLevel?.value, 10) || 80;
|
||||
const lcRank = selectedLcRank || 5;
|
||||
const lcProm = lcLevel > 70 ? 6 : (lcLevel > 60 ? 5 : (lcLevel > 50 ? 4 : (lcLevel > 40 ? 3 : (lcLevel > 20 ? 1 : 0))));
|
||||
|
||||
const existingLcIndex = state.fullFreesrData.lightcones.findIndex(lc => lc.equip_avatar === aid);
|
||||
if (lcId && lcId > 0) {
|
||||
if (existingLcIndex !== -1) {
|
||||
state.fullFreesrData.lightcones[existingLcIndex].item_id = lcId;
|
||||
state.fullFreesrData.lightcones[existingLcIndex].level = lcLevel;
|
||||
state.fullFreesrData.lightcones[existingLcIndex].rank = lcRank;
|
||||
state.fullFreesrData.lightcones[existingLcIndex].promotion = lcProm;
|
||||
} else {
|
||||
state.fullFreesrData.lightcones.push({
|
||||
internal_uid: state.fullFreesrData.lightcones.length,
|
||||
item_id: lcId,
|
||||
equip_avatar: aid,
|
||||
level: lcLevel,
|
||||
rank: lcRank,
|
||||
promotion: lcProm
|
||||
});
|
||||
}
|
||||
} else if (existingLcIndex !== -1) {
|
||||
state.fullFreesrData.lightcones[existingLcIndex].equip_avatar = null;
|
||||
}
|
||||
|
||||
renderAvatarsGrid();
|
||||
loadEquipment();
|
||||
el.modalAvatarDetail.classList.remove('active');
|
||||
|
||||
if (state.isConnected) {
|
||||
try {
|
||||
if (state.serverMode === 'gac') {
|
||||
await apiCall('/api/freesr/avatars', 'POST', { avatar_id: aid, level, promotion, rank });
|
||||
await saveFreesrChangesToServer();
|
||||
} else {
|
||||
await saveFreesrChangesToServer();
|
||||
}
|
||||
showToast(`บันทึกตัวละคร ${getAvatarInfo(aid).name} เรียบร้อย (Lv. ${level}, Prom. ${promotion}, E${rank})`, 'success');
|
||||
showToast(`บันทึกตัวละคร ${getAvatarInfo(aid).name} เรียบร้อย`, 'success');
|
||||
} catch (err) {
|
||||
showToast(`บันทึกในเครื่องแล้ว (ส่งเซิร์ฟเวอร์ไม่ได้: ${err.message})`, 'warning');
|
||||
}
|
||||
} else {
|
||||
showToast(`บันทึกตัวละคร ${getAvatarInfo(aid).name} เรียบร้อย (Lv. ${level}, Prom. ${promotion}, E${rank})`, 'success');
|
||||
showToast(`บันทึกตัวละคร ${getAvatarInfo(aid).name} เรียบร้อย`, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
// Max Skills & Traces for character
|
||||
async function maxSkillsForAvatar() {
|
||||
if (!state.selectedAvatarDetail) return;
|
||||
const aid = state.selectedAvatarDetail.avatar_id;
|
||||
el.modalCharLevel.value = 80;
|
||||
el.modalCharPromotion.value = 6;
|
||||
state.selectedAvatarDetail.level = 80;
|
||||
state.selectedAvatarDetail.promotion = 6;
|
||||
state.selectedAvatarDetail.rank = 6;
|
||||
renderModalEidolonPips(6);
|
||||
|
||||
if (state.fullFreesrData?.avatars?.[aid]) {
|
||||
const av = state.fullFreesrData.avatars[aid];
|
||||
av.level = 80;
|
||||
av.promotion = 6;
|
||||
av.data.rank = 6;
|
||||
if (av.data.skills) {
|
||||
for (const k in av.data.skills) av.data.skills[k] = 10;
|
||||
}
|
||||
}
|
||||
|
||||
showToast(`อัพเลเวล 80, เลื่อนขั้น 6, E6 และ Traces 10/10 สำหรับ ${getAvatarInfo(aid).name} สำเร็จ!`, 'success');
|
||||
}
|
||||
|
||||
// Max all avatars
|
||||
async function maxAllAvatars() {
|
||||
if (!confirm(t('confirmMaxAll'))) return;
|
||||
@@ -1184,17 +1484,62 @@ function setupEvents() {
|
||||
document.getElementById('btn-modal-cancel-avatar')?.addEventListener('click', () => el.modalAvatarDetail.classList.remove('active'));
|
||||
el.btnModalSaveAvatar.addEventListener('click', saveAvatarDetails);
|
||||
el.btnModalMaxSkills.addEventListener('click', maxSkillsForAvatar);
|
||||
document.getElementById('btn-modal-max-all-char')?.addEventListener('click', maxAllBuildForCurrentAvatar);
|
||||
|
||||
document.getElementById('btn-modal-max-all-char')?.addEventListener('click', async () => {
|
||||
if (!state.selectedAvatarDetail) return;
|
||||
el.modalCharLevel.value = 80;
|
||||
el.modalCharPromotion.value = 6;
|
||||
state.selectedAvatarDetail.level = 80;
|
||||
state.selectedAvatarDetail.promotion = 6;
|
||||
state.selectedAvatarDetail.rank = 6;
|
||||
renderModalEidolonPips(6);
|
||||
await maxSkillsForAvatar();
|
||||
await saveAvatarDetails();
|
||||
// Steppers for 4 Skills (Basic, Skill, Ultra, Talent)
|
||||
const setupSkillStepper = (rangeEl, valEl, decBtn, incBtn, maxBtn, maxVal) => {
|
||||
if (!rangeEl || !valEl) return;
|
||||
rangeEl.addEventListener('input', () => { valEl.textContent = rangeEl.value; });
|
||||
decBtn?.addEventListener('click', () => {
|
||||
let cur = parseInt(rangeEl.value, 10);
|
||||
if (cur > parseInt(rangeEl.min, 10)) {
|
||||
rangeEl.value = cur - 1;
|
||||
valEl.textContent = rangeEl.value;
|
||||
}
|
||||
});
|
||||
incBtn?.addEventListener('click', () => {
|
||||
let cur = parseInt(rangeEl.value, 10);
|
||||
if (cur < parseInt(rangeEl.max, 10)) {
|
||||
rangeEl.value = cur + 1;
|
||||
valEl.textContent = rangeEl.value;
|
||||
}
|
||||
});
|
||||
maxBtn?.addEventListener('click', () => {
|
||||
rangeEl.value = maxVal;
|
||||
valEl.textContent = maxVal;
|
||||
});
|
||||
};
|
||||
|
||||
setupSkillStepper(el.rangeSkillBasic, el.valSkillBasic, el.btnStepDecBasic, el.btnStepIncBasic, el.btnSkillMaxBasic, 6);
|
||||
setupSkillStepper(el.rangeSkillSkill, el.valSkillSkill, el.btnStepDecSkill, el.btnStepIncSkill, el.btnSkillMaxSkill, 10);
|
||||
setupSkillStepper(el.rangeSkillUltra, el.valSkillUltra, el.btnStepDecUltra, el.btnStepIncUltra, el.btnSkillMaxUltra, 10);
|
||||
setupSkillStepper(el.rangeSkillTalent, el.valSkillTalent, el.btnStepDecTalent, el.btnStepIncTalent, el.btnSkillMaxTalent, 10);
|
||||
|
||||
// Ultimate Energy Controls
|
||||
el.rangeCharEnergy?.addEventListener('input', () => {
|
||||
el.valCharEnergy.textContent = `${el.rangeCharEnergy.value}/${el.rangeCharEnergy.max}`;
|
||||
});
|
||||
el.btnModalEnergy50?.addEventListener('click', () => {
|
||||
const half = Math.floor(parseInt(el.rangeCharEnergy.max, 10) * 0.5);
|
||||
el.rangeCharEnergy.value = half;
|
||||
el.valCharEnergy.textContent = `${half}/${el.rangeCharEnergy.max}`;
|
||||
});
|
||||
el.btnModalEnergy100?.addEventListener('click', () => {
|
||||
el.rangeCharEnergy.value = el.rangeCharEnergy.max;
|
||||
el.valCharEnergy.textContent = `${el.rangeCharEnergy.max}/${el.rangeCharEnergy.max}`;
|
||||
});
|
||||
|
||||
// Lightcone Controls in Modal
|
||||
document.querySelectorAll('.lc-super-pip').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const r = parseInt(btn.getAttribute('data-rank'), 10);
|
||||
updateLcRankPips(r);
|
||||
});
|
||||
});
|
||||
el.btnModalMaxLc?.addEventListener('click', () => {
|
||||
if (el.modalLcLevel) el.modalLcLevel.value = 80;
|
||||
updateLcRankPips(5);
|
||||
showToast('ปรับกรวยแสงเป็นเลเวล 80 ขัดเกลา 5 เรียบร้อย', 'info');
|
||||
});
|
||||
|
||||
// Quick Preset Buttons for Character Modal
|
||||
@@ -1430,6 +1775,7 @@ function init() {
|
||||
setupServerModes();
|
||||
setupLanguageSelector();
|
||||
setupTabs();
|
||||
setupModalSubtabs();
|
||||
setupEvents();
|
||||
applyTranslations();
|
||||
refreshStatus();
|
||||
|
||||
+8
-8
@@ -46,8 +46,8 @@ export const TRANSLATIONS = {
|
||||
btnSyncPersistent: "💾 ซิงค์ข้อมูล Persistent ล่าสุด",
|
||||
currentLineupTitle: "🎮 ทีมปัจจุบัน",
|
||||
slot: "ตำแหน่งที่",
|
||||
slotEmpty: "(ว่าง)",
|
||||
coords: "พิกัดตัวละคร (X, Y, Z)",
|
||||
slotEmpty: "ว่าง",
|
||||
coords: "พิกัดตัวละคร XYZ",
|
||||
// Avatars
|
||||
searchPlaceholder: "🔍 ค้นหาชื่อหรือ ID ตัวละคร...",
|
||||
filterAllRarity: "ทุกระดับดาว",
|
||||
@@ -56,7 +56,7 @@ export const TRANSLATIONS = {
|
||||
btnReload: "🔄 รีโหลด",
|
||||
eidolonLabel: "Eidolon:",
|
||||
confirmMaxAll: "ยืนยันปรับตัวละครทั้งหมดให้เป็นเลเวล 80 และปลด E6 หรือไม่?",
|
||||
maxAllSuccess: "ปรับ Max ทุกตัวละครสำเร็จแล้ว!",
|
||||
maxAllSuccess: "ปรับเต็มทุกตัวละครสำเร็จแล้ว!",
|
||||
avatarRankSuccess: "ปรับตัวละคร {id} เป็น E{rank} สำเร็จ",
|
||||
// Ex-Rank
|
||||
exrankTitle: "⚡ ระบบปลดล็อค Ex-Rank Skill",
|
||||
@@ -74,20 +74,20 @@ export const TRANSLATIONS = {
|
||||
customTeleportSubtitle: "ใส่ PlaneID, FloorID และ EntryID ตามต้องการ",
|
||||
btnExecuteWarp: "🚀 ยืนยันการวาร์ป",
|
||||
warpBtn: "🚀 วาร์ป",
|
||||
warpSuccess: "วาร์ปไปยัง {label} สำเร็จ (บันทึกลง Persistent)",
|
||||
warpSuccess: "วาร์ปไปยัง {label} สำเร็จ",
|
||||
warpFailed: "วาร์ปไม่สำเร็จ: {err}",
|
||||
// JSON
|
||||
btnFreesr: "📄 freesr-data.json",
|
||||
btnPersistent: "💾 ไฟล์เซฟ persistent",
|
||||
btnBeautify: "✨ จัดรูปแบบ JSON",
|
||||
btnDownload: "📥 ดาวน์โหลด Backup",
|
||||
btnDownload: "📥 ดาวน์โหลดสำรอง",
|
||||
btnUpload: "📤 อัปโหลดไฟล์",
|
||||
btnSaveServer: "💾 บันทึกไปยังเซิร์ฟเวอร์",
|
||||
jsonTip: "Tip: สามารถแก้ไขค่าดิบแล้วกด 'บันทึกไปยังเซิร์ฟเวอร์' ได้ทันที",
|
||||
jsonTip: "คำแนะนำ: สามารถแก้ไขค่าดิบแล้วกด 'บันทึกไปยังเซิร์ฟเวอร์' ได้ทันที",
|
||||
jsonStatusReady: "พร้อมใช้งาน",
|
||||
jsonStatusOpening: "กำลังเปิด: {name}",
|
||||
jsonSaveSuccess: "บันทึกไฟล์ {name} เรียบร้อยแล้ว",
|
||||
jsonInvalid: "JSON ไม่ถูกต้อง",
|
||||
jsonInvalid: "รูปแบบ JSON ไม่ถูกต้อง",
|
||||
// Settings
|
||||
apiSettingsTitle: "⚙️ ตั้งค่าการเชื่อมต่อ API Gateway",
|
||||
apiSettingsSubtitle: "ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามไฟล์ server.json ของคุณ",
|
||||
@@ -95,7 +95,7 @@ export const TRANSLATIONS = {
|
||||
lblPingInterval: "ความถี่ในการตรวจสอบสถานะ (วินาที)",
|
||||
btnSaveSettings: "💾 บันทึกการตั้งค่า",
|
||||
btnResetDefault: "คืนค่าเริ่มต้น",
|
||||
hostGuideTitle: "🌐 วิธีนำขึ้นโฮสต์ GitHub Pages (ฟรี 100%)",
|
||||
hostGuideTitle: "🌐 วิธีนำขึ้นโฮสต์ GitHub Pages",
|
||||
hostGuideSubtitle: "สามารถเปิดใช้งานเว็บนี้บนอินเทอร์เน็ตได้โดยไม่ต้องเสียค่าโฮสต์ใดๆ",
|
||||
settingsSaved: "บันทึกการตั้งค่าสำเร็จ!",
|
||||
settingsReset: "คืนค่าเริ่มต้นเรียบร้อย",
|
||||
|
||||
Reference in new issue
Block a user