fix(dashboard): coins, universe score, imagination, reputation and life from the locale

Pages label the game's currencies and stats with the client's words
(UI_COINS, UI_UNIVERSE_SCORE, UI_IMAGINATION, UI_REPUTATION, UI_HEALTH)
through game.terms and GameText.term instead of writing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 22:06:37 -05:00
parent ade116c985
commit 597269b4c5
12 changed files with 41 additions and 41 deletions

View File

@@ -27,9 +27,9 @@
}).join('');
var r = m.rewards, rewards = [];
if (r) {
if (r.coins) rewards.push(nf.format(r.coins) + ' coins');
if (r.uscore) rewards.push(nf.format(r.uscore) + ' U-score');
if (r.reputation) rewards.push(nf.format(r.reputation) + ' reputation');
if (r.coins) rewards.push(nf.format(r.coins) + ' ' + GameText.term('coins'));
if (r.uscore) rewards.push(nf.format(r.uscore) + ' ' + GameText.term('uscore'));
if (r.reputation) rewards.push(nf.format(r.reputation) + ' ' + GameText.term('reputation'));
r.items.forEach(function (i) { rewards.push(nf.format(i.count) + 'x ' + (i.name || 'LOT ' + i.lot)); });
}
return (tasks ? '<ul class="small mb-1">' + tasks + '</ul>' : '') +

View File

@@ -62,7 +62,7 @@
function describeChanges() {
var body = buildEdit(), parts = [];
['coins', 'uscore', 'level'].forEach(function (k) { if (body[k] !== undefined) parts.push(k === 'uscore' ? 'U-score' : k); });
['coins', 'uscore', 'level'].forEach(function (k) { if (body[k] !== undefined) parts.push(k === 'level' ? k : GameText.term(k)); });
if (body.counts) parts.push(Object.keys(body.counts).length + ' count(s)');
if (body.remove) parts.push(body.remove.length + ' removed');
if (body.add) parts.push(body.add.length + ' added');
@@ -148,8 +148,8 @@
function compare(before, after) {
var rows = [];
[['coins', 'Coins'], ['uscore', 'U-score'], ['level', 'Level'], ['missions_done', 'Missions done']].forEach(function (f) {
if (before[f[0]] !== after[f[0]]) rows.push('<tr><td>' + f[1] + '</td><td>' + nf.format(before[f[0]]) + '</td><td>' + nf.format(after[f[0]]) + '</td></tr>');
[['coins', GameText.term('coins')], ['uscore', GameText.term('uscore')], ['level', 'Level'], ['missions_done', 'Missions done']].forEach(function (f) {
if (before[f[0]] !== after[f[0]]) rows.push('<tr><td>' + esc(f[1]) + '</td><td>' + nf.format(before[f[0]]) + '</td><td>' + nf.format(after[f[0]]) + '</td></tr>');
});
var a = itemsById(before), b = itemsById(after);
Object.keys(a).forEach(function (id) {

View File

@@ -495,14 +495,14 @@
switch (type) {
case 'treasure_hunt':
return '<div class="mb-2"><label class="form-label small" for="evp-' + i + '-lot">Treasure</label><input class="form-control form-control-sm" id="evp-' + i + '-lot" data-search="treasure" data-p="' + i + '" data-f="config.lot" data-value="' + esc(c.lot || '') + '" value="' + esc(c.lot ? 'LOT ' + c.lot : '') + '" placeholder="Search objects"></div>' +
'<div class="row g-2">' + ['count|Treasures|1', 'coins|Coins per find|0', 'radius|Pickup distance|2'].map(function (x) {
'<div class="row g-2">' + ['count|Treasures|1', 'coins|' + esc(GameText.term('coins')) + ' per find|0', 'radius|Pickup distance|2'].map(function (x) {
var bits = x.split('|');
return '<div class="col-4">' + field(i, 'config.' + bits[0], bits[1], c[bits[0]], 'type="number" min="' + bits[2] + '" step="any"') + '</div>';
}).join('') + '</div>' +
'<div class="row g-2"><div class="col-8"><label class="form-label small" for="evp-' + i + '-item">Item reward <span class="text-body-secondary">(optional)</span></label><input class="form-control form-control-sm" id="evp-' + i + '-item" data-search="item" data-p="' + i + '" data-f="config.itemLot" data-value="' + esc(c.itemLot || '') + '" value="' + esc(c.itemLot ? 'LOT ' + c.itemLot : '') + '" placeholder="Search items"></div>' +
'<div class="col-4">' + field(i, 'config.itemCount', 'Count', c.itemCount, 'type="number" min="1" max="999"') + '</div></div>';
case 'bonus':
return '<div class="row g-2">' + ['coins|Coins ×', 'uscore|U-score ×', 'lootChance|Loot chance ×'].map(function (x) {
return '<div class="row g-2">' + ['coins|' + esc(GameText.term('coins')) + ' ×', 'uscore|' + esc(GameText.term('uscore')) + ' ×', 'lootChance|Loot chance ×'].map(function (x) {
var bits = x.split('|');
return '<div class="col-4">' + field(i, 'config.' + bits[0], bits[1], c[bits[0]], 'type="number" min="1" step="0.1"') + '</div>';
}).join('') + '</div>';

View File

@@ -17,7 +17,7 @@
var set = o ? esc(o.price) + ' coins' + (o.periodDays ? ' / ' + esc(o.periodDays) + ' days' : '') +
'<div class="small text-body-secondary">' + esc(o.updated_by) + ', ' + esc(fmt.unix(o.updated_at)) + '</div>' : '<span class="text-body-secondary">Template</span>';
var edit = canManage ? '<form class="d-flex gap-1 justify-content-end" data-map="' + esc(w.mapId) + '">' +
'<input type="number" class="form-control form-control-sm" style="width:7rem" min="0" name="price" placeholder="Coins" value="' + (o ? esc(o.price) : '') + '" aria-label="Price">' +
'<input type="number" class="form-control form-control-sm" style="width:7rem" min="0" name="price" placeholder="' + esc(GameText.term('coins')) + '" value="' + (o ? esc(o.price) : '') + '" aria-label="Price">' +
'<input type="number" class="form-control form-control-sm" style="width:6rem" min="0" name="days" placeholder="Days" value="' + (o && o.periodDays ? esc(o.periodDays) : '') + '" aria-label="Period in days">' +
'<button type="submit" class="btn btn-sm btn-primary">Save</button>' +
(o ? '<button type="button" class="btn btn-sm btn-outline-secondary" data-reset="' + esc(w.mapId) + '">Template</button>' : '') + '</form>' : '';

View File

@@ -246,8 +246,8 @@
sourceTable(document.getElementById(ids.sources), summary, labels);
}
var COIN_LABELS = { title: 'Coins', up: 'Coins gained', down: 'Coins spent' };
var USCORE_LABELS = { title: 'U-score', up: 'U-score gained', down: 'U-score lost' };
var COIN_LABELS = { title: GameText.term('coins'), up: GameText.term('coins') + ' gained', down: GameText.term('coins') + ' spent' };
var USCORE_LABELS = { title: GameText.term('uscore'), up: GameText.term('uscore') + ' gained', down: GameText.term('uscore') + ' lost' };
var ITEM_LABELS = { title: 'Items', up: 'Items created', down: 'Items destroyed' };
function loadCoins() {
@@ -575,7 +575,7 @@
// ---- Saved views ----
var TAB_TARGETS = { coins: '#tabCoins', uscore: '#tabUScore', items: '#tabItems', activity: '#tabActivity', map: '#tabMap', transfers: '#tabTransfers', trace: '#tabTrace', dupes: '#tabDupes', flags: '#tabFlags' };
var TAB_NAMES = { coins: 'Coins', uscore: 'U-Score', items: 'Items', activity: 'Activity', map: 'World Map', transfers: 'Trades & Mail', trace: 'Trace', dupes: 'Duplicates', flags: 'Flags' };
var TAB_NAMES = { coins: GameText.term('coins'), uscore: GameText.term('uscore'), items: 'Items', activity: 'Activity', map: 'World Map', transfers: 'Trades & Mail', trace: 'Trace', dupes: 'Duplicates', flags: 'Flags' };
var views = [], emailReady = false;
function currentTab() {
@@ -699,7 +699,7 @@
transfersTable = serverTable('#transfersTable', '/api/reports/transfers', [
{ data: 'time', orderable: false, render: function (d) { return esc(fmt.unix(d)); } },
{ data: 'method', orderable: false, render: function (d) { return methodBadge(d); } },
{ data: 'lot', orderable: false, render: function (d, t, row) { return d ? itemLink(d, row.name) : '<span class="text-body-secondary">Coins</span>'; } },
{ data: 'lot', orderable: false, render: function (d, t, row) { return d ? itemLink(d, row.name) : '<span class="text-body-secondary">' + esc(GameText.term('coins')) + '</span>'; } },
{ data: 'count', orderable: false, className: 'text-end', render: function (d) { return d ? num(d) : ''; } },
{ data: 'coins', orderable: false, className: 'text-end', render: function (d) { return d ? num(d) : ''; } },
{ data: 'from_character', orderable: false, render: function (d, t, row) { return charLink(d, row.from_name); } },
@@ -852,7 +852,7 @@
el.innerHTML = '<p class="text-body-secondary mb-0">Nothing recorded.</p>';
} else {
el.innerHTML = '<table class="table table-sm align-middle mb-0"><thead><tr><th>Time</th><th>How</th><th>From</th><th>To</th><th>Zone</th>' +
'<th class="text-end">Count</th><th class="text-end">Coins</th><th>Old ID → new ID</th></tr></thead><tbody>' +
'<th class="text-end">Count</th><th class="text-end">' + esc(GameText.term('coins')) + '</th><th>Old ID → new ID</th></tr></thead><tbody>' +
history.map(function (h) {
var marks = [];
if (h.role === 'BRANCH') marks.push('<span class="badge text-bg-light border" title="Another part of a stack this item came from">other part</span>');

View File

@@ -62,7 +62,7 @@
<fieldset class="border rounded p-3 mb-3">
<legend class="fs-6 float-none w-auto px-1 mb-0">Rewards for everyone who helped</legend>
<div class="row g-2">
<div class="col-sm-4"><label class="form-label small" for="chCoins">Coins</label><input type="number" class="form-control form-control-sm" id="chCoins" min="0" max="1000000" value="0"></div>
<div class="col-sm-4"><label class="form-label small" for="chCoins">{{ game.terms.coins }}</label><input type="number" class="form-control form-control-sm" id="chCoins" min="0" max="1000000" value="0"></div>
<div class="col-sm-4"><label class="form-label small" for="chMin">Needs at least</label><input type="number" class="form-control form-control-sm" id="chMin" min="1" value="1"></div>
</div>
<div class="mt-2"><label class="form-label small" for="chItemsSearch">Items (up to 5; one mail each)</label><div data-picker="item" data-multi="5" id="chItems"></div>

View File

@@ -152,19 +152,19 @@
</div>
<div class="col-6 col-md-3">
<div class="card text-center"><div class="card-body py-3">
<div class="text-body-secondary small text-uppercase mb-1">Coins</div>
<div class="text-body-secondary small text-uppercase mb-1">{{ game.terms.coins }}</div>
<div class="fs-3 fw-bold">{{ character.stats.coins }}</div>
</div></div>
</div>
<div class="col-6 col-md-3">
<div class="card text-center"><div class="card-body py-3">
<div class="text-body-secondary small text-uppercase mb-1">U-Score</div>
<div class="text-body-secondary small text-uppercase mb-1">{{ game.terms.uscore }}</div>
<div class="fs-3 fw-bold">{{ character.stats.universe_score }}</div>
</div></div>
</div>
<div class="col-6 col-md-3">
<div class="card text-center"><div class="card-body py-3">
<div class="text-body-secondary small text-uppercase mb-1">Reputation</div>
<div class="text-body-secondary small text-uppercase mb-1">{{ game.terms.reputation }}</div>
<div class="fs-3 fw-bold">{{ character.stats.reputation }}</div>
</div></div>
</div>
@@ -196,7 +196,7 @@
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<div class="d-flex justify-content-between"><span>Health</span><span>{{ character.stats.health }} / {{ character.stats.max_health }}</span></div>
<div class="d-flex justify-content-between"><span>{{ game.terms.life }}</span><span>{{ character.stats.health }} / {{ character.stats.max_health }}</span></div>
<div class="progress mt-1" style="height: 8px;">{% if character.stats.max_health > 0 %}<div class="progress-bar bg-danger" style="width: {{ character.stats.health / character.stats.max_health * 100 }}%"></div>{% endif %}</div>
</div>
<div class="col-md-4">
@@ -204,7 +204,7 @@
<div class="progress mt-1" style="height: 8px;">{% if character.stats.max_armor > 0 %}<div class="progress-bar bg-secondary" style="width: {{ character.stats.armor / character.stats.max_armor * 100 }}%"></div>{% endif %}</div>
</div>
<div class="col-md-4">
<div class="d-flex justify-content-between"><span>Imagination</span><span>{{ character.stats.imagination }} / {{ character.stats.max_imagination }}</span></div>
<div class="d-flex justify-content-between"><span>{{ game.terms.imagination }}</span><span>{{ character.stats.imagination }} / {{ character.stats.max_imagination }}</span></div>
<div class="progress mt-1" style="height: 8px;">{% if character.stats.max_imagination > 0 %}<div class="progress-bar bg-primary" style="width: {{ character.stats.imagination / character.stats.max_imagination * 100 }}%"></div>{% endif %}</div>
</div>
</div>
@@ -319,7 +319,7 @@
<div class="modal-body">
<div class="table-responsive">
<table class="table table-striped table-hover mb-0">
<thead><tr><th>Zone</th><th>Achievements</th><th>Coins</th><th>Enemies</th></tr></thead>
<thead><tr><th>Zone</th><th>Achievements</th><th>{{ game.terms.coins }}</th><th>Enemies</th></tr></thead>
<tbody>
{% for zs in character.stats.zone_stats %}
<tr>
@@ -381,8 +381,8 @@
<div class="modal-body">
<div class="alert alert-warning small">Saving disconnects the player if they are online. The current version is kept in the history, so it can be put back.</div>
<div class="row g-2 mb-3">
<div class="col-sm-4"><label class="form-label small" for="editCoins">Coins</label><input type="number" min="0" max="2000000000" class="form-control" id="editCoins"></div>
<div class="col-sm-4"><label class="form-label small" for="editUScore">U-score</label><input type="number" min="0" max="2000000000" class="form-control" id="editUScore"></div>
<div class="col-sm-4"><label class="form-label small" for="editCoins">{{ game.terms.coins }}</label><input type="number" min="0" max="2000000000" class="form-control" id="editCoins"></div>
<div class="col-sm-4"><label class="form-label small" for="editUScore">{{ game.terms.uscore }}</label><input type="number" min="0" max="2000000000" class="form-control" id="editUScore"></div>
<div class="col-sm-4"><label class="form-label small" for="editLevel">Level</label><input type="number" min="1" max="45" class="form-control" id="editLevel"></div>
</div>
<ul class="nav nav-tabs" id="editInventoryTabs" role="tablist"></ul>
@@ -530,9 +530,9 @@ document.querySelectorAll('[data-restriction]').forEach(function (input) {
function statsHtml(stats) {
var lines = [];
if (stats.life) lines.push('+' + esc(stats.life) + ' Health');
if (stats.life) lines.push('+' + esc(stats.life) + ' ' + esc(GameText.term('life')));
if (stats.armor) lines.push('+' + esc(stats.armor) + ' Armor');
if (stats.imagination) lines.push('+' + esc(stats.imagination) + ' Imagination');
if (stats.imagination) lines.push('+' + esc(stats.imagination) + ' ' + esc(GameText.term('imagination')));
var html = lines.length ? '<div>' + lines.join('<br>') + '</div>' : '';
stats.skills.forEach(function (skill) {
html += '<div class="d-flex gap-1 mt-1 text-start">' +

View File

@@ -6,7 +6,7 @@
<div class="d-flex flex-wrap justify-content-between align-items-end gap-3 mb-3">
<div>
<h2 class="mb-1">Live Events</h2>
<p class="text-body-secondary mb-0">Start something fun in game for a while: a treasure hunt, bonus coins, U-score or loot, an invasion or a
<p class="text-body-secondary mb-0">Start something fun in game for a while: a treasure hunt, bonus {{ game.terms.coins }}, {{ game.terms.uscore }} or loot, an invasion or a
celebration. It is announced in its zones, runs in every instance of them (worlds that open later join in), and everything it spawned is removed
when it ends. Players can follow it with <code>/challenge</code>. To run one at set times, together with other things, add it to a
<a href="/events">scheduled event</a>.</p>
@@ -36,7 +36,7 @@
<label class="form-label" for="evType">Kind of event</label>
<select class="form-select" id="evType">
<option value="treasure_hunt">Treasure hunt: hide objects for players to find</option>
<option value="bonus">Bonus: more coins, U-score or loot</option>
<option value="bonus">Bonus: more {{ game.terms.coins }}, {{ game.terms.uscore }} or loot</option>
<option value="invasion">Invasion: waves of enemies</option>
<option value="celebration">Celebration: effects on every player</option>
</select>
@@ -67,7 +67,7 @@
<div class="row g-2">
<div class="col-sm-3"><label class="form-label small" for="thCount">How many (per instance)</label><input type="number" class="form-control form-control-sm" id="thCount" min="1" max="50" value="10"></div>
<div class="col-sm-3"><label class="form-label small" for="thRadius">Pickup distance</label><input type="number" class="form-control form-control-sm" id="thRadius" min="2" max="15" value="4"></div>
<div class="col-sm-3"><label class="form-label small" for="thCoins">Coins per find</label><input type="number" class="form-control form-control-sm" id="thCoins" min="0" max="100000" value="250"></div>
<div class="col-sm-3"><label class="form-label small" for="thCoins">{{ game.terms.coins }} per find</label><input type="number" class="form-control form-control-sm" id="thCoins" min="0" max="100000" value="250"></div>
<div class="col-sm-3"><label class="form-label small" for="thItemCount">Items per find</label><input type="number" class="form-control form-control-sm" id="thItemCount" min="1" max="999" value="1"></div>
</div>
<div class="mt-2"><label class="form-label small" for="thItemSearch">Item reward <span class="text-body-secondary">(optional)</span></label><div data-picker="item" id="thItem"></div></div>
@@ -79,8 +79,8 @@
<fieldset class="border rounded p-3" data-for="bonus">
<legend class="fs-6 float-none w-auto px-1 mb-0">Multipliers</legend>
<div class="row g-2">
<div class="col-sm-4"><label class="form-label small" for="bnCoins">Coins from loot</label><input type="number" class="form-control form-control-sm" id="bnCoins" min="1" max="10" step="0.25" value="2"></div>
<div class="col-sm-4"><label class="form-label small" for="bnUscore">U-score from missions</label><input type="number" class="form-control form-control-sm" id="bnUscore" min="1" max="10" step="0.25" value="1"></div>
<div class="col-sm-4"><label class="form-label small" for="bnCoins">{{ game.terms.coins }} from loot</label><input type="number" class="form-control form-control-sm" id="bnCoins" min="1" max="10" step="0.25" value="2"></div>
<div class="col-sm-4"><label class="form-label small" for="bnUscore">{{ game.terms.uscore }} from missions</label><input type="number" class="form-control form-control-sm" id="bnUscore" min="1" max="10" step="0.25" value="1"></div>
<div class="col-sm-4"><label class="form-label small" for="bnLoot">Loot drop chance</label><input type="number" class="form-control form-control-sm" id="bnLoot" min="1" max="10" step="0.25" value="1"></div>
</div>
<p class="small text-body-secondary mb-0 mt-2">Leave the zones empty for every world. Bonuses running at once don't stack: the biggest one counts.</p>
@@ -132,7 +132,7 @@
if (e.type === 'invasion') return nf.format(t.kills || 0) + ' of ' + nf.format(t.spawned || 0) + ' invaders smashed, ' + nf.format(t.wavesSent || 0) + ' waves';
if (e.type === 'celebration') return nf.format(t.bursts || 0) + ' bursts';
var c = e.config || {};
return 'coins x' + c.coins + ', U-score x' + c.uscore + ', loot x' + c.lootChance;
return GameText.term('coins') + ' x' + c.coins + ', ' + GameText.term('uscore') + ' x' + c.uscore + ', loot x' + c.lootChance;
}
function instanceStatus(e, i) {
var s = i.status || {};

View File

@@ -48,7 +48,7 @@
<div class="card-header"><h5 class="mb-0">Pending Properties</h5></div>
<div class="card-body">
<table id="pendingPropsTable" class="table table-striped table-hover table-stack">
<thead><tr><th>ID</th><th>Name</th><th>Owner</th><th>Models</th><th>Reputation</th><th>Zone</th><th>Actions</th></tr></thead>
<thead><tr><th>ID</th><th>Name</th><th>Owner</th><th>Models</th><th>{{ game.terms.reputation }}</th><th>Zone</th><th>Actions</th></tr></thead>
<tbody></tbody>
</table>
</div>

View File

@@ -44,7 +44,7 @@
<th>Name</th>
<th>Owner</th>
<th>Moderation</th>
<th>Reputation</th>
<th>{{ game.terms.reputation }}</th>
<th title="Load estimate of the placed models, reported by the owner's game client">Performance cost</th>
<th>Zone</th>
<th>Actions</th>

View File

@@ -152,7 +152,7 @@ function renderDetails(p) {
field('Privacy', esc(Labels.name('privacy', p.privacy_option) || p.privacy_option)),
field('Moderation', p.mod_approved ? fmt.badge('Approved', 'success') : (p.rejection_reason ? fmt.badge('Rejected', 'danger') : fmt.badge('Unapproved', 'warning')) +
(p.rejection_reason ? ' <span class="small">' + esc(p.rejection_reason) + '</span>' : '')),
field('Reputation', num(p.reputation)),
field(GameText.term('reputation'), num(p.reputation)),
field('Performance cost', perf ? num(perf, 2) : muted('Not reported'), 'Load estimate of the placed models, reported by the owner\'s game client after building'),
field('Claimed', esc(fmt.unix(p.claimed))),
field('Last updated', esc(fmt.unix(p.last_updated))),
@@ -184,7 +184,7 @@ function renderDetails(p) {
field('Clone limit', num(t.cloneLimit)),
field('Achievement required', t.achievementRequired ? esc(t.achievementRequired) : muted('None')),
field('Max build height', num(t.maxBuildHeight, 1)),
field('Reputation per minute', num(t.reputationPerMinute)),
field(GameText.term('reputation') + ' per minute', num(t.reputationPerMinute)),
field('Zone position', [t.zoneX, t.zoneY, t.zoneZ].every((v) => typeof v === 'number') ? esc([t.zoneX, t.zoneY, t.zoneZ].map((v) => v.toFixed(1)).join(', ')) : muted('-')),
field('Build area outline', Array.isArray(t.path) ? esc(t.path.length + ' points') : muted('-'))
].concat(t.localized_description ? [ '<div class="col-12"><div class="label">Template description</div><div class="value small">' + esc(t.localized_description) + '</div></div>' ] : []).join('');

View File

@@ -6,7 +6,7 @@
<div class="d-flex flex-wrap align-items-end justify-content-between gap-3 mb-3">
<div>
<h2 class="mb-1">Economy</h2>
<p class="text-body-secondary mb-0">Where coins, U-score and items come from and go. World servers write totals every few seconds, and the page updates as they do.</p>
<p class="text-body-secondary mb-0">Where {{ game.terms.coins }}, {{ game.terms.uscore }} and items come from and go. World servers write totals every few seconds, and the page updates as they do.</p>
</div>
<div class="d-flex flex-wrap align-items-center gap-3" id="reportFilters">
<div class="btn-group btn-group-sm" role="group" aria-label="Date range">
@@ -28,8 +28,8 @@
</div>
<ul class="nav nav-tabs mb-3" role="tablist">
<li class="nav-item" role="presentation"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tabCoins" type="button" role="tab">Coins</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabUScore" type="button" role="tab">U-Score</button></li>
<li class="nav-item" role="presentation"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tabCoins" type="button" role="tab">{{ game.terms.coins }}</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabUScore" type="button" role="tab">{{ game.terms.uscore }}</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabItems" type="button" role="tab">Items</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabActivity" type="button" role="tab" id="activityTabBtn">Activity</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabMap" type="button" role="tab" id="mapTabBtn">World Map</button></li>
@@ -44,7 +44,7 @@
<div class="tab-pane fade show active" id="tabCoins" role="tabpanel">
<div class="row g-3 mb-3" id="coinTiles"></div>
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center"><h5 class="mb-0">Coins per day</h5><div class="d-flex align-items-center gap-3"><div class="viz-legend" data-legend="coins"></div><a class="btn btn-sm btn-outline-secondary" data-csv="currency" download>CSV</a></div></div>
<div class="card-header d-flex justify-content-between align-items-center"><h5 class="mb-0">{{ game.terms.coins }} per day</h5><div class="d-flex align-items-center gap-3"><div class="viz-legend" data-legend="coins"></div><a class="btn btn-sm btn-outline-secondary" data-csv="currency" download>CSV</a></div></div>
<div class="card-body"><div class="viz" id="coinChart"></div>
<details class="mt-2"><summary class="small text-body-secondary">Show as table</summary><div class="table-responsive mt-2" id="coinDaily"></div></details>
</div>
@@ -59,7 +59,7 @@
<div class="tab-pane fade" id="tabUScore" role="tabpanel">
<div class="row g-3 mb-3" id="uscoreTiles"></div>
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center"><h5 class="mb-0">U-score per day</h5><div class="d-flex align-items-center gap-3"><div class="viz-legend" data-legend="uscore"></div><a class="btn btn-sm btn-outline-secondary" data-csv="uscore" download>CSV</a></div></div>
<div class="card-header d-flex justify-content-between align-items-center"><h5 class="mb-0">{{ game.terms.uscore }} per day</h5><div class="d-flex align-items-center gap-3"><div class="viz-legend" data-legend="uscore"></div><a class="btn btn-sm btn-outline-secondary" data-csv="uscore" download>CSV</a></div></div>
<div class="card-body"><div class="viz" id="uscoreChart"></div>
<details class="mt-2"><summary class="small text-body-secondary">Show as table</summary><div class="table-responsive mt-2" id="uscoreDaily"></div></details>
</div>
@@ -179,7 +179,7 @@
</form>
<div class="card"><div class="card-body">
<table id="transfersTable" class="table table-striped table-hover w-100">
<thead><tr><th>Time</th><th>How</th><th>Item</th><th>Count</th><th>Coins</th><th>From</th><th>To</th><th>Object ID <span class="fw-normal small text-body-secondary">(click to trace)</span></th></tr></thead>
<thead><tr><th>Time</th><th>How</th><th>Item</th><th>Count</th><th>{{ game.terms.coins }}</th><th>From</th><th>To</th><th>Object ID <span class="fw-normal small text-body-secondary">(click to trace)</span></th></tr></thead>
<tbody></tbody>
</table>
</div></div>