pokesprite/includes/iconstack.php
2018-09-09 21:27:57 +02:00

1003 lines
32 KiB
PHP

<?php
// PokéSprite
// ----------
// The use of this source code is governed by the MIT license.
// See the COPYRIGHT file for more information.
namespace PkSpr;
require_once 'includes/growingpacker.php';
/**
* Class that parses the Pokémon data and analyzes it.
*/
class IconStack
{
/** @var mixed[] Pokémon data. */
public $pkmn_data = array();
/** @var mixed[] Icon data. */
public $icon_data = array();
/** @var int Pokémon sprite image width. */
public $pkmn_img_width;
/** @var int Pokémon sprite image height. */
public $pkmn_img_height;
/** @var int Pokémon sprite image padding. */
public $pkmn_img_padding;
/** @var int Amount of sprite images in one row. */
public $pkmn_row_count;
/** @var string[] Sprite versions to include (regular, shiny). */
public $versions = array();
/** @var int Stack counter. */
private $counter = 0;
/** @var mixed[] Pokémon sprite stack. */
public $pkmn_stack = array();
/** @var mixed[] Sprites variants list (in case we have duplicates). */
public $sprites_variants = array();
/** @var mixed[] Etc sprite stack (all non-Pokémon box sprite icons). */
public $etc_stack = array();
/** @var mixed[] Etc sprite sets. */
public $etc_sets = array();
/** @var int Etc sprite stack width. */
public $etc_sect_width = 0;
/** @var int Etc sprite stack height. */
public $etc_sect_height = 0;
/** @var mixed[] Icon set sizes. */
public $set_sizes = array();
/** @var mixed[] Icon data. */
public $type_tree = array();
/**
* Initializes a number of variables from the settings file.
*/
public function __construct()
{
$vars = array(
'pkmn_img_width',
'pkmn_img_height',
'pkmn_img_padding',
'pkmn_row_count',
);
foreach ($vars as $var) {
$this->$var = Settings::get($var);
}
$this->pkmn_img_width += $this->pkmn_img_padding;
$this->pkmn_img_height += $this->pkmn_img_padding;
}
/**
* Get icon stack of all icons besides Pokémon sprites.
*
* @return mixed[] Etc icon stack data.
*/
public function get_etc_icon_stack()
{
if (empty($this->etc_stack)) {
$this->create_etc_icon_stack();
}
return $this->etc_stack;
}
/**
* Get all icons.
*
* @return mixed[] Icon data.
*/
public function get_all_icons()
{
return array_merge($this->pkmn_stack, $this->etc_stack);
}
/**
* Get icon set info.
*
* @return mixed[] Etc icon set data.
*/
public function get_etc_icon_sets()
{
if (empty($this->etc_sets)) {
$this->create_etc_icon_stack();
}
return $this->etc_sets;
}
/**
* Creates a special type of data structure specially suited
* for the SCSS styler, JS generator and overview generator.
*/
public function create_icon_type_tree()
{
$icons = $this->get_all_icons();
$pkmn_sect_size = $this->get_pkmn_icon_stack_size();
$tree = array();
foreach ($icons as $icon) {
$subvariation = $icon['subvariation'];
$subvariation = isset($subvariation) ? $subvariation : '.';
$type = $icon['type'];
$idx = $icon['idx'];
$set = @$icon['set'];
$original = @$icon['original'];
$slug = $icon['slug'];
$variation = $icon['variation'];
$version = $icon['version'];
$is_standard = $variation == '.';
// All icons other than Pokémon sprites need to be offset
// with the height of the Pokémon sprite section.
$x = $icon['fit']['x'];
$y = $icon['fit']['y'];
if ($type != 'pkmn') {
$y += $pkmn_sect_size['h'];
}
$icon_info = array(
'std' => $is_standard,
'w' => $icon['w'],
'h' => $icon['h'],
'x' => $x + $this->pkmn_img_padding,
'y' => $y + $this->pkmn_img_padding,
'type' => $type,
'original' => $original,
'set' => $set,
'idx' => $idx,
'id' => $icon['id'],
);
if ($type == 'pkmn') {
$tree['pkmn'][$slug][$variation][$version][$subvariation] = $icon_info;
}
else {
$tree['etc'][$set][$slug] = $icon_info;
}
}
$this->type_tree = $tree;
}
/**
* Returns the type tree data structure for our icons.
*
* @return mixed[] Pokémon icon type tree data.
*/
public function get_icon_type_tree()
{
if (empty($this->type_tree)) {
$this->create_icon_type_tree();
}
return $this->type_tree;
}
/**
* Determines and returns the sizes of the Pokémon icon sets.
*
* @return mixed[] Pokémon icon set sizes.
*/
public function get_set_sizes()
{
if (!empty($this->set_sizes)) {
return $this->set_sizes;
}
$sizes = array();
// The Pokémon icon sizes are always static.
$sizes['pkmn'] = array(
'w' => $this->pkmn_img_width - $this->pkmn_img_padding,
'h' => $this->pkmn_img_height - $this->pkmn_img_padding,
);
// Define the sizes for other items.
if (empty($this->type_tree['etc'])) {
$this->type_tree['etc'] = array();
}
foreach ($this->type_tree['etc'] as $slug => $icons) {
$w = 0;
$h = 0;
foreach ($icons as $icon) {
// Define the size if we're at the first iteration.
// Continue if it's the same.
// If a different size is found than the one that
// was defined before, this section will be determined
// to have variable image sizes.
$same = ($w === $icon['w'] && $h === $icon['h']);
$zero = ($w === 0 && $h === 0);
if ($same) {
continue;
}
if ($zero) {
$w = $icon['w'];
$h = $icon['h'];
}
else {
// Images have variable sizes. Save an empty array only.
$sizes[$slug] = array();
continue(2);
}
}
$sizes[$slug] = array(
'w' => $w,
'h' => $h
);
}
$this->set_sizes = $sizes;
return $this->set_sizes;
}
/**
* Creates an icon stack of the etc sprites.
*/
private function create_etc_icon_stack()
{
// This stack will contain icons from all other icon sets.
$stack = array();
// Save an array of sets.
$sets = array();
$etc_icon_sets = Settings::get('etc_icon_sets');
$dir_base = Settings::get('dir_base');
$file_exts = Settings::get('file_exts');
// Start off where the Pokémon stack ended.
$n = count($this->pkmn_stack);
if (Settings::get('include_icon_sets')) {
foreach ($etc_icon_sets as $set) {
$dir = $dir_base.$set.'/';
try {
$dir_it = new \DirectoryIterator($dir);
} catch (Exception $e) {
print(I18n::lf('icon_dir_failure', array($dir)));
continue;
}
foreach ($dir_it as $file) {
// Some checks to ensure it's a valid image.
if ($file->isDot()) {
continue;
}
if ($file->isDir()) {
continue;
}
$fn = $file->getFilename();
$fn_bits = explode('.', $fn);
$fn_ext = strtolower(trim(end($fn_bits)));
if (!in_array($fn_ext, $file_exts)) {
continue;
}
$size = getimagesize($dir.$fn);
$fn_slug = slugify(implode('.', array_slice($fn_bits, 0, -1)));
$var = $this->get_icon_var_name($set, $fn_slug);
$n += 1;
$stack[$n] = array(
'type' => 'etc',
'var' => $var,
'set' => $set,
'section' => 'other',
'id' => $n,
'slug' => $fn_slug,
'w' => $size[0],
'h' => $size[1],
'file' => $dir.$fn,
);
$sets[$set][] = $n;
}
}
}
// Sort icons by size.
uasort($stack, array($this, 'etc_max_w_h_sort'));
// Deep convert $stack to a StdClass rather than a regular array.
// This is in order to be able to use it with the GrowingPacker, which
// only accepts a StdClass as input.
$stack = json_decode(json_encode($stack), false);
// Make sure to pass the width of the Pokémon section as
// the initial width. If we're not including Pokémon icons,
// set it to a static value.
$pkmn_sect_size = $this->get_pkmn_icon_stack_size();
$initial_width = @$pkmn_sect_size['w'];
if (!$initial_width) {
$initial_width = $this->pkmn_img_width * $this->pkmn_row_count;
}
// Initialize our packing algorithm and feed the icons.
// Permit horizontal growth only if we're not including Pokémon icons.
$packer = new GrowingPacker();
$packer->fit($stack, $initial_width, null, false, true);
// Convert back to array.
$stack = json_decode(json_encode($stack), true);
// Remove extraneous data from the stack.
foreach ($stack as $n => $icon) {
unset($stack[$n]['fit']['right']);
unset($stack[$n]['fit']['down']);
unset($stack[$n]['fit']['used']);
}
$this->etc_stack = $stack;
$this->etc_sets = $sets;
$this->etc_sect_width = intval($packer->root->w);
$this->etc_sect_height = intval($packer->root->h);
}
/**
* Sort by size (descending), then id (ascending).
*
* This is used to prepare data for use with the GrowingPacker class,
* which works best when the input data is sorted by max(width, height).
* In cases of identical size, we sort by the id number to keep things of
* the same size in their originally intended order.
*
* @param mixed $a Left comparison.
* @param mixed $b Right comparison.
*/
private function etc_max_w_h_sort($a, $b)
{
// Take either the width or the height, whichever is the largest.
$a_size = max($a['w'], $a['h']);
$b_size = max($b['w'], $b['h']);
// If the items have the same size,
if ($a_size == $b_size) {
// Check their series number instead.
$a_id = $a['id'];
$b_id = $b['id'];
if ($a_id == $b_id) {
return 0;
}
// Ascending
return ($a_id < $b_id) ? -1 : 1;
}
// Descending
return ($a_size > $b_size) ? -1 : 1;
}
/**
* Sorts the icons so that the standard variation is always at the top.
*
* This is necessary to properly link duplicates to the original icon.
* Must be used with uksort().
*
* @param mixed $a Left comparison.
* @param mixed $b Right comparison.
*/
private function pkmn_variation_sort($a, $b)
{
// '.' must always go above the rest.
$val = $a == '.' ? -1 : ($b == '.' ? 1 : 0);
if ($val == 0) {
// In all other cases, use alphabetic order.
return ($a < $b) ? -1 : 1;
}
else {
return $val;
}
}
/**
* Parse Pokémon data file.
*
* @param string $file Filename.
* @param string $range Range variable (undocumented; debugging only).
*/
public function parse_data_file($file, $range=null)
{
$this->pkmn_data = $this->slice_icons(json_decode(file_get_contents($file), true), $range);
}
/**
* Parse icons data file.
*
* @param string $file Filename.
* @param string $range Range variable (undocumented; debugging only).
*/
private function slice_icons($data, $range)
{
if (isset($range)) {
$range = str_replace(',', '-', $range);
$range = explode('-', $range);
$range[0] = $range[0] === '0' ? 1 : $range[0];
if ($range[1] <= $range[0]) {
$range[1] = $range[0];
}
$range = array(
intval($range[0]) - 1,
intval($range[1]) - intval($range[0]) + 1
);
return array_slice(
$data,
$range[0],
$range[1]
);
}
return $data;
}
/**
* Parse icons data file.
*
* @param string $file Filename.
* @param string $range Range variable (undocumented; debugging only).
*/
public function parse_icons_data_file($file, $range=null)
{
$this->icon_data = $this->slice_icons(json_decode(file_get_contents($file), true), $range);
}
/**
* Checks whether Pokémon data is loaded and non-empty.
*/
public function has_pkmn_data()
{
return !empty($this->pkmn_data);
}
/**
* Checks whether icon data is loaded and non-empty.
*/
public function has_icon_data()
{
return !empty($this->icon_data);
}
/**
* Checks whether Pokémon images can be found.
*/
public function has_pkmn_images()
{
// Check to see if the images are there.
$test_img = @reset($this->pkmn_data);
$final_img = (
Settings::get('dir_base').
Settings::get('dir_pkmn').
Settings::get('dir_pkmn_regular').
$test_img['slug'][Settings::get('img_slug_lang')].
'.png'
);
return is_file($final_img);
}
/**
* Checks whether icon images can be found.
*/
public function has_icon_images()
{
// Check to see if the images are there.
$test_group = 'medicine';
$test_item = @reset($this->icon_data['icons'][$test_group]);
$test_img = (
Settings::get('dir_base').
$test_group.'/'.
$test_item['icon']['filename']
);
return is_file($test_img);
}
/**
* Returns parsed Pokémon data.
*
* @return mixed[] Parsed Pokémon data.
*/
public function get_pkmn_data()
{
return $this->pkmn_data;
}
/**
* Returns parsed icon data.
*
* @return mixed[] Parsed icon data.
*/
public function get_icon_data()
{
return $this->icon_data;
}
/**
* Returns x/y coordinates for the next Pokémon icon.
*
* @param int $inc Whether to increment the counter.
* @return mixed[] Coordinates.
*/
private function get_pkmn_icon_fit($inc=true)
{
$width = $this->pkmn_img_width;
$height = $this->pkmn_img_height;
$row_count = $this->pkmn_row_count;
if ($inc != false) {
$this->counter += 1;
}
$x = ($this->counter % $row_count) * $width;
$y = floor($this->counter / $row_count) * $height;
$fit = array(
'fit' => array(
'x' => $x,
'y' => $y,
),
);
return $fit;
}
/**
* Returns the total size of the Pokémon icon stack.
*
* @return int[] Pokémon icon stack size.
*/
public function get_pkmn_icon_stack_size()
{
if (!empty($this->pkmn_sect_width)) {
return array(
'w' => $this->pkmn_sect_width,
'h' => $this->pkmn_sect_height,
);
}
$fit = $this->get_pkmn_icon_fit(false);
if ($this->counter > $this->pkmn_row_count) {
$width = ($this->pkmn_img_width * $this->pkmn_row_count);
}
else {
$width = $fit['fit']['x'] + $this->pkmn_img_width;
}
// See if the width is at least the size of 32 Pokémon icons.
// Note that padding has already been added to pkmn_img_width.
$min_width = (($this->pkmn_img_width) * $this->pkmn_row_count) + $this->pkmn_img_padding;
if ($width < $min_width) {
$width = $min_width;
}
$height = $fit['fit']['y'] + $this->pkmn_img_height;
$this->pkmn_sect_width = intval($width);
$this->pkmn_sect_height = intval($height);
return array(
'w' => $this->pkmn_sect_width,
'h' => $this->pkmn_sect_height,
);
}
/**
* Returns the total size of the etc icon stack.
*
* @return int[] Etc icon stack size.
*/
public function get_etc_icon_stack_size()
{
return array(
'w' => $this->etc_sect_width + $this->img_padding,
'h' => $this->etc_sect_height + $this->img_padding,
);
}
/**
* Returns the total size of the Pokémon stack plus the etc icon
* stack below it.
*
* @return int[] Full icon stack size.
*/
public function get_combined_stack_size()
{
$include_pkmn = Settings::get('include_pkmn');
$include_icon_sets = Settings::get('include_icon_sets');
$pkmn_size = $this->get_pkmn_icon_stack_size();
$etc_size = $this->get_etc_icon_stack_size();
$width = $include_pkmn ? $pkmn_size['w'] : $etc_size['w'];
$height = $include_pkmn ? $pkmn_size['h'] : 0;
$height += $include_icon_sets ? $etc_size['h'] : 0;
return array(
'w' => $width + $this->pkmn_img_padding,
'h' => $height + $this->pkmn_img_padding,
);
}
/**
* Creates an icon stack of the Pokémon sprites.
*
* This stack can then be fed to a number of other systems,
* including the sprite image and SCSS/JS generators.
*/
public function create_pkmn_icon_stack()
{
// Loop through the available Pokémon and adding each of their
// variants and forms to a stack.
// Initialize the sprite stack.
$this->pkmn_stack = array();
// List of sprite variants.
$this->sprites_variants = array();
// Include regular Pokémon, and shiny Pokémon if set.
$this->versions = array();
if (Settings::get('include_pkmn_nonshiny')) {
$this->versions[] = 'regular';
}
if (Settings::get('include_pkmn_shiny')) {
$this->versions[] = 'shiny';
}
$this->counter = -1;
// If we're skipping Pokémon sprite icons, $pkmn_data will be empty.
foreach ($this->pkmn_data as $id => $pkmn) {
$stack_items = $this->get_pkmn_stack_items($id, $pkmn);
foreach ($stack_items as $item) {
$this->pkmn_stack[] = $item;
}
}
// Add the special items (e.g. egg, unknown).
if (Settings::get('include_special_icons')) {
$special_items = $this->get_special_stack_items();
$this->pkmn_stack = array_merge($this->pkmn_stack, $special_items);
}
}
/**
* Returns the special items for the Pokémon stack.
* Currently, just "egg", "egg-manaphy" and "unknown".
* These have no shiny icons.
*/
public function get_special_stack_items()
{
// Unhatched egg.
$egg = array(
'idx' => null,
'slug' => array(
'eng' => 'egg',
'jpn' => 'tamago',
),
'icons' => array(
'.' => array(),
),
'name' => array(
'eng' => 'Egg',
'jpn' => 'タマゴ',
'jpn_ro' => 'Tamago',
),
);
// Manaphy egg.
$egg_manaphy = array(
'idx' => null,
'slug' => array(
'eng' => 'egg-manaphy',
'jpn' => 'tamago-manaphy',
),
'icons' => array(
'.' => array(),
),
'name' => array(
'eng' => 'Manaphy Egg',
'jpn' => 'マナフィのタマゴ',
'jpn_ro' => 'Manaphy Tamago',
),
);
// Unknown (not Unown) or glitch Pokémon.
$unknown = array(
'idx' => null,
'slug' => array(
'eng' => 'unknown',
'jpn' => 'fumei',
),
'icons' => array(
'.' => array(),
),
'name' => array(
'eng' => 'Unknown',
'jpn' => 'ふめい',
'jpn_ro' => 'Fumei',
),
);
return array_merge(
$this->get_pkmn_stack_items('egg', $egg, true),
$this->get_pkmn_stack_items('egg-manaphy', $egg_manaphy, true),
$this->get_pkmn_stack_items('unknown', $unknown, true)
);
}
/**
* Returns the icon stack (and generates it if it doesn't exist).
*
* @return mixed[] Pokémon icon stack data.
*/
public function get_pkmn_icon_stack()
{
if (empty($this->pkmn_stack)) {
$this->create_pkmn_icon_stack();
}
return $this->pkmn_stack;
}
/**
* Returns the standard icons that need to be referred to
* in the case of duplicates.
*
* @return mixed[] Pokémon icon stack data (only of standard icons).
*/
public function get_pkmn_std_icons()
{
if (empty($this->pkmn_stack)) {
$this->create_pkmn_icon_stack();
}
return $this->sprites_variants;
}
/**
* Produces and returns a variable name for an icon
* based on its attributes.
*
* @param string $type Type identifier.
* @param string $slug Slug name.
* @param string $variation Variation.
* @param string $subvariation Subvariation.
* @param string $version Version.
* @return string Variable name.
*/
private function get_icon_var_name($type, $slug, $variation=null,
$subvariation=null, $version=null)
{
$items = array();
$items[] = $type;
if ($type == 'pkmn') {
$items[] = $slug[Settings::get('pkmn_language_slugs')];
if ($variation != '.') {
$items[] = $variation;
}
if ($subvariation != '.') {
$items[] = $subvariation;
}
if ($version != 'regular') {
$items[] = $version;
}
}
else {
$items[] = $slug;
}
return implode('-', $items);
}
/**
* Returns stack items for a specific Pokémon.
*
* @return mixed[] Pokémon icon stack data.
*/
private function get_pkmn_stack_items($id, $pkmn, $special=false)
{
// Base info that's the same for each stack item.
$base_info = array(
'id' => $id,
'idx' => $pkmn['idx'],
'type' => 'pkmn',
'w' => $this->pkmn_img_width,
'h' => $this->pkmn_img_height,
'slug' => $pkmn['slug'][Settings::get('pkmn_language_slugs')],
'slug_img' => $pkmn['slug'][Settings::get('img_slug_lang')],
'section' => 'pkmn',
'name_display' => $pkmn['name'][Settings::get('pkmn_language')],
);
// Retrieve some variables from the settings.
$vars = array(
'dir_base', 'dir_pkmn', 'dir_pkmn_female', 'dir_pkmn_right',
'include_pkmn_forms', 'include_pkmn_right',
);
foreach ($vars as $var) {
$$var = Settings::get($var);
}
// Keep this Pokémon's stack items and return them at the end.
$tmp_stack = array();
// Sort the variations to ensure the standard variation
// comes first. The data file should already be pre-sorted,
// but we're making sure.
uksort($pkmn['icons'], array($this, 'pkmn_variation_sort'));
foreach ($pkmn['icons'] as $icon => $icon_data) {
// Zygarde has '10' and '50' formes. Make sure the icon name is a string.
$icon = (string)$icon;
// If this is a special icon (no variations, no shiny version),
// add only the plain icon and continue.
// This should be only 'egg', 'egg-manaphy' and 'unknown'.
if ($special) {
$var = $this->get_icon_var_name(
'pkmn',
$pkmn['slug'],
$icon,
'.',
'regular'
);
$pkmn_info = array_merge($base_info,
array(
'version' => 'regular',
'subvariation' => null,
'is_duplicate_from' => false,
),
$this->get_pkmn_icon_fit(),
array(
'variation' => $icon,
'var' => $var,
'file' => (
$dir_base.
$dir_pkmn.
Settings::get('dir_pkmn_special').
$pkmn['slug'][Settings::get('img_slug_lang')].
'.png'
),
)
);
$tmp_stack[] = $pkmn_info;
continue;
}
// Loop through each icon twice: once for regular versions, and
// (if requested), once more for shiny versions.
// Every variation is added to the stack.
foreach ($this->versions as $version) {
// Refer to either the regular or shiny icon directory.
$version_dir = Settings::get('dir_pkmn_'.$version);
$is_duplicate = !empty($icon_data['is_duplicate_from']);
$info = array(
'version' => $version,
'subvariation' => null,
'is_duplicate_from' => $icon_data['is_duplicate_from'],
);
// If this is a duplicate, keep a reference
// to the standard icon.
if ($icon_data['is_duplicate_from']) {
$info['original'] = @$this->sprites_variants[$id][$version][$icon_data['is_duplicate_from']];
}
$is_standard = $icon == '.';
// Don't include non-standard forms
// if we've got that turned off.
if ($include_pkmn_forms != true && !$is_standard) {
continue;
}
// The standard version is indicated by a single period.
// All other variants are present as slug-variant.png.
$variation = $is_standard ? '' : '-'.$icon;
// Produce a variable name.
$var = $this->get_icon_var_name(
'pkmn',
$pkmn['slug'],
$icon,
'.',
$version
);
// Add to the stack
$pkmn_info = array_merge($base_info,
$info,
$this->get_pkmn_icon_fit(!$is_duplicate),
array(
'variation' => $icon,
'var' => $var,
'file' => (
$dir_base.
$dir_pkmn.
$version_dir.
$pkmn['slug'][Settings::get('img_slug_lang')].
$variation.'.png'
),
)
);
$tmp_stack[] = $pkmn_info;
// Save this icon in the variant list.
// We need to re-use it for variations that
// don't have their own icon.
$this->sprites_variants[$id][$version][$icon] = $pkmn_info;
// Female variant
if ($include_pkmn_forms && $icon_data['has_female']) {
$var = $this->get_icon_var_name(
'pkmn',
$pkmn['slug'],
$icon,
'female',
$version
);
$tmp_stack[] = array_merge($base_info,
$info,
$this->get_pkmn_icon_fit(!$is_duplicate),
array(
'variation' => $icon,
'var' => $var,
'subvariation' => 'female',
'file' => (
$dir_base.
$dir_pkmn.
$version_dir.
$dir_pkmn_female.
$pkmn['slug'][Settings::get('img_slug_lang')].
'.png'
),
)
);
}
// Right-facing variant
if ($include_pkmn_forms && $icon_data['has_right']
&& $include_pkmn_right > 0) {
$var = $this->get_icon_var_name(
'pkmn',
$pkmn['slug'],
$icon,
'right',
$version
);
$tmp_stack[] = array_merge($base_info,
$info,
$this->get_pkmn_icon_fit(!$is_duplicate),
array(
'variation' => $icon,
'var' => $var,
'subvariation' => 'right',
'file' => (
$dir_base.
$dir_pkmn.
$version_dir.
$dir_pkmn_right.
$pkmn['slug'][Settings::get('img_slug_lang')].
$variation.'.png'
),
)
);
}
if ($include_pkmn_forms && !$icon_data['has_right']
&& $include_pkmn_right == 2) {
// If there's no right-facing variant, but we
// want all icons to have both left and right versions,
// we'll have to flip the regular image.
$var = $this->get_icon_var_name(
'pkmn',
$pkmn['slug'],
$icon,
'flipped',
$version
);
$tmp_stack[] = array_merge($base_info,
$info,
$this->get_pkmn_icon_fit(!$is_duplicate),
array(
'variation' => $icon,
'var' => $var,
'subvariation' => 'flipped',
'file' => (
$dir_base.
$dir_pkmn.
$version_dir.
$pkmn['slug'][Settings::get('img_slug_lang')].
$variation.'.png'
),
)
);
}
}
}
return $tmp_stack;
}
}