Refine NPC interactions and runtime item generation
This commit is contained in:
@@ -21,6 +21,10 @@ import {coerceWorldAttributeSchema} from './attributeValidation';
|
||||
const CUSTOM_WORLD_LIBRARY_STORAGE_KEY = 'tavernrealms.custom-world-library.v1';
|
||||
const CUSTOM_WORLD_LIBRARY_VERSION = 1;
|
||||
const MAX_SAVED_CUSTOM_WORLDS = 12;
|
||||
const MIN_CUSTOM_WORLD_AFFINITY = -40;
|
||||
const MAX_CUSTOM_WORLD_AFFINITY = 90;
|
||||
const DEFAULT_PLAYABLE_INITIAL_AFFINITY = 18;
|
||||
const DEFAULT_STORY_NPC_INITIAL_AFFINITY = 6;
|
||||
const ITEM_RARITIES = new Set<ItemRarity>(['common', 'uncommon', 'rare', 'epic', 'legendary']);
|
||||
const EQUIPMENT_SLOTS = new Set<EquipmentSlotId>(['weapon', 'armor', 'relic']);
|
||||
const CUSTOM_WORLD_NPC_VISUAL_RACES = new Set<CustomWorldNpcVisualRace>(['human', 'elf', 'orc', 'goblin']);
|
||||
@@ -52,6 +56,13 @@ function toOptionalInteger(value: unknown) {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? Math.round(value) : undefined;
|
||||
}
|
||||
|
||||
function normalizeInitialAffinity(value: unknown, fallback: number) {
|
||||
const resolved = typeof value === 'number' && Number.isFinite(value)
|
||||
? Math.round(value)
|
||||
: fallback;
|
||||
return Math.max(MIN_CUSTOM_WORLD_AFFINITY, Math.min(MAX_CUSTOM_WORLD_AFFINITY, resolved));
|
||||
}
|
||||
|
||||
function normalizeEquipmentSlot(value: unknown) {
|
||||
return typeof value === 'string' && EQUIPMENT_SLOTS.has(value as EquipmentSlotId)
|
||||
? value as EquipmentSlotId
|
||||
@@ -129,16 +140,24 @@ function normalizePlayableNpc(value: unknown, index: number): CustomWorldPlayabl
|
||||
|
||||
const name = toText(value.name);
|
||||
if (!name) return null;
|
||||
const title = toText(value.title, toText(value.role, '未命名角色'));
|
||||
const role = toText(value.role, title);
|
||||
const relationshipHooks = toStringArray(value.relationshipHooks);
|
||||
const tags = toStringArray(value.tags);
|
||||
|
||||
return {
|
||||
id: toText(value.id, `saved-playable-${index + 1}`),
|
||||
name,
|
||||
title: toText(value.title, '未命名角色'),
|
||||
title,
|
||||
role,
|
||||
description: toText(value.description),
|
||||
backstory: toText(value.backstory),
|
||||
personality: toText(value.personality),
|
||||
motivation: toText(value.motivation, toText(value.description)),
|
||||
combatStyle: toText(value.combatStyle),
|
||||
tags: toStringArray(value.tags),
|
||||
initialAffinity: normalizeInitialAffinity(value.initialAffinity, DEFAULT_PLAYABLE_INITIAL_AFFINITY),
|
||||
relationshipHooks: relationshipHooks.length > 0 ? relationshipHooks : tags.slice(0, 3),
|
||||
tags: tags.length > 0 ? tags : relationshipHooks.slice(0, 5),
|
||||
templateCharacterId: toText(value.templateCharacterId) || undefined,
|
||||
};
|
||||
}
|
||||
@@ -148,14 +167,24 @@ function normalizeStoryNpc(value: unknown, index: number): CustomWorldNpc | null
|
||||
|
||||
const name = toText(value.name);
|
||||
if (!name) return null;
|
||||
const title = toText(value.title, toText(value.role, '未命名场景角色'));
|
||||
const role = toText(value.role, title);
|
||||
const relationshipHooks = toStringArray(value.relationshipHooks);
|
||||
const tags = toStringArray(value.tags);
|
||||
|
||||
return {
|
||||
id: toText(value.id, `saved-story-${index + 1}`),
|
||||
name,
|
||||
role: toText(value.role, '未命名场景角色'),
|
||||
title,
|
||||
role,
|
||||
description: toText(value.description),
|
||||
backstory: toText(value.backstory),
|
||||
personality: toText(value.personality),
|
||||
motivation: toText(value.motivation),
|
||||
relationshipHooks: toStringArray(value.relationshipHooks),
|
||||
combatStyle: toText(value.combatStyle),
|
||||
initialAffinity: normalizeInitialAffinity(value.initialAffinity, DEFAULT_STORY_NPC_INITIAL_AFFINITY),
|
||||
relationshipHooks: relationshipHooks.length > 0 ? relationshipHooks : tags.slice(0, 3),
|
||||
tags: tags.length > 0 ? tags : relationshipHooks.slice(0, 5),
|
||||
imageSrc: toText(value.imageSrc) || undefined,
|
||||
visual: normalizeCustomWorldNpcVisual(value.visual),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user