step 10.9: done

This commit is contained in:
bepis
2026-10-23 01:06:48 +11:00
parent bf0b3867bd
commit 28210ed56c
6 changed files with 464 additions and 15 deletions
+101
View File
@@ -1022,3 +1022,104 @@ button:hover { background: #c73650; }
background: var(--accent, #e2822b);
color: #fff;
}
/* ===== Stat allocation UI ===== */
#allocation-section {
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid #333;
}
#allocation-section h4 {
color: #e94560;
font-size: 0.875rem;
margin-bottom: 0.5rem;
}
.alloc-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.2rem 0;
font-size: 0.8rem;
}
.alloc-label {
color: #a0a0a0;
min-width: 2.5em;
text-transform: uppercase;
}
.alloc-value {
font-weight: 600;
color: #eadfce;
min-width: 1.5em;
text-align: right;
}
.alloc-btn {
width: 24px;
height: 24px;
padding: 0;
margin: 0 0.35rem;
font-size: 1rem;
font-weight: bold;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
background: #0f3460;
border: 1px solid #4a7fb0;
border-radius: 3px;
color: #4a90d9;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.alloc-btn:hover:not(:disabled) {
background: #4a7fb0;
color: #fff;
}
.alloc-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
border-color: #444;
color: #666;
}
#alloc-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid #1a1a2e;
}
.alloc-total {
font-size: 0.8rem;
color: #a0a0a0;
}
.btn-alloc-apply {
background: #e94560;
color: #fff;
border: none;
border-radius: 4px;
padding: 0.35rem 1rem;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
}
.btn-alloc-apply:hover:not(:disabled) {
background: #c73650;
}
.btn-alloc-apply:disabled {
background: #444;
color: #888;
cursor: not-allowed;
}
+9
View File
@@ -70,6 +70,15 @@
<span>Crit: <span id="derived-crit"></span>%</span>
</div>
</div>
<!-- Stat allocation UI (shown when unspent points > 0) -->
<div id="allocation-section" style="display:none;">
<h4>Allocate Points (<span id="unspent-points">0</span> left)</h4>
<div id="alloc-rows"></div>
<div id="alloc-actions">
<div id="alloc-total" class="alloc-total">Allocated: 0</div>
<button id="alloc-apply-btn" class="btn-alloc-apply" disabled>Apply</button>
</div>
</div>
</section>
<!-- Center column: Tabs + action area -->
+149
View File
@@ -167,6 +167,9 @@ async function loadCharacter() {
// Update energy bar and start countdown
updateEnergyBar(energy.current, energy.max, energy.nextTickInSec);
startCountdown();
// Initialize allocation UI
initAllocationUI(character.unspent_points);
} catch (err) {
// Not authenticated — redirect to login
window.location.href = '/login.html';
@@ -849,6 +852,146 @@ function startExpeditionSync() {
}, 5000);
}
// ===== Stat allocation UI =====
const STAT_KEYS = ['str', 'agi', 'int', 'vit'];
const STAT_LABELS = { str: 'STR', agi: 'AGI', int: 'INT', vit: 'VIT' };
// Temporary allocation state (what the user has clicked, not yet sent)
let allocTemp = { str: 0, agi: 0, int: 0, vit: 0 };
/**
* Build the allocation rows (one per stat) with + buttons.
*/
function buildAllocationRows() {
const container = document.getElementById('alloc-rows');
if (!container) return;
container.replaceChildren();
for (const key of STAT_KEYS) {
const row = document.createElement('div');
row.className = 'alloc-row';
const label = document.createElement('span');
label.className = 'alloc-label';
label.textContent = STAT_LABELS[key];
const value = document.createElement('span');
value.className = 'alloc-value';
value.id = 'alloc-val-' + key;
value.textContent = '0';
const btn = document.createElement('button');
btn.className = 'alloc-btn';
btn.type = 'button';
btn.textContent = '+';
btn.dataset.stat = key;
btn.addEventListener('click', handleAllocIncrement);
row.appendChild(label);
row.appendChild(value);
row.appendChild(btn);
container.appendChild(row);
}
}
/**
* Handle + button click: increment temp allocation for that stat.
*/
function handleAllocIncrement(e) {
const stat = e.target.dataset.stat;
if (!stat) return;
allocTemp[stat]++;
document.getElementById('alloc-val-' + stat).textContent = allocTemp[stat];
updateAllocState();
}
/**
* Update button states and apply button enable/disable.
*/
function updateAllocState() {
const total = STAT_KEYS.reduce((sum, k) => sum + allocTemp[k], 0);
const totalEl = document.getElementById('alloc-total');
const applyBtn = document.getElementById('alloc-apply-btn');
if (totalEl) totalEl.textContent = 'Allocated: ' + total;
// Enable apply only when at least one point allocated and unspent points remain
const unspent = parseInt(document.getElementById('unspent-points').textContent, 10) || 0;
const canApply = total > 0 && total <= unspent;
if (applyBtn) applyBtn.disabled = !canApply;
// Disable + buttons when total would exceed unspent
for (const key of STAT_KEYS) {
const btn = document.querySelector('.alloc-btn[data-stat="' + key + '"]');
if (btn) {
btn.disabled = (total >= unspent);
}
}
}
/**
* Handle apply button: send allocation to server, refresh stats.
*/
async function handleAllocApply() {
const applyBtn = document.getElementById('alloc-apply-btn');
if (applyBtn.disabled) return;
// Build allocation object (only stats that were incremented)
const allocation = {};
let hasPoints = false;
for (const key of STAT_KEYS) {
if (allocTemp[key] > 0) {
allocation[key] = allocTemp[key];
hasPoints = true;
}
}
if (!hasPoints) return;
applyBtn.disabled = true;
applyBtn.textContent = 'Applying...';
try {
const data = await api.post('/character/allocate', allocation);
if (!data) {
showToast('Allocation failed.', 'error');
return;
}
showToast('Stats allocated!', 'success');
// Reset temp allocation
allocTemp = { str: 0, agi: 0, int: 0, vit: 0 };
// Reload character to get updated stats
await loadCharacter();
} catch (err) {
showToast(err.message || 'Allocation failed.', 'error');
// Reload to restore state
await loadCharacter();
}
}
/**
* Initialize or refresh the allocation UI based on current character data.
*/
function initAllocationUI(unspentPoints) {
const section = document.getElementById('allocation-section');
if (!section) return;
if (unspentPoints > 0) {
section.style.display = 'block';
document.getElementById('unspent-points').textContent = unspentPoints;
buildAllocationRows();
updateAllocState();
} else {
section.style.display = 'none';
allocTemp = { str: 0, agi: 0, int: 0, vit: 0 };
}
}
// ===== Init =====
document.addEventListener('DOMContentLoaded', async () => {
await loadCharacter();
@@ -861,6 +1004,12 @@ document.addEventListener('DOMContentLoaded', async () => {
document.querySelectorAll('.tab').forEach((tab) => {
tab.addEventListener('click', () => switchTab(tab.dataset.tab));
});
// Allocation apply button
const applyBtn = document.getElementById('alloc-apply-btn');
if (applyBtn) {
applyBtn.addEventListener('click', handleAllocApply);
}
});
// Logout handler