import type {CSSProperties} from 'react'; import type {InventoryItem} from './types'; export type NineSliceTexture = { src: string; slice: { top: number; right: number; bottom: number; left: number; }; padding?: { x: number; y: number; }; repeat?: 'stretch' | 'repeat' | 'round'; baseColor?: string; }; export function getNineSliceStyle( texture: NineSliceTexture, overrides?: Partial<{ paddingX: number; paddingY: number; scale: number; repeat: NineSliceTexture['repeat']; baseColor: string; }>, ): CSSProperties { const style = { '--frame-src': `url("${texture.src}")`, '--slice-top': `${texture.slice.top}`, '--slice-right': `${texture.slice.right}`, '--slice-bottom': `${texture.slice.bottom}`, '--slice-left': `${texture.slice.left}`, '--frame-pad-x': `${overrides?.paddingX ?? texture.padding?.x ?? 14}`, '--frame-pad-y': `${overrides?.paddingY ?? texture.padding?.y ?? 12}`, '--frame-repeat': overrides?.repeat ?? texture.repeat ?? 'round', } as CSSProperties & Record; const baseColor = overrides?.baseColor ?? texture.baseColor; if (baseColor) style['--frame-base'] = baseColor; if (typeof overrides?.scale === 'number') style['--frame-scale'] = `${overrides.scale}`; return style; } export const CUSTOM_WORLD_THEME_ICONS = { martial: '/Icons/38_sword.png', arcane: '/Icons/72_magic.png', } as const; export const BRAND_ASSETS = { kuranGamesLogo: '/branding/kuran-games-logo.svg', } as const; export const UI_CHROME = { appBackground: '/UI/Background_fill.png', // 图源 125×28:上下 slice 之和必须 < 28,否则中间行高度为 0,border-image fill 失效(见 UI_CODING_STANDARD.md) characterCardFrame: { src: '/UI/pick_hero_frame.png', slice: { top: 18, right: 18, bottom: 18, left: 18 }, padding: { x: 16, y: 14 }, repeat: 'round', }, tabActive: { src: '/UI/Shop_tab_picked.png', slice: { top: 16, right: 16, bottom: 16, left: 16 }, padding: { x: 10, y: 10 }, repeat: 'round', }, tabInactive: { src: '/UI/Shop_tab.png', slice: { top: 16, right: 16, bottom: 16, left: 16 }, padding: { x: 10, y: 10 }, repeat: 'round', }, panel: { src: '/UI/Frame_bg_big_2.png', slice: { top: 20, right: 20, bottom: 20, left: 20 }, padding: { x: 18, y: 16 }, repeat: 'round', }, storyPanel: { src: '/UI/Dialogue_frame.png', slice: { top: 18, right: 18, bottom: 18, left: 18 }, padding: { x: 18, y: 16 }, repeat: 'round', }, inventoryPanel: { src: '/UI/Inventory_bg.png', slice: { top: 18, right: 18, bottom: 18, left: 18 }, padding: { x: 18, y: 16 }, repeat: 'round', }, statsPanel: { src: '/UI/Stats_bar.png', slice: { top: 16, right: 16, bottom: 16, left: 16 }, padding: { x: 16, y: 14 }, repeat: 'round', }, choiceButton: { src: '/UI/Options_bar.png', slice: { top: 14, right: 14, bottom: 14, left: 14 }, padding: { x: 16, y: 12 }, repeat: 'round', }, /** 地图弹窗外壳:切片只含浅灰描边+角饰,勿把大块深色中心划进 border(否则会吃掉内容区) */ modalPanel: { src: '/UI/Frame_square_512.png', slice: { top: 6, right: 6, bottom: 6, left: 6 }, padding: { x: 14, y: 10 }, repeat: 'stretch', }, /** 仅用于地图弹窗内信息板(256 图为 512 的半尺寸,切片同比) */ infoPanel: { src: '/UI/Frame_square_256.png', slice: { top: 3, right: 3, bottom: 3, left: 3 }, padding: { x: 9, y: 6 }, repeat: 'stretch', }, /** 画布顶部可点击地点名 */ mapDiagramPanel: { src: '/UI/Map_frame.png', slice: { top: 12, right: 12, bottom: 12, left: 12 }, padding: { x: 18, y: 16 }, repeat: 'stretch', }, sceneTitle: { src: '/UI/special_button.png', slice: { top: 9, right: 14, bottom: 9, left: 14 }, padding: { x: 16, y: 5 }, repeat: 'stretch', }, /** 地图弹窗内节点卡片(方框) */ mapRoomCell: { src: '/UI/Frame_square_256.png', slice: { top: 3, right: 3, bottom: 3, left: 3 }, padding: { x: 8, y: 4 }, repeat: 'stretch', }, } as const; export const TAB_ICONS = { character: { active: '/Icons/42_shield.png', inactive: '/Icons/42_shield.png', }, adventure: { active: '/UI/1_weapon.png', inactive: '/UI/1_weapon_d.png', }, inventory: { active: '/Icons/28_bag.png', inactive: '/Icons/28_bag.png', }, } as const; export const CHROME_ICONS = { optionArrow: '/UI/11_right_arrow.png', map: '/UI/Map_icon_action.png', settings: "/Icons/Admurin's Pixel Items/Admurin's Pixel Items/General/Singles/499_Iron_Gear.png", close: '/UI/1_exit_s.png', refreshOptions: "/Icons/Admurin's Pixel Items/Admurin's Pixel Items/Miscellaneous/Singles/206_Dice.png", } as const; const EQUIPMENT_SLOT_ICONS: Record = { 武器: '/UI/Icon_Eq_Weapon.png', 护甲: '/UI/Icon_Eq_Chest.png', 饰品: '/UI/Icon_Eq_ring.png', }; const INVENTORY_CATEGORY_ICONS: Record = { 武器: '/UI/Icon_Eq_Weapon.png', 护甲: '/UI/Icon_Eq_Chest.png', 饰品: '/UI/Icon_Eq_ring.png', 消耗品: '/Icons/12_potion.png', 稀有品: '/Icons/68_relic.png', 专属品: '/Icons/47_treasure.png', 专属物: '/Icons/47_treasure.png', 专属物品: '/Icons/47_treasure.png', 材料: '/Icons/45_crystal.png', }; const ITEM_SEMANTIC_ICON_RULES: Array<{pattern: RegExp; icon: string}> = [ {pattern: /残页|书页|卷轴|卷|册|档案|秘典|图录|script|scroll|book/u, icon: '/Icons/01_Scroll.png'}, {pattern: /符印|符|印记|印章|印|sigil|seal|rune/u, icon: '/Icons/69_magic.png'}, {pattern: /戒|环|ring/u, icon: '/Icons/15_Silver_ring.png'}, {pattern: /坠|佩|链|neck|amulet/u, icon: '/Icons/13_neck.png'}, {pattern: /火|焰|烬|embers?|torch/u, icon: '/Icons/03_Torch.png'}, {pattern: /盔|冠|helm|helmet/u, icon: '/Icons/04_helm.png'}, {pattern: /甲|铠|壳|胸|护甲|armor|chest/u, icon: '/Icons/05_chest.png'}, {pattern: /裤|胫|腿|pants/u, icon: '/Icons/06_pants.png'}, {pattern: /靴|履|boots/u, icon: '/Icons/07_boots.png'}, {pattern: /药|露|浆|液|泉|瓶|bottle|potion|water/u, icon: '/Icons/12_potion.png'}, {pattern: /蘑|菇|菌/u, icon: '/Icons/24_Mushroom.png'}, {pattern: /肉|meat/u, icon: '/Icons/25_Meat.png'}, {pattern: /果|apple/u, icon: '/Icons/26_apple.png'}, {pattern: /骨|骸|skull/u, icon: '/Icons/27_Skull.png'}, {pattern: /囊|包|袋|bag|pouch/u, icon: '/Icons/29_bag.png'}, {pattern: /锤|mace|hammer/u, icon: '/Icons/30_mace.png'}, {pattern: /铲|spade/u, icon: '/Icons/31_spade.png'}, {pattern: /钱|币|coin/u, icon: '/Icons/32_coin.png'}, {pattern: /石|岩|stone/u, icon: '/Icons/33_stone.png'}, {pattern: /木|枝|wood/u, icon: '/Icons/34_wood.png'}, {pattern: /手套|glove|glowes/u, icon: '/Icons/35_glowes.png'}, {pattern: /花|flower/u, icon: '/Icons/55_flower.png'}, {pattern: /叶|leaf/u, icon: '/Icons/37_leaf.png'}, {pattern: /杖|wand|staff/u, icon: '/Icons/66_wand.png'}, {pattern: /弓|bow/u, icon: '/Icons/40_bow.png'}, {pattern: /箭|翎|羽|arrow/u, icon: '/Icons/41_arrow.png'}, {pattern: /盾|shield/u, icon: '/Icons/42_shield.png'}, {pattern: /绳|索|藤|须|rope|tendril/u, icon: '/Icons/44_rope.png'}, {pattern: /皮|膜|hide|skin|pelt/u, icon: '/Icons/46_skin.png'}, {pattern: /钥|匣|函|宝|treasure|relic|artifact|信物/u, icon: '/Icons/47_treasure.png'}, {pattern: /镐|pick|矿/u, icon: '/Icons/49_pick.png'}, {pattern: /银|silver|bar/u, icon: '/Icons/54_silverbar.png'}, {pattern: /晶|核|珠|瞳|眼|玉|lens|core|crystal|gem|pearl/u, icon: '/Icons/45_crystal.png'}, {pattern: /魔|灵|魂|mana|magic|essence/u, icon: '/Icons/69_magic.png'}, {pattern: /绷带|bandage/u, icon: '/Icons/67_bandage.png'}, {pattern: /剑|刃|牙|blade|fang|sword/u, icon: '/Icons/38_sword.png'}, ]; function buildInventoryLookupText( item: Pick, ) { return [ item.name, item.category, item.equipmentSlotId ?? '', ...(item.tags ?? []), ] .join(' ') .trim() .toLowerCase(); } export function getEquipmentSlotIcon(slot: string) { return EQUIPMENT_SLOT_ICONS[slot] ?? '/UI/Icon_Frame.png'; } export function getInventoryCategoryIcon(category: string) { return INVENTORY_CATEGORY_ICONS[category] ?? '/Icons/28_bag.png'; } export function getInventoryItemVisualSrc( item: Pick, ) { if (item.iconSrc) return item.iconSrc; const lookupText = buildInventoryLookupText(item); if (item.equipmentSlotId === 'weapon') { if (/弓|bow/u.test(lookupText)) return '/Icons/40_bow.png'; if (/杖|wand|staff|符/u.test(lookupText)) return '/Icons/66_wand.png'; if (/锤|mace|hammer/u.test(lookupText)) return '/Icons/30_mace.png'; return '/Icons/38_sword.png'; } if (item.equipmentSlotId === 'armor') { if (/盾|shield/u.test(lookupText)) return '/Icons/42_shield.png'; return '/Icons/05_chest.png'; } if (item.equipmentSlotId === 'relic') { if (/戒|环|ring/u.test(lookupText)) return '/Icons/15_Silver_ring.png'; if (/坠|佩|neck|amulet/u.test(lookupText)) return '/Icons/13_neck.png'; } const semanticMatch = ITEM_SEMANTIC_ICON_RULES.find(rule => rule.pattern.test(lookupText)); if (semanticMatch) return semanticMatch.icon; if (item.tags.includes('mana')) return '/Icons/69_magic.png'; if (item.tags.includes('healing')) return '/Icons/12_potion.png'; if (item.tags.includes('material')) return '/Icons/45_crystal.png'; if (item.tags.includes('relic')) return '/Icons/68_relic.png'; return getInventoryCategoryIcon(item.category); }