@php $seoBaseTitle = (string) ($meta['name'] ?? 'NPC'); $seoTitle = $seoBaseTitle . ' • Vortex Sky X Wiki'; $gameName = $game ?? 'Vortex Sky X'; $seoDescription = 'NPC details, shop items, and skills taught for ' . $seoBaseTitle . ' in ' . $gameName . '.'; @endphp {{ $seoTitle }} @include('wiki.partials.seo', [ 'title' => $seoTitle, 'description' => $seoDescription, 'type' => 'article', 'game' => $game ?? null, ]) @vite(['resources/css/app.css', 'resources/js/app.js']) @include('wiki.partials.header', ['databaseMenu' => $databaseMenu]) @php $name = (string) ($meta['name'] ?? $meta['title'] ?? ($meta['raw']['nName'] ?? 'NPC')); $raw = is_array($meta['raw'] ?? null) ? $meta['raw'] : []; $toNumber = function ($value): ?float { if (is_int($value) || is_float($value)) return (float) $value; if (is_string($value)) { $trim = trim($value); if ($trim !== '' && is_numeric($trim)) return (float) $trim; } return null; }; $formatNumber = function (float $value): string { if (abs($value - round($value)) < 0.0000001) { return (string) (int) round($value); } $txt = number_format($value, 4, '.', ''); return rtrim(rtrim($txt, '0'), '.'); }; $toDisplayValue = function ($value) use ($toNumber, $formatNumber): ?string { if (is_array($value)) { $parts = []; foreach ($value as $entry) { $num = $toNumber($entry); if ($num !== null && $num > 0) { $parts[] = $formatNumber($num); } } return count($parts) ? implode(' / ', $parts) : null; } $num = $toNumber($value); if ($num !== null) return $num > 0 ? $formatNumber($num) : null; $txt = trim((string) $value); return $txt !== '' ? $txt : null; }; $formatSilver = function ($value) use ($toNumber): string { $num = $toNumber($value); if ($num === null || $num <= 0) return '—'; return number_format((int) $num, 0, '.', ',') . ' Silver'; }; $formatCp = function ($value) use ($toNumber): string { $num = $toNumber($value); if ($num === null || $num <= 0) return '—'; return number_format((int) $num, 0, '.', ',') . ' CP'; }; $factionValue = trim((string) ($meta['faction'] ?? '')); if ($factionValue === '') { $tribe = $raw['nTribe'] ?? null; $tribeNum = $toNumber($tribe); if ($tribeNum !== null) { $factionValue = match ((int) $tribeNum) { 2 => 'Guanyin', 3 => 'Fujin', 4 => 'Jinong', 5 => 'Nangin', default => 'All Factions', }; } } if ($factionValue === '') { $factionValue = 'All Factions'; } $factionClass = match (mb_strtolower($factionValue)) { 'guanyin' => 'wk-f-guanyin', 'fujin' => 'wk-f-fujin', 'jinong' => 'wk-f-jinong', 'nangin' => 'wk-f-nangin', 'all factions' => 'wk-f-all-factions', default => '', }; $baseRows = []; $addBaseRow = function (string $label, $value, string $valueClass = '') use (&$baseRows, $toDisplayValue): void { $display = $toDisplayValue($value); if ($display === null) return; $baseRows[] = ['label' => $label, 'value' => $display, 'class' => $valueClass]; }; $addBaseRow('Faction', $factionValue, $factionClass); $speechLines = []; if (isset($meta['speech']) && is_array($meta['speech'])) { foreach ($meta['speech'] as $block) { if (!is_array($block)) continue; foreach ($block as $line) { $line = trim((string) $line); if ($line !== '') { $speechLines[] = $line; } } } } $shopPages = []; if (isset($raw['nShopInfo']) && is_array($raw['nShopInfo'])) { $summaryMap = wiki_item_summary_map($game); foreach ($raw['nShopInfo'] as $pageIdx => $row) { if (!is_array($row)) continue; $items = []; foreach ($row as $val) { $num = $toNumber($val); if ($num === null || $num <= 0) continue; $itemId = (string) ((int) $num); $itemRow = $summaryMap[$itemId] ?? null; if (!$itemRow) continue; $rarity = strtolower(trim((string) ($itemRow['rarity'] ?? ''))); $items[] = [ 'name' => (string) ($itemRow['name'] ?? $itemId), 'rarity_class' => $rarity !== '' ? 'wk-q-' . $rarity : '', 'url' => '/wiki/' . rawurlencode($game) . '/items/all/' . rawurlencode($itemId), 'silver' => $formatSilver($itemRow['buy_silver'] ?? 0), 'cp' => $formatCp($itemRow['buy_cp'] ?? 0), 'icon' => (string) ($itemRow['icon'] ?? ''), ]; } if (count($items)) { $shopPages[] = [ 'title' => 'Page ' . ($pageIdx + 1), 'items' => $items, ]; } } } $npcSkills = []; $flattenSkillIds = function ($value) use (&$flattenSkillIds, $toNumber): array { $ids = []; if (is_array($value)) { foreach ($value as $entry) { $ids = array_merge($ids, $flattenSkillIds($entry)); } return $ids; } $num = $toNumber($value); if ($num === null || $num <= 0) return []; return [(int) $num]; }; $skillSource = []; if (isset($raw['nSkillInfo1'])) { $skillSource[] = $raw['nSkillInfo1']; } if (isset($raw['nSkillInfo2'])) { $skillSource[] = $raw['nSkillInfo2']; } $skillIds = []; foreach ($skillSource as $block) { $skillIds = array_merge($skillIds, $flattenSkillIds($block)); } $seen = []; foreach ($skillIds as $skillId) { $skillId = (int) $skillId; if ($skillId <= 0 || $skillId === 307) continue; if (isset($seen[$skillId])) continue; $seen[$skillId] = true; $slug = wiki_skill_slug_from_id($game, $skillId); if (!$slug) continue; $skillPath = base_path('content' . DIRECTORY_SEPARATOR . $game . DIRECTORY_SEPARATOR . 'skills' . DIRECTORY_SEPARATOR . $slug . '.md'); if (!is_file($skillPath)) continue; $skillMeta = wiki_read_frontmatter_yaml($skillPath); $skillName = wiki_skill_name_from_id($game, $skillId) ?? ('Skill ' . $skillId); $skillIcon = wiki_skill_icon_from_data2d($game, $skillMeta['sDataNumber2D'] ?? null); $skillFaction = wiki_skill_faction_from_tribe($skillMeta['sTribeInfo'] ?? null); $skillFactionClass = match (mb_strtolower($skillFaction)) { 'guanyin' => 'wk-f-guanyin', 'fujin' => 'wk-f-fujin', 'jinong' => 'wk-f-jinong', 'nangin' => 'wk-f-nangin', default => 'wk-f-all-factions', }; $npcSkills[] = [ 'name' => $skillName, 'url' => '/wiki/' . rawurlencode($game) . '/skills/' . rawurlencode($slug), 'icon' => $skillIcon, 'faction' => $skillFaction, 'faction_class' => $skillFactionClass, ]; } @endphp

{{ $name }}

@if(count($baseRows))
Base Details
@foreach($baseRows as $row)
{{ $row['label'] }} {{ $row['value'] }}
@endforeach
@endif @if(count($speechLines))
Speech
@foreach($speechLines as $line)
{{ $line }}
@endforeach
@endif
Skills Taught
@if(count($npcSkills))
@foreach($npcSkills as $skill)
@if(!empty($skill['icon'])) {{ $skill['name'] }} @endif
{{ $skill['faction'] }}
@endforeach
@else
None
@endif
NPC Shop
@if(count($shopPages))
@foreach($shopPages as $page)
{{ $page['title'] }}
@foreach($page['items'] as $item)
@if(!empty($item['icon'])) {{ $item['name'] }} @endif
{{ $item['silver'] ?? '—' }}
{{ $item['cp'] ?? '—' }}
@endforeach
@endforeach
@else
None
@endif