100 gated gifts (level/rating-locked) + requirement system
Adds ~100 new purchasable gifts that are LOCKED until a level/rating gate is met, then buyable with coins — value scales with the gate: - 45 gift avatars (types.ts), 35 gift titles + 20 gift card backs (gamification.ts), all reusing existing renderers. Tier (1-5) encoded in the id (-t<n>-). - Gate model: GIFT_TIERS (shared) → reqLevel/reqRating on AvatarDef/TitleDef/ CardBackDef + ShopItem. Tiers: t1 free, t2 Lv10, t3 Lv20, t4 Lv35, t5 Rating1700. - Shop UI: locked cards dim + show the requirement (Lock + "Level 20"), buy disabled until met; mock buyItem enforces it offline. - Server enforces generically — ProfileService parses the tier from the id and checks the player's level/rating (no 100-entry mirror). Mirrors GIFT_TIERS. - i18n shop.reqLevel/reqRating (fa+en). Verified: tsc + sim + next build + dotnet build all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1031,6 +1031,8 @@ export class MockOnlineService implements OnlineService {
|
||||
preview: a.emoji,
|
||||
descFa: "آواتار نمایه شما در بازی و جدول",
|
||||
descEn: "Your profile avatar in games & leaderboard",
|
||||
reqLevel: a.reqLevel,
|
||||
reqRating: a.reqRating,
|
||||
}));
|
||||
const backItems: ShopItem[] = CARD_BACKS.filter((c) => c.price > 0).map((c) => ({
|
||||
id: c.id,
|
||||
@@ -1041,6 +1043,8 @@ export class MockOnlineService implements OnlineService {
|
||||
preview: c.accent,
|
||||
descFa: "طرح پشت کارتها روی میز",
|
||||
descEn: "The pattern on the back of your cards",
|
||||
reqLevel: c.reqLevel,
|
||||
reqRating: c.reqRating,
|
||||
}));
|
||||
const frontItems: ShopItem[] = CARD_FRONTS.filter((c) => c.price > 0).map((c) => ({
|
||||
id: c.id,
|
||||
@@ -1083,6 +1087,8 @@ export class MockOnlineService implements OnlineService {
|
||||
preview: "🏷️",
|
||||
descFa: "عنوان نمایه که زیر نام شما در بازی و لیستها نشان داده میشود",
|
||||
descEn: "A profile title shown under your name in games & lists",
|
||||
reqLevel: tt.reqLevel,
|
||||
reqRating: tt.reqRating,
|
||||
}));
|
||||
const xpItems: ShopItem[] = XP_PACKS.map((x) => ({
|
||||
id: x.id,
|
||||
@@ -1104,6 +1110,10 @@ export class MockOnlineService implements OnlineService {
|
||||
const item = items.find((i) => i.id === id);
|
||||
if (!item) return { ok: false, messageFa: "آیتم یافت نشد", messageEn: "Item not found" };
|
||||
|
||||
// Purchase gate: locked until the level/rating requirement is met.
|
||||
if ((item.reqLevel && p.level < item.reqLevel) || (item.reqRating && p.rating < item.reqRating))
|
||||
return { ok: false, messageFa: "هنوز باز نشده است", messageEn: "Locked — requirement not met" };
|
||||
|
||||
// XP packs are consumable — grant XP instead of adding to an owned list.
|
||||
if (item.kind === "xp") {
|
||||
if (p.coins < item.price)
|
||||
|
||||
Reference in New Issue
Block a user