Initial commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Polish-aware slug helper. Lowercases, strips diacritics, replaces
|
||||
* whitespace and punctuation with hyphens, collapses repeats.
|
||||
*/
|
||||
export function slugify(input: string): string {
|
||||
return input
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/ł/g, 'l')
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-{2,}/g, '-');
|
||||
}
|
||||
|
||||
/** Strip Polish diacritics for case-insensitive map lookups. */
|
||||
export function normalizeKey(input: string): string {
|
||||
return input
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, ' ')
|
||||
.normalize('NFC');
|
||||
}
|
||||
|
||||
/** Parse "5:2" -> { threshold: 5, successes: 2 }. */
|
||||
export function parseDifficulty(s: string): { threshold: number; successes: number } | null {
|
||||
const m = s.trim().match(/^(\d+)\s*:\s*([\d\w]+)$/);
|
||||
if (!m) return null;
|
||||
const successes = isNaN(Number(m[2])) ? 1 : Number(m[2]);
|
||||
return { threshold: Number(m[1]), successes };
|
||||
}
|
||||
|
||||
/** Parse "1 punkt", "3 punkty", "5 punktów" → 1/3/5. Returns 0 on failure. */
|
||||
export function parsePoints(s: string): number {
|
||||
const m = s.trim().match(/^(\d+)/);
|
||||
return m ? Number(m[1]) : 0;
|
||||
}
|
||||
|
||||
/** Parse a comma- or whitespace-separated symbol list. */
|
||||
export function parseList(s: string): string[] {
|
||||
return s
|
||||
.split(/[,;]+/)
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => x.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Polish range string into structure.
|
||||
* "Na siebie" → { kind: 'self' }
|
||||
* "Dotyk" → { kind: 'touch' }
|
||||
* "15 m" → { kind: 'distance', value: 15, unit: 'm' }
|
||||
* anything else → { kind: 'special', text }
|
||||
*/
|
||||
export function parseRange(s: string): { kind: string; value?: number; unit?: string; text?: string } {
|
||||
const t = s.trim();
|
||||
if (/^na siebie$/i.test(t)) return { kind: 'self' };
|
||||
if (/^dotyk$/i.test(t)) return { kind: 'touch' };
|
||||
const m = t.match(/^(\d+)\s*(m|km|cm)\b/i);
|
||||
if (m) return { kind: 'distance', value: Number(m[1]), unit: m[2].toLowerCase() };
|
||||
return { kind: 'special', text: t };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse area-of-effect text into structured shape.
|
||||
* "3×8 m" → rectangle 3×8
|
||||
* "promień 5 m" → sphere radius 5
|
||||
* "stożek 10 m" → cone length 10
|
||||
* "linia 20 m" → line length 20
|
||||
*/
|
||||
export function parseAreaOfEffect(s: string): { shape: string; x?: number; y?: number; unit?: string; text?: string } | null {
|
||||
const t = s.trim();
|
||||
let m = t.match(/(\d+)\s*[x×]\s*(\d+)\s*(m|km|cm)?/i);
|
||||
if (m) return { shape: 'rectangle', x: Number(m[1]), y: Number(m[2]), unit: (m[3] ?? 'm').toLowerCase() };
|
||||
m = t.match(/promień\s+(\d+)\s*(m|km|cm)?/i);
|
||||
if (m) return { shape: 'sphere', x: Number(m[1]), unit: (m[2] ?? 'm').toLowerCase() };
|
||||
m = t.match(/stożek\s+(\d+)\s*(m|km|cm)?/i);
|
||||
if (m) return { shape: 'cone', x: Number(m[1]), unit: (m[2] ?? 'm').toLowerCase() };
|
||||
m = t.match(/linia\s+(\d+)\s*(m|km|cm)?/i);
|
||||
if (m) return { shape: 'line', x: Number(m[1]), unit: (m[2] ?? 'm').toLowerCase() };
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user