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:
@@ -125,10 +125,28 @@ public class ProfileService
|
||||
["xp3"] = (8000, 1500),
|
||||
};
|
||||
|
||||
// Gated gifts encode their tier in the id (`-t<n>-`); the gate is derived from
|
||||
// the tier so the server enforces it without a 100-entry catalog mirror.
|
||||
// Mirrors GIFT_TIERS in src/lib/online/types.ts.
|
||||
private static readonly (int Level, int Rating)[] GiftGate =
|
||||
{ (0, 0), (0, 0), (10, 0), (20, 0), (35, 0), (0, 1700) }; // index = tier (1..5)
|
||||
|
||||
private static (int Level, int Rating) GiftGateFor(string id)
|
||||
{
|
||||
var m = System.Text.RegularExpressions.Regex.Match(id, @"-t(\d)-");
|
||||
if (m.Success && int.TryParse(m.Groups[1].Value, out var tier) && tier >= 1 && tier <= 5)
|
||||
return GiftGate[tier];
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
public async Task<(bool ok, ProfileDto? profile, string error)> ShopBuy(string uid, string kind, string id, int price)
|
||||
{
|
||||
var p = await GetOrCreate(uid, null);
|
||||
|
||||
// Gated gift: locked until the player meets the tier's level/rating gate.
|
||||
var gate = GiftGateFor(id);
|
||||
if (p.Level < gate.Level || p.Rating < gate.Rating) return (false, p, "locked");
|
||||
|
||||
// XP packs are consumable (grant XP, may level up) — not added to an owned list.
|
||||
if (kind == "xp")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user