Update 3D game 2026-07-10 21:20
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import type { BossCollection, GameModeId, GameSettings, HunterSave, SaveSlotId } from "./types";
|
||||
import { BOSS_ORDER } from "../game/bossCatalog";
|
||||
import { createClassInventory } from "../game/healers";
|
||||
import type { BossId } from "../game/types";
|
||||
|
||||
export const DEFAULT_SETTINGS: GameSettings = {
|
||||
masterVolume: 80,
|
||||
reducedMotion: false,
|
||||
damageNumbers: true,
|
||||
largeText: false,
|
||||
};
|
||||
|
||||
export const DEFAULT_COLLECTIONS: BossCollection[] = [
|
||||
{
|
||||
bossId: "bulldrome",
|
||||
bossName: "Bulldrome",
|
||||
defeated: true,
|
||||
drops: [
|
||||
{ id: "bull-horn", name: "Cinder Horn", icon: "♜", rarity: "Common", count: 7 },
|
||||
{ id: "bull-hide", name: "Ember Hide", icon: "▧", rarity: "Uncommon", count: 3 },
|
||||
{ id: "bull-idol", name: "Vault Idol", icon: "◇", rarity: "Rare", count: 1 },
|
||||
{ id: "bull-heart", name: "Furnace Heart", icon: "✦", rarity: "Mythic", count: 0 },
|
||||
],
|
||||
},
|
||||
{
|
||||
bossId: "vexa",
|
||||
bossName: "Vexa",
|
||||
defeated: false,
|
||||
drops: [
|
||||
{ id: "vexa-silk", name: "Living Silk", icon: "⌁", rarity: "Common", count: 0 },
|
||||
{ id: "vexa-venom", name: "Widow Venom", icon: "✣", rarity: "Uncommon", count: 0 },
|
||||
{ id: "vexa-eye", name: "Loom Eye", icon: "◉", rarity: "Rare", count: 0 },
|
||||
{ id: "vexa-heart", name: "Webmother Heart", icon: "✦", rarity: "Mythic", count: 0 },
|
||||
],
|
||||
},
|
||||
{
|
||||
bossId: "cindermaw",
|
||||
bossName: "Cindermaw",
|
||||
defeated: true,
|
||||
drops: [
|
||||
{ id: "maw-scale", name: "Soot Scale", icon: "◈", rarity: "Common", count: 4 },
|
||||
{ id: "maw-gland", name: "Mending Gland", icon: "+", rarity: "Uncommon", count: 2 },
|
||||
{ id: "maw-crest", name: "Ashen Crest", icon: "⌁", rarity: "Rare", count: 0 },
|
||||
{ id: "maw-breath", name: "Bottled Breath", icon: "☀", rarity: "Mythic", count: 0 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const MODE_COPY: Record<GameModeId, { eyebrow: string; title: string; description: string; detail: string; status: string }> = {
|
||||
"roguelike-pve": {
|
||||
eyebrow: "1–4 hunters · randomized PVE",
|
||||
title: "PVE",
|
||||
description: "Enter without an encounter briefing, adapt to two randomized guardians, and build toward a full roguelike run.",
|
||||
detail: "Two bosses selected when the run begins",
|
||||
status: "Playable prototype",
|
||||
},
|
||||
dungeons: {
|
||||
eyebrow: "1–4 hunters · chosen encounter",
|
||||
title: "Dungeons",
|
||||
description: "Choose a guardian, review its mechanics, and bring a prepared healing loadout into a focused encounter.",
|
||||
detail: "Bulldrome · Vexa · Cindermaw",
|
||||
status: "Playable now",
|
||||
},
|
||||
"roguelike-pvp": {
|
||||
eyebrow: "3v3 · mirrored expeditions",
|
||||
title: "Roguelike PvP",
|
||||
description: "Race a rival squad through shifting rooms. Send hazards across the veil while keeping your own formation alive.",
|
||||
detail: "Draft order, rival pressure, and sudden-death rules",
|
||||
status: "Mode shell ready",
|
||||
},
|
||||
"stadium-pvp": {
|
||||
eyebrow: "5v5 · objective arena",
|
||||
title: "Stadium PvP",
|
||||
description: "Bring a prepared loadout into short team battles where positioning, interrupts, and clutch healing decide the round.",
|
||||
detail: "Best of five rounds / normalized gear",
|
||||
status: "Mode shell ready",
|
||||
},
|
||||
};
|
||||
|
||||
export function selectRandomBoss(random: () => number = Math.random): BossId {
|
||||
return BOSS_ORDER[Math.floor(random() * BOSS_ORDER.length)] ?? BOSS_ORDER[0];
|
||||
}
|
||||
|
||||
export function selectRandomBossPair(random: () => number = Math.random): readonly [BossId, BossId] {
|
||||
const firstIndex = Math.floor(random() * BOSS_ORDER.length) % BOSS_ORDER.length;
|
||||
const secondOffset = 1 + Math.floor(random() * (BOSS_ORDER.length - 1));
|
||||
return [BOSS_ORDER[firstIndex], BOSS_ORDER[(firstIndex + secondOffset) % BOSS_ORDER.length]];
|
||||
}
|
||||
|
||||
export const MAX_HUNTER_NAME_LENGTH = 20;
|
||||
|
||||
export function normalizeHunterName(value: string): string {
|
||||
return value
|
||||
.replace(/[\u0000-\u001f\u007f]/g, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, MAX_HUNTER_NAME_LENGTH);
|
||||
}
|
||||
|
||||
export function createHunterSave(slotId: SaveSlotId, now: string, hunterName: string): HunterSave {
|
||||
const normalizedName = normalizeHunterName(hunterName);
|
||||
if (!normalizedName) throw new Error("Hunter name is required.");
|
||||
return {
|
||||
schemaVersion: 2,
|
||||
slotId,
|
||||
hunterName: normalizedName,
|
||||
activeClassId: "priest",
|
||||
healers: {
|
||||
priest: { level: 12, inventory: createClassInventory("priest") },
|
||||
druid: { level: 1, inventory: createClassInventory("druid") },
|
||||
shaman: { level: 1, inventory: createClassInventory("shaman") },
|
||||
},
|
||||
location: "Ember Vault Approach",
|
||||
playSeconds: 8 * 60 * 60 + 42 * 60,
|
||||
updatedAt: now,
|
||||
stats: {
|
||||
totalBossKills: 16,
|
||||
flawlessClears: 5,
|
||||
alliesSaved: 143,
|
||||
healingDone: 284_650,
|
||||
bossKills: { Bulldrome: 12, Vexa: 0, Cindermaw: 4 },
|
||||
},
|
||||
collections: structuredClone(DEFAULT_COLLECTIONS),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user