Inital commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared game constants & helpers (used by both the server sim and the client)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const TICK_RATE = 30; // server simulation ticks per second
|
||||
export const SNAP_EVERY = 2; // send a snapshot every N ticks (15 Hz)
|
||||
|
||||
export const ARENA_RADIUS = 4200; // playable circle radius in world px
|
||||
|
||||
export const BASE_SPEED = 178; // px per second
|
||||
export const BOOST_MULT = 1.95; // speed multiplier while boosting
|
||||
export const TURN_RATE = 4.4; // radians per second
|
||||
export const BOOST_TURN_RATE = 3.8; // slightly stiffer steering while boosting
|
||||
|
||||
export const START_SCORE = 25;
|
||||
export const MIN_BOOST_SCORE = 18; // cannot boost below this score
|
||||
export const BOOST_DRAIN = 16; // score per second burned while boosting
|
||||
export const BOOST_DROP_INTERVAL = 0.26; // seconds between tail pellets while boosting
|
||||
|
||||
export const PELLET_COUNT = 950; // ambient pellets kept alive in the arena
|
||||
export const BOT_TARGET_TOTAL = 12; // arena is kept filled to this many snakes
|
||||
export const RESPAWN_PROTECT_TICKS = TICK_RATE * 2; // ~2s of spawn protection
|
||||
|
||||
// --- derived snake geometry -------------------------------------------------
|
||||
|
||||
export function snakeRadius(score: number): number {
|
||||
return 5 + Math.sqrt(Math.max(0, score)) * 0.32;
|
||||
}
|
||||
|
||||
export function snakePointCount(score: number): number {
|
||||
return Math.min(420, Math.round(14 + Math.sqrt(Math.max(0, score)) * 2.1));
|
||||
}
|
||||
|
||||
export function snakeSpacing(score: number): number {
|
||||
return Math.max(5.5, snakeRadius(score) * 0.5);
|
||||
}
|
||||
|
||||
// --- math helpers ------------------------------------------------------------
|
||||
|
||||
export function clamp(v: number, lo: number, hi: number): number {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
/** Normalize an angle to (-PI, PI]. */
|
||||
export function normAngle(a: number): number {
|
||||
while (a > Math.PI) a -= Math.PI * 2;
|
||||
while (a <= -Math.PI) a += Math.PI * 2;
|
||||
return a;
|
||||
}
|
||||
|
||||
export function dist(x1: number, y1: number, x2: number, y2: number): number {
|
||||
return Math.hypot(x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
// --- palettes ---------------------------------------------------------------
|
||||
|
||||
export const SNAKE_COLORS = [
|
||||
'#ff5b5b', '#ffb84d', '#ffe14d', '#7dff5b', '#4dffd2', '#4dc9ff',
|
||||
'#8a7dff', '#c95bff', '#ff5bd0', '#9dff4d', '#5bff9d', '#ffd24d',
|
||||
];
|
||||
|
||||
export const PELLET_COLORS = [
|
||||
'#ff6b6b', '#ffd93d', '#6bff8f', '#4dd2ff',
|
||||
'#c586ff', '#ff8fe1', '#8ffcff', '#ffe28f',
|
||||
];
|
||||
|
||||
export const BOT_NAMES = [
|
||||
'Noodle', 'Viper', 'Slinky', 'Zigzag', 'Cobra', 'Python', 'Nagini',
|
||||
'Basilisk', 'Wiggles', 'Turbo', 'Boa', 'Rattles', 'Medusa',
|
||||
'Sidewinder', 'Anaconda', 'DangerNoodle', 'Hiss', 'Monty',
|
||||
];
|
||||
@@ -0,0 +1,72 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Network protocol shared between server and client (JSON over WebSocket)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface PelletData {
|
||||
id: number;
|
||||
x: number;
|
||||
y: number;
|
||||
v: number; // food value (also drives render size)
|
||||
c: number; // color index into PELLET_COLORS
|
||||
}
|
||||
|
||||
export interface SnakeSnap {
|
||||
id: number;
|
||||
n: string; // nickname
|
||||
c: number; // color index into SNAKE_COLORS
|
||||
s: number; // score
|
||||
p: number[]; // flat body points [x0,y0,x1,y1,...], head first
|
||||
b: boolean; // boosting
|
||||
k: number; // kills
|
||||
sp?: boolean; // spawn protection active (ghost)
|
||||
}
|
||||
|
||||
export type GameEvent =
|
||||
| { t: 'die'; id: number; x: number; y: number; c: number; s: number; p: number[] }
|
||||
| { t: 'join'; id: number; n: string }
|
||||
| { t: 'leave'; id: number };
|
||||
|
||||
export interface LeaderboardRow {
|
||||
id: number;
|
||||
n: string;
|
||||
s: number;
|
||||
}
|
||||
|
||||
export interface YouState {
|
||||
score: number;
|
||||
rank: number;
|
||||
alive: boolean;
|
||||
kills: number;
|
||||
best: number;
|
||||
}
|
||||
|
||||
// --- server -> client -------------------------------------------------------
|
||||
|
||||
export interface InitMsg {
|
||||
t: 'init';
|
||||
id: number; // your snake id
|
||||
arena: number; // arena radius
|
||||
pellets: PelletData[]; // full pellet list (deltas arrive afterwards)
|
||||
}
|
||||
|
||||
export interface SnapMsg {
|
||||
t: 'snap';
|
||||
tick: number;
|
||||
snakes: SnakeSnap[];
|
||||
add: PelletData[]; // pellets added since last snapshot
|
||||
rem: number[]; // pellet ids removed since last snapshot
|
||||
ev: GameEvent[]; // events since last snapshot
|
||||
lb: LeaderboardRow[];
|
||||
you?: YouState;
|
||||
total: number; // total snakes alive
|
||||
}
|
||||
|
||||
export type ServerMsg = InitMsg | SnapMsg;
|
||||
|
||||
// --- client -> server -------------------------------------------------------
|
||||
|
||||
export interface JoinMsg { t: 'join'; name: string }
|
||||
export interface InputMsg { t: 'in'; a: number; b: boolean } // angle, boosting
|
||||
export interface RespawnMsg { t: 'respawn' }
|
||||
|
||||
export type ClientMsg = JoinMsg | InputMsg | RespawnMsg;
|
||||
Reference in New Issue
Block a user