fix: tighten codex hint visibility and constants (A-5 polish)

- Hoist MAX_UNDISCOVERED_PER_TIER to module scope (was a local const that
  expressed game-design intent, not runtime state).
- Filter undiscovered slots to those with at least one known ingredient
  so a fresh player never sees useless `? + ?` cards on high tiers.
- Move card-level opacity off the wrapper so the recipe-hint span can
  stay full-color (especially the craftable-now blue) while sprite +
  name remain dimmed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 04:18:32 +09:00
parent 56664b56d3
commit 559ca8d934

View File

@@ -769,12 +769,19 @@ const undiscoveredCardStyle = css`
${elementCardBaseStyle}
background: ${adaptive.greyBackground};
border: 1.5px dashed ${adaptive.grey300};
opacity: 0.4;
cursor: default;
justify-content: center;
min-height: 80px;
`;
const undiscoveredDimStyle = css`
opacity: 0.45;
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
`;
const spriteWrapStyle = css`
width: 52px;
height: 52px;
@@ -1106,9 +1113,11 @@ const genealogyTextStyle = css`
`;
// ────────────────────────────────────────────────────────────
// Tier labels
// Tier labels & game design constants
// ────────────────────────────────────────────────────────────
const MAX_UNDISCOVERED_PER_TIER = 6;
const TIER_LABELS: Record<number, string> = {
1: 'Tier 1 — 기본 원소',
2: 'Tier 2 — 2차 원소',
@@ -1128,6 +1137,7 @@ const elementMap = Object.fromEntries(elementsData.map((el) => [el.id, el]));
interface RecipeHint {
display: string;
craftableNow: boolean;
partiallyKnown: boolean;
}
function getRecipeHintForElement(
@@ -1148,6 +1158,7 @@ function getRecipeHintForElement(
return {
display: `${aSym} + ${bSym}`,
craftableNow: aDiscovered && bDiscovered && hasA && hasB,
partiallyKnown: aDiscovered || bDiscovered,
};
}
@@ -1323,18 +1334,20 @@ const ElementCard = memo(function ElementCard({
if (state === 'undiscovered') {
return (
<div css={undiscoveredCardStyle}>
<div css={spriteWrapStyle}>
<div css={idleSpriteWrapperStyle(mood ?? 'awake')}>
<CharacterSprite
elementId={el.id}
elementColor={el.color}
tier={el.tier}
size={56}
state="undiscovered"
/>
<div css={undiscoveredDimStyle}>
<div css={spriteWrapStyle}>
<div css={idleSpriteWrapperStyle(mood ?? 'awake')}>
<CharacterSprite
elementId={el.id}
elementColor={el.color}
tier={el.tier}
size={56}
state="undiscovered"
/>
</div>
</div>
<span css={undiscoveredNameStyle}>???</span>
</div>
<span css={undiscoveredNameStyle}>???</span>
{hint && (
<span css={undiscoveredHintStyle(hint.craftableNow)}>{hint.display}</span>
)}
@@ -1539,8 +1552,6 @@ export function ElementsScreen() {
.filter((summary): summary is ActiveBoostSummary => summary !== null && summary.remainingMs > 0)
.sort((a, b) => a.remainingMs - b.remainingMs);
const MAX_UNDISCOVERED_PER_TIER = 6;
const tierGroups = [1, 2, 3, 4, 5].map((tier) => {
const tierElements = elementsData.filter((el) => el.tier === tier);
const owned = tierElements.filter((el) => discoveredElementIds.includes(el.id));
@@ -1548,6 +1559,7 @@ export function ElementsScreen() {
const undiscoveredWithHints = undiscovered
.map((el) => ({ el, hint: getRecipeHintForElement(el.id, discoveredElementIds, elements) }))
.filter(({ hint }) => hint?.partiallyKnown ?? false)
.sort((a, b) => {
const aCraftable = a.hint?.craftableNow ? 1 : 0;
const bCraftable = b.hint?.craftableNow ? 1 : 0;