Render forged rival art on Forge Clash opponent cards - #822
Merged
Conversation
… endpoint Co-authored-by: driver727-pixel <269849721+driver727-pixel@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
driver727-pixel
August 23, 2026 14:32
View session
driver727-pixel
marked this pull request as ready for review
August 23, 2026 14:33
There was a problem hiding this comment.
🟡 Changes recommended
The rival art top-up logic can incorrectly detect/merge art layers (notably weaponImageUrl) and may overwrite existing layers with undefined, which can regress rendering in some cases.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR fixes Forge Clash opponent cards rendering procedural SVG art pre-match by resolving forged rival art layers server-side (including Boss Assets fallback) and fetching the rival payload client-side so the pre-match lobby can render the same layered imagery as player cards.
Changes:
- Server: resolve rival art layers by scanning multiple collections and unify the emitted rival payload shape; add a new authenticated
/api/forge/clash/rivalendpoint. - Client: introduce a shared
ForgeClashRivaltype and addfetchForgeClashRival; fetch rival-on-mount and top up older match snapshots lacking art. - Tests: add coverage for Boss Assets fallback on match start and the new rival endpoint (including unauthenticated behavior).
File summaries
| File | Description |
|---|---|
| server/routes/forge.js | Adds multi-collection rival art resolution, extracts a shared rival payload builder, and introduces a new rival fetch endpoint. |
| src/services/forge.ts | Extracts ForgeClashRival and adds fetchForgeClashRival for the new endpoint. |
| src/pages/ForgeClash.tsx | Fetches rival payload for pre-match display and conditionally tops up art layers for older match snapshots. |
| server/test/forgeClashRoute.test.js | Adds tests ensuring Boss Assets art is applied and the new endpoint is authenticated and returns forged layers. |
Review details
Suppressed comments (1)
src/pages/ForgeClash.tsx:607
- When topping up
liveRivalwithrivalPreview, the code assigns fields directly fromrivalPreview(which may beundefined) and can overwrite existingliveRivallayers (notablyweaponImageUrl, which currently isn’t counted byhasRivalArt). Prefer nullish-coalescing (or conditional) merges so you only override with defined preview values.
return {
...liveRival,
backgroundImageUrl: rivalPreview.backgroundImageUrl,
characterImageUrl: rivalPreview.characterImageUrl,
frameImageUrl: rivalPreview.frameImageUrl,
weaponImageUrl: rivalPreview.weaponImageUrl,
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+282
to
+290
| function hasRivalArt(rival: ForgeClashRival | null): boolean { | ||
| if (!rival) return false; | ||
| return Boolean( | ||
| rival.characterImageUrl | ||
| || rival.backgroundImageUrl | ||
| || rival.frameImageUrl | ||
| || rival.board?.imageUrl, | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Forge Clash opponent card still renders the procedural SVG
CardArtinstead of the forged skater image that player cards show. #821 fixed which rival object reached the renderer, but that rival never carries any image layers, soCardThumbnailalways falls through to its SVG path.Two independent gaps caused this:
loadRivalArtLayersqueried arivalCardscollection that nothing in the app writes to. Forged boss art is saved toadminBossAssetsby the Card Forge (src/pages/cardForge/useForgeSave.ts) and surfaced on the Boss Assets page.DISTRICT_RIVALScatalogue, which has no image URLs by design.Changes
server/routes/forge.jsloadRivalArtLayersscansRIVAL_ART_COLLECTIONS(rivalCards, thenadminBossAssets), matching onidentity.namethenname, before falling back to the static catalogue.buildClashRival(definition, artLayers)so match start and the new endpoint emit one payload shape — stats stay server-authoritative, art layers can't overwrite them.GET /api/forge/clash/rival(authenticated, rate-limited) returning the rival plus its forged layers.src/services/forge.ts— extracted the inline rival type asForgeClashRival; addedfetchForgeClashRival.src/pages/ForgeClash.tsx— fetches the rival on mount for the pre-match showcase. A livematch.rivalstill wins, except when it was snapshotted before art existed, in which case its layers are topped up from the fetched rival (same rival id only). Static catalogue remains the last-resort fallback.server/test/forgeClashRoute.test.js— covers Boss Assets art resolution at match start and the new endpoint including the unauthenticated case.Note: this only renders art that already exists — a card named after the rival must be present in
adminBossAssets(orrivalCards). Without one, the SVG placeholder is still the correct fallback.