一键导入
new-game
Scaffold all files needed for a new card game in delcard_games
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scaffold all files needed for a new card game in delcard_games
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | new-game |
| description | Scaffold all files needed for a new card game in delcard_games |
| disable-model-invocation | true |
Scaffold a new card game named $ARGUMENTS.
src/lib/games/<name>.tsGame logic. Use war.ts as the simplest reference.
Required GameDefinition<State> fields:
id — string matching <name>name — display namedeckType — one of 'FrenchDeckWithJoker' | 'FrenchDeckWithoutJoker' | 'ColorDeck'minPlayers / maxPlayerssetup(players, options?) — returns initial state, must include { players, zones, turnPlayerId, phase, activeGameId: '<name>' }getValidActions(state, playerId) — returns Action[], never null/undefinedapplyAction(state, action) — returns NEW state, never mutatesisOver(state) — returns true iff phase === 'gameover' (or equivalent)getWinner(state) — returns player ID or null; must return null if !isOver(state)onPlayerDisconnect(state, playerId) — optional but recommended; set phase to 'gameover'State type must extend GameStateGeneric (imported from $lib/core/types).
src/lib/games/<name>.test.tsVitest tests. Pattern from war.test.ts:
import { describe, expect, it } from 'vitest'
import { <name> } from './<name>'
const P1 = 'p1'
const P2 = 'p2'
const PLAYERS = [P1, P2]
function setup() { return <name>.setup(PLAYERS) }
describe('<name>.setup', () => {
it('initialises with correct player count', () => { ... })
it('starts in correct phase', () => { ... })
})
describe('<name>.getValidActions', () => {
it('returns empty array when not player turn', () => { ... })
})
describe('<name>.applyAction', () => {
it('does not mutate input state', () => {
const state = setup()
const frozen = Object.freeze({ ...state })
expect(() => <name>.applyAction(frozen, ...)).not.toThrow()
})
})
describe('<name>.isOver / getWinner', () => {
it('getWinner returns null while game in progress', () => {
expect(<name>.getWinner(setup())).toBeNull()
})
})
src/lib/games/rules/<name>.json{ "en": "Rules in English.", "fr": "Règles en français." }
src/lib/games/index.tsAdd alongside existing imports:
import <name>Rules from './rules/<name>.json'
import { <name> } from './<name>'
Add to games object and gameRules record.
src/lib/components/games/<Name>View.svelteUse the frontend-design skill to create this component. Invoke it before writing any UI code.
The component receives these props — pass them as design context to the skill:
let {
state: gameState,
myPlayerId,
players,
validActions,
onAction,
isSpectator = false,
deckSlug
}: {
state: GameStateGeneric
myPlayerId: string
players: LobbyPlayer[]
validActions: Action[]
onAction: (action: Action) => void
isSpectator?: boolean
deckSlug?: string
} = $props()
If the game has opponents arrayed around a shared table/board (3+ players, spatial layout), use GameLayout:
import GameLayout from '$lib/components/games/GameLayout.svelte'
const opponents = $derived(gs.players.filter((p) => p !== myPlayerId))
{#snippet opponentTile(pid: string)}
<!-- opponent card, hand preview, status, etc. -->
{/snippet}
{#snippet center()}
<!-- draw pile, discard, dice tray, phase icon, etc. -->
{/snippet}
<GameLayout {opponents} {opponentTile} {center} />
GameLayout renders a vertical list on mobile and an ellipse arc on desktop automatically. See docs/game-layout.md for full details. Skip it for 2-player or non-spatial games (like WarView).
Constraints to give the skill:
$props, $derived, $effect)shadcn-svelte (bits-ui) for interactive elements when availablelucide-svelte for any iconsWarView.svelte as reference for 2-player prop wiring; use FightView.svelte as reference for multi-player arc layoutapplyAction uses spread ({ ...state }) not mutationgetValidActions returns [] (not null) for inactive playersgetWinner returns null when !isOver(state)activeGameId set to '<name>' in setupfrontend-design skill invoked for the View componentGameLayout (not a hand-rolled arc)