Fix appendages parsing to handle multiple appendages

This commit is contained in:
Yaroslav (Ice) Sheremet
2026-02-01 19:02:11 +02:00
parent 119a9ce62c
commit d034ef44cc
3 changed files with 112 additions and 15 deletions

View File

@@ -298,9 +298,86 @@ function decodeSimpleTag(label, tag, descriptions) {
}; };
} }
// Decode a single appendage
function decodeSingleAppendage(appendage) {
const baseDescription = TAG_DESCRIPTIONS.appendages.base[appendage.baseType];
if (!baseDescription) {
return appendage.baseType;
}
// If no modifiers, return base description
if (!appendage.modifiers || appendage.modifiers.length === 0) {
return baseDescription;
}
// Check for modifier
const modifier = appendage.modifiers[0];
const modifierDescription = TAG_DESCRIPTIONS.appendages.modifiers[modifier];
if (modifierDescription) {
// Replace placeholders in modifier description
let value = modifierDescription;
// Determine singular and plural forms
let singular = baseDescription.toLowerCase().replace(/^a pair of /, '').replace(/^a /, '');
let plural = singular;
// Handle specific cases
if (singular === 'legs' || singular === 'wings' || singular === 'arms') {
// already plural
} else if (singular === 'head') {
plural = 'heads';
singular = 'head';
} else if (singular === 'tail') {
plural = 'tails';
singular = 'tail';
} else if (singular.endsWith('limbs')) {
plural = singular;
singular = singular.replace(/s$/, '');
}
value = value.replace(/{type}/g, singular);
value = value.replace(/{plural}/g, plural.charAt(0).toUpperCase() + plural.slice(1));
return value;
}
return baseDescription;
}
// Decode appendages // Decode appendages
function decodeAppendages(appendages) { function decodeAppendages(appendages) {
// Get base description // Handle new multi-appendage structure
if (appendages.appendages && Array.isArray(appendages.appendages)) {
const decodedAppendages = appendages.appendages.map((app, index) => {
const decoded = decodeSingleAppendage(app);
// Lowercase the first letter for all items after the first
if (index > 0 && decoded) {
return decoded.charAt(0).toLowerCase() + decoded.slice(1);
}
return decoded;
});
// Combine with commas and "and"
let value;
if (decodedAppendages.length === 1) {
value = decodedAppendages[0];
} else if (decodedAppendages.length === 2) {
value = decodedAppendages.join(' and ');
} else {
const lastAppendage = decodedAppendages.pop();
value = decodedAppendages.join(', ') + ', and ' + lastAppendage;
}
return {
label: 'Appendages',
value: value,
tag: appendages.raw
};
}
// Legacy support for old structure
const baseDescription = TAG_DESCRIPTIONS.appendages.base[appendages.baseType]; const baseDescription = TAG_DESCRIPTIONS.appendages.base[appendages.baseType];
if (!baseDescription) { if (!baseDescription) {

View File

@@ -486,23 +486,42 @@ function parseAppendages(token) {
// Remove 'P' prefix // Remove 'P' prefix
let content = token.substring(1); let content = token.substring(1);
// Check for Pw' (wings as arms) special case const appendagesList = [];
if (content.startsWith("w'")) { let i = 0;
const modifiers = content.substring(2);
return { while (i < content.length) {
baseType: "w'", let appendage = {
modifiers: modifiers, baseType: null,
raw: token modifiers: ''
}; };
// Check for Pw' (wings as arms) special case
if (content.substring(i, i + 2) === "w'") {
appendage.baseType = "w'";
i += 2;
} else {
// Get base type (single character)
appendage.baseType = content[i];
i++;
} }
// Get base type (first character after P) // Collect modifiers for this appendage (until we hit another letter that's a base type)
const baseType = content[0]; while (i < content.length) {
const modifiers = content.substring(1); const char = content[i];
// Check if this is a new appendage base type (a, f, h, k, l, p, t, v, w)
if ('afhklptvw'.includes(char.toLowerCase())) {
break;
}
// Otherwise it's a modifier
appendage.modifiers += char;
i++;
}
appendagesList.push(appendage);
}
return { return {
baseType: baseType, appendages: appendagesList,
modifiers: modifiers,
raw: token raw: token
}; };
} }

View File

@@ -404,7 +404,8 @@ const TEST_CASES = {
{ code: 'Ph+', expected: 'One more head than normal - \'I got ahead in advertising!\'' }, { code: 'Ph+', expected: 'One more head than normal - \'I got ahead in advertising!\'' },
{ code: 'Pw-', expected: 'One less wing than normal - \'I have only one wing!\'' }, { code: 'Pw-', expected: 'One less wing than normal - \'I have only one wing!\'' },
{ code: 'Pl!', expected: 'Many legs - \'I am a millipede!\'' }, { code: 'Pl!', expected: 'Many legs - \'I am a millipede!\'' },
{ code: 'Pl^', expected: 'Legs end in webbed feet - \'Don\'t call me a frog!\'' } { code: 'Pl^', expected: 'Legs end in webbed feet - \'Don\'t call me a frog!\'' },
{ code: 'Phvwalt', expected: 'A head, a pair of horns or spines on the head, a pair of wings, a pair of arms, a pair of legs, and a tail' }
] ]
}; };