fix: precise item classification in catalog audit and repair
Detect badges by items_base.type='b' (authoritative, album gifs as fallback), recognize pet/animals (a0 pet<N>, pet<N> interaction) and system items (effects, bots, sticky notes), and resolve asset names for dot/star classname variants so the audit no longer floods with false missing nitro/icon errors and repair skips non-furni items.
This commit is contained in:
1 parent
b1a1e5125c
commit
d587749b50
5 files changed
+445
-36
No files matched your search
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Classification of `items_base` rows so the catalog audit and repairs know
|
||||
* exactly which items are expected to ship normal furni assets (`.nitro`
|
||||
* bundles + `_icon.png` files) and which ones are special:
|
||||
*
|
||||
* - **badge** — stored with type `b`. Badges use album1584 `.gif` files and
|
||||
* never ship furni `.nitro`/`_icon.png` assets. They are awarded, not
|
||||
* purchased through the catalog.
|
||||
* - **pet** — pet purchase items (`a0 pet<N>`, interaction `pet<N>`). Pet
|
||||
* assets live in the pet asset pipeline (`public/nitro-assets/bundled/pet`),
|
||||
* not the furniture bundle dirs.
|
||||
* - **effect / bot / sticky** — system items with no furniture visuals at all.
|
||||
* - **furni** — everything else; must ship a `.nitro` and `_icon.png`.
|
||||
*/
|
||||
|
||||
export type CatalogItemKind =
|
||||
| "badge"
|
||||
| "pet"
|
||||
| "effect"
|
||||
| "bot"
|
||||
| "sticky"
|
||||
| "furni";
|
||||
|
||||
export interface CatalogItemLike {
|
||||
type: string;
|
||||
item_name: string;
|
||||
interaction_type?: string | null;
|
||||
}
|
||||
|
||||
/** Badges are stored in `items_base` with type `b`. */
|
||||
export function isBadgeType(type: string): boolean {
|
||||
return type.trim().toLowerCase() === "b";
|
||||
}
|
||||
|
||||
/** Pet purchase items use classnames like `a0 pet0` … `a0 pet80`. */
|
||||
export function isPetClassname(itemName: string): boolean {
|
||||
return /^a0\s*pet\d+$/i.test(itemName.trim());
|
||||
}
|
||||
|
||||
/** Pet purchase items use `pet<N>` as their interaction type. */
|
||||
export function isPetInteraction(interactionType?: string | null): boolean {
|
||||
return /^pet\d+$/i.test((interactionType ?? "").trim());
|
||||
}
|
||||
|
||||
/** Classify an `items_base` row into a catalog item kind. */
|
||||
export function classifyCatalogItem(item: CatalogItemLike): CatalogItemKind {
|
||||
const name = item.item_name.trim();
|
||||
if (isBadgeType(item.type)) return "badge";
|
||||
if (isPetClassname(name) || isPetInteraction(item.interaction_type)) {
|
||||
return "pet";
|
||||
}
|
||||
if (/^avatar_effect/i.test(name)) return "effect";
|
||||
if (/^(bot_|rentable_bot_)/i.test(name)) return "bot";
|
||||
if (/^post[._]?it/i.test(name)) return "sticky";
|
||||
return "furni";
|
||||
}
|
||||
|
||||
/** Kinds that never ship furni `.nitro` / `_icon.png` assets. */
|
||||
export const NO_ASSET_KINDS: ReadonlySet<CatalogItemKind> = new Set([
|
||||
"badge",
|
||||
"pet",
|
||||
"effect",
|
||||
"bot",
|
||||
"sticky",
|
||||
]);
|
||||
|
||||
/** Kinds that are not purchasable furniture and should not get catalog entries. */
|
||||
export const NO_CATALOG_KINDS: ReadonlySet<CatalogItemKind> = new Set([
|
||||
"badge",
|
||||
"effect",
|
||||
"bot",
|
||||
"sticky",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Asset file names for a classname, mirroring how `importSingleFurni` writes
|
||||
* files. Returns `.nitro` and icon candidates (most specific first):
|
||||
* the exact classname, the star-variant base, the star→underscore name and the
|
||||
* dot→underscore name (`post.it` → `post_it.nitro`).
|
||||
*/
|
||||
export function assetNameCandidates(classname: string): {
|
||||
nitro: string[];
|
||||
icon: string[];
|
||||
} {
|
||||
const unique = (names: string[]): string[] => {
|
||||
const seen = new Set<string>();
|
||||
const out: string[] = [];
|
||||
for (const n of names) {
|
||||
if (!n || seen.has(n)) continue;
|
||||
seen.add(n);
|
||||
out.push(n);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const starBase = classname.split("*")[0];
|
||||
const starUnderscore = classname.replace(/\*/g, "_");
|
||||
const dotUnderscore = classname.replace(/\./g, "_");
|
||||
|
||||
return {
|
||||
nitro: unique([classname, starBase, dotUnderscore, starUnderscore]).map(
|
||||
(n) => `${n}.nitro`,
|
||||
),
|
||||
icon: unique([classname, starUnderscore, dotUnderscore, starBase]).map(
|
||||
(n) => `${n}_icon.png`,
|
||||
),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user