feat: add mana tracker panel to inventory
Derive equipped item mana state and time-remaining data in inventory-service, then render a Mana panel inside the inventory window with live icon, state, mana, and countdown display.
This commit is contained in:
parent
4e73a5d07d
commit
0e8186b8e5
4 changed files with 3930 additions and 2283 deletions
127
static/script.js
127
static/script.js
|
|
@ -1484,6 +1484,108 @@ function renderInventoryState(state) {
|
|||
}
|
||||
state.itemGrid.appendChild(cell);
|
||||
}
|
||||
|
||||
renderInventoryManaPanel(state);
|
||||
}
|
||||
|
||||
function getManaTrackedItems(state) {
|
||||
if (!state || !state.items) return [];
|
||||
|
||||
const snapshotMs = Date.now();
|
||||
return state.items
|
||||
.filter(item => (item.current_wielded_location || 0) > 0)
|
||||
.filter(item => item.is_mana_tracked || item.current_mana !== undefined || item.max_mana !== undefined || item.spellcraft !== undefined)
|
||||
.map(item => {
|
||||
const result = { ...item };
|
||||
if (result.mana_time_remaining_seconds !== undefined && result.mana_time_remaining_seconds !== null) {
|
||||
const snapshotUtc = result.mana_snapshot_utc ? Date.parse(result.mana_snapshot_utc) : NaN;
|
||||
if (!Number.isNaN(snapshotUtc)) {
|
||||
const elapsed = Math.max(0, Math.floor((snapshotMs - snapshotUtc) / 1000));
|
||||
result.live_mana_time_remaining_seconds = Math.max((result.mana_time_remaining_seconds || 0) - elapsed, 0);
|
||||
} else {
|
||||
result.live_mana_time_remaining_seconds = result.mana_time_remaining_seconds;
|
||||
}
|
||||
} else {
|
||||
result.live_mana_time_remaining_seconds = null;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aRemaining = a.live_mana_time_remaining_seconds;
|
||||
const bRemaining = b.live_mana_time_remaining_seconds;
|
||||
if (aRemaining === null && bRemaining === null) return (a.name || '').localeCompare(b.name || '');
|
||||
if (aRemaining === null) return 1;
|
||||
if (bRemaining === null) return -1;
|
||||
if (aRemaining !== bRemaining) return aRemaining - bRemaining;
|
||||
return (a.name || '').localeCompare(b.name || '');
|
||||
});
|
||||
}
|
||||
|
||||
function formatManaRemaining(totalSeconds) {
|
||||
if (totalSeconds === null || totalSeconds === undefined) return '--';
|
||||
const safeSeconds = Math.max(0, Math.floor(totalSeconds));
|
||||
const hours = Math.floor(safeSeconds / 3600);
|
||||
const minutes = Math.floor((safeSeconds % 3600) / 60);
|
||||
return `${hours}h${String(minutes).padStart(2, '0')}m`;
|
||||
}
|
||||
|
||||
function renderInventoryManaPanel(state) {
|
||||
if (!state || !state.manaListBody || !state.manaSummary) return;
|
||||
|
||||
const items = getManaTrackedItems(state);
|
||||
state.manaListBody.innerHTML = '';
|
||||
|
||||
if (items.length === 0) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'inv-mana-empty';
|
||||
empty.textContent = 'No equipped mana-bearing items';
|
||||
state.manaListBody.appendChild(empty);
|
||||
state.manaSummary.textContent = 'Mana: 0 tracked';
|
||||
return;
|
||||
}
|
||||
|
||||
const activeCount = items.filter(item => item.mana_state === 'active').length;
|
||||
const lowCount = items.filter(item => (item.live_mana_time_remaining_seconds || 0) > 0 && item.live_mana_time_remaining_seconds <= 7200).length;
|
||||
state.manaSummary.textContent = `Mana: ${items.length} tracked, ${activeCount} active, ${lowCount} low`;
|
||||
|
||||
items.forEach(item => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'inv-mana-row';
|
||||
|
||||
const iconWrap = document.createElement('div');
|
||||
iconWrap.className = 'inv-mana-icon';
|
||||
iconWrap.appendChild(createInventorySlot(item));
|
||||
|
||||
const nameEl = document.createElement('div');
|
||||
nameEl.className = 'inv-mana-name';
|
||||
nameEl.textContent = item.name || item.Name || 'Unknown Item';
|
||||
|
||||
const stateEl = document.createElement('div');
|
||||
const stateName = item.mana_state || 'unknown';
|
||||
stateEl.className = `inv-mana-state mana-state-${stateName}`;
|
||||
stateEl.textContent = stateName.replace(/_/g, ' ');
|
||||
|
||||
const manaEl = document.createElement('div');
|
||||
manaEl.className = 'inv-mana-value';
|
||||
if (item.current_mana !== undefined && item.max_mana !== undefined) {
|
||||
manaEl.textContent = `${item.current_mana} / ${item.max_mana}`;
|
||||
} else if (item.mana_display) {
|
||||
manaEl.textContent = item.mana_display;
|
||||
} else {
|
||||
manaEl.textContent = '--';
|
||||
}
|
||||
|
||||
const timeEl = document.createElement('div');
|
||||
timeEl.className = 'inv-mana-time';
|
||||
timeEl.textContent = formatManaRemaining(item.live_mana_time_remaining_seconds);
|
||||
|
||||
row.appendChild(iconWrap);
|
||||
row.appendChild(nameEl);
|
||||
row.appendChild(stateEl);
|
||||
row.appendChild(manaEl);
|
||||
row.appendChild(timeEl);
|
||||
state.manaListBody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function showInventoryWindow(name) {
|
||||
|
|
@ -1559,6 +1661,23 @@ function showInventoryWindow(name) {
|
|||
|
||||
const bottomSection = document.createElement('div');
|
||||
bottomSection.className = 'inv-bottom-section';
|
||||
|
||||
const itemSection = document.createElement('div');
|
||||
itemSection.className = 'inv-item-section';
|
||||
|
||||
const manaPanel = document.createElement('div');
|
||||
manaPanel.className = 'inv-mana-panel';
|
||||
const manaHeader = document.createElement('div');
|
||||
manaHeader.className = 'inv-mana-header';
|
||||
manaHeader.textContent = 'Mana';
|
||||
const manaSummary = document.createElement('div');
|
||||
manaSummary.className = 'inv-mana-summary';
|
||||
manaSummary.textContent = 'Mana: loading';
|
||||
const manaListBody = document.createElement('div');
|
||||
manaListBody.className = 'inv-mana-list';
|
||||
manaPanel.appendChild(manaHeader);
|
||||
manaPanel.appendChild(manaSummary);
|
||||
manaPanel.appendChild(manaListBody);
|
||||
|
||||
const contentsHeader = document.createElement('div');
|
||||
contentsHeader.className = 'inv-contents-header';
|
||||
|
|
@ -1567,8 +1686,10 @@ function showInventoryWindow(name) {
|
|||
const itemGrid = document.createElement('div');
|
||||
itemGrid.className = 'inv-item-grid';
|
||||
|
||||
bottomSection.appendChild(contentsHeader);
|
||||
bottomSection.appendChild(itemGrid);
|
||||
itemSection.appendChild(contentsHeader);
|
||||
itemSection.appendChild(itemGrid);
|
||||
bottomSection.appendChild(itemSection);
|
||||
bottomSection.appendChild(manaPanel);
|
||||
|
||||
invContent.appendChild(topSection);
|
||||
invContent.appendChild(bottomSection);
|
||||
|
|
@ -1613,6 +1734,8 @@ function showInventoryWindow(name) {
|
|||
burdenFill: burdenFill,
|
||||
burdenLabel: burdenLabel,
|
||||
contentsHeader: contentsHeader,
|
||||
manaSummary: manaSummary,
|
||||
manaListBody: manaListBody,
|
||||
characterName: name
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue