deadhead-space-freight-tycoon/apps/server/src/game/new-player.ts

77 lines
2.3 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { content } from "@deadhead/content";
import type { RealTime, Seed } from "@deadhead/sim";
import type { Db, Queryable } from "../db/client";
import { companies, discoveredLocations, pilots, players } from "../db/schema";
import { companyName } from "./names";
export const AVATAR_NAME = "Captain";
export interface NewPlayer {
playerId: string;
companyId: string;
avatarId: string;
}
/**
* Creates a fresh player in tutorial mode: starting credits, every location
* that starts discovered, the avatar (level 0, unassigned), and the loaner at
* Earth. The avatar's position comes from the loaner while the tutorial runs.
*/
export async function createPlayer(
db: Db,
args: { userId: string; worldSeed: Seed; now: RealTime; playerId?: string },
): Promise<NewPlayer> {
const { worldSeed, now } = args;
const playerId = args.playerId ?? randomUUID();
const companyId = randomUUID();
const avatarId = randomUUID();
await db.transaction(async (tx) => {
await tx.insert(players).values({
id: playerId,
userId: args.userId,
worldSeed,
tutorialActive: true,
loanerLocationId: "earth",
createdAt: now,
});
await createStartingState(tx, { playerId, companyId, avatarId, worldSeed, now });
});
return { playerId, companyId, avatarId };
}
/**
* A player's starting company (CR-001 tutorial state): starting credits, every
* location that starts discovered, and the avatar at Earth beside the loaner.
* Shared by guest creation and "Start a new company" (decision 0.5.74).
*/
export async function createStartingState(
tx: Queryable,
ids: { playerId: string; companyId: string; avatarId: string; worldSeed: Seed; now: RealTime },
): Promise<void> {
const { playerId, companyId, avatarId, worldSeed, now } = ids;
await tx.insert(companies).values({
id: companyId,
playerId,
name: companyName(worldSeed),
credits: content.config.startingCredits,
});
await tx
.insert(discoveredLocations)
.values(
content.locations
.filter((l) => l.startsDiscovered)
.map((l) => ({ playerId, locationId: l.id, discoveredAt: now })),
);
await tx.insert(pilots).values({
id: avatarId,
playerId,
name: AVATAR_NAME,
isAvatar: true,
locationId: "earth",
createdAt: now,
});
}