' +
@@ -1028,12 +721,15 @@
'
' +
'
No job progress
' +
+ '
' +
+
+ '
' +
+ '
No active effects
' +
'
';
document.body.appendChild(el);
makeTabSwitcher(el);
- // Attach click listeners and mod tooltips to stat cells
STAT_DEFS.forEach(d => {
const cell = el.querySelector('.cw-stat-cell:has(#cw-stat-' + d.key + ')');
if (cell) {
@@ -1046,17 +742,10 @@
}
});
- // Attach tooltip and click-menu listeners to all equipment rows
- EQUIP_SLOTS.forEach(s => {
- const rowEl = el.querySelector('#cw-eqrow-' + s.key);
- if (!rowEl) { return; }
- attachTooltip(rowEl);
- rowEl.addEventListener('click', function(e) {
- const menuItems = _equipMenuItems(rowItemData.get(rowEl));
- if (menuItems) { uiMenu(e, menuItems); }
- });
- rowEl.style.cursor = 'pointer';
- });
+ const spBadge = el.querySelector('#cw-badge-sp');
+ if (spBadge) { spBadge.addEventListener('click', () => Client.GMCPRequest('Help stat-train')); }
+ const tpBadge = el.querySelector('#cw-badge-tp');
+ if (tpBadge) { tpBadge.addEventListener('click', () => Client.GMCPRequest('Help train')); }
return el;
}
@@ -1067,7 +756,7 @@
const win = new VirtualWindow('Character', {
dock: 'left',
defaultDocked: true,
- dockedHeight: 390,
+ dockedHeight: 200,
factory() {
const el = createDOM();
return {
@@ -1078,7 +767,7 @@
x: 0,
y: 0,
width: 300,
- height: 580,
+ height: 180,
header: 20,
bottom: 60,
};
@@ -1090,41 +779,51 @@
// -----------------------------------------------------------------------
function updateOverview() {
const info = Client.GMCPStructs.Char && Client.GMCPStructs.Char.Info;
+ if (!info) { return; }
- if (info) {
- const nameEl = document.getElementById('cw-char-name');
- nameEl.innerHTML = '';
+ const nameEl = document.getElementById('cw-char-name');
+ nameEl.innerHTML = '';
- const parts = [info.name, info.class].filter(Boolean);
+ const parts = [info.name, info.class].filter(Boolean);
+ if (parts.length) {
+ nameEl.appendChild(document.createTextNode(parts.join(' \u00b7 ')));
+ }
+
+ if (info.race) {
if (parts.length) {
- nameEl.appendChild(document.createTextNode(parts.join(' · ')));
+ nameEl.appendChild(document.createTextNode(' \u00b7 '));
}
+ const raceSpan = document.createElement('span');
+ raceSpan.className = 'cw-char-race';
+ raceSpan.textContent = info.race;
+ raceSpan.addEventListener('click', () => {
+ Client.GMCPRequest('Help race ' + info.race.toLowerCase());
+ });
+ nameEl.appendChild(raceSpan);
+ }
- if (info.race) {
- if (parts.length) {
- nameEl.appendChild(document.createTextNode(' · '));
- }
- const raceSpan = document.createElement('span');
- raceSpan.className = 'cw-char-race';
- raceSpan.textContent = info.race;
- raceSpan.addEventListener('click', () => {
- Client.GMCPRequest('Help race ' + info.race.toLowerCase());
- });
- nameEl.appendChild(raceSpan);
- }
+ if (!nameEl.textContent) {
+ nameEl.textContent = '\u2014';
+ }
- if (!nameEl.textContent) {
- nameEl.textContent = '—';
- }
+ document.getElementById('cw-char-level').textContent = info.level ? 'Level ' + info.level : 'Level \u2014';
- document.getElementById('cw-char-level').textContent = info.level ? 'Level ' + info.level : 'Level —';
+ const alignEl = document.getElementById('cw-char-alignment');
+ alignEl.textContent = info.alignment || '';
+ const a = (info.alignment || '').toLowerCase();
+ alignEl.className = 'cw-char-alignment ' +
+ (a.includes('good') ? 'cw-align-good' : a.includes('evil') ? 'cw-align-evil' : 'cw-align-neutral');
- const alignEl = document.getElementById('cw-char-alignment');
- alignEl.textContent = info.alignment || '';
- const a = (info.alignment || '').toLowerCase();
- alignEl.className = 'cw-char-alignment ' +
- (a.includes('good') ? 'cw-align-good' : a.includes('evil') ? 'cw-align-evil' : 'cw-align-neutral');
- }
+ const sp = info.skillpoints || 0;
+ const tp = info.trainingpoints || 0;
+ const spEl = document.getElementById('cw-sp');
+ const tpEl = document.getElementById('cw-tp');
+ const spBadge = document.getElementById('cw-badge-sp');
+ const tpBadge = document.getElementById('cw-badge-tp');
+ if (spEl) { spEl.textContent = sp; }
+ if (tpEl) { tpEl.textContent = tp; }
+ if (spBadge) { spBadge.classList.toggle('has-points', sp > 0); }
+ if (tpBadge) { tpBadge.classList.toggle('has-points', tp > 0); }
}
function updateStats() {
@@ -1133,7 +832,7 @@
STAT_DEFS.forEach(def => {
const el = document.getElementById('cw-stat-' + def.key);
- if (el) { el.textContent = stats[def.key] || '—'; }
+ if (el) { el.textContent = stats[def.key] || '\u2014'; }
const mod = stats[def.key + 'mod'];
const modEl = document.getElementById('cw-stat-mod-' + def.key);
@@ -1148,136 +847,6 @@
});
}
- function updateEquipment() {
- const inv = Client.GMCPStructs.Char && Client.GMCPStructs.Char.Inventory;
- if (!inv || !inv.Worn) { return; }
-
- const worn = inv.Worn;
- EQUIP_SLOTS.forEach(slot => {
- const item = worn[slot.key];
- const rowEl = document.getElementById('cw-eqrow-' + slot.key);
- const nameEl = document.getElementById('cw-eq-' + slot.key);
- const badgeEl = document.getElementById('cw-eqb-' + slot.key);
-
- if (!item || !item.name) {
- nameEl.textContent = 'empty';
- nameEl.className = 'cw-equip-name empty';
- badgeEl.style.display = 'none';
- rowItemData.delete(rowEl);
- prevEquipNames[slot.key] = '';
- return;
- }
-
- // Store full item data on the row for the tooltip
- rowItemData.set(rowEl, item);
-
- const isCursed = item.details && item.details.includes('cursed');
- const isQuest = item.details && item.details.includes('quest');
-
- nameEl.textContent = item.name;
- nameEl.className = 'cw-equip-name' + (isCursed ? ' cursed' : isQuest ? ' quest' : '');
-
- if (isCursed) {
- badgeEl.textContent = 'cursed'; badgeEl.className = 'cw-equip-badge cursed'; badgeEl.style.display = '';
- } else if (isQuest) {
- badgeEl.textContent = 'quest'; badgeEl.className = 'cw-equip-badge quest'; badgeEl.style.display = '';
- } else if (item.uses > 0) {
- badgeEl.textContent = item.uses + 'x'; badgeEl.className = 'cw-equip-badge uses'; badgeEl.style.display = '';
- } else {
- badgeEl.style.display = 'none';
- }
- });
- }
-
- function updateBackpack() {
- const inv = Client.GMCPStructs.Char && Client.GMCPStructs.Char.Inventory;
- if (!inv || !inv.Backpack) { return; }
-
- const bp = inv.Backpack;
- const items = bp.items || [];
- const summary = bp.Summary || {};
- const count = summary.count !== undefined ? summary.count : items.length;
- const max = summary.max || 0;
-
- // Update carry capacity header
- const numEl = document.getElementById('cw-bp-num');
- const maxEl = document.getElementById('cw-bp-max');
- if (numEl) {
- numEl.textContent = count;
- numEl.classList.toggle('full', max > 0 && count >= max);
- }
- if (maxEl) { maxEl.textContent = max || '—'; }
-
- const list = document.getElementById('cw-bp-list');
- if (!list) { return; }
-
- // Remove old tooltip registrations for rows about to be replaced
- list.querySelectorAll('.cw-bp-row').forEach(r => rowItemData.delete(r));
- list.innerHTML = '';
-
- if (items.length === 0) {
- list.innerHTML = '
Empty
';
- return;
- }
-
- // Sort: quest items first, then cursed, then alphabetical by name
- const sorted = [...items].sort((a, b) => {
- const aq = a.details && a.details.includes('quest');
- const bq = b.details && b.details.includes('quest');
- if (aq !== bq) { return aq ? -1 : 1; }
- const ac = a.details && a.details.includes('cursed');
- const bc = b.details && b.details.includes('cursed');
- if (ac !== bc) { return ac ? -1 : 1; }
- return (a.name || '').localeCompare(b.name || '');
- });
-
- sorted.forEach(item => {
- const isCursed = item.details && item.details.includes('cursed');
- const isQuest = item.details && item.details.includes('quest');
-
- const row = document.createElement('div');
- row.className = 'cw-bp-row';
-
- const typeEl = document.createElement('span');
- typeEl.className = 'cw-bp-type';
- typeEl.textContent = item.type || '';
-
- const nameEl = document.createElement('span');
- nameEl.className = 'cw-bp-name' + (isCursed ? ' cursed' : isQuest ? ' quest' : '');
- nameEl.textContent = item.name || '';
-
- const badgeEl = document.createElement('span');
- badgeEl.className = 'cw-bp-badge';
- if (isCursed) {
- badgeEl.textContent = 'cursed';
- badgeEl.classList.add('cursed');
- } else if (isQuest) {
- badgeEl.textContent = 'quest';
- badgeEl.classList.add('quest');
- } else if (item.uses > 0) {
- badgeEl.textContent = item.uses + 'x';
- badgeEl.classList.add('uses');
- } else {
- badgeEl.style.display = 'none';
- }
-
- row.appendChild(typeEl);
- row.appendChild(nameEl);
- row.appendChild(badgeEl);
- list.appendChild(row);
-
- // Register item data and attach tooltip — same mechanism as equipment
- rowItemData.set(row, item);
- attachTooltip(row);
- row.style.cursor = 'pointer';
- row.addEventListener('click', function(e) {
- const menuItems = _backpackMenuItems(rowItemData.get(row));
- if (menuItems) { uiMenu(e, menuItems); }
- });
-
- });
- }
-
function updateSkills() {
const skillList = Client.GMCPStructs.Char && Client.GMCPStructs.Char.Skills;
const panel = document.getElementById('cw-skills');
@@ -1288,9 +857,7 @@
return;
}
- // Sort alphabetically by name
const sorted = [...skillList].sort((a, b) => (a.name || '').localeCompare(b.name || ''));
-
panel.innerHTML = '';
sorted.forEach(function(skill) {
@@ -1341,7 +908,6 @@
return;
}
- // Sort: highest completion first, then alphabetical
const sorted = [...jobs].sort(function(a, b) {
if (b.completion !== a.completion) { return b.completion - a.completion; }
return (a.name || '').localeCompare(b.name || '');
@@ -1356,7 +922,6 @@
const item = document.createElement('div');
item.className = 'cjb-item' + (complete ? ' complete' : '');
item.style.cursor = 'help';
-
item.innerHTML =
'