Skip to main content
Glama
camouflages.ts3.97 kB
import { readXML } from './xml.js'; import { getString } from './strings.js'; import type { Config } from '../config.js'; import type { Camouflage, TankClass } from '../data/types.js'; interface CamoCategory { lightTank: number; mediumTank: number; heavyTank: number; 'AT-SPG': number; } interface RawCamouflage { id: number; userString: string; description?: string; category: string; group: string; kind: 'summer' | 'winter' | 'desert'; notInShop?: boolean; vehicleFilter?: { include?: { nations?: string | string[] }; exclude?: unknown; }; } interface CamouflagesXML { root: { groups: Record<string, { userString: string; rarity: number }>; invisibility: { categories: Record<string, CamoCategory>; }; camouflages: Record<string, RawCamouflage>; }; } export interface CamoBonusTable { [category: string]: { lightTank: number; mediumTank: number; heavyTank: number; 'AT-SPG': number; }; } let camouflagesCache: Camouflage[] | null = null; let bonusTableCache: CamoBonusTable | null = null; export async function loadCamouflages(config: Config): Promise<void> { if (camouflagesCache) return; camouflagesCache = []; bonusTableCache = {}; const data = await readXML<CamouflagesXML>(config.paths.camouflages); for (const [categoryName, bonuses] of Object.entries(data.root.invisibility.categories)) { bonusTableCache[categoryName] = { lightTank: bonuses.lightTank, mediumTank: bonuses.mediumTank, heavyTank: bonuses.heavyTank, 'AT-SPG': bonuses['AT-SPG'], }; } for (const [camoTag, rawCamo] of Object.entries(data.root.camouflages)) { if (typeof rawCamo !== 'object' || !rawCamo.id) continue; const nameKey = rawCamo.userString?.replace('#', '') ?? camoTag; const name = getString(nameKey); const nations = rawCamo.vehicleFilter?.include?.nations; let nationList: string[] | undefined; if (nations) { nationList = Array.isArray(nations) ? nations : [nations]; } const descKey = rawCamo.description?.startsWith('#vehicle_customization:') ? rawCamo.description : rawCamo.description?.replace('#', ''); const categoryBonus = bonusTableCache[rawCamo.category]; camouflagesCache.push({ id: rawCamo.id, name: name !== nameKey ? name : formatCamoName(camoTag), description: descKey ? getString(descKey) : undefined, category: rawCamo.category, kind: rawCamo.kind ?? 'universal', nations: nationList, invisibilityBonus: categoryBonus, }); } } function formatCamoName(tag: string): string { return tag .split('_') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); } export function getCamouflages(): Camouflage[] { return camouflagesCache ?? []; } export function getCamouflage(id: number): Camouflage | undefined { return camouflagesCache?.find(c => c.id === id); } export function getCamouflageByName(name: string): Camouflage | undefined { if (!camouflagesCache) return undefined; const lowerName = name.toLowerCase(); return camouflagesCache.find(c => c.name.toLowerCase().includes(lowerName)); } export function getCamouflageBonus(category: string, tankClass: TankClass): number { return bonusTableCache?.[category]?.[tankClass] ?? 0; } export function getCamoBonusTable(): CamoBonusTable { return bonusTableCache ?? {}; } export function searchCamouflages(options: { nation?: string; kind?: 'summer' | 'winter' | 'desert' | 'universal'; query?: string; }): Camouflage[] { let results = getCamouflages(); if (options.nation) { results = results.filter(c => !c.nations || c.nations.includes(options.nation!)); } if (options.kind) { results = results.filter(c => c.kind === options.kind); } if (options.query) { const q = options.query.toLowerCase(); results = results.filter(c => c.name.toLowerCase().includes(q)); } return results; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Revenant30102000/wotblitz-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server