Phase 0-2: Vite/Phaser 4/TS boilerplate + Platform Adapter + core match (vs 4 AI personalities)
Phase 0 (foundation): - package.json, tsconfig.json (strict), vite.config.ts (base: './'), .eslintrc.cjs - index.html, src/main.ts (Phaser game init + platform detection) - src/config/defaults.ts (Remote Config defaults, palette, constants) - src/types/index.ts (все schemas из data-contracts: SaveState, MatchResult, RemoteConfig, etc.) - src/platform/: PlatformAdapter interface + MockPlatformAdapter + YgPlatformAdapter Phase 1 (core mechanics): - src/scenes/: Boot, Preload, MainMenu (Quick Match selector), Setup, Match, Result - src/game/: Table (геометрия + walls + ворота), Ball (last-touch + color tracking), Flipper (player + AI, swing + cooldown), Bumper (4 типа: standard/slingshot/curve/turbo) - src/scoring/: MatchTracker (last-touch + bumper points), calculateScores (unified scoring pipeline per data-contracts v3.8 requirement) - src/ai/: AIPlayer interface + DefensiveAI Easy/Medium Phase 2 (setup + AI personalities): - src/scenes/SetupScene (5 active slots, click-cycle bumper types, 30s timer) - src/ai/personalities/: AggressiveAI, TricksterAI, GhostAI (Easy/Medium; Hard cradle-aim = Phase 3) - src/ai/factory.ts (createAIPlayer) - MainMenuScene: выбор AI-личности + сложности - MatchScene использует factory вместо хардкода Anti-A2W enforcement: calculateScores применяет penalty per v3.5 model (Continue × 0.5, Campaign Skip × 0.7, +1 заряд × 0.9, реролл × 0.85, Boost active × 0.7; ×2 sezon очки только в local). Что НЕ в этом commit'е (следующие фазы): - Phase 3: бустер «Захват» (cradle + slow-mo), Hard AI (cradle-aim solver), Ghost adaptation, YG Player API cloud-save, achievements - Phase 4: Campaign (12 матчей), Tournament (8/16 bracket), шейдеры (CRT + bloom + chromatic aberration), 29 AI-арт ассетов, музыка - Phase 5: реальная YG SDK интеграция (ads + payments), Remote Config Nakama RPC - Phase 6: YG submission Pre-greenlit concept-пакет — ~/Knowledge/Projects/pinball-duel/ (19 файлов, 10 audit-раундов). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
163
src/config/defaults.ts
Normal file
163
src/config/defaults.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { RemoteConfig } from '../types';
|
||||
|
||||
// Bundled defaults для Remote Config — fail-open если сервер недоступен
|
||||
// (см. ~/Knowledge/Projects/pinball-duel/concepts/data-contracts.md#remote-config-schema)
|
||||
export const BUNDLED_DEFAULTS: RemoteConfig = {
|
||||
version: 1,
|
||||
configVersion: 0,
|
||||
fetchedAt: 0,
|
||||
ttl: 3600,
|
||||
|
||||
flags: {
|
||||
sprintMode: false,
|
||||
tournamentPassVisible: false,
|
||||
seasonalDlcActive: {},
|
||||
crashReportingEnabled: true,
|
||||
},
|
||||
|
||||
ai: {
|
||||
defensive: {
|
||||
easy: { reactionMs: 250, jitterMs: 100, useBooster: false },
|
||||
medium: { reactionMs: 150, jitterMs: 50, useBooster: true },
|
||||
hard: { reactionMs: 80, jitterMs: 20, useBooster: true },
|
||||
},
|
||||
aggressive: {
|
||||
easy: { reactionMs: 250, jitterMs: 100, useBooster: false },
|
||||
medium: { reactionMs: 150, jitterMs: 50, useBooster: true },
|
||||
hard: { reactionMs: 80, jitterMs: 20, useBooster: true },
|
||||
},
|
||||
trickster: {
|
||||
easy: { reactionMs: 250, jitterMs: 100, useBooster: false },
|
||||
medium: { reactionMs: 150, jitterMs: 50, useBooster: true },
|
||||
hard: { reactionMs: 80, jitterMs: 20, useBooster: true },
|
||||
},
|
||||
ghost: {
|
||||
easy: { reactionMs: 250, jitterMs: 100, useBooster: false, adaptationStrength: 0.3 },
|
||||
medium: { reactionMs: 150, jitterMs: 50, useBooster: true, adaptationStrength: 0.3 },
|
||||
hard: { reactionMs: 80, jitterMs: 20, useBooster: true, adaptationStrength: 0.3 },
|
||||
},
|
||||
},
|
||||
|
||||
booster: {
|
||||
chargeThreshold: 100,
|
||||
maxCharges: 2,
|
||||
unavailableSeconds: 30,
|
||||
cradleSlowMoFactor: 0.3,
|
||||
cradleDurationMs: 1500,
|
||||
},
|
||||
|
||||
match: {
|
||||
goalsToWin: 5,
|
||||
softCapMinutes: 6,
|
||||
goldenGoalEnabled: true,
|
||||
ballSpeedStart: 400,
|
||||
ballSpeedCap: 900,
|
||||
},
|
||||
|
||||
telemetry: {
|
||||
eventSamplingRate: 1.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Tier-S synthwave палитра (5 HEX + чёрный)
|
||||
// см. ~/Knowledge/Projects/pinball-duel/concepts/visual-style.md
|
||||
export const PALETTE = {
|
||||
bg: 0x0a0a14, // глубокий тёмно-синий
|
||||
player: 0xff006e, // маджента
|
||||
ai: 0x00f0ff, // циан
|
||||
accent: 0xffbe0b, // янтарно-золотой
|
||||
white: 0xffffff,
|
||||
black: 0x000000,
|
||||
neutral: 0x808080, // серый для нейтрального мяча
|
||||
} as const;
|
||||
|
||||
// AI personality color tints (дифференциация — visual-style.md)
|
||||
export const AI_PERSONALITY_TINTS = {
|
||||
defensive: 0x00d0e8, // steel-cyan
|
||||
aggressive: 0x00f0ff, // base cyan + magenta corona (применяется отдельно)
|
||||
trickster: 0x00f0ff, // alternating
|
||||
ghost: 0x00f0ff, // semi-transparent (alpha 0.4)
|
||||
} as const;
|
||||
|
||||
// Bumper points by type (см. data-contracts.md#leaderboard-formula)
|
||||
// КРИТИЧНО: leaderboard formula использует bumper_points_earned (не bumper_hits×5)
|
||||
export const BUMPER_POINTS = {
|
||||
standard: 5,
|
||||
slingshot: 3,
|
||||
curve: 1,
|
||||
turbo: 10,
|
||||
} as const;
|
||||
|
||||
export const GOAL_POINTS = 50; // +50 в bumper-points-equivalent для забившего
|
||||
|
||||
// LB Penalty multipliers (Anti-A2W строгий, v3.5+)
|
||||
// см. monetization.md + data-contracts.md
|
||||
export const LB_PENALTIES = {
|
||||
continueUsed: 0.5, // Continue (любой режим)
|
||||
campaignSkipUsed: 0.7,
|
||||
campaignBoostActive: 0.7, // для всех matches 4-12
|
||||
rewardedBoosterCharge: 0.9,
|
||||
rewardedSetupReroll: 0.85,
|
||||
// rewardedDoublePoints: НЕ применяется к base (только к local statistics)
|
||||
} as const;
|
||||
|
||||
// Mode multipliers
|
||||
export const MODE_MULTIPLIERS = {
|
||||
quick: 1.0,
|
||||
campaign: 1.5,
|
||||
tournament: 2.0,
|
||||
} as const;
|
||||
|
||||
export const DIFFICULTY_MULTIPLIER_HARD = 1.25;
|
||||
|
||||
// Game dimensions — 9:16 portrait (см. concept-design.md#стол-и-геометрия)
|
||||
export const GAME_WIDTH = 720;
|
||||
export const GAME_HEIGHT = 1280;
|
||||
|
||||
// Table zones (в долях от height)
|
||||
export const TABLE_ZONES = {
|
||||
aiTerritoryTop: 0.0,
|
||||
aiTerritoryBottom: 0.3,
|
||||
centerTop: 0.3,
|
||||
centerBottom: 0.7,
|
||||
playerTerritoryTop: 0.7,
|
||||
playerTerritoryBottom: 1.0,
|
||||
} as const;
|
||||
|
||||
// Ball params (см. concept-design.md#мяч-и-физика)
|
||||
export const BALL_RADIUS = 22; // ~3% width
|
||||
export const BALL_RESTITUTION = 1.0;
|
||||
export const BALL_DAMPING = 0.99;
|
||||
export const WALL_RESTITUTION = 0.95;
|
||||
|
||||
// Flipper params
|
||||
export const FLIPPER_LENGTH = 110;
|
||||
export const FLIPPER_WIDTH = 18;
|
||||
export const FLIPPER_REST_ANGLE_DEG = 30; // покой
|
||||
export const FLIPPER_ACTIVE_ANGLE_DEG = -45; // взмах вверх
|
||||
export const FLIPPER_SWING_MS = 80;
|
||||
export const FLIPPER_RETURN_MS = 120;
|
||||
export const FLIPPER_COOLDOWN_MS = 50;
|
||||
|
||||
// Goals — gap ~15% ширины ворот в центре
|
||||
export const GOAL_GAP_RATIO = 0.15;
|
||||
|
||||
// Setup slots (game-design фиксирует "5 активных из 7 anchor positions" в MVP)
|
||||
// см. data-contracts.md#setupconfig-schema
|
||||
export const ACTIVE_SETUP_SLOTS: ReadonlyArray<
|
||||
| 'goal_corner_left'
|
||||
| 'goal_corner_right'
|
||||
| 'near_goal_center'
|
||||
| 'mid_left'
|
||||
| 'mid_right'
|
||||
> = [
|
||||
'goal_corner_left',
|
||||
'goal_corner_right',
|
||||
'near_goal_center',
|
||||
'mid_left',
|
||||
'mid_right',
|
||||
];
|
||||
|
||||
// Match defaults
|
||||
export const SETUP_PHASE_DURATION_SEC = 30;
|
||||
export const STARTUP_BOOSTER_LOCKOUT_SEC = 30;
|
||||
Reference in New Issue
Block a user