step 10.9: done
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user