Split card design into front+back, add sound effects & background music
Card design: - Separate cardFront + cardBack (each own/equip independently) - Fronts: classic (free), ivory/rosegold (buy), parchment/mint (earned) - Backs: classic (free), sapphire/emerald (buy), ruby/royal (earned) - PlayingCard `front` prop; table applies front to all faces, back to opponents - Profile has front + back pickers; shop has both sections Audio: - Web Audio synth engine (no asset files): SFX for card/deal/trump/trick, win/lose, message, notify, award, levelup, purchase, kot + ambient music - Toggles in profile (Audio) + mute button in game HUD; prefs persisted - Wired across game-store, rewards, daily, shop, chat Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-1
@@ -13,6 +13,7 @@ import {
|
||||
} from "./hokm/engine";
|
||||
import { Card, GameState, RoundResult, Seat, Suit } from "./hokm/types";
|
||||
import { avatarEmoji } from "./online/types";
|
||||
import { sound } from "./sound";
|
||||
|
||||
const KOT_POINTS = 2;
|
||||
|
||||
@@ -111,6 +112,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
if (cur.phase !== "playing" || cur.turn !== seat) return;
|
||||
const card = chooseCardAI(cur, seat);
|
||||
set({ game: playCard(cur, seat, card) });
|
||||
sound.play("cardPlay");
|
||||
scheduleAuto();
|
||||
}
|
||||
|
||||
@@ -123,6 +125,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
set({ turnDeadline: null, disconnectedSeat: null, reconnectDeadline: null });
|
||||
pending = setTimeout(() => {
|
||||
set({ game: dealForTrump(get().game) });
|
||||
sound.play("deal");
|
||||
scheduleAuto();
|
||||
}, TIMING.hakemDraw);
|
||||
break;
|
||||
@@ -145,6 +148,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
const cur = get().game;
|
||||
const suit = chooseTrumpAI(cur.players[cur.hakem!].hand);
|
||||
set({ game: engineChooseTrump(cur, suit) });
|
||||
sound.play("trump");
|
||||
scheduleAuto();
|
||||
}, TIMING.aiTrump);
|
||||
}
|
||||
@@ -160,6 +164,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
const cur = get().game;
|
||||
if (cur.phase !== "playing" || cur.turn !== seat) return;
|
||||
set({ game: playCard(cur, seat, chooseCardAI(cur, seat)), turnDeadline: null });
|
||||
sound.play("cardPlay");
|
||||
scheduleAuto();
|
||||
}, TURN_MS);
|
||||
} else {
|
||||
@@ -190,10 +195,16 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
|
||||
case "trick-complete":
|
||||
set({ turnDeadline: null, disconnectedSeat: null, reconnectDeadline: null });
|
||||
sound.play("trickWin");
|
||||
pending = setTimeout(() => {
|
||||
const next = advanceAfterTrick(get().game, KOT_POINTS);
|
||||
set({ game: next });
|
||||
if (next.phase === "match-over") recordRound(next.lastRoundResult);
|
||||
if (next.phase === "match-over") {
|
||||
recordRound(next.lastRoundResult);
|
||||
sound.play(next.matchWinner === 0 ? "win" : "lose");
|
||||
} else if (next.phase === "round-over" && next.lastRoundResult?.kot) {
|
||||
sound.play("kot");
|
||||
}
|
||||
scheduleAuto();
|
||||
}, TIMING.trickPause);
|
||||
break;
|
||||
@@ -226,6 +237,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
|
||||
newMatch: (settings) => {
|
||||
clearPending();
|
||||
sound.init();
|
||||
const initial = createInitialState(settings);
|
||||
set({
|
||||
game: selectHakem(initial),
|
||||
@@ -247,6 +259,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
|
||||
newOnlineMatch: (cfg) => {
|
||||
clearPending();
|
||||
sound.init();
|
||||
const names = cfg.players.map((p) => p.displayName) as GameSettings["names"];
|
||||
const initial = createInitialState({ names, targetScore: cfg.targetScore });
|
||||
set({
|
||||
@@ -271,6 +284,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
const g = get().game;
|
||||
if (g.phase !== "choosing-trump") return;
|
||||
set({ game: engineChooseTrump(g, suit), turnDeadline: null });
|
||||
sound.play("trump");
|
||||
scheduleAuto();
|
||||
},
|
||||
|
||||
@@ -278,6 +292,7 @@ export const useGameStore = create<GameStore>((set, get) => {
|
||||
const g = get().game;
|
||||
if (g.phase !== "playing" || g.turn !== 0) return;
|
||||
set({ game: playCard(g, 0, card), turnDeadline: null });
|
||||
sound.play("cardPlay");
|
||||
scheduleAuto();
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user