Skip to content

feat: per-town leaderboard popover + explore/instructions polish#35

Merged
harshithmullapudi merged 5 commits into
mainfrom
feat/public-towns-and-instructions-modal
Jul 8, 2026
Merged

feat: per-town leaderboard popover + explore/instructions polish#35
harshithmullapudi merged 5 commits into
mainfrom
feat/public-towns-and-instructions-modal

Conversation

@harshithmullapudi

Copy link
Copy Markdown
Member

Summary

  • Adds a per-town leaderboard: trophy HudButton next to the Population pill opens a right-anchored popover ranking visitors by items + tags earned in that town. Backed by new GET /api/towns/[slug]/leaderboard (gated via resolveViewer, same policy as the items endpoint).
  • /explore polish: drops the technical aura + 10 × visitors scoring formula in the subtitle for a friendlier one-liner. Metadata description tightened to match.
  • TownInstructionsModal: new brown-path hint underneath the door row so first-timers realize the paths lead to buildings.
  • Popover consistency: PopulationPopover + new LeaderboardPopover both use a fixed height: 440 (with 70vh guard) so the frame stays constant regardless of list size.
  • InfoPageShell: "Back to city" → "Back to town" (was already staged on the branch).

Test plan

  • Load a town → confirm the trophy button appears next to the Population pill and stays icon-only.
  • Click the trophy → popover opens right-anchored under the button; Esc, outside-click, and X all dismiss it.
  • Confirm the leaderboard lists visitors sorted by (items + tags) earned in that town. Names + avatars pulled from latest TownActivity.
  • Confirm an unauthenticated / no-cookie viewer hitting /api/towns/<slug>/leaderboard gets a 403.
  • Load /explore → verify the new subtitle text renders and the towns table is unchanged.
  • Open the instructions modal in-town → confirm the new brown-path row shows between "walk into a door" and the aura tip.
  • Open the Population popover → confirm the frame no longer grows or shrinks with the guest/NPC count; contents scroll internally.

🤖 Generated with Claude Code

harshithmullapudi and others added 5 commits July 7, 2026 16:58
…umns

Row now reads as a single Town cell (name + aura bar + @owner) plus one
Score column — the separate Aura and Visits numbers were redundant with
the composite score.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a trophy HudButton next to the Population pill on the town view.
Clicking it opens a right-anchored popover with the town's top visitors
ranked by items + tags earned inside that town. Backed by a new
GET /api/towns/[slug]/leaderboard, gated through resolveViewer (same
policy as the items endpoint). Names + character come from the latest
TownActivity row per subjectKey.

Also:
- /explore subtitle drops the aura + visitors formula for a friendlier
  one-liner; metadata description tightened to match.
- TownInstructionsModal adds a brown-path hint underneath the door row.
- Both popovers now use a fixed height (440) with a 70vh guard so the
  frame no longer grows with content.
- InfoPageShell "Back to city" wording -> "Back to town" (already staged
  on branch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five wins, all in the game module. Together they cut per-frame allocation
churn and per-frame work on the overworld — the surface where the lag
was most visible.

1. Guard boot() with a `cancelled` flag flipped by onSceneLeave. Every
   await in the async boot pipeline (loadPlot, loadManifest,
   loadPlotSprites) checks it before proceeding, so a stale in-flight
   boot from a previous scene entry doesn't land its k.onUpdate handlers
   or k.add game objects on the next scene. This was the accumulator —
   each round-trip could double the camera-follow tick + proximity tick
   + duplicate every building sprite. Only a full page reload reset it.

2. Cache the extras manifest at module scope + drop `cache: "no-store"`.
   Fetched once per page instead of every interior->overworld transition.
   Silent-fail path also flips worldReady=true so TransitionLoading can't
   get stuck if the fetch 5xx's; boot() is wrapped in .catch() with the
   same rescue.

3. Delete three hot-path console.log calls in realtime.ts (per-remote
   position update, publishLocal-queued, publish-ok success). Removed
   string-interpolation garbage every publication + heartbeat.

4. Mutate `entry.parent.pos.x/.y` in place in the remote tween callback
   instead of allocating a fresh k.vec2() per frame per remote. The
   camera-follow pattern in kaplay's own docs uses direct mutation for
   the same reason.

5. Add a per-scene RemotePlayer[] cache in realtime.ts, rebuilt inside
   notify(). getRemotePlayersForScene() is now O(1) instead of O(N)
   per call — the proximity tick (60Hz on the overworld) is the main
   beneficiary.

6. Migrate drawPaths, drawPonds, and building signs from N x k.add() to
   a single container + container.onDraw per surface. Positions are
   pre-baked as Vec2 so the draw callback is allocation-free. Kaplay's
   optimization guide explicitly calls this out ("Game objects have a
   cost. Static visuals should render via onDraw calls, not as objects.")
   On a dense plot this trims thousands of game objects off the per-frame
   update list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ons to the HIGH pass. Neither is user-visible on its own; both
strip per-frame CPU/GC from hot paths.

1. remotePlayer.ts reconcile() no longer allocates a throwaway
   Map<string, RemotePlayer> on every Centrifugo publication. Rewritten
   as a two-pass diff over a Set<string> reused across calls: first pass
   spawns / updates / marks, second pass destroys entries the current
   roster didn't mention. Fires on every remote position update.

2. interior.ts nearestInteract() is memoized on the player's tile key.
   Result is a pure function of player.tile, but ran a linear scan
   over spec.interacts every frame — collapses from 60Hz down to
   whatever the actual tile-change cadence is (a few Hz during
   movement, 0 while standing still).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@harshithmullapudi
harshithmullapudi merged commit 6d53b8c into main Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant