Last modified : 2024-11-25 19:41:37 UTC
<?php

if (isset($_GET['src']) && $_GET['src'] == 'show') {
    echo "<b>Last modified :</b> ".date("Y-m-d H:i:s", filemtime(__FILE__))." UTC<br>\n";
    echo "<div style=\"font-size: 12px;\">\n";
    highlight_string(file_get_contents(__FILE__));
    print "</div>\n";
    exit;
}


if ($_SERVER['HTTP_HOST'] == 'ryapp') {
    error_reporting(E_ALL);
    ini_set('display_errors', true);
} else {
    error_reporting(0);
    ini_set('display_errors', false);
}

use Ryzom\Common\EGender;
use Ryzom\Common\EVisualSlot;
use Ryzom\Common\TPeople;

if (!function_exists('_h')) {
    function _h($str)
    {
        return htmlspecialchars($str);
    }
}
function __($sheet, $lang = LANG)
{
    return _h(ryzom_translate($sheet, $lang));
}

require __DIR__ . '/../vendor/autoload.php';

//
// form
//
$form = [
    'language' => is($_REQUEST['language'], is($_GET['lang'], 'en')),
    'zoom' => is($_REQUEST['zoom'], 'body'),
    'dir' => (int) is($_REQUEST['dir'], 0),
    //
    'age' => clamp((int) is($_REQUEST['age'], 0), 0, 2),
    'race' => clamp((int) is($_REQUEST['race'], 0), 0, 3),
    'gender' => clamp((int) is($_REQUEST['gender'], 0), 0, 1),
    'eyes' => clamp((int) is($_REQUEST['eyes'], 0), 0, 6),
    'tattoo' => (int) is($_REQUEST['tattoo'], 0),
    'haircut' => (int) is($_REQUEST['haircut'], 0),
    'haircolor' => (int) is($_REQUEST['haircolor'], 0),
    //
    'chest' => ['item' => (int) is($_REQUEST['slot1']['item'], 0), 'color' => (int) is($_REQUEST['slot1']['color'], 0)],
    'head' => ['item' => (int) is($_REQUEST['slot3']['item'], 0), 'color' => (int) is($_REQUEST['slot3']['color'], 0)],
    'legs' => ['item' => (int) is($_REQUEST['slot2']['item'], 0), 'color' => (int) is($_REQUEST['slot2']['color'], 0)],
    'arms' => ['item' => (int) is($_REQUEST['slot4']['item'], 0), 'color' => (int) is($_REQUEST['slot4']['color'], 0)],
    'hands' => ['item' => (int) is($_REQUEST['slot6']['item'], 0), 'color' => (int) is($_REQUEST['slot6']['color'], 0)],
    'feet' => ['item' => (int) is($_REQUEST['slot7']['item'], 0), 'color' => (int) is($_REQUEST['slot7']['color'], 0)],
    //
    'handr' => (int) is($_REQUEST['slot8'], 0),
    'handl' => (int) is($_REQUEST['slot9'], 0),
    //
    'gabarit' => [
        (int) is($_REQUEST['gabarit'][0], 7),// char height
        (int) is($_REQUEST['gabarit'][1], 7),// torso width
        (int) is($_REQUEST['gabarit'][2], 7),// arms width
        (int) is($_REQUEST['gabarit'][3], 7),// legs width
        (int) is($_REQUEST['gabarit'][4], 7),// breast size
    ],
    'morph' => [
        (int) is($_REQUEST['morph'][0], 3),// morph target 1, uiFace1Fy, uiFace1Ma, uiFace1Tr, uiFace1Zo
        (int) is($_REQUEST['morph'][1], 3),// morph target 2
        (int) is($_REQUEST['morph'][2], 3),// morph target 3
        (int) is($_REQUEST['morph'][3], 3),// morph target 4
        (int) is($_REQUEST['morph'][4], 3),// morph target 5
        (int) is($_REQUEST['morph'][5], 3),// morph target 6
        (int) is($_REQUEST['morph'][6], 3),// morph target 7
        (int) is($_REQUEST['morph'][7], 3),// morph target 8
    ]
];

if (!in_array($form['zoom'], ['body', 'portrait'])) {
    $form['zoom'] = 'body';
}

$form['language'] = (in_array($form['language'], ['en', 'fr', 'de', 'ru', 'es'])) ? $form['language'] : 'en';
define('LANG', $form['language']);

//
// build character
//
$char = new \Rrs\Character();
$char->setRace($form['race']);
$char->setAge($form['age']);
$char->setDirection($form['dir']);
$char->setGabarit($form['gabarit']);
$char->setGender($form['gender']);
$char->setMorph($form['morph']);
$char->setSlot(EVisualSlot::FACE_SLOT, $form['tattoo'], $form['eyes']);
$char->setFaceShot($form['zoom'] != 'body', false);
$slots = [
    EVisualSlot::CHEST_SLOT => 'chest',
    EVisualSlot::HEAD_SLOT => 'head',
    EVisualSlot::LEGS_SLOT => 'legs',
    EVisualSlot::ARMS_SLOT => 'arms',
    EVisualSlot::HANDS_SLOT => 'hands',
    EVisualSlot::FEET_SLOT => 'feet',
    //
    EVisualSlot::RIGHT_HAND_SLOT => 'handr',
    EVisualSlot::LEFT_HAND_SLOT => 'handl',
];

foreach ($slots as $idx => $key) {
    if (isset($form[$key]['color'])) {
        $char->setSlot($idx, $form[$key]['item'], $form[$key]['color']);
    } else {
        $char->setSlot($idx, $form[$key]);
    }
}
// if no helmet is selected, then use haircut
list($hcut, $hcol) = $char->getSlot(EVisualSlot::HEAD_SLOT);
if ($hcut == 0) {
    $char->setSlot(EVisualSlot::HEAD_SLOT, $form['haircut'], $form['haircolor']);
}

// override values from user supplied vpx
if (!empty($_REQUEST['vpa'])) {
    $is_hex = (is($_REQUEST['vpax'], 'off') === 'on') && substr($_REQUEST['vpa'], 0, 2) !== '0x';
    $char->setVpa(($is_hex ? '0x' : '') . $_REQUEST['vpa']);
}
if (!empty($_REQUEST['vpb'])) {
    $is_hex = (is($_REQUEST['vpbx'], 'off') === 'on') && substr($_REQUEST['vpb'], 0, 2) !== '0x';
    $char->setVpb(($is_hex ? '0x' : '') . $_REQUEST['vpb']);
}
if (!empty($_REQUEST['vpc'])) {
    $is_hex = (is($_REQUEST['vpcx'], 'off') === 'on') && substr($_REQUEST['vpc'], 0, 2) !== '0x';
    $char->setVpc(($is_hex ? '0x' : '') . $_REQUEST['vpc']);
}

//
// ajax
//
if (is($_SERVER['HTTP_X_REQUESTED_WITH'], '') && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
    header('Content-Type: application/json; charset=utf-8');
    header('Access-Control-Allow-Origin: *');

    $json = [
        'result' => 'success',
        'data' => [
            'image' => render_3d_url($char),
            'vpa' => '<br>(hex) '.strtoupper($char->getVpa(true)) . '<br>(dec) '.$char->getVpa(),
            'vpb' => '<br>(hex) '.strtoupper($char->getVpb(true)) . '<br>(dec) '.$char->getVpb(),
            'vpc' => '<br>(hex) '.strtoupper($char->getVpc(true)) . '<br>(dec) '.$char->getVpc(),
        ],
    ];

    echo json_encode($json, JSON_UNESCAPED_UNICODE);

    die();
}

header('Content-Type: text/html; charset=utf-8');
//header('Access-Control-Allow-Origin: *');

//
// build interface
//
$tpl2 = '<li>{$name}: <span id="{$id}">{$vpx}</span></li>';

$vpx = '<ul>';
$vpx .= strtr($tpl2, ['{$name}' => 'VPA', '{$id}' => 'vpa', '{$vpx}' => '<br>(hex) '.strtoupper($char->getVpa(true)) . '<br>(dec) '.$char->getVpa()]);
$vpx .= strtr($tpl2, ['{$name}' => 'VPB', '{$id}' => 'vpb', '{$vpx}' => '<br>(hex) '.strtoupper($char->getVpb(true)) . '<br>(dec) '.$char->getVpb()]);
$vpx .= strtr($tpl2, ['{$name}' => 'VPC', '{$id}' => 'vpc', '{$vpx}' => '<br>(hex) '.strtoupper($char->getVpc(true)) . '<br>(dec) '.$char->getVpc()]);
$vpx .= '</ul>';

//$vp = $char->getVpa(true);
//$vp = preg_replace('~(..)(?!$)\.?~', '\1:', $vp);
//echo $vp;

//************************************************************************************
$langArray = [
    'en' => __('LanguageName.uxt', 'en'),
    'fr' => __('LanguageName.uxt', 'fr'),
    'de' => __('LanguageName.uxt', 'de'),
    'ru' => __('LanguageName.uxt', 'ru'),
    'es' => __('LanguageName.uxt', 'es'),
];
$langTable = '<table><tr>';
$langTable .= '<td>' . __('uigcLanguage.uxt') . '</td>';
$langTable .= '<td>' . html_select('language', $langArray, LANG) . '</td>';
$langTable .= '</tr></table>';

$tpl = '<html>
<head>
    <title>{$title}</title>
    <script src="js/jquery-2.1.4.min.js"></script>
    <script src="js/main.js"></script>
    <style>
        #status {
            position: absolute;
            top: 0;
            left: 0;
            padding-top: 200px;
            width: 300px;
            height: 400px; /* -padding*/
            background-color: rgba(0, 0, 0, .5);
            color: yellowgreen;
            font-size: 26px;
            text-align: center;
        }
        #form2 input[type="text"] {
            width: 200px;
        }
    </style>
</head>
<body>

<a href="https://github.com/nimetu/dr.bmsite.net">
    <img style="position: absolute; top: 0; right: 0; border: 0;" src="images/forkme_right_darkblue_121621.png" alt="Fork me on GitHub">
</a>

{$title} <small>(<a href="?src=show">source</a>)</small>

<hr>

<form id="form" method="POST" action="?">
    <table>
        <tr>
            <td valign="top" style="position:relative;">
                {$image}
                <br>{$vpx}<br>{$image_opts}
                <div id="status"></div>
            </td>
            <td valign="top">{$options}</td>
        </tr>
    </table>
    {$lang}
</form>

<h2>Set VPX</h2>
<form id="form2" method="POST" action="?">
    <table>
        <tr>
            <td valign="top">VPA</td>
            <td>
                <input type="text" name="vpa" value="" size="100">
            </td>
            <td>
                <input type="checkbox" name="vpax"> hex
            </td>
        </tr>
        <tr>
            <td valign="top">VPB</td>
            <td>
                <input type="text" name="vpb" value="" size="100">
            </td>
            <td>
                <input type="checkbox" name="vpbx"> hex
            </td>
        </tr>
        <tr>
            <td valign="top">VPC</td>
            <td>
                <input type="text" name="vpc" value="" size="100">
            </td>
            <td>
                <input type="checkbox" name="vpcx"> hex
            </td>
        </tr>
    </table>
    <input type="submit" name="submit" value="submit">
</form>
</body>
</html>
';

echo strtr(
    $tpl,
    [
    '{$title}' => 'api.bmsite.net - Character Creator',
        '{$lang}' => $langTable,
        '{$vpx}' => $vpx,
        '{$image_opts}' => image_options($char),
        //
        '{$image}' => '<img id="preview" src="' . render_3d_url($char) . '" width="300" height="600">',
        '{$options}' => option_pane($char),
        //
    ]
);

exit;

/**
 * @param \Rrs\Character $char
 *
 * @return string
 */
function render_3d_url(\Rrs\Character $char)
{
    // race, gender, age, eyes, hair, tattoo, gabarit, morph, dir, zoom [body, face, portrait]
    $race_names = [0 => 'fy', 1 => 'ma', 2 => 'tr', 3 => 'zo'];
    $race = $race_names[$char->getRace()];
    $dir = $char->getDirection();
    // age not in vpx
    //$age = $char->getAge();

    $vpa = $char->getVpa();
    $vpb = $char->getVpb();
    $vpc = $char->getVpc();

    if ($char->isFaceShot()) {
        $zoom = 'portrait';
    } else {
        $zoom = 'body';
    }

    return "https://api.bmsite.net/char/render/3d?race={$race}&dir={$dir}&zoom={$zoom}&vpa={$vpa}&vpb={$vpb}&vpc={$vpc}";
}

function image_options(\Rrs\Character $char)
{
    $tplRow = '<tr><td>{$name}</td><td>{$value}</td>';

    $zoomArray = [
        'body' => 'body',
        'portrait' => 'portrait',
    ];
    if ($char->isFaceShot()) {
        $zoom = 'portrait';
    } else {
        $zoom = 'body';
    }

    $html = '';

    //************************************************************************************
    // image options
    //************************************************************************************
    $angles = [
        0 => '0 (front)',
        45 => 45,
        90 => 90,
        135 => 135,
        180 => '180 (back)',
        225 => 225,
        270 => 270,
        315 => 315,
    ];
    $html .= strtr(
        $tplRow,
        ['{$name}' => 'Zoom', '{$value}' => html_select('zoom', $zoomArray, $zoom)]
    );

    $html .= strtr(
        $tplRow,
        [
            '{$name}' => 'Direction',
            '{$value}' => html_select('dir', $angles, (int) $char->getDirection())
        ]
    );

    return '<table>' . $html . '</table>';
}

/**
 * @param \Rrs\Character $char
 *
 * @return string
 */
function option_pane(\Rrs\Character $char)
{
    $race = strtolower(TPeople::toString($char->getRace()));
    $gender = $char->getGender() == 0 ? 'm' : 'f';

    $raceArray = [
        0 => 'Fyros',
        1 => 'Matis',
        2 => 'Tryker',
        3 => 'Zorai',
    ];
    $ageArray = [
        0 => 'age=0',
        1 => 'age=1',
        2 => 'age=2',
    ];
    $genderArray = [
        0 => 'male',
        1 => 'female',
    ];

    $tplRow = '<tr><td>{$name}</td><td>{$value}</td>';
    $sep = '<tr><td>---</td><td></td></tr>';

    $btnSubmit = strtr(
        $tplRow,
        [
            '{$name}' => '<input type="submit" name="submit" value="submit">',
            '{$value}' => ''
        ]
    );

    //************************************************************************************
    // page 1
    //************************************************************************************
    $html = '';
    $html .= strtr($tplRow, ['{$name}' => '', '{$value}' => __('uiAppear_Infos2.uxt')]);
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiR2EdRace.uxt'), '{$value}' => html_select('race', $raceArray, $char->getRace())]
    );
    // age is not in vpx
    //$html .= strtr($tplRow, ['{$name}' => 'Age', '{$value}' => html_select('age', $ageArray, $char->getAge())]);
    $html .= strtr(
        $tplRow,
        ['{$name}' => 'Gender', '{$value}' => html_select('gender', $genderArray, $char->getGender())]
    );

    $hairColors = [];
    for ($i = 0; $i < 6; $i++) {
        $hairColors[$i] = sprintf('%s_ho%s_haircolor%d.sitem', $race, $gender, $i);
    }

    $slot = EVisualSlot::HEAD_SLOT;
    list($haircut, $haircolor) = $char->getSlot($slot);
    $haircuts = [0 => '-'] + slot_items($char, $slot, 'hair');
    if (!isset($haircuts[$haircut])) {
        $haircut = 0;
    }

    $sb = '<table><tr>';
    $sb .= '<td>' . html_select("haircolor", $hairColors, $haircolor, true, false) . '</td>';
    $sb .= '<td>' . html_select("haircut", $haircuts, $haircut, true) . '</td>';
    $sb .= '</tr></table>';
    $html .= strtr(
        $tplRow,
        ['{$name}' => 'Haircut', '{$value}' => $sb]
    );

    //************************************************************************************
    $tattooArray = [];
    for ($i = 0; $i < 64; $i++) {
        $txt = sprintf('%s_ho%s_tatoo%d.sitem', $race, $gender, $i);
        $tattooArray[$i] = __($txt);
    }
    list($tattoo, $eyes) = $char->getSlot(EVisualSlot::FACE_SLOT);
    $html .= strtr(
        $tplRow,
        ['{$name}' => 'Eye color', '{$value}' => html_select('eyes', range(0, 7, 1), $eyes)]
    );
    $html .= strtr(
        $tplRow,
        ['{$name}' => 'Tattoo', '{$value}' => html_select('tattoo', $tattooArray, $tattoo)]
    );

    //************************************************************************************
    // page
    //************************************************************************************
    $html .= strtr($tplRow, ['{$name}' => '', '{$value}' => __('uiAppear_Infos2.uxt')]);
    $slots = [
        EVisualSlot::HEAD_SLOT => 'uiHelmet.uxt',
        EVisualSlot::CHEST_SLOT => 'uiTorso.uxt',
        EVisualSlot::ARMS_SLOT => 'uiArms.uxt',
        EVisualSlot::HANDS_SLOT => 'uiGloves.uxt',
        EVisualSlot::LEGS_SLOT => 'uiLegs.uxt',
        EVisualSlot::FEET_SLOT => 'uiFeet.uxt',
    ];
    $colors = [
        RyzomExtra::COLOR_RED => RyzomExtra::uxt_color(RyzomExtra::COLOR_RED) . '.uxt',
        RyzomExtra::COLOR_BEIGE => RyzomExtra::uxt_color(RyzomExtra::COLOR_BEIGE) . '.uxt',
        RyzomExtra::COLOR_GREEN => RyzomExtra::uxt_color(RyzomExtra::COLOR_GREEN) . '.uxt',
        RyzomExtra::COLOR_TURQUOISE => RyzomExtra::uxt_color(RyzomExtra::COLOR_TURQUOISE) . '.uxt',
        RyzomExtra::COLOR_BLUE => RyzomExtra::uxt_color(RyzomExtra::COLOR_BLUE) . '.uxt',
        RyzomExtra::COLOR_PURPLE => RyzomExtra::uxt_color(RyzomExtra::COLOR_PURPLE) . '.uxt',
        RyzomExtra::COLOR_WHITE => RyzomExtra::uxt_color(RyzomExtra::COLOR_WHITE) . '.uxt',
        RyzomExtra::COLOR_BLACK => RyzomExtra::uxt_color(RyzomExtra::COLOR_BLACK) . '.uxt',
    ];

    // FIXME: split helmet and haircut - different colors
    foreach ($slots as $slot => $txt) {
        list($item, $color) = $char->getSlot($slot);

        $items = [0 => '-'] + slot_items($char, $slot, 'item');

        $sb = '<table><tr>';
        $sb .= '<td>' . html_select("slot{$slot}[color]", $colors, $color, true, false) . '</td>';
        $sb .= '<td>' . html_select("slot{$slot}[item]", $items, $item, true) . '</td>';
        $sb .= '</tr></table>';

        $html .= strtr($tplRow, ['{$name}' => __($txt), '{$value}' => $sb]);
    }
    //***********************************************************************************
    list($item) = $char->getSlot(EVisualSlot::RIGHT_HAND_SLOT);
    $items = [0 => '-'] + slot_items($char, EVisualSlot::RIGHT_HAND_SLOT);
    $html .= strtr(
        $tplRow,
        [
            '{$name}' => 'Right hand',
            '{$value}' => html_select(
                'slot' . EVisualSlot::RIGHT_HAND_SLOT,
                $items,
                $item,
                true
            )
        ]
    );
    list($item) = $char->getSlot(EVisualSlot::LEFT_HAND_SLOT);
    $items = [0 => '-'] + slot_items($char, EVisualSlot::LEFT_HAND_SLOT);
    $html .= strtr(
        $tplRow,
        [
            '{$name}' => 'Left hand',
            '{$value}' => html_select(
                'slot' . EVisualSlot::LEFT_HAND_SLOT,
                $items,
                $item,
                true
            )
        ]
    );

    $html .= $btnSubmit;

    //************************************************************************************
    // page
    //************************************************************************************
    $tmp = '<table>';
    $tmp .= '<tr><td>' . __('uiAppear_Infos3.uxt') . '</td><td>' . __('uiAppear_Infos4.uxt') . '</td></tr>';
    $tmp .= '<tr><td valign="top">' . render_gabarit($char) . '</td><td valign="top">' . render_morph(
            $char
        ) . '</td></tr>';
    $tmp .= '</table>';
    $html .= strtr($tplRow, ['{$name}' => '', '{$value}' => $tmp]);
    //$html .= strtr($tplRow, ['{$name}' => '', '{$value}' => __('uiAppear_Infos3.uxt')]);
    //$html .= strtr($tplRow, ['{$name}' => '', '{$value}' => render_gabarit($char)]);
    //************************************************************************************
    //$html .= strtr($tplRow, ['{$name}' => '', '{$value}' => __('uiAppear_Infos4.uxt')]);
    //$html .= strtr($tplRow, ['{$name}' => '', '{$value}' => render_morph($char)]);

    $html .= $btnSubmit;

    return '<table>' . $html . '</table>';
}

/**
 * @param \Rrs\Character $char
 *
 * @return string
 */
function render_gabarit(\Rrs\Character $char)
{
    $tplRow = '<tr><td>{$name}</td><td>{$value}</td></tr>';
    $gabarit = $char->getGabarit();
    $html = '';
    $html .= '<table>';
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiHeight.uxt'), '{$value}' => html_select('gabarit[0]', range(0, 14, 1), $gabarit[0])]
    );
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiTorso.uxt'), '{$value}' => html_select('gabarit[1]', range(0, 14, 1), $gabarit[1])]
    );
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiArms.uxt'), '{$value}' => html_select('gabarit[2]', range(0, 14, 1), $gabarit[2])]
    );
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiLegs.uxt'), '{$value}' => html_select('gabarit[3]', range(0, 14, 1), $gabarit[3])]
    );
    $html .= strtr(
        $tplRow,
        ['{$name}' => __('uiBreasts.uxt'), '{$value}' => html_select('gabarit[4]', range(0, 14, 1), $gabarit[4])]
    );
    $html .= '</table>';
    return $html;
}

/**
 * @param \Rrs\Character $char
 *
 * @return string
 */
function render_morph(\Rrs\Character $char)
{
    $race = TPeople::toString($char->getRace());
    $tplRow = '<tr><td>{$name}</td><td>{$value}</td></tr>';

    // uiFace1Fy
    $uiMorph = 'uiFace%d' . ucfirst($race) . '.uxt';
    $morph = $char->getMorph();
    $html = '<table>';
    for ($i = 0; $i < 8; $i++) {
        $html .= strtr(
            $tplRow,
            [
                '{$name}' => __(sprintf($uiMorph, $i + 1)),
                '{$value}' => html_select("morph[{$i}]", range(0, 7, 1), $morph[$i])
            ]
        );
    }
    $html .= '</table>';
    return $html;
}


/**
 * @param string     $name
 * @param array      $options
 * @param int|string $selected
 * @param bool       $trans
 * @param bool       $verbose
 *
 * @return string
 */
function html_select($name, array $options, $selected, $trans = false, $verbose = true)
{
    $ret = '<select name="' . $name . '">';
    foreach ($options as $k => $v) {
        if ($v === '-') {
            $txt = '-';
        } else {
            $txt = ($trans ? __($v) : _h($v));
            if (substr($txt, 0, 10) == 'NotFound:(') {
                $txt = _h($v);
            } elseif ($verbose && $txt != $v) {
                $txt = '[' . _h($v) . '] ' . $txt;
            }
        }
        $ret .= '<option value="' . _h($k) . '"' . ($selected == $k ? ' selected="selected"' : '') . '>' .
            $txt . '</option>';
    }
    $ret .= '</select>';
    //$ret = '['.$selected.']'.$ret;
    return $ret;
}

/**
 * FIXME: filter items for race and gender?
 *
 * @param \Rrs\Character $char
 * @param int            $index
 * @param string         $what [all, hair, item]
 *
 * @return mixed
 */
function slot_items(\Rrs\Character $char, $index, $what = 'all')
{
    static $visual = null;

    if ($visual === null) {
        $visual = ryzom_extra_load_vs();
    }

    if ($what !== 'all' && $index == EVisualSlot::HEAD_SLOT) {
        $ret = [];
        $race = strtolower(TPeople::toString($char->getRace()));
        $gender = strtolower(EGender::toString($char->getGender()));
        foreach ($visual[$index] as $index => $sheet) {
            // FIXME: fy_hof_haircolor0.sitem ... color5
            if (is_haircut($sheet)) {
                if ($what == 'hair') {
                    // 'fy_hom_'
                    $prefix = sprintf('%s_ho%s_', $race, $gender);
                    // 'fy_cheveux'
                    $prefix2 = sprintf('%s_cheveux', $race);
                    $pos = stripos($sheet, $prefix);
                    $pos2 = stripos($sheet, $prefix2);
                    if ($pos === 0 || $pos2 === 0) {
                        $ret[$index] = $sheet;
                    }
                }
            } elseif ($what == 'item') {
                $ret[$index] = $sheet;
            }
        }
        //$items = filter_haircut($char, $slot);
        return $ret;
    }

    return $visual[$index];
}

/**
 * @param string $sheet
 *
 * @return bool
 */
function is_haircut($sheet)
{
    return strstr($sheet, '_hair_') || strstr($sheet, '_cheveux');
}

/**
 * @param int $var
 * @param int $min
 * @param int $max
 *
 * @return mixed
 */
function clamp($var, $min, $max)
{
    if ($var < $min) {
        return $min;
    }
    if ($var > $max) {
        return $max;
    }
    return $var;
}

/**
 * @param mixed $var
 * @param mixed $default
 *
 * @return mixed
 */
function is(&$var, $default = null)
{
    return (isset($var) ? $var : $default);
}