Add reactions (Sheklak), fix hakem card visibility during trump choice

- Reactions/emotes in-game: tray of owned emojis + animated per-seat bubbles
  (feature named "شکلک / Sheklak"). Packs: starter (free), champion/legend
  (earned by rating/wins), emotions/taunt (purchasable in shop)
- OnlineService.sendReaction/onReaction; mock echoes you + random opponents
- Fix: human hakem's 5 cards were blurred behind the trump-chooser overlay —
  raise hand to z-50 during choosing-trump so cards stay readable

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 11:02:25 +03:30
parent 13ec0d4300
commit f9425dea01
8 changed files with 222 additions and 8 deletions
+34
View File
@@ -10,6 +10,7 @@ import {
PlayerStats,
RankTier,
RankTierId,
ReactionPackDef,
RewardResult,
TitleDef,
TitleUnlock,
@@ -228,6 +229,39 @@ export function cardStyleById(id: string): CardStyleDef {
return CARD_STYLES.find((c) => c.id === id) ?? CARD_STYLES[0];
}
/* --------------------- Reactions (Sheklak / شکلک) -------------------- */
export const REACTION_PACKS: ReactionPackDef[] = [
{ id: "starter", nameFa: "پایه", nameEn: "Starter", reactions: ["👍", "👏", "😂", "😮"], price: 0, default: true },
{ id: "emotions", nameFa: "احساسات", nameEn: "Emotions", reactions: ["😎", "😭", "🤯", "🥳", "😍"], price: 600 },
{ id: "taunt", nameFa: "طعنه", nameEn: "Taunts", reactions: ["😏", "🤡", "🙄", "😴", "🥱"], price: 900 },
{ id: "champion", nameFa: "قهرمان", nameEn: "Champion", reactions: ["👑", "🏆", "💪", "🔥"], price: 0, unlockRating: 1300 },
{ id: "legend", nameFa: "اسطوره", nameEn: "Legend", reactions: ["💎", "⚡", "🐐", "🎯"], price: 0, unlockWins: 100 },
];
export function reactionPackById(id: string): ReactionPackDef | undefined {
return REACTION_PACKS.find((p) => p.id === id);
}
/** Which packs the player currently owns (default + earned + purchased). */
export function ownedReactionPackIds(profile: UserProfile): string[] {
const purchased = profile.ownedReactionPacks ?? [];
const ids = new Set<string>();
for (const p of REACTION_PACKS) {
const earned =
(p.unlockRating != null && profile.rating >= p.unlockRating) ||
(p.unlockWins != null && profile.stats.wins >= p.unlockWins);
if (p.default || earned || purchased.includes(p.id)) ids.add(p.id);
}
return [...ids];
}
/** Flattened emoji list the player can send. */
export function ownedReactions(profile: UserProfile): string[] {
const ids = new Set(ownedReactionPackIds(profile));
return REACTION_PACKS.filter((p) => ids.has(p.id)).flatMap((p) => p.reactions);
}
/* ---------------------- Apply a match result ------------------------- */
function applyStats(stats: PlayerStats, summary: MatchSummary): PlayerStats {