From 8aa3c7ea20a71a2678af1cbf694f2e3199f221d0 Mon Sep 17 00:00:00 2001 From: Michiel Sikma Date: Sun, 19 Aug 2018 22:20:26 +0200 Subject: [PATCH] Script for generating the icon database json file --- scripts/link-item-icons.php | 290 ++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100755 scripts/link-item-icons.php diff --git a/scripts/link-item-icons.php b/scripts/link-item-icons.php new file mode 100755 index 00000000..53afdb11 --- /dev/null +++ b/scripts/link-item-icons.php @@ -0,0 +1,290 @@ +#!/usr/bin/env php +. +$ignored_items = array( + // Technical machines. + 'tm00', 'tm01', 'tm02', 'tm03', 'tm04', 'tm05', 'tm06', 'tm07', 'tm08', + 'tm09', 'tm10', 'tm11', 'tm12', 'tm13', 'tm14', 'tm15', 'tm16', 'tm17', + 'tm18', 'tm19', 'tm20', 'tm21', 'tm22', 'tm23', 'tm24', 'tm25', 'tm26', + 'tm27', 'tm28', 'tm29', 'tm30', 'tm31', 'tm32', 'tm33', 'tm34', 'tm35', + 'tm36', 'tm37', 'tm38', 'tm39', 'tm40', 'tm41', 'tm42', 'tm43', 'tm44', + 'tm45', 'tm46', 'tm47', 'tm48', 'tm49', 'tm50', 'tm51', 'tm52', 'tm53', + 'tm54', 'tm55', 'tm56', 'tm57', 'tm58', 'tm59', 'tm60', 'tm61', 'tm62', + 'tm63', 'tm64', 'tm65', 'tm66', 'tm67', 'tm68', 'tm69', 'tm70', 'tm71', + 'tm72', 'tm73', 'tm74', 'tm75', 'tm76', 'tm77', 'tm78', 'tm79', 'tm80', + 'tm81', 'tm82', 'tm83', 'tm84', 'tm85', 'tm86', 'tm87', 'tm88', 'tm89', + 'tm90', 'tm91', 'tm92', 'tm93', 'tm94', 'tm95', 'tm96', 'tm97', 'tm98', + 'tm99', 'tm100', + + // Data cards. + 'data-card-00', 'data-card-01', 'data-card-02', 'data-card-03', + 'data-card-04', 'data-card-05', 'data-card-06', 'data-card-07', + 'data-card-08', 'data-card-09', 'data-card-10', 'data-card-11', + 'data-card-12', 'data-card-13', 'data-card-14', 'data-card-15', + 'data-card-16', 'data-card-17', 'data-card-18', 'data-card-19', + 'data-card-20', 'data-card-21', 'data-card-22', 'data-card-23', + 'data-card-24', 'data-card-25', 'data-card-26', 'data-card-27', + 'data-card-28', 'data-card-29', 'data-card-30', + + // Hidden machines. + 'hm00', 'hm01', 'hm02', 'hm03', 'hm04', 'hm05', 'hm06', 'hm07', 'hm08', + 'hm09', 'hm10', + + // Mega- items are in the database but don't have an icon representation. + 'mega-pendant', 'mega-glasses', 'mega-glove', 'mega-anchor', 'mega-stickpin', + 'mega-tiara', 'mega-anklet', +); + +// Typoed items: these items have a typo in the database and need to be redirected +// to the correct file or we'll be unable to find the icon. +// See . +$typoed_items = array( + // The Xtransceiver icons. + 'xtranceiver--red' => 'xtransceiver--red', + 'xtranceiver--yellow' => 'xtransceiver--yellow', +); + +// Aliased items: this is for items that share an icon but have a different name. +// I'm not sure if any of these need to have a different icon. It could be that there are +// extra icons that we are missing. Worth trying to check this out sometime. +$aliased_items = array( + 'dna-splicers--merge' => 'dna-splicers', + 'dna-splicers--split' => 'dna-splicers', + + 'ss-ticket--hoenn' => 'ss-ticket', + + 'n-solarizer--merge' => 'n-solarizer', + 'n-lunarizer--merge' => 'n-lunarizer', + 'n-solarizer--split' => 'n-solarizer', + 'n-lunarizer--split' => 'n-lunarizer', + + 'ilimas-normalium-z' => 'normalium-z--bag', + 'left-poke-ball' => 'poke-ball', +); + +// Some icons are present in multiple sets. This disambiguates them. +$set_disambiguation = array( + // 'evo-item' and 'hold-item' have some identical icons. + 'hold-item' => array('evo-item', 'hold-item'), + // Prefer 'medicine' and 'battle-item' over 'wonder-launcher'. + 'medicine' => array('wonder-launcher', 'medicine'), + 'battle-item' => array('wonder-launcher', 'battle-item'), +); + +// We will attempt to match every item from the item export to an icon on the disk. +// Every item that doesn't have a matching icon is reported. +// Every icon that doesn't get used is reported. +$sets = get_icon_sets(); +$items = get_item_export(); + +// Finally, we will generate a new JSON file with the following structure: +// First, a list of icon sets, which we will use when generating the icon spritesheet. +// Secondly, a key-value store of item ID to icon file. +$item_icon_database = array(); +$icon_database = array(); +$icon_usage = array(); + +// Run through every item in the database, match it with an icon (if it's not in the ignored item list) +// and save the resulting item/icon object to the final data set. +foreach ($items as $item) { + $obj = find_item_icon($item, $sets); + // Save the icon this item will use in the usage array. + $icon_path = $obj['icon']['set'].'/'.$obj['icon']['filename']; + $icon_usage[$icon_path] = true; + // Save the object to the item/icon database. + // If this item does not have an associated icon, the value will be null instead of a string. + // If it does have an icon, the string is a reference to an icon ID in 'icons'. + $item_icon_database[$obj['item']['id']] = !empty($obj['icon']['set']) ? array($obj['icon']['set'], $obj['icon']['filename']) : null; + if (!isset($icon_database[$obj['icon']['set']])) { + $icon_database[$obj['icon']['set']] = array(); + } + $icon_database[$obj['icon']['set']][$obj['icon']['filename']] = $obj; +} + +// Now we need to run through all existing physical icons to see which ones were not linked to an item. +// We will also add these icons to the item/icon database, but without item data. +foreach ($sets as $set_name => $set_icons) { + foreach ($set_icons as $slug => $icon) { + $icon_path = $set_name.'/'.$icon['filename']; + if (isset($icon_usage[$icon_path])) { + continue; + } + // This icon has not been matched with an item. + $obj = make_item_icon(false, $slug, $set_name, $set_icons[$slug]); + $icon_usage[$icon_path] = true; + + // Save the object to the item/icon database. + if (!isset($icon_database[$set_name])) { + $icon_database[$set_name] = array(); + } + $icon_database[$set_name][$obj['icon']['filename']] = $obj; + } +} + +// Now output the final file. +file_put_contents('icons.json', json_encode(array('items' => $item_icon_database, 'icons' => $icon_database), JSON_PRETTY_PRINT)); +die("Output: './icons.json'. Done.\n"); + +/** + * Returns a matching icon file for a specific item. + * This is highly inefficient and any programmer would hate this code, + * but it doesn't really matter. + */ +function find_item_icon($item, $sets) { + global $ignored_items, $typoed_items, $aliased_items, $set_disambiguation; + + // Match on the English language slug. + $slug = $item['slug']['eng']; + + // Check if we are ignoring this item. + if (in_array($slug, $ignored_items)) { + return make_item_icon($item, $slug); + } + + // Check if the item we're looking for is typoed. + $is_typoed = false; + if (isset($typoed_items[$slug])) { + $slug = $typoed_items[$slug]; + $is_typoed = true; + } + + // Check if the item is aliased to a different icon file. + $is_aliased = false; + if (isset($aliased_items[$slug])) { + $slug = $aliased_items[$slug]; + $is_aliased = true; + } + + $matches = array(); + foreach ($sets as $set_name => $set_icons) { + $set_icon_slugs = array_keys($set_icons); + if (!in_array($slug, $set_icon_slugs)) { + continue; + } + // Found a match. + $matches[$set_name] = make_item_icon($item, $slug, $set_name, $set_icons[$slug], $is_typoed, $is_aliased); + } + if (count($matches) > 1) { + $keys = array_keys($matches); + $disamb = null; + print('Found '.count($matches).' matches for item #'.$item['id'].' ('.$item['slug']['eng'].'): '); + foreach ($set_disambiguation as $target => $items) { + if ($keys != $items) { + continue; + } + $disamb = $target; + } + if (!$disamb) { + print(implode(', ', $keys)." (no disambiguation)\n"); + } + else { + print(implode(', ', $keys)); + foreach ($matches as $set_name => $match_obj) { + if ($set_name === $disamb) { + print(" (".$disamb.")\n"); + return $match_obj; + } + } + print(" (could not find disambiguation: ".$disamb.")\n"); + } + } + return $matches[0]; + //if (strpos($obj['icon']['slug'], 'revive') > -1) { + + // NOTE: we're quitting the script here on error because all items should be accounted for. + // If one isn't, we need to add it to ignored/aliased/typoed items. + print("Error: could not find item icon: \n"); + var_dump($item); + die(); +} + +/** + * Returns a new format array with item and icon data. + */ +function make_item_icon($item = false, $slug = null, $set_name = null, $icon = false, $typoed = false, $aliased = false) { + $icon_data = array(); + if ($item !== false) { + $icon_data['item'] = array( + 'id' => $item['id'], + 'slug' => $item['slug'], + 'name' => $item['name'], + ); + } + if ($typoed !== false) { + $icon_data['typoed'] = $typoed; + } + if ($aliased !== false) { + $icon_data['aliased'] = $aliased; + } + if ($icon !== false) { + $icon_data['icon'] = array( + 'slug' => $slug, + 'set' => $set_name, + 'filename' => $icon['filename'], + ); + } + return $icon_data; +} + +/** + * Returns a list of all icon sets and icons inside: this is based on physical files. + */ +function get_icon_sets() { + global $suffix, $prefix; + + $sets = array(); + + // Iterate through every directory inside 'icons'. + $root = new DirectoryIterator('./icons/'); + foreach ($root as $set) { + $set_name = $set->getFilename(); + if ($set->isDot() || $set_name === 'pokemon' || !$set->isDir()) continue; + + if (!isset($sets[$set_name])) { + $sets[$set_name] = array(); + } + + // Now iterate over all files inside. + $dir = new DirectoryIterator('./icons/'.$set_name); + foreach ($dir as $icon) { + if ($icon->isDot()) continue; + + $filename = $icon->getFilename(); + $icon_name = pathinfo($icon->getFilename(), PATHINFO_FILENAME); + + if (in_array($set_name, $suffix)) { + $icon_name = $icon_name.'-'.$set_name; + } + if (in_array($set_name, $prefix)) { + $icon_name = $set_name.'-'.$icon_name; + } + + $sets[$set_name][$icon_name] = array( + 'filename' => $filename + ); + } + } + + return $sets; +} + +/** + * Returns the items from the database export. + */ +function get_item_export() { + $items = json_decode(file_get_contents('./data/item-export.json'), true); + return $items; +}