diff --git a/public/img/activity_1.png b/public/img/activity_1.png deleted file mode 100644 index 78cbed6..0000000 Binary files a/public/img/activity_1.png and /dev/null differ diff --git a/public/img/activity_event.png b/public/img/activity_event.png new file mode 100644 index 0000000..80107bf Binary files /dev/null and b/public/img/activity_event.png differ diff --git a/src/api/auraServer.ts b/src/api/auraServer.ts index bdbe527..31fecbb 100644 --- a/src/api/auraServer.ts +++ b/src/api/auraServer.ts @@ -3,8 +3,8 @@ import type { RegisterResponse, LoginRequest, LoginResponse, - FarcasterLoginRequest, - FarcasterLoginResponse, + PrivyLoginRequest, + PrivyLoginResponse, BindWalletRequestRequest, BindWalletRequestResponse, BindWalletConfirmRequest, @@ -154,8 +154,8 @@ export async function editGemDeck( export async function loginWithPrivy( privyAccessToken: string, chain_id?: string -): Promise { - const requestBody: FarcasterLoginRequest = { +): Promise { + const requestBody: PrivyLoginRequest = { privyAccessToken, chain_id: chain_id || 'soneium-testnet', // 預設為 soneium-testnet }; @@ -170,39 +170,9 @@ export async function loginWithPrivy( return data; } -/** - * 使用錢包簽名進行 Farcaster 登入(向後兼容) - * @param walletAddress 錢包地址 - * @param signature 簽名 - * @param options 可選參數 - * @returns Farcaster 登入回應 - */ -export async function loginWithFarcaster( - walletAddress: string, - signature: string, - options?: { - message?: string; - farcasterId?: string; - chain_id?: string; - } -): Promise { - const requestBody: FarcasterLoginRequest = { - walletAddress, - signature, - message: options?.message, - farcasterId: options?.farcasterId, - chain_id: options?.chain_id || 'soneium-testnet', // 預設為 soneium-testnet - }; - - const response = await fetch(`${BASE_URL}/privy-login`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(requestBody), - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error || 'Farcaster login failed'); - return data; -} +// 註:loginWithFarcaster(錢包簽名登入)已移除。 +// 它送的是 walletAddress + signature,但後端 /privy-login 只接受 +// privyAccessToken;且專案未啟用 Farcaster,此函式已無呼叫者。 /** * 獲取活躍交易訂單 (公開 API) diff --git a/src/components/InfoMenuModal.tsx b/src/components/InfoMenuModal.tsx index 188fdd2..c80bc70 100644 --- a/src/components/InfoMenuModal.tsx +++ b/src/components/InfoMenuModal.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef, type ComponentType } from "react" import { useRouter } from "next/router" -import { X, LogOut, Gamepad2, BookOpen, Gem, ShoppingBag, ArrowLeft, Zap, Loader2, Palette, Diamond, Sparkles, Scale, Gift, ImageOff } from "lucide-react" +import { X, LogOut, Gamepad2, BookOpen, ShoppingBag, ArrowLeft, Zap, Loader2, Gift, ImageOff, MousePointerClick } from "lucide-react" import { useTranslation } from "react-i18next" import { useInfoPanelLayout } from "../hooks/useInfoPanelLayout" import { LanguageSwitcher } from "./LanguageSwitcher" @@ -95,11 +95,15 @@ function CardImage({ }, [url]) if (status === "error") { + // 尚未提供卡圖(如活動限定卡)與「載入失敗」是兩件事,訊息需區分。 + const messageKey = url + ? "infoMenu.encyclopedia.imageFailed" + : "infoMenu.encyclopedia.imageComingSoon" return (
- {t("infoMenu.encyclopedia.imageFailed")} + {t(messageKey)}
) @@ -134,8 +138,7 @@ interface InfoMenuModalProps { onExitGame?: () => void } -type CategoryId = "event" | "gameplay" | "encyclopedia" | "rarity" | "shop" -type Rarity = "common" | "rare" | "epic" | "legendary" +type CategoryId = "event" | "gameplay" | "encyclopedia" | "shop" interface Category { id: CategoryId @@ -147,71 +150,27 @@ const categories: Category[] = [ { id: "event", labelKey: "infoMenu.categories.event", icon: Gift }, { id: "gameplay", labelKey: "infoMenu.categories.gameplay", icon: Gamepad2 }, { id: "encyclopedia", labelKey: "infoMenu.categories.encyclopedia", icon: BookOpen }, - { id: "rarity", labelKey: "infoMenu.categories.rarity", icon: Gem }, { id: "shop", labelKey: "infoMenu.categories.shop", icon: ShoppingBag }, ] /* ═══════════════════════════════════════════════ - * 卡片資料表(40 張寶石) - * 名稱/效果改由 i18n 字典 (cards.{id}.name/effect) 取得,此處只保留 id 與 rarity。 + * 卡片資料表 + * 基礎寶石 40 張 + 活動限定卡 1 張(id 41)。 + * 名稱/效果由 i18n 字典 (cards.{id}.name/effect) 取得,此處只保留 id。 + * 稀有度資訊已下架 —— 分級規則調整中,待新規則定案再回歸。 * ═══════════════════════════════════════════════ */ interface CardInfo { id: number - rarity: Rarity } -const RARITY_META: Record = { - common: { label: "Common", color: "#9a9a9a", glow: "rgba(154,154,154,0.5)", tier: 1 }, - rare: { label: "Rare", color: "#3b82f6", glow: "rgba(59,130,246,0.5)", tier: 2 }, - epic: { label: "Epic", color: "#8b5cf6", glow: "rgba(139,92,246,0.5)", tier: 3 }, - legendary: { label: "Legendary", color: "#ffc100", glow: "rgba(255,193,0,0.6)", tier: 4 }, -} +/** 基礎寶石(40 種) */ +const CARD_DATA: CardInfo[] = Array.from({ length: 40 }, (_, i) => ({ id: i + 1 })) -const CARD_DATA: CardInfo[] = [ - { id: 1, rarity: "common" }, - { id: 2, rarity: "common" }, - { id: 3, rarity: "common" }, - { id: 4, rarity: "common" }, - { id: 5, rarity: "common" }, - { id: 6, rarity: "common" }, - { id: 7, rarity: "common" }, - { id: 8, rarity: "common" }, - { id: 9, rarity: "common" }, - { id: 10, rarity: "common" }, - { id: 11, rarity: "rare" }, - { id: 12, rarity: "rare" }, - { id: 13, rarity: "rare" }, - { id: 14, rarity: "rare" }, - { id: 15, rarity: "rare" }, - { id: 16, rarity: "rare" }, - { id: 17, rarity: "rare" }, - { id: 18, rarity: "epic" }, - { id: 19, rarity: "epic" }, - { id: 20, rarity: "epic" }, - { id: 21, rarity: "epic" }, - { id: 22, rarity: "epic" }, - { id: 23, rarity: "legendary" }, - { id: 24, rarity: "legendary" }, - { id: 25, rarity: "common" }, - { id: 26, rarity: "common" }, - { id: 27, rarity: "common" }, - { id: 28, rarity: "common" }, - { id: 29, rarity: "common" }, - { id: 30, rarity: "common" }, - { id: 31, rarity: "rare" }, - { id: 32, rarity: "rare" }, - { id: 33, rarity: "rare" }, - { id: 34, rarity: "rare" }, - { id: 35, rarity: "rare" }, - { id: 36, rarity: "epic" }, - { id: 37, rarity: "epic" }, - { id: 38, rarity: "epic" }, - { id: 39, rarity: "legendary" }, - { id: 40, rarity: "legendary" }, -] +/** 活動限定卡:不列入 40 種基礎寶石,於圖鑑下方獨立一區顯示 */ +const EVENT_CARDS: CardInfo[] = [{ id: 41 }] -const cardMap = new Map(CARD_DATA.map((c) => [c.id, c])) +const cardMap = new Map([...CARD_DATA, ...EVENT_CARDS].map((c) => [c.id, c])) /* ═══════════════════════════════════════════════ * 主 Modal @@ -423,7 +382,6 @@ export function InfoMenuModal({ isOpen, onClose, onExitGame }: InfoMenuModalProp ? setSelectedCardId(null)} /> : )} - {activeCategory === "rarity" && } {activeCategory === "shop" && } @@ -550,7 +508,6 @@ export function InfoMenuModal({ isOpen, onClose, onExitGame }: InfoMenuModalProp ? setSelectedCardId(null)} /> : )} - {activeCategory === "rarity" && } {activeCategory === "shop" && } @@ -592,44 +549,6 @@ function SectionTitle({ children }: { children: React.ReactNode }) { ) } -function RarityBadge({ rarity, size = "sm" }: { rarity: Rarity; size?: "sm" | "lg" }) { - const meta = RARITY_META[rarity] - if (size === "lg") { - return ( -
- - {meta.label} -
- ) - } - return ( -
- - {meta.label} -
- ) -} - /* ═══════════════════════════════════════════════ * 各分類內容 * ═══════════════════════════════════════════════ */ @@ -637,7 +556,6 @@ function RarityBadge({ rarity, size = "sm" }: { rarity: Rarity; size?: "sm" | "l function EventContent() { const { t } = useTranslation() const howToItems = t("infoMenu.event.howToItems", { returnObjects: true }) as string[] - const rewardItems = t("infoMenu.event.rewardItems", { returnObjects: true }) as string[] return (
@@ -658,12 +576,23 @@ function EventContent() { {/* 活動截圖 */}
{t("infoMenu.event.title")}
+ {/* 活動入口:主畫面右下角的活動 Icon */} +
+ {t("infoMenu.event.entryTitle")} +
+ +

+ {t("infoMenu.event.entryBody")} +

+
+
+ {/* 如何參與 */}
{t("infoMenu.event.howToTitle")} @@ -677,13 +606,9 @@ function EventContent() { {/* 獎勵內容 */}
{t("infoMenu.event.rewardTitle")} -
- {rewardItems.map((item, idx) => ( -
- - {item} -
- ))} +
+ + {t("infoMenu.event.rewardBody")}
@@ -698,9 +623,6 @@ function EventContent() { function GameplayContent() { const { t } = useTranslation() const flowItems = t("infoMenu.gameplay.flow.items", { returnObjects: true }) as string[] - const mechanicsItems = t("infoMenu.gameplay.coreMechanics.items", { - returnObjects: true, - }) as Array<{ t: string; d: string }> return (
@@ -722,58 +644,91 @@ function GameplayContent() {
{t("infoMenu.gameplay.coreMechanics.title")} -
- {mechanicsItems.map((item) => ( -
-
{item.t}
-
{item.d}
-
- ))} +
+
+ {t("infoMenu.gameplay.coreMechanics.heading")} +
+
+ {t("infoMenu.gameplay.coreMechanics.body")} +
) } +function CardGridButton({ + card, + onClick, + badge, +}: { + card: CardInfo + onClick: (id: number) => void + badge?: string +}) { + const { t } = useTranslation() + const name = t(`cards.${card.id}.name`) + return ( + + ) +} + function EncyclopediaContent({ onCardClick }: { onCardClick: (id: number) => void }) { const { t } = useTranslation() return (
+ {/* 基礎寶石(40 種) */}
{t("infoMenu.encyclopedia.title")}

{t("infoMenu.encyclopedia.hint")}

- {CARD_DATA.map((card) => { - const meta = RARITY_META[card.rarity] - const name = t(`cards.${card.id}.name`) - return ( - - ) - })} + {CARD_DATA.map((card) => ( + + ))} +
+
+ + {/* 活動限定卡(獨立一區,不列入 40 種基礎寶石) */} +
+ {t("infoMenu.encyclopedia.eventCardTitle")} +

+ {t("infoMenu.encyclopedia.eventCardHint")} +

+
+ {EVENT_CARDS.map((card) => ( + + ))}
@@ -782,9 +737,9 @@ function EncyclopediaContent({ onCardClick }: { onCardClick: (id: number) => voi function CardDetail({ card, onBack }: { card: CardInfo; onBack: () => void }) { const { t } = useTranslation() - const meta = RARITY_META[card.rarity] const name = t(`cards.${card.id}.name`) const effect = t(`cards.${card.id}.effect`) + const isEventCard = EVENT_CARDS.some((c) => c.id === card.id) return (
@@ -798,15 +753,8 @@ function CardDetail({ card, onBack }: { card: CardInfo; onBack: () => void }) { {t("infoMenu.aria.backToEncyclopedia")} - {/* 主視覺區:卡圖 + 名稱/稀有度 */} -
+ {/* 主視覺區:卡圖 + 名稱 */} +
{/* 卡圖 */}
void }) { />
- {/* 名稱 + 稀有度 */} + {/* 名稱 */}
{t("infoMenu.cardDetail.cardLabel")} · #{String(card.id).padStart(3, "0")} @@ -825,9 +773,13 @@ function CardDetail({ card, onBack }: { card: CardInfo; onBack: () => void }) {

{name}

-
- -
+ {isEventCard && ( +
+ + {t("infoMenu.encyclopedia.eventCardBadge")} + +
+ )}
@@ -841,103 +793,6 @@ function CardDetail({ card, onBack }: { card: CardInfo; onBack: () => void }) {

- - {/* 稀有度說明 */} -
- {t("infoMenu.cardDetail.rarityExplanation")} -
-
- - {t("infoMenu.cardDetail.tier")} - - - {t("infoMenu.cardDetail.tierOfFour", { tier: meta.tier })} - -
-
- {[1, 2, 3, 4].map((tier) => ( -
- ))} -
-

- {t(`infoMenu.rarity.descriptions.${card.rarity}`)} -

-
-
-
- ) -} - -// 4C 對應的主題圖示(順序需與 i18n 字典的 fourC 一致:Color / Cut / Clarity / Carat) -const FOUR_C_ICONS = [Palette, Diamond, Sparkles, Scale] - -function RarityContent() { - const { t } = useTranslation() - const fourC = t("infoMenu.rarity.fourC", { returnObjects: true }) as Array<{ - title: string - desc: string - }> - - return ( -
-
- {t("infoMenu.rarity.fourCTitle")} -

- {t("infoMenu.rarity.fourCIntro")} -

- -
- {fourC.map((c, idx) => { - const Icon = FOUR_C_ICONS[idx] ?? Gem - return ( -
-
- -
-
-
- {c.title} -
-
{c.desc}
-
-
- ) - })} -
-
- -
- {t("infoMenu.rarity.levelsTitle")} -
- {(["common", "rare", "epic", "legendary"] as Rarity[]).map((r) => { - const meta = RARITY_META[r] - return ( -
-
-
- {meta.label} -
-
- ) - })} -
-
) } diff --git a/src/components/WalletInfo.tsx b/src/components/WalletInfo.tsx index 3742710..e75ed69 100644 --- a/src/components/WalletInfo.tsx +++ b/src/components/WalletInfo.tsx @@ -43,9 +43,11 @@ export function WalletInfo() { } } - const farcasterUsername = privyUser?.farcaster?.username; - const farcasterPfp = privyUser?.farcaster?.pfp; - const farcasterFid = privyUser?.farcaster?.fid; + // 顯示名稱與頭像改讀 Privy 的 Google 帳號。 + // 先前讀的是 privyUser.farcaster,但 Privy 的 loginMethods 只有 + // email / wallet / google,該物件恆為 undefined,這段 UI 從未顯示過。 + const displayName = privyUser?.google?.name ?? undefined; + const avatarUrl = undefined; // Privy 的 google 物件不含頭像;待後端 avatarUrl 接上後再補 // Check if we need to prompt for wallet connection/creation const showConnectButton = !walletAddress && wallets.length === 0; @@ -61,17 +63,17 @@ export function WalletInfo() { aria-label="Open profile" className="w-12 h-12 sm:w-16 sm:h-16 bg-[#050505] border border-[#050505] rounded-lg flex items-center justify-center overflow-hidden shrink-0 shadow-[0_5px_14px_rgba(0,0,0,0.16)] cursor-pointer hover:opacity-80 transition-opacity" > - {farcasterPfp ? ( - {farcasterUsername - ) : farcasterUsername ? ( - {farcasterUsername.charAt(0).toUpperCase()} + {avatarUrl ? ( + {displayName + ) : displayName ? ( + {displayName.charAt(0).toUpperCase()} ) : ( )}
- {farcasterUsername && ( -
@{farcasterUsername} (FID: {farcasterFid})
+ {displayName && ( +
{displayName}
)}
{displayAddress} diff --git a/src/hooks/useChainSwitch.ts b/src/hooks/useChainSwitch.ts index aa2aa44..6910520 100644 --- a/src/hooks/useChainSwitch.ts +++ b/src/hooks/useChainSwitch.ts @@ -3,13 +3,11 @@ import { useAccount, useSignMessage } from 'wagmi'; import { usePrivy, useWallets } from '@privy-io/react-auth'; import { useUser } from '../context/UserContext'; import { - loginWithFarcaster, loginWithPrivy, loginWithPassword, getUserGems, getUserDeck, } from '../api/auraServer'; -import { generateFarcasterMessage } from '../lib/farcaster'; import type { SupportedChain } from '../types/auraServer'; export function useChainSwitch() { @@ -42,84 +40,12 @@ export function useChainSwitch() { // 根據登入類型重新登入 switch (user.loginType) { - case 'farcaster': { - // Farcaster 登入 - 優先使用 Privy token 方式 - if (privyUser?.farcaster && getAccessToken) { - // 使用 Privy token 方式(推薦,無需簽名) - const privyToken = await getAccessToken(); - - if (!privyToken) { - throw new Error('無法獲取 Privy access token'); - } - - const response = await loginWithPrivy( - privyToken, - targetChain - ); - - newToken = response.token; - newChainId = response.chainId; - newUserData = { - ...user, - token: response.token, - chainId: response.chainId, - walletAddress: response.walletAddress, - name: response.name, - farcasterId: response.farcasterId, - farcasterUsername: response.farcasterUsername, - farcasterPfpUrl: response.farcasterPfpUrl, - }; - } else { - // 回退到簽名方式(向後兼容) - const walletAddress = - connectedAddress || - wallets.find((w) => w.walletClientType !== 'privy')?.address || - user.walletAddress; - - if (!walletAddress) { - throw new Error('無法獲取錢包地址'); - } - - if ( - !isConnected && - !wallets.some((w) => w.address === walletAddress) - ) { - throw new Error('請先連接錢包'); - } - - const farcasterId = user.farcasterId || privyUser?.farcaster?.fid; - const message = generateFarcasterMessage( - walletAddress, - farcasterId ? String(farcasterId) : undefined - ); - - const signature = await signMessageAsync({ message }); - const response = await loginWithFarcaster( - walletAddress, - signature, - { - farcasterId: farcasterId ? String(farcasterId) : undefined, - chain_id: targetChain, - } - ); - - newToken = response.token; - newChainId = response.chainId; - newUserData = { - ...user, - token: response.token, - chainId: response.chainId, - walletAddress: response.walletAddress, - name: response.name, - farcasterId: response.farcasterId, - farcasterUsername: response.farcasterUsername, - farcasterPfpUrl: response.farcasterPfpUrl, - }; - } - break; - } - - case 'google': { + // Privy 登入(google / email / wallet):一律用 Privy token 重新換發。 + // 先前 'farcaster' 與 'google' 是兩個分支,但兩者最終都呼叫 + // loginWithPrivy,且 Privy 未啟用 Farcaster,簽名回退路徑從未執行。 + case 'google': + case 'email': + case 'wallet': { const privyToken = await getAccessToken(); if (!privyToken) { throw new Error('無法獲取 Privy access token'); @@ -133,6 +59,9 @@ export function useChainSwitch() { chainId: response.chainId, walletAddress: response.walletAddress, name: response.name, + loginType: response.loginType, + email: response.email, + avatarUrl: response.avatarUrl, }; break; } diff --git a/src/hooks/useLogin.ts b/src/hooks/useLogin.ts index 402c3a4..349bd7f 100644 --- a/src/hooks/useLogin.ts +++ b/src/hooks/useLogin.ts @@ -92,8 +92,9 @@ export function useLogin(options: UseLoginOptions = {}) { getUserDeck(response.token), ]); - const isFarcaster = Boolean(privyUser.farcaster); - + // loginType 由後端依 Privy 的 linked_accounts 判定(google / email / wallet)。 + // 先前是用 privyUser.farcaster 推導,但 Privy 的 loginMethods 未啟用 + // Farcaster,該判斷恆為 false,等於永遠回報 "google"。 setUser({ token: response.token, userId: response.userId, @@ -102,14 +103,9 @@ export function useLogin(options: UseLoginOptions = {}) { walletAddress: response.walletAddress || undefined, deck, gems, - loginType: isFarcaster ? "farcaster" : "google", - ...(isFarcaster - ? { - farcasterId: response.farcasterId, - farcasterUsername: response.farcasterUsername, - farcasterPfpUrl: response.farcasterPfpUrl, - } - : {}), + loginType: response.loginType, + email: response.email, + avatarUrl: response.avatarUrl, }); loginCompleted = true; diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index f7cba5b..daa92f3 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -35,28 +35,24 @@ const en: Translation = { event: "Events", gameplay: "Gameplay", encyclopedia: "Gem Encyclopedia", - rarity: "Rarity", shop: "Gem Shop", }, event: { title: "Limited-Time Event", badge: "Live Now", - description: "Watch an ad to earn a gem pack! After each completed ad, you'll receive a random pack containing gem fragments to help upgrade your deck.", + description: "Participate in limited-time event leaderboards. Earn a high score, climb the rankings, and receive exclusive event rewards.", + entryTitle: "Where to Find It", + entryBody: "Tap the event Icon in the bottom-right corner of the main screen to open the Event page.", howToTitle: "How to Participate", howToItems: [ - "Head to the Battle page.", - "Tap the \"Watch Ad\" button on screen.", - "Watch the full ad (approx. 15–30 seconds).", - "Your gem pack reward is automatically granted once the ad ends.", + "Open the Event page.", + "Select the leaderboard stage for the current event.", + "Deal damage to the enemy to build your score and place on the leaderboard.", + "The top three players* will receive exclusive rewards for the current event.", ], rewardTitle: "Rewards", - rewardItems: [ - "Gem fragments (random rarity)", - "Chance to obtain Rare / Epic / Legendary fragments", - "Unlimited daily entries", - ], - imagePlaceholder: "Event illustration (coming soon)", - note: "Event details and rewards are subject to change with future updates. Refer to in-game announcements for the latest information.", + rewardBody: "Rewards vary by event. Please refer to the current event details for more information.", + note: "* The exact reward-eligible rankings may vary by event. Event details and rewards are subject to change with future updates; refer to in-game announcements for the latest information.", }, gameplay: { whatIsAurayale: { @@ -67,61 +63,30 @@ const en: Translation = { title: "Game Flow", items: [ "Connect your wallet, sign in, and claim your starter deck.", - "On the Deck page, pick 5 gems to form your battle deck.", - "Enter Battle and play gem cards each turn to form poker hands, dealing ATK × Mult damage.", - "Win matches to earn gem fragments and trade for deck upgrades.", + "Choose 10 Gem Cards on the Deck page to build your battle deck.", + "Enter Battle. Each round, draw 6 Poker Cards and refill your Gem Card hand to 5; play Gem Cards to modify Poker Cards or enhance ATK, MULT, and survivability.", + "The game scores every valid Pattern in the Poker hand. ATK and MULT from all Patterns are summed separately. After Gem Card effects resolve, damage is calculated as Total ATK × Total MULT.", + "Win battles to earn Gem Shards, trade in the marketplace, and upgrade your deck.", ], }, coreMechanics: { title: "Core Mechanics", - items: [ - { t: "Poker Scoring", d: "Form poker hands (Straight / Flush / Full House); damage = ATK × Mult" }, - { t: "Upgrade System", d: "Fuse two same-color gems into a higher-tier card" }, - { t: "Rarity Tiers", d: "4C standard: Common / Rare / Epic / Legendary" }, - { t: "Gem Marketplace", d: "Trade gems directly with other players" }, - ], + heading: "Pattern Scoring", + body: "A single Poker hand can form multiple Patterns at the same time. Every valid Pattern is scored.", }, }, encyclopedia: { - title: "Base Gems (24 Types)", - hint: "Tap any card to see its details (name, effect, rarity). Every gem has three upgrade stages (base / +1 / +2).", + title: "Basic Gems (40)", + hint: "Select any card to view its name and effect. Each Basic Gem still has three upgrade stages (Base / +1 / +2).", imageFailed: "Image failed to load", + imageComingSoon: "Artwork coming soon", + eventCardTitle: "Event-Exclusive Card", + eventCardHint: "Obtainable only during specific events; not counted among the 40 Basic Gems.", + eventCardBadge: "Event Only", }, cardDetail: { cardLabel: "CARD", effect: "Card Effect", - rarityExplanation: "Rarity", - tier: "Tier", - tierOfFour: "{{tier}} / 4", - }, - rarity: { - fourCTitle: "The 4C Standard for Gem Rarity", - fourCIntro: "Aurayale adopts the real-world gem industry's 4C grading system to determine each card's rarity and market value.", - levelsTitle: "Rarity Tiers", - fourC: [ - { - title: "Color", - desc: "Purer hues and higher saturation drive greater value. Aurayale groups gems into cool (blue/green), warm (red/yellow), and neutral (white/black) palettes, each with its own rarity curve.", - }, - { - title: "Cut", - desc: "The cut determines how light refracts and how a gem fires. The finer the cut, the more often its battle effects trigger.", - }, - { - title: "Clarity", - desc: "Clarity measures how rare a gem's internal inclusions and surface flaws are. Higher clarity supports stronger enchantment tiers.", - }, - { - title: "Carat", - desc: "Carat is the weight (size) of the gem. At equal quality, value grows geometrically with carat — but so does the energy required to wield it.", - }, - ], - descriptions: { - common: "Entry-level gems easily obtained through pulls or daily quests. Great for beginner decks.", - rare: "Rare-tier gems with stronger single-target effects, typically pulled from advanced packs.", - epic: "Epic-tier gems carrying game-changing skills. Core picks for mid- to late-game decks.", - legendary: "Legendary-tier gems — extremely rare, with unique battlefield-wide effects and dazzling visuals.", - }, }, shop: { title: "Gem Shop", @@ -264,46 +229,47 @@ const en: Translation = { }, }, cards: { - "1": { name: "Flame Ruby", effect: "ATK + 50 if pattern contains Straight." }, - "2": { name: "Deep-Sea Sapphire", effect: "Mult + 15 if pattern contains Straight." }, - "3": { name: "Heart of Jade", effect: "Target card rank +1." }, - "4": { name: "Topaz Light", effect: "Target card rank -1." }, - "5": { name: "Moonstone", effect: "If all card ranks are odd, ATK + 50." }, - "6": { name: "Amber Eye", effect: "If all card ranks are even, Mult + 10." }, - "7": { name: "Coral Abyss", effect: "ATK + 50 if pattern contains Flush." }, - "8": { name: "Pearl Star", effect: "Mult + 15 if pattern contains Flush." }, - "9": { name: "Smoky Quartz", effect: "Set Heart suit to Spade." }, - "10": { name: "Amethyst Reverie", effect: "Set Spade suit to Club." }, - "11": { name: "Bloodstone Dragon Breath", effect: "Set Diamond suit to Heart." }, - "12": { name: "Frost Crystal", effect: "Set Club suit to Diamond." }, - "13": { name: "Thunder Agate", effect: "ATK + 25 if pattern contains Two Pair." }, - "14": { name: "Verdant Olivine", effect: "Mult + 5 if pattern contains Two Pair." }, - "15": { name: "Fire Opal", effect: "If next round contains Two Pair, ATK + 50." }, - "16": { name: "Obsidian Spear", effect: "ATK + 35 if pattern contains Three of a Kind." }, - "17": { name: "Aquamarine Tears", effect: "Mult + 10 if pattern contains Three of a Kind." }, - "18": { name: "Imperial Emerald", effect: "If next round contains Three of a Kind, Mult + 15." }, - "19": { name: "Tiger Eye Fury", effect: "ATK + 15." }, - "20": { name: "Star Sapphire", effect: "Mult + 4." }, - "21": { name: "Golden Sunstone", effect: "Set Mult to 1 this round. Next round Mult x 3." }, - "22": { name: "Night Obsidian", effect: "Mult x 2 if pattern contains Straight." }, - "23": { name: "Eternal Diamond", effect: "Permanently gain ATK + 10 when pattern contains Flush." }, - "24": { name: "Genesis Stone", effect: "Permanently gain Mult + 4 when pattern contains Flush." }, - "25": { name: "Jade Ice Heart", effect: "If Spade Flush, gain Mult + 2 for 3 rounds." }, - "26": { name: "Red Spinel", effect: "If Heart Flush, restore 5 HP for 3 rounds." }, - "27": { name: "Citrine", effect: "If Diamond Flush, permanently gain ATK + 5 for 3 rounds." }, - "28": { name: "Pink Diamond Heart", effect: "If Club Flush, reduce damage taken by 15% for 3 rounds." }, - "29": { name: "Black Tourmaline", effect: "ATK - 25 this round. Next round ATK + 100." }, - "30": { name: "Kunzite", effect: "Mult increases by the sum of all card ranks." }, - "31": { name: "Sapphire Eye", effect: "ATK - 25. Mult + 30." }, - "32": { name: "Imperial Topaz", effect: "If pattern contains Two Pair, restore 20 HP." }, - "33": { name: "Morganite Kiss", effect: "Mult - 5 this round. Next round Mult + 15." }, - "34": { name: "Tsavorite", effect: "If pattern contains Full House, reduce damage taken by 50%." }, - "35": { name: "Crimson Lattice", effect: "Mult - 5. ATK + 30." }, - "36": { name: "Tanzanite Star", effect: "If pattern contains Three of a Kind, reduce damage taken by 30%." }, - "37": { name: "Paraiba Tourmaline", effect: "Set ATK to 30." }, - "38": { name: "Alexandrite", effect: "Set Mult to 10." }, - "39": { name: "Dragon Breath Heart", effect: "ATK increases by the sum of all card ranks." }, - "40": { name: "Genesis Void", effect: "Reduce damage taken by 15%." }, + "1": { name: "Flame Ruby", effect: "ATK +75 if hand contains Rune Chain III or higher." }, + "2": { name: "Deep-Sea Sapphire", effect: "MULT +15 if hand contains Rune Chain III or higher." }, + "3": { name: "Heart of Jade", effect: "Increase one target Poker Card's rank by 1." }, + "4": { name: "Topaz Light", effect: "Decrease one target Poker Card's rank by 1." }, + "5": { name: "Moonstone", effect: "Set ATK to 250 this round, then MULT +3." }, + "6": { name: "Amber Eye", effect: "Restore 20 HP, up to maximum HP." }, + "7": { name: "Coral Abyss", effect: "ATK +60 if hand contains Color Resonance III or higher." }, + "8": { name: "Pearl Star", effect: "MULT +10 if hand contains Color Resonance III or higher." }, + "9": { name: "Smoky Quartz", effect: "Set all Hearts in the current Poker hand to Spades." }, + "10": { name: "Amethyst Reverie", effect: "Set all Spades in the current Poker hand to Clubs." }, + "11": { name: "Bloodstone Dragon Breath", effect: "Set all Diamonds in the current Poker hand to Hearts." }, + "12": { name: "Frost Crystal", effect: "Set all Clubs in the current Poker hand to Diamonds." }, + "13": { name: "Thunder Agate", effect: "ATK +40 if hand contains Rune Resonance II or higher." }, + "14": { name: "Verdant Olivine", effect: "MULT +5 if hand contains Rune Resonance II or higher." }, + "15": { name: "Fire Opal", effect: "Next round: ATK +75 if hand contains Rune Resonance II or higher." }, + "16": { name: "Obsidian Spear", effect: "ATK +100 if hand contains Rune Resonance III or higher." }, + "17": { name: "Aquamarine Tears", effect: "MULT +20 if hand contains Rune Resonance III or higher." }, + "18": { name: "Imperial Emerald", effect: "Next round: MULT +30 if hand contains Rune Resonance III or higher." }, + "19": { name: "Tiger Eye Fury", effect: "ATK +15." }, + "20": { name: "Star Sapphire", effect: "MULT +4." }, + "21": { name: "Golden Sunstone", effect: "If hand contains Rune Chain III or higher, MULT -10 this round and MULT +30 next round." }, + "22": { name: "Night Obsidian", effect: "MULT ×2 this round if hand contains Rune Chain III or higher." }, + "23": { name: "Eternal Diamond", effect: "If hand contains Color Resonance III or higher, ATK +15 permanently." }, + "24": { name: "Genesis Stone", effect: "If hand contains Color Resonance III or higher, MULT +2 permanently." }, + "25": { name: "Jade Ice Heart", effect: "If hand contains Color Resonance III or higher, gain MULT +2 for 3 rounds." }, + "26": { name: "Red Spinel", effect: "If hand contains Color Resonance III or higher, restore 15 HP each round for 3 rounds." }, + "27": { name: "Citrine", effect: "If hand contains Color Resonance III or higher, permanently gain ATK +10 each round for the next 3 rounds." }, + "28": { name: "Pink Diamond Heart", effect: "If hand contains Color Resonance III or higher, reduce damage taken by 15% for 3 rounds." }, + "29": { name: "Black Tourmaline", effect: "If hand contains Rune Chain III or higher, ATK -25 this round and ATK +125 next round." }, + "30": { name: "Kunzite", effect: "If hand contains Rune Chain IV or higher, MULT + the sum of all 6 Poker Card ranks." }, + "31": { name: "Sapphire Eye", effect: "If hand contains Rune Chain III or higher, ATK -25 and MULT +30 this round." }, + "32": { name: "Imperial Topaz", effect: "Restore 50 HP, up to maximum HP, if hand contains Rune Resonance II or higher." }, + "33": { name: "Morganite Kiss", effect: "If hand contains Rune Chain III or higher, MULT -5 this round and MULT +15 next round." }, + "34": { name: "Tsavorite", effect: "If hand contains Rune Chain IV or higher, reduce damage taken by 50% this round." }, + "35": { name: "Crimson Lattice", effect: "If hand contains Rune Chain III or higher, MULT -5 and ATK +100 this round." }, + "36": { name: "Tanzanite Star", effect: "If hand contains Rune Resonance III or higher, reduce damage taken by 30% this round." }, + "37": { name: "Paraiba Tourmaline", effect: "ATK +150 and MULT -10 this round." }, + "38": { name: "Alexandrite", effect: "MULT +25 and ATK -100 this round." }, + "39": { name: "Dragon Breath Heart", effect: "ATK + the sum of all 6 Poker Card ranks." }, + "40": { name: "Genesis Void", effect: "Reduce damage taken by 15% this round." }, + "41": { name: "Aura Genesis", effect: "If hand contains Perfect Resonance II or higher, ATK +100 and MULT +20 this round." }, }, } diff --git a/src/i18n/locales/zh-TW.ts b/src/i18n/locales/zh-TW.ts index 636ed7c..8c6e466 100644 --- a/src/i18n/locales/zh-TW.ts +++ b/src/i18n/locales/zh-TW.ts @@ -33,18 +33,18 @@ export interface Translation { event: string gameplay: string encyclopedia: string - rarity: string shop: string } event: { title: string badge: string description: string + entryTitle: string + entryBody: string howToTitle: string howToItems: string[] rewardTitle: string - rewardItems: string[] - imagePlaceholder: string + rewardBody: string note: string } gameplay: { @@ -52,28 +52,22 @@ export interface Translation { flow: { title: string; items: string[] } coreMechanics: { title: string - items: Array<{ t: string; d: string }> + heading: string + body: string } } - encyclopedia: { title: string; hint: string; imageFailed: string } + encyclopedia: { + title: string + hint: string + imageFailed: string + imageComingSoon: string + eventCardTitle: string + eventCardHint: string + eventCardBadge: string + } cardDetail: { cardLabel: string effect: string - rarityExplanation: string - tier: string - tierOfFour: string - } - rarity: { - fourCTitle: string - fourCIntro: string - levelsTitle: string - fourC: Array<{ title: string; desc: string }> - descriptions: { - common: string - rare: string - epic: string - legendary: string - } } shop: { title: string @@ -241,28 +235,24 @@ const zhTW: Translation = { event: "活動", gameplay: "遊戲玩法", encyclopedia: "寶石圖鑑", - rarity: "稀有判別", shop: "寶石商店", }, event: { title: "限時活動", badge: "進行中", - description: "觀看廣告即可獲得寶石卡包!每次看完廣告,系統將隨機發放一個卡包,內含稀有寶石碎片,助你升級牌組。", + description: "參加期間限定活動的排行榜!在排行榜中取得高分,爭取高名次,獲得限定活動的各項獎勵。", + entryTitle: "活動入口", + entryBody: "點擊主畫面右下角的活動 Icon,即可進入活動頁面。", howToTitle: "如何參與", howToItems: [ - "進入 Battle 對戰頁面。", - "點擊畫面中的「觀看廣告」按鈕。", - "完整觀看廣告(約 15–30 秒)。", - "廣告結束後自動發放寶石卡包獎勵。", + "進入活動頁面。", + "點擊該次活動的排行榜關卡。", + "對敵人造成傷害累積分數,在排行榜上留下自己的名次。", + "前三名*的玩家可以獲得該次活動的限定獎勵。", ], rewardTitle: "獎勵內容", - rewardItems: [ - "寶石碎片(隨機稀有度)", - "機率獲得稀有 / 史詩 / 傳說等級碎片", - "每日可無限次參與", - ], - imagePlaceholder: "活動示意圖(即將上線)", - note: "活動說明與獎勵內容可能隨版本更新調整,請以遊戲內公告為準。", + rewardBody: "隨活動不同會有不同的獎勵,詳情請見當下活動說明。", + note: "*實際可以獲得獎勵的名次以當下活動為準。活動說明與獎勵內容可能隨版本更新調整,請以遊戲內公告為準。", }, gameplay: { whatIsAurayale: { @@ -273,61 +263,30 @@ const zhTW: Translation = { title: "遊戲流程", items: [ "連接錢包,完成登入並領取起始牌組。", - "於「Deck」頁面挑選 5 張寶石組成戰鬥牌組。", - "進入「Battle」開始對局,每回合打出寶石卡組成撲克牌型,以 ATK × Mult 結算傷害。", - "勝場可獲得寶石碎片,於市場交易升級你的牌組。", + "於「Deck」頁面選擇 10 張寶石卡組成戰鬥牌組。", + "進入「Battle」開始對局。每回合抽取 6 張撲克牌,並將寶石手牌補至 5 張;使用寶石調整撲克牌或強化 ATK、MULT 與生存能力。", + "系統會計算場上成立的所有組合;各組合的 ATK 與 MULT 分別加總,套用寶石效果後,以 Total ATK × Total MULT 結算傷害。", + "戰鬥勝利後可獲得寶石碎片,並於市場交易或升級牌組。", ], }, coreMechanics: { title: "核心機制", - items: [ - { t: "牌型計分", d: "打出順子 / 同花 / 葫蘆等撲克牌型,傷害 = ATK × Mult" }, - { t: "升級系統", d: "兩張同色寶石可合成更高級的卡片" }, - { t: "稀有度判定", d: "依 4C 標準分為一般 / 稀有 / 史詩 / 傳說" }, - { t: "寶石市集", d: "與其他玩家直接以寶石換寶石" }, - ], + heading: "組合計分", + body: "同一手牌可以同時成立多種組合,所有成立的組合都會計分。", }, }, encyclopedia: { - title: "基礎寶石(24 種)", - hint: "點擊任意卡片查看詳細資料(名稱、效果、稀有度)。每種寶石都有三個升級階段(基礎 / +1 / +2)。", + title: "基礎寶石(40 種)", + hint: "點擊任意卡片查看名稱與效果。每種基礎寶石仍有三個升級階段(基礎/+1/+2)。", imageFailed: "圖片載入失敗", + imageComingSoon: "卡圖準備中", + eventCardTitle: "活動限定卡", + eventCardHint: "僅於特定活動期間取得,不列入 40 種基礎寶石。", + eventCardBadge: "活動限定", }, cardDetail: { cardLabel: "CARD", effect: "卡牌效果", - rarityExplanation: "稀有度說明", - tier: "等級", - tierOfFour: "{{tier}} / 4", - }, - rarity: { - fourCTitle: "寶石稀有度的 4C 標準", - fourCIntro: "Aurayale 沿用真實寶石業界的 4C 評鑑系統,綜合判定卡片的稀有度與市場價值。", - levelsTitle: "稀有度級別", - fourC: [ - { - title: "Color · 顏色", - desc: "顏色越純淨、飽和度越高的寶石價值越高。Aurayale 中分為冷色(藍/綠)、暖色(紅/黃)與中性(白/黑),每種色系有獨立的稀有曲線。", - }, - { - title: "Cut · 切工", - desc: "切工決定光線折射的角度與火彩。遊戲中切工越精細的寶石,在戰鬥中觸發特效的機率越高。", - }, - { - title: "Clarity · 淨度", - desc: "淨度衡量寶石內部包裹體與表面瑕疵的稀少程度。淨度越高的寶石可承載的附魔等級越高。", - }, - { - title: "Carat · 克拉", - desc: "克拉指寶石的重量(體積)。同等品質下,克拉越大價值呈幾何級數成長,但也意味著更高的能量消耗。", - }, - ], - descriptions: { - common: "常見的入門級寶石,容易在抽卡或日常任務中取得,適合新手組牌。", - rare: "稀有等級寶石,具有較強的單體技能效果,通常需透過進階卡包獲得。", - epic: "史詩等級寶石,擁有改變戰局的關鍵技能,是中後期牌組的核心。", - legendary: "傳說等級寶石,極為罕見,擁有獨特的全場性效果與絢麗的視覺特效。", - }, }, shop: { title: "寶石商店", @@ -470,46 +429,47 @@ const zhTW: Translation = { }, }, cards: { - "1": { name: "Flame Ruby", effect: "若牌型包含順子(Straight),ATK + 50。" }, - "2": { name: "Deep-Sea Sapphire", effect: "若牌型包含順子(Straight),Mult + 15。" }, - "3": { name: "Heart of Jade", effect: "目標牌點數 +1。" }, - "4": { name: "Topaz Light", effect: "目標牌點數 -1。" }, - "5": { name: "Moonstone", effect: "若所有牌點數皆為奇數,ATK + 50。" }, - "6": { name: "Amber Eye", effect: "若所有牌點數皆為偶數,Mult + 10。" }, - "7": { name: "Coral Abyss", effect: "若牌型包含同花(Flush),ATK + 50。" }, - "8": { name: "Pearl Star", effect: "若牌型包含同花(Flush),Mult + 15。" }, - "9": { name: "Smoky Quartz", effect: "將紅心(Heart)花色變更為黑桃(Spade)。" }, - "10": { name: "Amethyst Reverie", effect: "將黑桃(Spade)花色變更為梅花(Club)。" }, - "11": { name: "Bloodstone Dragon Breath", effect: "將方塊(Diamond)花色變更為紅心(Heart)。" }, - "12": { name: "Frost Crystal", effect: "將梅花(Club)花色變更為方塊(Diamond)。" }, - "13": { name: "Thunder Agate", effect: "若牌型包含兩對(Two Pair),ATK + 25。" }, - "14": { name: "Verdant Olivine", effect: "若牌型包含兩對(Two Pair),Mult + 5。" }, - "15": { name: "Fire Opal", effect: "若下一回合牌型包含兩對(Two Pair),ATK + 50。" }, - "16": { name: "Obsidian Spear", effect: "若牌型包含三條(Three of a Kind),ATK + 35。" }, - "17": { name: "Aquamarine Tears", effect: "若牌型包含三條(Three of a Kind),Mult + 10。" }, - "18": { name: "Imperial Emerald", effect: "若下一回合牌型包含三條(Three of a Kind),Mult + 15。" }, - "19": { name: "Tiger Eye Fury", effect: "ATK + 15。" }, - "20": { name: "Star Sapphire", effect: "Mult + 4。" }, - "21": { name: "Golden Sunstone", effect: "本回合 Mult 設為 1。下一回合 Mult x 3。" }, - "22": { name: "Night Obsidian", effect: "若牌型包含順子(Straight),Mult x 2。" }, - "23": { name: "Eternal Diamond", effect: "每當牌型包含同花(Flush)時,永久 ATK + 10。" }, - "24": { name: "Genesis Stone", effect: "每當牌型包含同花(Flush)時,永久 Mult + 4。" }, - "25": { name: "Jade Ice Heart", effect: "若為黑桃同花(Spade Flush),獲得 Mult + 2,持續 3 回合。" }, - "26": { name: "Red Spinel", effect: "若為紅心同花(Heart Flush),3 回合內每回合恢復 5 HP。" }, - "27": { name: "Citrine", effect: "若為方塊同花(Diamond Flush),3 回合內每回合永久 ATK + 5。" }, - "28": { name: "Pink Diamond Heart", effect: "若為梅花同花(Club Flush),3 回合內受到傷害減少 15%。" }, - "29": { name: "Black Tourmaline", effect: "本回合 ATK - 25。下一回合 ATK + 100。" }, - "30": { name: "Kunzite", effect: "Mult 增加值等於所有牌點數之和。" }, - "31": { name: "Sapphire Eye", effect: "ATK - 25。Mult + 30。" }, - "32": { name: "Imperial Topaz", effect: "若牌型包含兩對(Two Pair),恢復 20 HP。" }, - "33": { name: "Morganite Kiss", effect: "本回合 Mult - 5。下一回合 Mult + 15。" }, - "34": { name: "Tsavorite", effect: "若牌型包含葫蘆(Full House),受到傷害減少 50%。" }, - "35": { name: "Crimson Lattice", effect: "Mult - 5。ATK + 30。" }, - "36": { name: "Tanzanite Star", effect: "若牌型包含三條(Three of a Kind),受到傷害減少 30%。" }, - "37": { name: "Paraiba Tourmaline", effect: "ATK 設為 30。" }, - "38": { name: "Alexandrite", effect: "Mult 設為 10。" }, - "39": { name: "Dragon Breath Heart", effect: "ATK 增加值等於所有牌點數之和。" }, - "40": { name: "Genesis Void", effect: "受到傷害減少 15%。" }, + "1": { name: "Flame Ruby", effect: "如果手牌包含符文連鎖 III 以上,則攻擊 +75。" }, + "2": { name: "Deep-Sea Sapphire", effect: "如果手牌包含符文連鎖 III 以上,則倍數 +15。" }, + "3": { name: "Heart of Jade", effect: "將一張目標撲克牌的點數增加1。" }, + "4": { name: "Topaz Light", effect: "將一張目標撲克牌的點數減少1。" }, + "5": { name: "Moonstone", effect: "將本回合攻擊設定為250,然後倍數 +3。" }, + "6": { name: "Amber Eye", effect: "恢復20 HP。" }, + "7": { name: "Coral Abyss", effect: "如果手牌包含同色共鳴 III 以上,則攻擊 +60。" }, + "8": { name: "Pearl Star", effect: "如果手牌包含同色共鳴 III 以上,則倍數 +10。" }, + "9": { name: "Smoky Quartz", effect: "將場上所有紅心花色設定為黑桃。" }, + "10": { name: "Amethyst Reverie", effect: "將場上所有黑桃花色設定為梅花。" }, + "11": { name: "Bloodstone Dragon Breath", effect: "將場上所有方塊花色設定為紅心。" }, + "12": { name: "Frost Crystal", effect: "將場上所有梅花花色設定為方塊。" }, + "13": { name: "Thunder Agate", effect: "如果手牌包含符文共鳴 II 以上,則攻擊 +40。" }, + "14": { name: "Verdant Olivine", effect: "如果手牌包含符文共鳴 II 以上,則倍數 +5。" }, + "15": { name: "Fire Opal", effect: "下回合:如果手牌包含符文共鳴 II 以上,則攻擊 +75。" }, + "16": { name: "Obsidian Spear", effect: "如果手牌包含符文共鳴 III 以上,則攻擊 +100。" }, + "17": { name: "Aquamarine Tears", effect: "如果手牌包含符文共鳴 III 以上,則倍數 +20。" }, + "18": { name: "Imperial Emerald", effect: "下回合:如果手牌包含符文共鳴 III 以上,則倍數 +30。" }, + "19": { name: "Tiger Eye Fury", effect: "攻擊 +15。" }, + "20": { name: "Star Sapphire", effect: "倍數 +4。" }, + "21": { name: "Golden Sunstone", effect: "如果手牌包含符文連鎖 III 以上,則本回合倍數 -10,下回合倍數 +30。" }, + "22": { name: "Night Obsidian", effect: "如果手牌包含符文連鎖 III 以上,則本回合倍數 ×2。" }, + "23": { name: "Eternal Diamond", effect: "如果手牌包含同色共鳴 III 以上,則攻擊永久 +15。" }, + "24": { name: "Genesis Stone", effect: "如果手牌包含同色共鳴 III 以上,則倍數永久 +2。" }, + "25": { name: "Jade Ice Heart", effect: "如果手牌包含同色共鳴 III 以上,則倍數 +2(持續3回合)。" }, + "26": { name: "Red Spinel", effect: "如果手牌包含同色共鳴 III 以上,則每回合恢復15 HP(持續3回合)。" }, + "27": { name: "Citrine", effect: "如果手牌包含同色共鳴 III 以上,則接下來3回合每回合永久獲得攻擊 +10(持續3回合)。" }, + "28": { name: "Pink Diamond Heart", effect: "如果手牌包含同色共鳴 III 以上,則受到的傷害減少15%(持續3回合)。" }, + "29": { name: "Black Tourmaline", effect: "如果手牌包含符文連鎖 III 以上,則本回合攻擊 -25,下回合攻擊 +125。" }, + "30": { name: "Kunzite", effect: "如果手牌包含符文連鎖 IV 以上,則倍數 +(6張撲克牌點數總和)。" }, + "31": { name: "Sapphire Eye", effect: "如果手牌包含符文連鎖 III 以上,則本回合攻擊 -25、倍數 +30。" }, + "32": { name: "Imperial Topaz", effect: "如果手牌包含符文共鳴 II 以上,則恢復50 HP。" }, + "33": { name: "Morganite Kiss", effect: "如果手牌包含符文連鎖 III 以上,則本回合倍數 -5,下回合倍數 +15。" }, + "34": { name: "Tsavorite", effect: "如果手牌包含符文連鎖 IV 以上,則本回合受到的傷害減少50%。" }, + "35": { name: "Crimson Lattice", effect: "如果手牌包含符文連鎖 III 以上,則本回合倍數 -5、攻擊 +100。" }, + "36": { name: "Tanzanite Star", effect: "如果手牌包含符文共鳴 III 以上,則本回合受到的傷害減少30%。" }, + "37": { name: "Paraiba Tourmaline", effect: "本回合攻擊 +150、倍數 -10。" }, + "38": { name: "Alexandrite", effect: "本回合倍數 +25、攻擊 -100。" }, + "39": { name: "Dragon Breath Heart", effect: "攻擊 +(6張撲克牌點數總和)。" }, + "40": { name: "Genesis Void", effect: "本回合受到的傷害減少15%。" }, + "41": { name: "Aura Genesis", effect: "如果手牌包含完美共鳴 II 以上,則本回合攻擊 +100、倍數 +20。" }, }, } diff --git a/src/lib/chainUtils.ts b/src/lib/chainUtils.ts index 803f8da..bc5c7e4 100644 --- a/src/lib/chainUtils.ts +++ b/src/lib/chainUtils.ts @@ -41,7 +41,6 @@ export function getDefaultChainForLoginType(loginType: string | undefined): Chai switch (loginType) { case 'google': return avaxFuji; - case 'farcaster': default: return soneiumMinato; } diff --git a/src/lib/farcaster.ts b/src/lib/farcaster.ts deleted file mode 100644 index 3f5ccb5..0000000 --- a/src/lib/farcaster.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Farcaster 登入相關工具函數 - */ - -/** - * 生成 Farcaster 認證消息 - * @param walletAddress 錢包地址 - * @param farcasterId Farcaster ID (fid),可選 - * @returns 格式化的認證消息 - */ -export function generateFarcasterMessage( - walletAddress: string, - farcasterId?: string | number -): string { - const fid = farcasterId ? String(farcasterId) : ''; - return `Sign in to Aura Server with Farcaster - -Wallet: ${walletAddress}${fid ? `\nFID: ${fid}` : ''}`; -} - -/** - * 使用 wagmi 簽名消息 - * 注意:這個函數需要在 React 組件中使用 wagmi hooks - * 這裡提供一個輔助函數來處理簽名邏輯 - */ -export async function signMessageWithWallet( - signMessageAsync: (message: string) => Promise<`0x${string}`>, - message: string -): Promise { - try { - const signature = await signMessageAsync(message); - return signature; - } catch (error: any) { - throw new Error(error.message || 'Failed to sign message'); - } -} - diff --git a/src/pages/platform.tsx b/src/pages/platform.tsx index b85ee28..3e719e7 100644 --- a/src/pages/platform.tsx +++ b/src/pages/platform.tsx @@ -29,11 +29,6 @@ export default function PlatformPage() { const router = useRouter() const { safeAreaInsetBottom } = useViewportRequirements() - const [farcasterUser, setFarcasterUser] = useState<{ - username?: string; - fid?: number; - } | null>(null); - // 優先使用後端認證的地址(與 WalletInfo 顯示一致),避免多錢包時地址不匹配 const walletAddress = user?.walletAddress || privyUser?.wallet?.address || connectedAddress || null @@ -183,17 +178,9 @@ export default function PlatformPage() { fetchTrades() // 刷新訂單列表 } - // 如果用戶通過 Privy 登入但沒有有效的 AuraServer token,重定向到登入頁面 - // Farcaster 登入會在 login.tsx 中處理 + // 通過 Privy 登入但沒有有效的 AuraServer token 時,回登入頁重新換發。 useEffect(() => { if (ready && authenticated && privyUser && !user?.token) { - // 如果有 Farcaster 登入但沒有 token,重定向到登入頁面處理 - if (privyUser.farcaster) { - router.push("/aurayale"); - return; - } - // 對於非 Farcaster 登入,也重定向到登入頁面 - // 因為我們需要有效的 AuraServer token router.push("/aurayale"); } }, [ready, authenticated, privyUser, user, router]); diff --git a/src/types/auraServer.ts b/src/types/auraServer.ts index 1a75786..fc58cd3 100644 --- a/src/types/auraServer.ts +++ b/src/types/auraServer.ts @@ -90,38 +90,35 @@ export interface GoogleLoginResponse { } /** - * Farcaster 登入請求 - * 支援兩種登入方式: - * 1. 使用 Privy Access Token(推薦,無需簽名) - * 2. 使用錢包簽名(向後兼容) + * Privy 登入請求 */ -export interface FarcasterLoginRequest { - // 方式 1: Privy Token(推薦) +export interface PrivyLoginRequest { privyAccessToken?: string; chain_id?: string; // 選填,預設為 "bsc-testnet" - - // 方式 2: 錢包簽名(向後兼容) - walletAddress?: string; - signature?: string; - message?: string; // 選填,預設會自動生成 - farcasterId?: string; // 選填,Farcaster ID (fid) } /** - * Farcaster 登入回應 + * 玩家實際使用的登入手段。 + * Privy 底下可能是 Google OAuth、email 或直接連錢包。 + */ +export type LoginType = 'google' | 'email' | 'wallet' | 'password'; + +/** + * Privy 登入回應 */ -export interface FarcasterLoginResponse { +export interface PrivyLoginResponse { token: string; userId: number; chainId: 'bsc-testnet' | 'soneium-testnet' | string; walletAddress: string; gems: Record; - farcasterId?: string; - farcasterUsername?: string; - farcasterPfpUrl?: string; + loginType: LoginType; name: string; + email?: string | null; + avatarUrl?: string | null; } + // ==================== 錢包相關類型 ==================== /** @@ -172,7 +169,7 @@ export interface UserProfile { name: string; walletAddress: string | null; hasGoogleAccount: boolean; - loginType: 'google' | 'password' | 'farcaster'; + loginType: LoginType; } /** @@ -228,10 +225,9 @@ export interface UserInfo { walletAddress?: string; deck?: number[]; gems?: GemItem[]; - loginType?: 'google' | 'password' | 'farcaster'; // 登入類型 - farcasterId?: string; // Farcaster ID(如果是 Farcaster 登入) - farcasterUsername?: string; // Farcaster 用戶名 - farcasterPfpUrl?: string; // Farcaster 頭像 URL + loginType?: LoginType; // 登入類型 + email?: string | null; // 來自 Privy 的 email(Google / email 登入) + avatarUrl?: string | null; // 來自 Privy 的頭像 } // ==================== 交易訂單相關類型 ====================