see /admin/mapping page.
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
// data/builderSlots.ts
|
||||
|
||||
/**
|
||||
* Builder "slots" represent the major assemblies in a Battl Builder rifle:
|
||||
* - Lower Receiver Assembly
|
||||
* - Upper Receiver Assembly
|
||||
* - Fire Control / Optics / Accessories, etc.
|
||||
*
|
||||
* Each slot can be satisfied by one or more "patterns" of categories.
|
||||
* Example:
|
||||
* Lower Assembly is satisfied by EITHER:
|
||||
* - a single Complete Lower
|
||||
* - OR a combination of stripped lower + parts + buffer + stock + grip + safety
|
||||
*
|
||||
* The frontend can use this file to:
|
||||
* - show build progress for each slot
|
||||
* - grey out / collapse sub-categories when a complete assembly is selected
|
||||
* - validate that a build is "complete" before sharing/saving
|
||||
*/
|
||||
|
||||
export type SlotGroup = "LOWER" | "UPPER" | "SYSTEM" | "ACCESSORIES";
|
||||
|
||||
/**
|
||||
* CategoryId should correspond to CATEGORIES[id] from data/gunbuilderParts.ts
|
||||
* We keep it loose here to avoid circular type imports.
|
||||
*/
|
||||
export type CategoryId = string;
|
||||
|
||||
export type BuilderSlotId =
|
||||
| "lowerAssembly"
|
||||
| "upperAssembly"
|
||||
| "opticSystem"
|
||||
| "sightSystem"
|
||||
| "suppressorSystem"
|
||||
| "magazineSystem"
|
||||
| "accessoriesSupport";
|
||||
|
||||
export interface BuilderSlot {
|
||||
id: BuilderSlotId;
|
||||
label: string;
|
||||
group: SlotGroup;
|
||||
/**
|
||||
* Human-facing description for tooltips / help text.
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* All category IDs that are visually associated with this slot.
|
||||
* (Useful for layout / grouping in the UI.)
|
||||
*/
|
||||
categories: CategoryId[];
|
||||
/**
|
||||
* Each inner array is a valid way to "satisfy" the slot.
|
||||
*
|
||||
* Example (lowerAssembly):
|
||||
* [
|
||||
* ["completeLower"], // Pattern 1: single complete lower
|
||||
* ["lower", "lowerParts", "trigger", "grip", "safety", "buffer", "stock"] // Pattern 2: custom lower
|
||||
* ]
|
||||
*/
|
||||
satisfactionPatterns: CategoryId[][];
|
||||
}
|
||||
|
||||
/**
|
||||
* Core Battl Builder slots.
|
||||
*
|
||||
* These line up with your existing CATEGORIES in data/gunbuilderParts.ts:
|
||||
*
|
||||
* LOWER:
|
||||
* - lower
|
||||
* - completeLower
|
||||
* - lowerParts
|
||||
* - trigger
|
||||
* - grip
|
||||
* - safety
|
||||
* - buffer
|
||||
* - stock
|
||||
*
|
||||
* UPPER:
|
||||
* - upper
|
||||
* - completeUpper
|
||||
* - bcg
|
||||
* - barrel
|
||||
* - gasBlock
|
||||
* - gasTube
|
||||
* - muzzleDevice
|
||||
* - suppressor
|
||||
* - handguard
|
||||
* - chargingHandle
|
||||
* - sights
|
||||
* - optic
|
||||
*
|
||||
* ACCESSORIES:
|
||||
* - magazine
|
||||
* - weaponLight
|
||||
* - foregrip
|
||||
* - bipod
|
||||
* - sling
|
||||
* - railAccessory
|
||||
* - tools
|
||||
*/
|
||||
export const BUILDER_SLOTS: BuilderSlot[] = [
|
||||
{
|
||||
id: "lowerAssembly",
|
||||
label: "Lower Receiver Assembly",
|
||||
group: "LOWER",
|
||||
description:
|
||||
"The serialized foundation of the rifle: receiver, controls, fire control, stock, and buffer system.",
|
||||
categories: [
|
||||
"lower",
|
||||
"completeLower",
|
||||
"lowerParts",
|
||||
"trigger",
|
||||
"grip",
|
||||
"safety",
|
||||
"buffer",
|
||||
"stock",
|
||||
],
|
||||
satisfactionPatterns: [
|
||||
// Pattern 1: single complete lower does it all
|
||||
["completeLower"],
|
||||
// Pattern 2: fully custom lower built from individual parts
|
||||
["lower", "lowerParts", "trigger", "grip", "safety", "buffer", "stock"],
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: "upperAssembly",
|
||||
label: "Upper Receiver Assembly",
|
||||
group: "UPPER",
|
||||
description:
|
||||
"The working end of the rifle: upper, barrel, gas system, handguard, BCG, charging handle, and muzzle device.",
|
||||
categories: [
|
||||
"upper",
|
||||
"completeUpper",
|
||||
"bcg",
|
||||
"barrel",
|
||||
"gasBlock",
|
||||
"gasTube",
|
||||
"handguard",
|
||||
"muzzleDevice",
|
||||
"chargingHandle",
|
||||
],
|
||||
satisfactionPatterns: [
|
||||
// Pattern 1: complete upper
|
||||
["completeUpper"],
|
||||
// Pattern 2: custom upper from parts
|
||||
[
|
||||
"upper",
|
||||
"barrel",
|
||||
"gasBlock",
|
||||
"gasTube",
|
||||
"handguard",
|
||||
"bcg",
|
||||
"chargingHandle",
|
||||
"muzzleDevice",
|
||||
],
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: "opticSystem",
|
||||
label: "Optic System",
|
||||
group: "SYSTEM",
|
||||
description:
|
||||
"Primary glass for the rifle: LPVOs, red dots, prisms, and magnifiers.",
|
||||
categories: ["optic"],
|
||||
satisfactionPatterns: [
|
||||
// For now: any optic fills the slot.
|
||||
["optic"],
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: "sightSystem",
|
||||
label: "Backup / Iron Sights",
|
||||
group: "SYSTEM",
|
||||
description:
|
||||
"Backup irons or primary sights for no-glass builds and hard-use setups.",
|
||||
categories: ["sights"],
|
||||
satisfactionPatterns: [["sights"]],
|
||||
},
|
||||
|
||||
{
|
||||
id: "suppressorSystem",
|
||||
label: "Muzzle / Suppressor",
|
||||
group: "SYSTEM",
|
||||
description:
|
||||
"Dedicated suppressor slot for QD or direct-thread cans. (Muzzle device is handled in the upper assembly patterns.)",
|
||||
categories: ["suppressor"],
|
||||
satisfactionPatterns: [["suppressor"]],
|
||||
},
|
||||
|
||||
{
|
||||
id: "magazineSystem",
|
||||
label: "Magazines",
|
||||
group: "ACCESSORIES",
|
||||
description:
|
||||
"Feeding the beast: mags appropriate to the platform and caliber.",
|
||||
categories: ["magazine"],
|
||||
satisfactionPatterns: [["magazine"]],
|
||||
},
|
||||
|
||||
{
|
||||
id: "accessoriesSupport",
|
||||
label: "Support Gear",
|
||||
group: "ACCESSORIES",
|
||||
description:
|
||||
"Lights, lasers, foregrips, bipods, slings, rail widgets, and tools that keep the rifle honest.",
|
||||
categories: [
|
||||
"weaponLight",
|
||||
"foregrip",
|
||||
"bipod",
|
||||
"sling",
|
||||
"railAccessory",
|
||||
"tools",
|
||||
],
|
||||
// Accessories are all optional; this slot is never strictly "required"
|
||||
satisfactionPatterns: [
|
||||
// An empty pattern would technically always be satisfied,
|
||||
// but we can treat this slot as "nice to have" in the UI instead.
|
||||
["weaponLight"],
|
||||
["foregrip"],
|
||||
["bipod"],
|
||||
["sling"],
|
||||
["railAccessory"],
|
||||
["tools"],
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Helper: determine whether a slot is satisfied given currently selected parts.
|
||||
*
|
||||
* `selectedByCategory` is expected to be a map of categoryId → truthy if selected.
|
||||
* The value can be the actual Part, or just `true` if you track it elsewhere.
|
||||
*/
|
||||
export function isSlotSatisfied(
|
||||
slot: BuilderSlot,
|
||||
selectedByCategory: Record<CategoryId, unknown>
|
||||
): boolean {
|
||||
return slot.satisfactionPatterns.some((pattern) =>
|
||||
pattern.every((categoryId) => Boolean(selectedByCategory[categoryId]))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional helper: get the first pattern that is currently satisfied.
|
||||
* Can be used to:
|
||||
* - show which "path" the user took (complete vs custom)
|
||||
* - conditionally collapse/expand sub-categories in the UI
|
||||
*/
|
||||
export function getSatisfiedPattern(
|
||||
slot: BuilderSlot,
|
||||
selectedByCategory: Record<CategoryId, unknown>
|
||||
): CategoryId[] | null {
|
||||
for (const pattern of slot.satisfactionPatterns) {
|
||||
const ok = pattern.every((categoryId) =>
|
||||
Boolean(selectedByCategory[categoryId])
|
||||
);
|
||||
if (ok) return pattern;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user