From e1667fa109ce01ba2dbb6b2ee74072d2e9145e29 Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Fri, 16 Oct 2026 22:37:28 +0700 Subject: [PATCH] feat: complete SrTool overhaul with lineup builder, character build modal, equipment, and FireflyGo/RobinSR modes --- css/styles.css | 359 ++++++++++++++++++++++--------- index.html | 406 +++++++++++++++++++++++++++--------- js/app.js | 556 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 1067 insertions(+), 254 deletions(-) diff --git a/css/styles.css b/css/styles.css index eae968e..b1344f0 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1,10 +1,10 @@ :root { - --bg-primary: #0a0d14; - --bg-secondary: #121826; - --bg-card: rgba(22, 30, 48, 0.7); - --bg-card-hover: rgba(30, 41, 66, 0.85); + --bg-primary: #080a10; + --bg-secondary: #0f1422; + --bg-card: rgba(18, 24, 38, 0.75); + --bg-card-hover: rgba(26, 35, 56, 0.9); --border-color: rgba(99, 120, 160, 0.25); - --border-focus: #4ef2bb; + --border-focus: #38bdf8; --text-main: #f0f4fc; --text-muted: #8e9bb5; @@ -13,18 +13,18 @@ --accent-cyan: #38bdf8; --accent-green: #34d399; --accent-gold: #fbbf24; - --accent-purple: #a855f7; + --accent-purple: #c084fc; --accent-red: #f87171; - --glow-cyan: 0 0 15px rgba(56, 189, 248, 0.35); - --glow-green: 0 0 15px rgba(52, 211, 153, 0.35); - --glow-gold: 0 0 15px rgba(251, 191, 36, 0.35); + --glow-cyan: 0 0 20px rgba(56, 189, 248, 0.35); + --glow-gold: 0 0 20px rgba(251, 191, 36, 0.35); + --glow-purple: 0 0 20px rgba(192, 132, 252, 0.35); --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; --radius-sm: 6px; --radius-md: 10px; --radius-lg: 16px; - --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + --transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); } * { @@ -37,8 +37,9 @@ body { font-family: var(--font-family); background-color: var(--bg-primary); background-image: - radial-gradient(circle at 15% 15%, rgba(56, 189, 248, 0.08) 0%, transparent 40%), - radial-gradient(circle at 85% 85%, rgba(168, 85, 247, 0.08) 0%, transparent 45%); + radial-gradient(circle at 10% 10%, rgba(56, 189, 248, 0.08) 0%, transparent 40%), + radial-gradient(circle at 90% 90%, rgba(168, 85, 247, 0.08) 0%, transparent 45%), + linear-gradient(180deg, rgba(8, 10, 16, 0.95) 0%, rgba(12, 16, 26, 0.98) 100%); color: var(--text-main); min-height: 100vh; display: flex; @@ -52,8 +53,8 @@ body { position: sticky; top: 0; z-index: 50; - background: rgba(10, 13, 20, 0.85); - backdrop-filter: blur(14px); + background: rgba(8, 10, 16, 0.88); + backdrop-filter: blur(16px); border-bottom: 1px solid var(--border-color); padding: 0.75rem 1.5rem; display: flex; @@ -65,28 +66,29 @@ body { .brand { display: flex; align-items: center; - gap: 0.75rem; + gap: 0.85rem; } .brand-icon { - width: 38px; - height: 38px; + width: 42px; + height: 42px; border-radius: var(--radius-md); - background: linear-gradient(135deg, #38bdf8, #818cf8); + background: linear-gradient(135deg, #0284c7, #6366f1); display: flex; align-items: center; justify-content: center; box-shadow: var(--glow-cyan); - font-size: 1.25rem; + font-size: 1.35rem; font-weight: bold; color: #fff; + border: 1px solid rgba(56, 189, 248, 0.4); } .brand-title { - font-size: 1.25rem; - font-weight: 700; + font-size: 1.35rem; + font-weight: 800; letter-spacing: -0.025em; - background: linear-gradient(90deg, #ffffff, #cbd5e1); + background: linear-gradient(90deg, #ffffff, #cbd5e1, #38bdf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } @@ -95,25 +97,55 @@ body { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; - letter-spacing: 0.05em; + letter-spacing: 0.06em; } .header-controls { display: flex; align-items: center; - gap: 0.85rem; + gap: 0.75rem; + flex-wrap: wrap; +} + +/* Server Mode Selector */ +.server-mode-badge { + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.4rem 0.85rem; + background: rgba(2, 132, 199, 0.15); + border: 1px solid rgba(56, 189, 248, 0.4); + border-radius: 9999px; + font-size: 0.825rem; + font-weight: 700; + color: var(--accent-cyan); +} + +.server-mode-select { + background: transparent; + border: none; + color: inherit; + font-size: inherit; + font-weight: inherit; + cursor: pointer; + outline: none; +} + +.server-mode-select option { + background: var(--bg-secondary); + color: var(--text-main); } .connection-pill { display: flex; align-items: center; gap: 0.5rem; - padding: 0.35rem 0.75rem; + padding: 0.4rem 0.85rem; background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 9999px; font-size: 0.8rem; - font-weight: 500; + font-weight: 600; cursor: pointer; transition: var(--transition); } @@ -123,8 +155,8 @@ body { } .status-dot { - width: 8px; - height: 8px; + width: 9px; + height: 9px; border-radius: 50%; background-color: var(--accent-red); box-shadow: 0 0 8px var(--accent-red); @@ -133,13 +165,13 @@ body { .status-dot.connected { background-color: var(--accent-green); - box-shadow: var(--glow-green); + box-shadow: 0 0 10px var(--accent-green); } /* Navigation Tabs */ .nav-tabs { display: flex; - gap: 0.5rem; + gap: 0.35rem; background: var(--bg-secondary); padding: 0.5rem 1.5rem; border-bottom: 1px solid var(--border-color); @@ -150,7 +182,7 @@ body { background: transparent; border: none; color: var(--text-muted); - padding: 0.5rem 1rem; + padding: 0.6rem 1.1rem; border-radius: var(--radius-sm); font-size: 0.875rem; font-weight: 600; @@ -164,21 +196,21 @@ body { .tab-btn:hover { color: var(--text-main); - background: rgba(255, 255, 255, 0.04); + background: rgba(255, 255, 255, 0.05); } .tab-btn.active { color: #fff; background: rgba(56, 189, 248, 0.15); - border: 1px solid rgba(56, 189, 248, 0.3); - box-shadow: 0 0 10px rgba(56, 189, 248, 0.15); + border: 1px solid rgba(56, 189, 248, 0.35); + box-shadow: 0 0 12px rgba(56, 189, 248, 0.2); } -/* Container */ +/* Main Container */ .main-container { flex: 1; padding: 1.5rem; - max-width: 1300px; + max-width: 1400px; width: 100%; margin: 0 auto; } @@ -197,7 +229,7 @@ body { to { opacity: 1; transform: translateY(0); } } -/* Cards & Grid */ +/* Grids */ .grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); @@ -212,13 +244,14 @@ body { .grid-4 { display: grid; - grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); - gap: 1.1rem; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 1.25rem; } +/* Cards */ .card { background: var(--bg-card); - backdrop-filter: blur(12px); + backdrop-filter: blur(14px); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: 1.25rem; @@ -229,8 +262,8 @@ body { .card:hover { background: var(--bg-card-hover); - border-color: rgba(99, 120, 160, 0.45); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); + border-color: rgba(99, 120, 160, 0.5); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); } .card-title { @@ -253,11 +286,11 @@ body { .stat-box { display: flex; flex-direction: column; - gap: 0.25rem; + gap: 0.35rem; } .stat-number { - font-size: 2rem; + font-size: 2.25rem; font-weight: 800; color: var(--accent-cyan); line-height: 1; @@ -268,6 +301,7 @@ body { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; + font-weight: 600; } /* Buttons */ @@ -276,7 +310,7 @@ body { align-items: center; justify-content: center; gap: 0.5rem; - padding: 0.6rem 1.1rem; + padding: 0.6rem 1.15rem; border-radius: var(--radius-sm); font-size: 0.875rem; font-weight: 600; @@ -304,14 +338,14 @@ body { } .btn-success:hover { - box-shadow: var(--glow-green); + box-shadow: 0 0 15px rgba(52, 211, 153, 0.4); filter: brightness(1.1); } .btn-gold { background: linear-gradient(135deg, #d97706, #f59e0b); color: #fff; - border-color: rgba(251, 191, 36, 0.4); + border-color: rgba(251, 191, 36, 0.5); } .btn-gold:hover { @@ -335,34 +369,62 @@ body { font-size: 0.775rem; } -/* Avatar Card */ +/* ========================================================================== + Honkai: Star Rail Themed Avatar Cards & Portraits + ========================================================================== */ + .avatar-card { display: flex; flex-direction: column; justify-content: space-between; - min-height: 180px; + min-height: 220px; position: relative; + cursor: pointer; + border-width: 1.5px; +} + +/* 5-Star Card Theme */ +.avatar-card.card-rarity-5 { + border-color: rgba(251, 191, 36, 0.4); + background: linear-gradient(145deg, rgba(251, 191, 36, 0.05) 0%, rgba(18, 24, 38, 0.8) 100%); +} + +.avatar-card.card-rarity-5:hover { + border-color: rgba(251, 191, 36, 0.8); + box-shadow: var(--glow-gold); +} + +/* 4-Star Card Theme */ +.avatar-card.card-rarity-4 { + border-color: rgba(192, 132, 252, 0.35); + background: linear-gradient(145deg, rgba(192, 132, 252, 0.05) 0%, rgba(18, 24, 38, 0.8) 100%); +} + +.avatar-card.card-rarity-4:hover { + border-color: rgba(192, 132, 252, 0.8); + box-shadow: var(--glow-purple); } .avatar-badge-rarity { position: absolute; - top: 1rem; - right: 1rem; + top: 0.85rem; + right: 0.85rem; font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: var(--radius-sm); - font-weight: 700; + font-weight: 800; + letter-spacing: 0.05em; } .rarity-5 { background: rgba(251, 191, 36, 0.2); - border: 1px solid rgba(251, 191, 36, 0.5); + border: 1px solid rgba(251, 191, 36, 0.6); color: var(--accent-gold); } .rarity-4 { - background: rgba(168, 85, 247, 0.2); - border: 1px solid rgba(168, 85, 247, 0.5); + background: rgba(192, 132, 252, 0.2); + border: 1px solid rgba(192, 132, 252, 0.6); color: var(--accent-purple); } @@ -372,29 +434,39 @@ body { align-items: center; } +/* Circular / Rounded Avatar Portrait */ .avatar-image-container { position: relative; - width: 52px; - height: 52px; - border-radius: var(--radius-md); + width: 60px; + height: 60px; + border-radius: 50%; overflow: hidden; background: var(--bg-secondary); - border: 1px solid var(--border-color); + border: 2px solid var(--border-color); flex-shrink: 0; display: flex; align-items: center; justify-content: center; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4); +} + +.card-rarity-5 .avatar-image-container { + border-color: var(--accent-gold); +} + +.card-rarity-4 .avatar-image-container { + border-color: var(--accent-purple); } .avatar-img { width: 100%; height: 100%; object-fit: cover; - transition: transform 0.25s ease; + transition: transform 0.3s ease; } -.card:hover .avatar-img { - transform: scale(1.08); +.avatar-card:hover .avatar-img { + transform: scale(1.12); } .avatar-icon-placeholder { @@ -405,28 +477,59 @@ body { align-items: center; justify-content: center; font-weight: bold; - font-size: 1.1rem; + font-size: 1.25rem; + color: var(--text-muted); } .avatar-name { - font-size: 1rem; - font-weight: 700; + font-size: 1.05rem; + font-weight: 800; color: var(--text-highlight); + line-height: 1.25; } .avatar-subinfo { - font-size: 0.75rem; - color: var(--text-muted); + font-size: 0.775rem; + margin-top: 0.35rem; display: flex; - gap: 0.5rem; + gap: 0.4rem; + flex-wrap: wrap; + align-items: center; } +/* Star Rail Element Badges */ +.element-badge { + padding: 0.15rem 0.45rem; + border-radius: 4px; + font-size: 0.725rem; + font-weight: 700; +} + +.elem-fire { background: rgba(239, 68, 68, 0.2); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.4); } +.elem-ice { background: rgba(56, 189, 248, 0.2); color: #38bdf8; border: 1px solid rgba(56, 189, 248, 0.4); } +.elem-lightning { background: rgba(168, 85, 247, 0.2); color: #c084fc; border: 1px solid rgba(168, 85, 247, 0.4); } +.elem-wind { background: rgba(16, 185, 129, 0.2); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.4); } +.elem-physical { background: rgba(156, 163, 175, 0.2); color: #e5e7eb; border: 1px solid rgba(156, 163, 175, 0.4); } +.elem-quantum { background: rgba(99, 102, 241, 0.2); color: #818cf8; border: 1px solid rgba(99, 102, 241, 0.4); } +.elem-imaginary { background: rgba(245, 158, 11, 0.2); color: #fbbf24; border: 1px solid rgba(245, 158, 11, 0.4); } + +.path-badge { + background: rgba(255, 255, 255, 0.08); + border: 1px solid var(--border-color); + color: var(--text-main); + padding: 0.15rem 0.45rem; + border-radius: 4px; + font-size: 0.725rem; + font-weight: 600; +} + +/* Eidolon Selector */ .eidolon-selector { display: flex; align-items: center; justify-content: space-between; margin-top: 1rem; - background: rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.35); padding: 0.5rem 0.75rem; border-radius: var(--radius-sm); border: 1px solid var(--border-color); @@ -434,18 +537,18 @@ body { .eidolon-buttons { display: flex; - gap: 0.25rem; + gap: 0.3rem; } .eidolon-pip { - width: 24px; - height: 24px; + width: 25px; + height: 25px; border-radius: 4px; border: 1px solid var(--border-color); background: transparent; color: var(--text-muted); font-size: 0.75rem; - font-weight: bold; + font-weight: 800; cursor: pointer; display: flex; align-items: center; @@ -459,10 +562,69 @@ body { } .eidolon-pip.active { - background: var(--accent-cyan); - border-color: var(--accent-cyan); + background: linear-gradient(135deg, #0284c7, #38bdf8); + border-color: #38bdf8; color: #0b0d14; - box-shadow: 0 0 8px rgba(56, 189, 248, 0.5); + box-shadow: 0 0 10px rgba(56, 189, 248, 0.6); +} + +/* ========================================================================== + Party / Lineup Builder + ========================================================================== */ +.lineup-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1.25rem; +} + +.lineup-slot-card { + background: var(--bg-card); + border: 2px dashed var(--border-color); + border-radius: var(--radius-lg); + padding: 1.25rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + min-height: 200px; + cursor: pointer; + transition: var(--transition); + position: relative; +} + +.lineup-slot-card.filled { + border-style: solid; + border-color: rgba(56, 189, 248, 0.4); + background: linear-gradient(180deg, rgba(56, 189, 248, 0.08) 0%, rgba(18, 24, 38, 0.85) 100%); +} + +.lineup-slot-card:hover { + border-color: var(--accent-cyan); + box-shadow: var(--glow-cyan); +} + +.lineup-slot-img { + width: 72px; + height: 72px; + border-radius: 50%; + border: 2px solid var(--accent-cyan); + object-fit: cover; + margin-bottom: 0.75rem; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); +} + +.lineup-slot-num { + position: absolute; + top: 0.75rem; + left: 0.75rem; + font-size: 0.75rem; + font-weight: 800; + color: var(--accent-cyan); + background: rgba(2, 132, 199, 0.2); + padding: 0.2rem 0.5rem; + border-radius: 9999px; + border: 1px solid rgba(56, 189, 248, 0.3); } /* Ex Rank Toggle Switch */ @@ -470,7 +632,7 @@ body { display: flex; align-items: center; justify-content: space-between; - padding: 1rem; + padding: 1rem 1.25rem; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); @@ -480,6 +642,7 @@ body { .ex-switch-card:hover { background: var(--bg-card-hover); + border-color: rgba(99, 120, 160, 0.45); } .switch { @@ -518,14 +681,14 @@ body { input:checked + .slider { background-color: var(--accent-green); - box-shadow: var(--glow-green); + box-shadow: 0 0 10px var(--accent-green); } input:checked + .slider:before { transform: translateX(22px); } -/* Inputs & Form */ +/* Forms */ .form-group { margin-bottom: 1rem; } @@ -542,7 +705,7 @@ input:checked + .slider:before { .form-input, .form-select { width: 100%; - padding: 0.6rem 0.85rem; + padding: 0.65rem 0.85rem; background: rgba(10, 13, 20, 0.7); border: 1px solid var(--border-color); border-radius: var(--radius-sm); @@ -554,10 +717,10 @@ input:checked + .slider:before { .form-input:focus, .form-select:focus { border-color: var(--accent-cyan); - box-shadow: 0 0 10px rgba(56, 189, 248, 0.2); + box-shadow: 0 0 10px rgba(56, 189, 248, 0.25); } -/* JSON Code Editor Area */ +/* JSON Editor */ .json-editor-wrapper { display: flex; flex-direction: column; @@ -583,7 +746,7 @@ input:checked + .slider:before { border-color: var(--accent-cyan); } -/* Toast notifications */ +/* Toast */ .toast-container { position: fixed; bottom: 1.5rem; @@ -622,12 +785,12 @@ input:checked + .slider:before { to { transform: translateX(0); opacity: 1; } } -/* Modal */ +/* Modals */ .modal-overlay { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(6px); + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center; @@ -643,10 +806,11 @@ input:checked + .slider:before { background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-lg); - max-width: 560px; + max-width: 600px; width: 100%; - padding: 1.5rem; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6); + padding: 1.75rem; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.7); + animation: fadeIn 0.2s ease-out; } .modal-header { @@ -654,10 +818,18 @@ input:checked + .slider:before { justify-content: space-between; align-items: center; margin-bottom: 1.25rem; + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.75rem; } /* Responsive */ -@media (max-width: 768px) { +@media (max-width: 900px) { + .lineup-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 640px) { .app-header { flex-direction: column; align-items: flex-start; @@ -666,10 +838,7 @@ input:checked + .slider:before { width: 100%; justify-content: space-between; } - .nav-tabs { - padding: 0.5rem 1rem; - } - .main-container { - padding: 1rem; + .lineup-grid { + grid-template-columns: 1fr; } } diff --git a/index.html b/index.html index 6f19eb8..ac6d348 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - GaC-Srtool — FreeSR & Game Management Dashboard + GaC-Srtool — Ultimate Star Rail Server & Build Manager @@ -14,54 +14,78 @@
✦
-
GaC-Srtool
-
Star Rail FreeSR & Server Controller
+
GaC-Srtool
+
Star Rail FreeSR & Server Controller
- - + + + + +
+ + + -
+ +
- กำลังตรวจสอบ... + กำลังตรวจสอบ...
-
- +
0
-
ตัวละครที่ครอบครอง (Avatars)
+
ตัวละครที่ครอบครอง (Avatars)
0
-
กรวยแสง (Lightcones)
+
กรวยแสง (Lightcones)
0
-
รีลิกส์ (Relics)
+
รีลิกส์ (Relics)
+
+
+ + +
+
+ 🎮 สมาชิกในทีมปัจจุบัน (Active Party Lineup) + * คลิกที่ช่องเพื่อเปลี่ยนตัวละครในทีม +
+
+
@@ -69,12 +93,16 @@
- 📍 สถานะเซิร์ฟเวอร์ & ตำแหน่งปัจจุบัน + 📍 สถานะเซิร์ฟเวอร์ & พิกัดปัจจุบัน API Live
- API Gateway Endpoint + โหมดเซิร์ฟเวอร์ที่ใช้งาน + GaC-hkrpg-go +
+
+ API Gateway Endpoint -
@@ -88,7 +116,7 @@
- พิกัดตัวละคร (X, Y, Z) + พิกัดตัวละคร (X, Y, Z) -
@@ -96,84 +124,192 @@
-
⚡ ดำเนินการด่วน (Quick Actions)
-

สั่งการส่งไปยัง REST API Gateway ทันที

+
⚡ ดำเนินการด่วน (Quick Actions)
+

ส่งคำสั่งไปยังเซิร์ฟเวอร์ทันที

- - -
-
🎮 ทีมปัจจุบัน (Current Lineup)
-
-
กำลังโหลดข้อมูลทีม...
-
-
- +
-
- +
+ +
- - + +
- กำลังดึงข้อมูลตัวละครจากเซิร์ฟเวอร์... + กำลังดึงข้อมูลตัวละคร...
- + +
+
+ +
+
+ 🗡️ กรวยแสงในคลัง (Lightcones) + 0 ชิ้น +
+

รายการกรวยแสงทั้งหมดจาก freesr-data.json

+
+
กำลังโหลดข้อมูลกรวยแสง...
+
+
+ + +
+
+ 🛡️ รีลิกส์ในคลัง (Relics) + 0 ชิ้น +
+

รายการรีลิกส์ทั้งหมดจาก freesr-data.json

+
+
กำลังโหลดข้อมูลรีลิกส์...
+
+
+
+
+ + +
+
+
+ 💾 จัดการไฟล์เซฟ Persistent (GaC-hkrpg-go Dedicated) + เซิร์ฟเวอร์ของเราเอง +
+

+ ไฟล์ persistent เก็บการตั้งค่าสถานะผู้เล่นเฉพาะของเซิร์ฟเวอร์ GaC เช่น เพศ/ธาตุตัวเอก, สเตตัส March 7th, สวิตช์ฟังก์ชัน Global ต่างๆ +

+
+ +
+ +
+
👤 ผู้บุกเบิก & March 7th
+
+
+ + +
+ +
+ + +
+ + +
+
+ + +
+
⚙️ สวิตช์ระบบพิเศษของ GaC
+
+
+
+
Silver Wolf Global Feature
+
เปิดใช้งานฟังก์ชันพิเศษ SW ทั่วทั้งเซิร์ฟเวอร์
+
+ +
+ +
+
+
Castorice Global Feature
+
เปิดใช้งานฟังก์ชันพิเศษ Castorice ทั่วทั้งเซิร์ฟเวอร์
+
+ +
+ + +
+
+
+
+ +
- ⚡ ระบบปลดล็อค Ex-Rank Skill + ⚡ ระบบปลดล็อค Ex-Rank Skill
- - + +
-

+

เปิดใช้งานพรสวรรค์และกลไกพิเศษ Ex-Rank สำหรับตัวละครเฉพาะใน FreeSR (เช่น Jing Yuan โดน CC ไม่หยุดชะงัก, Dan Heng IL, Acheron)

-
- กำลังโหลดข้อมูล Ex-Rank... -
+
- +
-
📍 จุดวาร์ปปลอดภัยยอดนิยม (Safe Presets)
-

คลิกจุดวาร์ปเพื่อย้ายฉากของผู้เล่นในไฟล์เซฟได้ทันที

+
📍 จุดวาร์ปปลอดภัยยอดนิยม (Safe Presets)
+

คลิกจุดวาร์ปเพื่อย้ายฉากของผู้เล่นในไฟล์เซฟได้ทันที

@@ -181,8 +317,8 @@
-
🎯 กำหนดพิกัดวาร์ปเอง (Custom Teleport)
-

ใส่ PlaneID, FloorID และ EntryID ตามต้องการ

+
🎯 กำหนดพิกัดวาร์ปเอง (Custom Teleport)
+

ใส่ PlaneID, FloorID และ EntryID ตามต้องการ

@@ -215,7 +351,7 @@
- @@ -223,106 +359,170 @@
- +
- - + +
- - + + - +
- พร้อมใช้งาน - Tip: สามารถแก้ไขค่าดิบแล้วกด 'บันทึกไปยังเซิร์ฟเวอร์' ได้ทันที + พร้อมใช้งาน + Tip: สามารถแก้ไขค่าดิบแล้วกด 'บันทึกไปยังเซิร์ฟเวอร์' ได้ทันที
- +
-
⚙️ ตั้งค่าการเชื่อมต่อ API Gateway
-

ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามไฟล์ server.json ของคุณ

+
⚙️ ตั้งค่าการเชื่อมต่อ API Gateway
+

ปรับเปลี่ยนพอร์ตหรือ IP ได้ตามเซิร์ฟเวอร์ที่คุณใช้

- + + +
+
+ - * Port 21000 by default (GaC-hkrpg-go SDK Server) + * ค่าเริ่มต้นพอร์ต 21000 (SDK server)
- +
- - + +
- +
-
🌐 วิธีนำขึ้นโฮสต์ GitHub Pages (ฟรี 100%)
-

สามารถเปิดใช้งานเว็บนี้บนอินเทอร์เน็ตได้โดยไม่ต้องเสียค่าโฮสต์ใดๆ

- -
    -
  1. - 1. Create GitHub Repository:
    - Create new repo e.g. GaC-srtool -
  2. -
  3. - 2. Push to GitHub:
    - git remote add origin https://github.com/<user>/GaC-srtool.git
    - git add . && git commit -m "Init" && git push -u origin main -
  4. -
  5. - 3. Enable GitHub Pages:
    - Go to Repo > Settings > Pages > Source: Deploy from a branch > Branch: main / (root) > Click Save -
  6. -
  7. - 4. Done:
    - Your dashboard will be online at https://<user>.github.io/GaC-srtool/! -
  8. -
+
💡 ข้อมูลโหมดเซิร์ฟเวอร์ & สำหรับผู้อื่น
+
+
+ ⚡ GaC-hkrpg-go (Full Access):
+ โหมดเฉพาะของเซิร์ฟเวอร์เรา สามารถอ่านและเขียนทั้ง freesr-data.json และ persistent ได้แบบ 100% เรียลไทม์ผ่าน REST API Gateway พร้อมระบบ Ex-Rank และ Teleport +
+
+ 🔥 FireflyGo:
+ สำหรับผู้ใช้งานที่รันเซิร์ฟเวอร์ FireflyGo สามารถใช้เครื่องมือนี้จัดทีม, แม็กซ์ตัวละคร, หรือปรับแต่งอาวุธ/รีลิกส์ แล้วสั่งซิงค์ผ่าน /srtools หรือกดดาวน์โหลดไฟล์ freesr-data.json ไปวางในเซิร์ฟเวอร์ได้ทันที +
+
+ 🕊️ RobinSR:
+ สำหรับผู้ใช้งาน RobinSR สามารถใช้เครื่องมือนี้ในการสร้างเซฟและตัวละครตามมาตรฐาน FreeSR ได้อย่างสมบูรณ์แบบ +
+
+ + + + + +
- ${t('eidolonLabel')} + ${t('eidolonLabel')}
${pipsHtml}
@@ -347,17 +554,121 @@ function renderAvatarsGrid() { }); }); + // Clicking card opens full character build modal! + card.addEventListener('click', () => { + openAvatarDetailModal(item); + }); + el.avatarsGrid.appendChild(card); }); } +// Character Detail / Build Modal +function openAvatarDetailModal(avatarItem) { + state.selectedAvatarDetail = avatarItem; + const meta = getAvatarInfo(avatarItem.avatar_id); + const elemClass = getElementClass(meta.element); + + el.modalCharTitle.textContent = `ปรับแต่งตัวละคร: ${meta.name} (ID: ${avatarItem.avatar_id})`; + el.modalCharImg.src = `./assets/avatars/${avatarItem.avatar_id}.png`; + el.modalCharImg.onerror = function() { + this.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${avatarItem.avatar_id}.png`; + }; + el.modalCharName.textContent = meta.name; + el.modalCharMeta.innerHTML = ` + ${meta.element} + ${meta.path} + ★ ${meta.rarity} + `; + + el.modalCharLevel.value = avatarItem.level || 80; + el.modalCharPromotion.value = avatarItem.promotion || 6; + + // Render modal eidolon pips + el.modalCharEidolonBtns.innerHTML = ''; + for (let r = 0; r <= 6; r++) { + const btn = document.createElement('button'); + btn.className = `eidolon-pip ${avatarItem.rank >= r ? 'active' : ''}`; + btn.textContent = r; + btn.style.width = '32px'; + btn.style.height = '32px'; + btn.style.fontSize = '0.9rem'; + btn.addEventListener('click', () => { + avatarItem.rank = r; + el.modalCharEidolonBtns.querySelectorAll('.eidolon-pip').forEach((b, idx) => { + if (idx <= r) b.classList.add('active'); + else b.classList.remove('active'); + }); + }); + el.modalCharEidolonBtns.appendChild(btn); + } + + el.modalAvatarDetail.classList.add('active'); +} + +// Save avatar details from modal +async function saveAvatarDetails() { + if (!state.selectedAvatarDetail) return; + const aid = state.selectedAvatarDetail.avatar_id; + const level = parseInt(el.modalCharLevel.value, 10) || 80; + const promotion = parseInt(el.modalCharPromotion.value, 10) || 6; + const rank = state.selectedAvatarDetail.rank ?? 6; + + try { + await apiCall('/api/freesr/avatars', 'POST', { + avatar_id: aid, + level, + promotion, + rank + }); + showToast(`บันทึกตัวละคร ${aid} สำเร็จ (Lv. ${level}, Prom. ${promotion}, E${rank})`, 'success'); + el.modalAvatarDetail.classList.remove('active'); + + // Update local state and re-render + const target = state.avatarsList.find(a => a.avatar_id === aid); + if (target) { + target.level = level; + target.promotion = promotion; + target.rank = rank; + renderAvatarsGrid(); + } + } catch (err) { + showToast(`เกิดข้อผิดพลาด: ${err.message}`, 'error'); + } +} + +// Max Skills & Traces for character +async function maxSkillsForAvatar() { + if (!state.selectedAvatarDetail) return; + const aid = state.selectedAvatarDetail.avatar_id; + try { + // Read freesr data, max skill levels for this avatar, and push back + const freesr = await apiCall('/api/freesr'); + if (freesr && freesr.data && freesr.data.avatars && freesr.data.avatars[aid]) { + const av = freesr.data.avatars[aid]; + if (av.data && av.data.skills) { + for (const k in av.data.skills) { + av.data.skills[k] = 10; + } + } + av.level = 80; + av.promotion = 6; + av.data.rank = 6; + await apiCall('/api/freesr', 'POST', freesr.data); + showToast(`อัพสกิลและ Traces ทั้งหมดของตัวละคร ${aid} เป็น 10/10 สำเร็จ!`, 'success'); + loadAvatars(); + } + } catch (err) { + showToast(`เกิดข้อผิดพลาด: ${err.message}`, 'error'); + } +} + // Set avatar rank API async function setAvatarRank(avatarId, rank) { try { await apiCall('/api/avatar/rank', 'POST', { avatar_id: avatarId, rank }); showToast(t('avatarRankSuccess', { id: avatarId, rank }), 'success'); - // Update local state and re-render const target = state.avatarsList.find(a => a.avatar_id === avatarId); if (target) { target.rank = rank; @@ -381,10 +692,94 @@ async function maxAllAvatars() { } } +// Load Equipment (Lightcones & Relics) +async function loadEquipment() { + try { + const res = await apiCall('/api/freesr'); + if (!res || !res.data) return; + state.fullFreesrData = res.data; + + const lcs = res.data.lightcones || []; + const relics = res.data.relics || []; + + el.badgeTotalLc.textContent = `${lcs.length} ชิ้น`; + el.badgeTotalRelics.textContent = `${relics.length} ชิ้น`; + + // Render Lightcones + el.lightconesList.innerHTML = ''; + lcs.slice(0, 100).forEach(lc => { + const item = document.createElement('div'); + item.className = 'card'; + item.style.padding = '0.75rem'; + item.style.display = 'flex'; + item.style.justifyContent = 'space-between'; + item.style.alignItems = 'center'; + + const equipped = lc.equip_avatar ? getAvatarInfo(lc.equip_avatar).name : 'ไม่ได้สวมใส่'; + item.innerHTML = ` +
+
Lightcone ID: ${lc.item_id} (UID: ${lc.internal_uid})
+
+ Lv. ${lc.level} • ขัดเกลา S${lc.rank} • สวมใส่โดย: ${equipped} +
+
+ `; + el.lightconesList.appendChild(item); + }); + + // Render Relics + el.relicsList.innerHTML = ''; + relics.slice(0, 100).forEach(r => { + const item = document.createElement('div'); + item.className = 'card'; + item.style.padding = '0.75rem'; + item.style.display = 'flex'; + item.style.justifyContent = 'space-between'; + item.style.alignItems = 'center'; + + const equipped = r.equip_avatar ? getAvatarInfo(r.equip_avatar).name : 'ไม่ได้สวมใส่'; + const slotNum = r.relic_id % 10; + item.innerHTML = ` +
+
Relic ID: ${r.relic_id} (Slot: ${slotNum})
+
+ Lv. ${r.level} • สเตตัสหลัก: ${r.main_affix_id} • สวมใส่โดย: ${equipped} +
+
+ `; + el.relicsList.appendChild(item); + }); + + } catch (err) { + showToast(`โหลดคลังอุปกรณ์ไม่สำเร็จ: ${err.message}`, 'error'); + } +} + +// Load Persistent Save Settings +async function loadPersistentSettings() { + try { + const res = await apiCall('/api/persistent'); + if (!res || !res.persistent) return; + const p = res.persistent; + state.persistentData = p; + + if (p.main_character) { + el.persistentMcSelect.value = p.main_character; + } + if (p.march_type) { + el.persistentMarchSelect.value = p.march_type; + } + el.toggleSwGlobal.checked = !!p.enable_sw_global; + el.toggleCastoriceGlobal.checked = !!p.enable_castorice_global; + } catch (err) { + console.warn('Persistent settings not available in current mode:', err.message); + } +} + // Render Ex-Ranks function renderExRanks(exList) { if (!exList || exList.length === 0) { - el.exrankList.innerHTML = '
No Ex-Rank Avatars
'; + el.exrankList.innerHTML = '
ไม่มีข้อมูลตัวละคร Ex-Rank
'; return; } @@ -396,7 +791,7 @@ function renderExRanks(exList) { card.innerHTML = `
-
+
${localizedMeta.name.charAt(0)}
-
${localizedMeta.name}
+
${localizedMeta.name}
Avatar ID: ${item.avatar_id} • Ex-Rank Skill ID: ${item.ex_rank_id}
@@ -514,7 +909,7 @@ async function loadJsonData(mode) { async function saveJsonData() { const text = el.jsonEditor.value.trim(); if (!text) { - showToast('No JSON data to save', 'error'); + showToast('ไม่มีข้อมูล JSON ให้บันทึก', 'error'); return; } @@ -554,6 +949,10 @@ function setupTabs() { // On-demand tab refresh if (targetId === 'tab-avatars' && state.avatarsList.length === 0) { loadAvatars(); + } else if (targetId === 'tab-equipment') { + loadEquipment(); + } else if (targetId === 'tab-persistent') { + loadPersistentSettings(); } else if (targetId === 'tab-json') { loadJsonData(state.currentJsonMode); } @@ -590,6 +989,15 @@ function setupEvents() { } }); + // Character Detail Modal Controls + el.btnCloseAvatarModal.addEventListener('click', () => el.modalAvatarDetail.classList.remove('active')); + el.btnModalSaveAvatar.addEventListener('click', saveAvatarDetails); + el.btnModalMaxSkills.addEventListener('click', maxSkillsForAvatar); + + // Lineup Picker Modal Controls + el.btnClosePickerModal.addEventListener('click', () => el.modalLineupPicker.classList.remove('active')); + el.pickerSearch.addEventListener('input', renderPickerGrid); + // Quick Action Buttons el.btnQuickMaxAll.addEventListener('click', maxAllAvatars); el.btnMaxAllAvatars.addEventListener('click', maxAllAvatars); @@ -602,16 +1010,50 @@ function setupEvents() { el.btnQuickSyncPersistent.addEventListener('click', async () => { try { await apiCall('/api/persistent'); - showToast('Persistent data synced', 'success'); + showToast('ซิงค์ข้อมูล Persistent เรียบร้อย', 'success'); refreshStatus(); } catch (err) { showToast(`Sync failed: ${err.message}`, 'error'); } }); - // Avatar Search & Filter + // Persistent Form Submit + el.formPersistentChars.addEventListener('submit', async (e) => { + e.preventDefault(); + const mc = el.persistentMcSelect.value; + const march = el.persistentMarchSelect.value; + try { + await apiCall('/api/persistent', 'POST', { + main_character: mc, + march_type: march + }); + showToast('บันทึกการตั้งค่าตัวละครสำเร็จ!', 'success'); + refreshStatus(); + } catch (err) { + showToast(`เกิดข้อผิดพลาด: ${err.message}`, 'error'); + } + }); + + // Persistent Toggles + el.btnSavePersistentToggles.addEventListener('click', async () => { + const sw = el.toggleSwGlobal.checked; + const castorice = el.toggleCastoriceGlobal.checked; + try { + await apiCall('/api/persistent', 'POST', { + enable_sw_global: sw, + enable_castorice_global: castorice + }); + showToast('บันทึกสวิตช์ Global เรียบร้อย!', 'success'); + refreshStatus(); + } catch (err) { + showToast(`เกิดข้อผิดพลาด: ${err.message}`, 'error'); + } + }); + + // Avatar Search & Filters el.avatarSearch.addEventListener('input', renderAvatarsGrid); el.avatarFilterRarity.addEventListener('change', renderAvatarsGrid); + el.avatarFilterElement.addEventListener('change', renderAvatarsGrid); el.btnReloadAvatars.addEventListener('click', loadAvatars); // Ex-Rank All Controls @@ -657,7 +1099,7 @@ function setupEvents() { try { const parsed = JSON.parse(el.jsonEditor.value); el.jsonEditor.value = JSON.stringify(parsed, null, 2); - showToast('JSON beautified', 'info'); + showToast('จัดรูปแบบ JSON สวยงามแล้ว', 'info'); } catch (err) { showToast(t('jsonInvalid'), 'error'); } @@ -672,7 +1114,7 @@ function setupEvents() { a.href = URL.createObjectURL(blob); a.download = `${state.currentJsonMode}-${Date.now()}.json`; a.click(); - showToast(`Downloaded ${a.download}`, 'success'); + showToast(`ดาวน์โหลด ${a.download} เรียบร้อย`, 'success'); }); el.jsonFileInput.addEventListener('change', (e) => { @@ -683,7 +1125,7 @@ function setupEvents() { try { const parsed = JSON.parse(event.target.result); el.jsonEditor.value = JSON.stringify(parsed, null, 2); - showToast(`Loaded ${file.name}`, 'success'); + showToast(`โหลดเนื้อหาไฟล์ ${file.name} เรียบร้อย`, 'success'); } catch (err) { showToast(t('jsonInvalid'), 'error'); } @@ -728,11 +1170,13 @@ function startPingLoop() { // Initialize Application function init() { + setupServerModes(); setupLanguageSelector(); setupTabs(); setupEvents(); applyTranslations(); refreshStatus(); + loadAvatars(); startPingLoop(); }