Fix log entries appearing to lag one action behind
All checks were successful
Docker / build-and-push (push) Successful in 1m16s

Real bug, two parts:

1. renderLog appended the newest entry LAST in the DOM (so it could
   get its own element for the typewriter effect), but log.scrollTop
   = 0 scrolls to the TOP, where the older entries now sat. The
   newest line rendered below the fold, invisible until enough later
   entries pushed it into view — exactly "nothing appears until the
   next event." Fixed by putting the newest entry first.

2. Separately, the typewriter itself was applying to every log kind,
   including the player's own actions (upgrades, visits). Those took
   1-2s to fully print, so even after fixing the ordering, taking a
   second action while the first was still typing would silently
   snap the first to full text right as the second started — still
   reads as a one-action lag. Now only narrative kinds (event, kaos)
   animate; upgrades and visits confirm instantly. Also sped up the
   animation itself (4-8ms/char, was 8-18ms) so even narrative beats
   don't drag.

Also fixed Malmö universitet's map marker — it was placed near
Hyllie, nowhere near the actual campus at Universitetsholmen by the
harbor.

Verified via headless browser: a kaos event and a free location
visit both now appear at the top of the log immediately, not after
a subsequent action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-08-01 22:14:06 +02:00
parent b610d27542
commit 3a741a7bfc
2 changed files with 16 additions and 6 deletions

View File

@@ -54,7 +54,7 @@ const LOCATIONS: Record<string, LocationInfo> = {
blurb: 'Marknad, dofter från femtio kök samtidigt, musik från tre högtalare som aldrig riktigt spelar samma låt.',
},
universitet: {
lat: 55.5721, lng: 12.9998, label: 'Malmö universitet',
lat: 55.6083, lng: 12.9974, label: 'Malmö universitet',
blurb: 'Bibliotek och föreläsningssalar. Ett rum för sällan efterfrågade samlingar som er kod alltid fungerar till.',
},
};

View File

@@ -424,22 +424,32 @@ function renderLog(state: State): void {
// state.log is newest-first (unshift). The newest line gets its own
// element so it can type itself out; everything else renders plainly.
// It has to come FIRST in the DOM to match scrollTop = 0 below — it
// previously came last, which put the newest entry below the fold,
// scrolled out of view until enough later entries pushed it up.
const [newest, ...rest] = state.log;
log.innerHTML = rest.map(e => `<div class="log-entry ${e.kind}">${e.text}</div>`).join('')
+ (newest ? `<div class="log-entry ${newest.kind}" id="log-newest"></div>` : '');
log.innerHTML = (newest ? `<div class="log-entry ${newest.kind}" id="log-newest"></div>` : '')
+ rest.map(e => `<div class="log-entry ${e.kind}">${e.text}</div>`).join('');
log.scrollTop = 0;
if (typewriterTimer !== undefined) { window.clearInterval(typewriterTimer); typewriterTimer = undefined; }
if (newest) {
const el = document.getElementById('log-newest')!;
if (isFirstRender || state.phase !== 'playing') {
// Don't animate the save you just loaded, or the ending screen's line.
// Only narrative beats (story events, kaos) get the typewriter — the
// player's own actions (upgrades, visits) confirm instantly. Animating
// those too made the log feel like it was lagging a full action behind:
// the reveal took 1-2s, and if you acted again before it finished, the
// old line would silently snap to full text right as the new one
// started typing, which read exactly like "nothing happened until the
// next event."
const animate = !isFirstRender && state.phase === 'playing' && (newest.kind === 'event' || newest.kind === 'kaos');
if (!animate) {
el.textContent = newest.text;
} else {
const dreadIntensity = Math.min(1, state.dread / 150);
const text = newest.text;
const stepMs = text.length > 120 ? 8 : 18;
const stepMs = text.length > 90 ? 4 : 8;
let i = 0;
typewriterTimer = window.setInterval(() => {
i += 1;