Source code for TomeNET monster search

common.inc.php

<?php

//if (ob_get_level()) ob_end_clean();
//ob_start('ob_gzhandler');

$colors_hex = array(
    'd' => '#000000',    // black
    'D' => '#747474',    // dark
    's' => '#9D9D9D',    // slate
    'W' => '#D7D7D7',    // white
    'w' => '#FFF',        // bright white
    'b' => '#0000FF',    // blue
    'g' => '#009D44',    // green
    'r' => '#D70000',    // red
    'u' => '#8D6600',    // umber
    'y' => '#FFFF00',    // yellow
    'B' => '#00FFFF',    // light blue
    'G' => '#00FF00',    // light green
    'R' => '#FF0000',    // pink
    'U' => '#C79D55',    // light umber
    'o' => '#FF8D00',    // orange
    'v' => '#AF00FF',    // violet

    'a' => '#AF00FF',    // acidic, monsters of bad luck (FIXME: animate)
    'I' => '#AF00FF',    // new color for disenchantment / invulnerability (FIXME: animate)
    'f' => '#D70000',    // fire (red) (FIXME: animate)
    'p' => '#00FF00',    // poison (light green) (FIXME: animate)
    'M' => '#FFF',        // manashield (white for now) (FIXME: animate)
    'L' => '#FFFF00',    // lite (candlebearer) (FIXME: animate)
    'c' => '#FFF',        // cold (will o' wisp) (FIXME: animate)
    'e' => '#00FFFF',    // electric (living lightning) (FIXME: animate)
    'Z' => '#D70000',    // ember (FIXME: animate)
    'K' => '#009D44',    // unbreath (FIXME)
    'C' => '#C79D55',    // confusion (FIXME)
    'q' => '#9D9D9D',    // inertia (FIXME)
    'A' => '#747474',    // darkness (FIXME)
    'S' => '#FFFF00',    // sound (FIXME)
    'k' => '#D70000',    // nuke (FIXME)
);

$colors_name = array(
    'd' => 'black',
    'D' => 'dark',
    's' => 'slate',
    'W' => 'white',
    'w' => 'bright white',
    'b' => 'blue',
    'g' => 'green',
    'r' => 'red',
    'u' => 'umber',
    'y' => 'yellow',
    'B' => 'light blue',
    'G' => 'light green',
    'R' => 'light red',
    'U' => 'light umber',
    'o' => 'orange',
    'v' => 'violet',

    'a' => 'acidic',
    'I' => 'invulnerability',
    'f' => 'fiery',
    'p' => 'poisony',
    'M' => 'manashieldy',
    'L' => 'lite',
    'c' => 'cold',
    'e' => 'electric',
    'Z' => 'ember',
    'K' => 'unbreath',
    'C' => 'confusion',
    'q' => 'inertia',
    'A' => 'darkness',
    'S' => 'sound',
    'k' => 'nuke',
);

$monster_types = array(
    'NONEBAND',
    'BASEANGBAND',
    'ZANGBAND',
    'CTHANGBAND',
    'JOKEANGBAND',
    'PERNANGBAND',
    'BLUEBAND',
);

$monster_types_desc = array(
    'None',
    'Base',
    'Zangband',
    'Cthulhu',
    'Joke',
    'Pern',
    'Blue',
);

function get_hex_color($m) {
    return($GLOBALS['colors_hex'][$m['color']]);
}

// Old code
function get_type($m) {
    if (array_search('BASEANGBAND', $m['flags']) !== FALSE) return 'Base';
    elseif (array_search('ZANGBAND', $m['flags']) !== FALSE) return 'Zangband';
    elseif (array_search('CTHANGBAND', $m['flags']) !== FALSE) return 'Cthulhu';
    elseif (array_search('JOKEANGBAND', $m['flags']) !== FALSE) return 'Joke';
    elseif (array_search('PERNANGBAND', $m['flags']) !== FALSE) return 'Pern';
    elseif (array_search('BLUEBAND', $m['flags']) !== FALSE) return 'Blue';
    else return '?';
}

$monster_bg_colors_by_type = array(
    '#FFF',
    '#B7FFB4', // BASEANGBAND
    '#FFFFB4', // ZANGBAND
    '#B4FFF6', // CTHANGBAND
    '#FFE6B4', // JOKEANGBAND
    '#FFB4E0', // PERNANGBAND
    '#B4B9FF', // BLUEBAND
);

// Old code
function get_result_bg_color($m) {
    if (array_search('BASEANGBAND', $m['flags']) !== FALSE) return '#B7FFB4';
    if (array_search('ZANGBAND', $m['flags']) !== FALSE) return '#FFFFB4';
    if (array_search('CTHANGBAND', $m['flags']) !== FALSE) return '#B4FFF6';
    if (array_search('JOKEANGBAND', $m['flags']) !== FALSE) return '#FFE6B4';
    if (array_search('PERNANGBAND', $m['flags']) !== FALSE) return '#FFB4E0';
    if (array_search('BLUEBAND', $m['flags']) !== FALSE) return '#B4B9FF';
    return '#FFF';
}

function is_unique($m) {
    if (array_search('UNIQUE', $m['flags']) !== FALSE) return 'Yes';
    return 'No';
}

function count_hp($m) {
    $d = explode('d', $m['hp']);
    if (array_search('FORCE_MAXHP', $m['flags']) === FALSE) {
        $hp = '~' . ($d[0] * ($d[1] + 1) / 2);
    } else {
        $hp = $d[0] * $d[1];
    }
    return($hp);
}

function count_hp_no_tilde($m) {
    $d = explode('d', $m['hp']);
    if (array_search('FORCE_MAXHP', $m['flags']) === FALSE) {
        $hp = $d[0] * ($d[1] + 1) / 2;
    } else {
        $hp = $d[0] * $d[1];
    }
    return($hp);
}

function parse_gpc($a) {
    // Fix for PHP 8
    return($a);

    if (get_magic_quotes_gpc()) {
        return(stripslashes($a));
    } else {
        return($a);
    }
}

// Old compability code
if (!function_exists('stripos')) {
    function stripos($a, $b) {
        return(strpos(strtolower($a), strtolower($b)));
    }
}

?>

flags.php

<?php
$flags = array('AI_ANNOY', 'AI_SPECIAL', 'ALLOW_RUNNING', 'ANIMAL', 'AQUATIC', 'ASTAR', 'ATTR_ANY', 'ATTR_BASE', 'ATTR_BNW', 'ATTR_CLEAR', 'ATTR_MULTI', 'AURA_COLD', 'AURA_ELEC', 'AURA_FIRE', 'AVOID_PERMAWALLS', 'BASEANGBAND', 'BASH_DOOR', 'BLUEBAND', 'CAN_FLY', 'CAN_SPEAK', 'CAN_SWIM', 'CHAR_CLEAR', 'CHAR_MULTI', 'COLD_BLOOD', 'CTHANGBAND', 'DEATH_ORB', 'DEMON', 'DG_CURSE', 'DISBELIEVE', 'DRAGON', 'DRAGONRIDER', 'DROP_1', 'DROP_1D2', 'DROP_2D2', 'DROP_3D2', 'DROP_4D2', 'DROP_60', 'DROP_90', 'DROP_CHOSEN', 'DROP_CORPSE', 'DROP_GOOD', 'DROP_GREAT', 'DROP_SKELETON', 'DROP_USEFUL', 'DUN_9', 'ELDRITCH_HORROR', 'EMPTY_MIND', 'ESCORT', 'ESCORTS', 'EVIL', 'FEMALE', 'FORCE_DEPTH', 'FORCE_MAXHP', 'FORCE_SLEEP', 'FRIEND', 'FRIENDS', 'GENO_NO_THIN', 'GENO_PERSIST', 'GIANT', 'GOOD', 'HAS_EGG', 'HAS_LITE', 'HURT_LITE', 'HURT_ROCK', 'IMPRESSED', 'IM_ACID', 'IM_COLD', 'IM_ELEC', 'IM_FIRE', 'IM_POIS', 'IM_PSI', 'IM_TELE', 'IM_WATER', 'INVISIBLE', 'JOKEANGBAND', 'KILL_BODY', 'KILL_ITEM', 'KILL_TREES', 'KILL_WALL', 'MALE', 'MIMIC', 'MORTAL', 'MOVE_BODY', 'MULTIPLY', 'NAZGUL', 'NETHER_REALM', 'NEUTRAL', 'NEVER_ACT', 'NEVER_BLOW', 'NEVER_MOVE', 'NONLIVING', 'NO_AUTORET', 'NO_BLOCK', 'NO_CONF', 'NO_CUT', 'NO_DEATH', 'NO_ESCORT', 'NO_FEAR', 'NO_NEST', 'NO_SLEEP', 'NO_STUN', 'NO_TARGET', 'ONLY_DEPTH', 'ONLY_GOLD', 'ONLY_ITEM', 'OOD_10', 'OPEN_DOOR', 'ORC', 'PASS_WALL', 'PERNANGBAND', 'PET', 'PLURAL', 'POSSESSOR', 'POWERFUL', 'PSEUDO_UNIQUE', 'RAND_25', 'RAND_5', 'RAND_50', 'REFLECTING', 'REGENERATE', 'RES_ACID', 'RES_CHAOS', 'RES_COLD', 'RES_DARK', 'RES_DISE', 'RES_ELEC', 'RES_FIRE', 'RES_LITE', 'RES_MANA', 'RES_NETH', 'RES_NEXU', 'RES_PLAS', 'RES_POIS', 'RES_PSI', 'RES_SHARDS', 'RES_SOUND', 'RES_TELE', 'RES_TIME', 'RES_WATE', 'ROAMING', 'SHAPECHANGER', 'SMART', 'SPECIAL_GENE', 'SPIDER', 'STUPID', 'SUSCEP_ACID', 'SUSCEP_COLD', 'SUSCEP_ELEC', 'SUSCEP_FIRE', 'S_LOWEXP', 'TAKE_ITEM', 'TROLL', 'UNDEAD', 'UNIQUE', 'WEIRD_MIND', 'WILD_DESERT', 'WILD_EASY', 'WILD_GRASS', 'WILD_MOUNTAIN', 'WILD_OCEAN', 'WILD_ONLY', 'WILD_SHORE', 'WILD_SWAMP', 'WILD_TOO', 'WILD_TOWN', 'WILD_VOLCANO', 'WILD_WASTE', 'WILD_WOOD', 'WYRM_PROTECT', 'ZANGBAND');
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html><head><title>List of flags | TomeNET monster search</title></head><body bgcolor=#ffffff><h1>List of flags used in r_info.txt</h1><table border=0 cellpadding=0 cellspacing=1 width=100%><tr>
<?php
for ($i = 0, $n = count($flags); $i < $n; ++$i)
{
    echo('<td>' . $flags[$i] . '</td>');
    if (($i + 1) % 3 == 0)
    {
        echo ('</tr><tr>');
    }
}
?>
</tr></table>
<p><a href="javascript: history.go(-1)">Back</a></p>
</body></html>

index.php

<?php

$r_info_file = 'r_info.txt.gz';

require('common.inc.php');

// Set the timezone for date()
date_default_timezone_set('UTC');

?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
<title>TomeNET monster search</title>
<link rel="stylesheet" href="monsters.css" type="text/css">
</head>
<body>
<h1>TomeNET monster search</h1>
<p><form action="<?php echo(htmlspecialchars(basename(__FILE__))); ?>" name="s" method="GET">

<?php
$get_values = array(
    'name', 'char', 'color', 'level_min', 'level_max', 'rarity_min', 'rarity_max',
    'hp_min', 'hp_max', 'spell_chance_min', 'spell_chance_max', 'spells', 'flags',
    'description'
);
$htmlvalues = array();

// Try to load any previously submitted values
foreach ($get_values as $name) {
    if (isset($_GET[$name])) {
        $htmlvalues[$name] = htmlspecialchars(parse_gpc($_GET[$name]));
    } else {
        $htmlvalues[$name] = '';
    }
}

echo('<b>Name:</b> <input type="text" name="name" id="monster_name" autofocus value="' . $htmlvalues['name'] . '">
<b>Character:</b> <input type="text" name="char" size="2" maxlength="1" value="' . $htmlvalues['char'] . '">
<b>Color:</b> <input type="text" name="color" size="2" maxlength="1" value="' . $htmlvalues['color'] . '"><br><br>');

// Default ticks for the checkboxes
if (!isset($_GET['type']) || !is_array($_GET['type'])) {
    $_GET['type'] = array('base', 'zang', 'cthulhu', 'pern', 'blue', 'none');
}

$type_base = in_array('base', $_GET['type']);
$type_zang = in_array('zang', $_GET['type']);
$type_cthulhu = in_array('cthulhu', $_GET['type']);
$type_pern = in_array('pern', $_GET['type']);
$type_blue = in_array('blue', $_GET['type']);
$type_joke = in_array('joke', $_GET['type']);
$type_none = in_array('none', $_GET['type']);

echo("<b>Monster type:</b>\n");
if ($type_base) echo('<input type="checkbox" name="type[]" id="base" value="base" CHECKED>');
else echo('<input type="checkbox" name="type[]" id="base" value="base">');
echo('<label for="base"><span style="background-color: #B7FFB4">Base</span></label>');
if ($type_zang) echo('<input type=checkbox name="type[]" id="zang" value="zang" CHECKED>');
else echo('<input type=checkbox name="type[]" id="zang" value="zang">');
echo('<label for="zang"><span style="background-color: #FFFFB4">Zangband</span></label>');
if ($type_cthulhu) echo('<input type=checkbox name="type[]" id="cthulhu" value="cthulhu" CHECKED>');
else echo('<input type=checkbox name="type[]" id="cthulhu" value="cthulhu">');
echo('<label for="cthulhu"><span style="background-color: #B4FFF6">Cthulhu</span></label>');
if ($type_pern) echo('<input type=checkbox name="type[]" id="pern" value="pern" CHECKED>');
else echo('<input type=checkbox name="type[]" id="pern" value="pern">');
echo('<label for="pern"><span style="background-color: #FFB4E0">Pernband</span></label>');
if ($type_blue) echo('<input type=checkbox name="type[]" id="blue" value="blue" CHECKED>');
else echo('<input type=checkbox name="type[]" id="blue" value="blue">');
echo('<label for="blue"><span style="background-color: #B4B9FF">Blueband</span></label>');
if ($type_joke) echo('<input type=checkbox name="type[]" id="joke" value="joke" CHECKED>');
else echo('<input type=checkbox name="type[]" id="joke" value="joke">');
echo('<label for="joke"><span style="background-color: #FFE6B4">Joke</span></label>');
if ($type_none) echo('<input type=checkbox name="type[]" id="none" value="none" CHECKED>');
else echo('<input type=checkbox name="type[]" id="none" value="none">');
echo('<label for="none"><span style="background-color: #FFE6B4">None</span></label><br><br>');

echo('<table border="0" cellpadding="0" cellspacing="2">
<col>
<col width="30">
<col>
<col width="30">
<col>
<tr>
    <td><input type="text" name="level_min" size="5" maxlength="3" value="' . $htmlvalues['level_min'] . '"></td>
    <td align="center">&#8804;</td>
    <td align="center"><b>Level</b></td>
    <td align="center">&#8804;</td><td><input type="text" name="level_max" size="5" maxlength="3" value="' . $htmlvalues['level_max'] . '"></td>
</tr>
<tr>
    <td><input type="text" name="rarity_min" size="5" maxlength="3" value="' . $htmlvalues['rarity_min'] . '"></td>
    <td align="center">&#8804;</td>
    <td align="center"><b>Rarity</b></td>
    <td align="center">&#8804;</td><td><input type="text" name="rarity_max" size="5" maxlength="3" value="' . $htmlvalues['rarity_max'] . '"></td>
</tr>
<tr>
    <td><input type="text" name="hp_min" size="5" maxlength="5" value="' . $htmlvalues['hp_min'] . '"></td>
    <td align="center">&#8804;</td>
    <td align="center"><b>HP</b></td>
    <td align="center">&#8804;</td>
    <td><input type="text" name="hp_max" size="5" maxlength="5" value="' . $htmlvalues['hp_max'] . '"></td>
</tr>
<tr>
    <td><input type="text" name="spell_chance_min" size="5" maxlength="2" value="' . $htmlvalues['spell_chance_min'] . '"></td>
    <td align="center">&#8804;</td>
    <td align="center"><b>1_IN_x</b></td>
    <td align="center">&#8804;</td>
    <td><input type="text" name="spell_chance_max" size="5" maxlength="2" value="' . $htmlvalues['spell_chance_max'] . '"><br></td>
</tr>
</table>

<table border="0" cellpadding="0" cellspacing="2">
<col width="100">
<col>
<tr>
    <td><b>Spells:</b></td>
    <td><input type="text" name="spells" value="' . $htmlvalues['spells'] . '" size="40"></td>
</tr>
<tr>
    <td>&nbsp;</td>
    <td><small>(e.g. BR_NETH (breaths nether) or !S_HI_DRAGON (doesn\'t summon high dragons)) (<a href="spells.php">list of spells</a>)</small></td>
</tr>
<tr>
    <td><b>Flags:</b></td>
    <td><input type="text" name="flags" value="' . $htmlvalues['flags'] . '" size="40"></td>
</tr>
<tr>
    <td>&nbsp;</td>
    <td><small>(e.g. ATTR_MULTI (multi-colored) or !UNIQUE (not unique)) (<a href="flags.php">list of flags</a>)</small></td>
</tr>
<tr>
    <td><b>Description:</b></td>
    <td><input type="text" name="description" value="' . $htmlvalues['description'] . '" size="40"></td>
</tr>
</table>');

?>

<input type="submit" name="submit" value="Search">
<input type="reset" name="reset" value="Reset">
</form>

<p><a href="../index.php">Back to mikaelh's TomeNET site</a></p>

<?php
/* Flush before loading the monsters */
flush();

/* Load the monsters (takes some time ...) */
require('parser.php');

function filter_monsters_by_name_contains_string(&$monsters, $value) {
    $parts = explode(' ', $value);
    foreach ($monsters as &$monster) {
        $name = $monster['name'];
        foreach ($parts as $part) {
            if (stripos($name, $part) === FALSE) {
                $monster['matches'] = FALSE;
                continue(2);
            }
        }
    }
}

function filter_monsters_by_string_match(&$monsters, $key, $value) {
    foreach ($monsters as &$monster) {
        if ($monster[$key] != $value) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_type(&$monsters, $allowed_types_bitmask) {
    foreach ($monsters as &$monster) {
        if (!($monster['type_bit'] & $allowed_types_bitmask)) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_greater_or_equal(&$monsters, $key, $value) {
    foreach ($monsters as &$monster) {
        if ($monster[$key] < $value) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_less_or_equal(&$monsters, $key, $value) {
    foreach ($monsters as &$monster) {
        if ($monster[$key] > $value) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_hp_greater_or_equal(&$monsters, $value) {
    foreach ($monsters as &$monster) {
        if (count_hp_no_tilde($monster) < $value) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_hp_less_or_equal(&$monsters, $value) {
    foreach ($monsters as &$monster) {
        if (count_hp_no_tilde($monster) > $value) {
            $monster['matches'] = FALSE;
        }
    }
}

function filter_monsters_by_spells(&$monsters, $spells) {
    foreach ($monsters as &$monster) {
        foreach ($spells as $spell) {
            if ($spell[0] == '!') {
                if (in_array(substr($spell, 1), $monster['spells'])) {
                    $monster['matches'] = FALSE;
                    continue(2);
                }
            } else {
                if (!in_array($spell, $monster['spells'])) {
                    $monster['matches'] = FALSE;
                    continue(2);
                }
            }
        }
    }
}

function filter_monsters_by_flags(&$monsters, $flags) {
    foreach ($monsters as &$monster) {
        foreach ($flags as $flag) {
            if ($flag[0] == '!') {
                if (in_array(substr($flag, 1), $monster['flags'])) {
                    $monster['matches'] = FALSE;
                    continue(2);
                }
            } else {
                if (!in_array($flag, $monster['flags'])) {
                    $monster['matches'] = FALSE;
                    continue(2);
                }
            }
        }
    }
}

function filter_monsters_by_desc_contains_string(&$monsters, $value) {
    $parts = explode(' ', $value);
    foreach ($monsters as &$monster) {
        $desc = $monster['description'];
        foreach ($parts as $part) {
            if (stripos($desc, $part) === FALSE) {
                $monster['matches'] = FALSE;
                continue(2);
            }
        }
    }
}

function get_search_results($monsters) {
    global $monster_types_bits;
    $start = microtime(TRUE);

    // Determine which checkboxes have been ticked
    if (isset($_GET['type'])) {
        $GET_type = $_GET['type'];
    } else {
        $GET_type = array();
    }

    if (isset($_GET['name']) && strlen(trim($_GET['name'])) > 0) {
        filter_monsters_by_name_contains_string($monsters, trim(parse_gpc($_GET['name'])));
    }
    if (isset($_GET['char']) && strlen(trim($_GET['char'])) > 0) {
        filter_monsters_by_string_match($monsters, 'symbol', substr(trim(parse_gpc($_GET['char'])), 0, 1));
    }
    if (isset($_GET['color']) && strlen(trim($_GET['color'])) > 0) {
        filter_monsters_by_string_match($monsters, 'color', substr(trim(parse_gpc($_GET['color'])), 0, 1));
    }
    if (count($GET_type) > 0) {
        $allowed_types_bitmask = 0;
        if (in_array('base', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['BASEANGBAND'];
        if (in_array('zang', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['ZANGBAND'];
        if (in_array('cthulhu', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['CTHANGBAND'];
        if (in_array('pern', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['PERNANGBAND'];
        if (in_array('blue', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['BLUEBAND'];
        if (in_array('joke', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['JOKEANGBAND'];
        if (in_array('none', $GET_type)) $allowed_types_bitmask |= $monster_types_bits['NONEBAND'];
        filter_monsters_by_type($monsters, $allowed_types_bitmask);
    }
    if (isset($_GET['level_min']) && strlen(trim($_GET['level_min'])) > 0) {
        $min_level = (int) $_GET['level_min'];
        filter_monsters_by_greater_or_equal($monsters, 'depth', $min_level);
    }
    if (isset($_GET['level_max']) && strlen(trim($_GET['level_max'])) > 0) {
        $max_level = (int) $_GET['level_max'];
        filter_monsters_by_less_or_equal($monsters, 'depth', $max_level);
    }
    if (isset($_GET['rarity_min']) && strlen(trim($_GET['rarity_min'])) > 0) {
        $min_rarity = (int) $_GET['rarity_min'];
        filter_monsters_by_greater_or_equal($monsters, 'rarity', $min_rarity);
    }
    if (isset($_GET['rarity_max']) && strlen(trim($_GET['rarity_max'])) > 0) {
        $max_rarity = (int) $_GET['rarity_max'];
        filter_monsters_by_less_or_equal($monsters, 'rarity', $max_rarity);
    }
    if (isset($_GET['hp_min']) && strlen(trim($_GET['hp_min'])) > 0) {
        $min_hp = (int) $_GET['hp_min'];
        filter_monsters_by_hp_greater_or_equal($monsters, $min_hp);
    }
    if (isset($_GET['hp_max']) && strlen(trim($_GET['hp_max'])) > 0) {
        $max_hp = (int) $_GET['hp_max'];
        filter_monsters_by_hp_less_or_equal($monsters, $max_hp);
    }
    if (isset($_GET['spell_chance_min']) && strlen(trim($_GET['spell_chance_min'])) > 0) {
        $min_spell_chance = (int) $_GET['spell_chance_min'];
        filter_monsters_by_greater_or_equal($monsters, 'spell_chance', $min_spell_chance);
    }
    if (isset($_GET['spell_chance_max']) && strlen(trim($_GET['spell_chance_max'])) > 0) {
        $max_spell_chance = (int) $_GET['spell_chance_max'];
        filter_monsters_by_less_or_equal($monsters, 'spell_chance', $max_spell_chance);
    }
    if (isset($_GET['spells']) && strlen(trim($_GET['spells'])) > 0) {
        $spells = explode(' ', trim(parse_gpc($_GET['spells'])));
        $spells_uppercased = array();
        foreach ($spells as $spell) {
            $spells_uppercased[] = strtoupper(trim($spell));
        }
        filter_monsters_by_spells($monsters, $spells_uppercased);
    }
    if (isset($_GET['flags']) && strlen(trim($_GET['flags'])) > 0) {
        $flags = explode(' ', trim(parse_gpc($_GET['flags'])));
        $flags_uppercased = array();
        foreach ($flags as $flag) {
            $flags_uppercased[] = strtoupper(trim($flag));
        }
        filter_monsters_by_flags($monsters, $flags_uppercased);
    }
    if (isset($_GET['description']) && strlen(trim($_GET['description'])) > 0) {
        filter_monsters_by_desc_contains_string($monsters, trim($_GET['description']));
    }

    $results = array();
    foreach ($monsters as $monster) {
        if ($monster['matches']) {
            $results[] = $monster;
        }
    }

    $end = microtime(TRUE);
    //echo(($end - $start) * 1000 . " milliseconds");

    return $results;
}

function show_search_results($results) {
    $monster_bg_colors_by_type = $GLOBALS['monster_bg_colors_by_type'];
    $start = microtime(TRUE);
    $num_results = count($results);
    echo('<p><b>Number of results: ' . $num_results . "</b></p>\n");

    // Split the list into 3 columns
    $n_rows = ceil($num_results / 3);
    $num_rows = array($n_rows, $n_rows, $n_rows);

    if ($num_results % 3 == 1) {
        // Columns 2 and 3 will be shorter
        $num_rows[1]--;
        $num_rows[2]--;
    } elseif ($num_results % 3 == 2) {
        // Column 3 will be shorter
        $num_rows[2]--;
    }

    $col_1 = array();
    $col_2 = array();
    $col_3 = array();

    for ($i = 0, $n = $num_rows[0]; $i < $n; $i++) {
        $col_1[] = $results[$i];
    }
    for ($n = $num_rows[0] + $num_rows[1]; $i < $n; $i++) {
        $col_2[] = $results[$i];
    }
    for ($n = $num_rows[0] + $num_rows[1] + $num_rows[2]; $i < $n; $i++) {
        $col_3[] = $results[$i];
    }

    $cols = array($col_1, $col_2, $col_3);

    $output = "<p><table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n" .
        "<col width=\"20\"><col width=\"300\"><col width=\"40\">\n" .
        "<col width=\"20\"><col width=\"300\"><col width=\"40\">\n" .
        "<col width=\"20\"><col width=\"300\"><col width=\"40\">\n" .
        "<tr><th>&nbsp;</th><th><b>Name</b></th><th><b>Lvl</b></th><th>&nbsp;</th><th><b>Name</b></th><th><b>Lvl</b></th><th>&nbsp;</th><th><b>Name</b></th><th><b>Lvl</b></th></tr>\n";
    $n = count($results);
    if ($n) $output .= "<tr>\n";
    for ($i = 0; $i < $n; ++$i) {
        $result = $cols[$i % 3][floor($i / 3)];

        $output .= '<td class="symbol" style="color: ' . get_hex_color($result) . '">'  . $result['symbol'] . '</td><td style="background-color: ' .
            $monster_bg_colors_by_type[$result['type']] . '"><a href="monster.php?' . $result['number'] . '">' . htmlspecialchars($result['name']) . '</a></td><td>' . $result['depth'] . "</td>\n";

        if (($i + 1) % 3 == 0) {
            $output .= "</tr><tr>\n";
        }
    }
    if ($n) $output .= "</tr>\n";
    $output .= "</table></p>\n";
    echo($output);
    $end = microtime(TRUE);
    //echo(($end - $start) * 1000 . " milliseconds");
}

$results = get_search_results($monsters);
show_search_results($results);
?>

<p style="font-size: 0.75em">TomeNET monster search v. 0.1.8<br>
r_info.txt was last updated on <?php echo(date("d.m.Y", filemtime($r_info_file))); ?>.<br>
<a href="source.php">Source code</a>

<script>
document.getElementById('monster_name').focus();
</script>

</body></html>

makeflags.php

<?php
require('parser.php');
$flags = array();

foreach ($monsters as $monster)
{
    foreach ($monster['flags'] as $flag)
    {
        if (!in_array($flag, $flags))
        {
            $flags[] = $flag;
        }
    }
}

sort($flags);

echo("\$flags = array('" . implode("', '", $flags) . "');\n");
?>

makespells.php

<?php
require('parser.php');
$spells = array();

foreach ($monsters as $monster)
{
    foreach ($monster['spells'] as $spell)
    {
        if (!in_array($spell, $spells))
        {
            $spells[] = $spell;
        }
    }
}

sort($spells);

echo("\$spells = array('" . implode("', '", $spells) . "');\n");
?>

monster.php

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
<link rel="stylesheet" href="monsters.css" type="text/css">
<?php

/* Flush the link to stylesheet before loading monsters */
flush();

require('common.inc.php');
require('parser.php');

function get_depth($l) {
    return(($l * 50) . 'ft');
}

function symbol_info($m) {
    if (array_search('SHAPECHANGER', $m['flags']) !== FALSE) return 'shapechanger';
    if (array_search('CHAR_CLEAR', $m['flags']) !== FALSE) return 'absorbs symbol';
    return 'Symbol ' . $m['symbol'];
}

function color_info($m) {
    if (array_search('ATTR_MULTI', $m['flags']) !== FALSE && array_search('ATTR_ANY', $m['flags']) !== FALSE) return 'truly multi-colored';
    if (array_search('ATTR_MULTI', $m['flags']) !== FALSE) return 'multi-colored ' . $m['color'];
    if (array_search('ATTR_CLEAR', $m['flags']) !== FALSE) return 'absorbs color';
    return 'Color ' . $m['color'];
}

function show_info_html($m) {
    $monster_types_desc = $GLOBALS['monster_types_desc'];
    echo("<div class=\"monster\">\n");
    echo("<h1>" . $m['name'] . "</h1>\n");

    echo("<span class=\"header\">Id:</span> " . $m['number']);
    echo(" <span class=\"header\">Name:</span> " . $m['name'] . " <span class=\"symbol\" style=\"color: " . get_hex_color($m) . "\">&nbsp;"  . $m['symbol'] . "&nbsp;</span> (" . symbol_info($m) . ", " . color_info($m) .")<br>\n");

    // Some monsters lack a description
    echo("<span class=\"header\">Description:</span> " . $m['description'] . "<br>\n");

    echo("<span class=\"header\">Type:</span> " . $monster_types_desc[$m['type']]);
    echo(" <span class=\"header\">Unique:</span> " . is_unique($m) . "<br>\n");

    echo("<span class=\"header\">HP:</span> " . count_hp($m) . " (" . $m['hp'] . ")");
    echo(" <span class=\"header\">AC:</span> " . $m['ac']);
    echo(" <span class=\"header\">Experience:</span> " . $m['exp']);
    echo(" <span class=\"header\">Speed:</span> " . $m['speed'] . "<br>\n");

    echo("<span class=\"header\">Depth:</span> " . $m['depth'] . " (" . get_depth($m['depth']) . ")");
    echo(" <span class=\"header\">Rarity:</span> " . $m['rarity']);
    echo(" <span class=\"header\">Vision:</span> " . $m['vision']);
    echo(" <span class=\"header\">Alertness:</span> " . $m['alertness'] . "<br>\n");

    echo("<span class=\"header\">Body:</span><br>");
    echo("<div class=\"sub\">\n");
    echo("<span class=\"header\">Weight:</span> " . $m['weight']);
    echo(" <span class=\"header\">Weapons:</span> " . $m['weapons']);
    echo(" <span class=\"header\">Torso:</span> " . $m['torso']);
    echo(" <span class=\"header\">Arm:</span> " . $m['arms']);
    echo(" <span class=\"header\">Finger:</span> " . $m['finger']);
    echo(" <span class=\"header\">Head:</span> " . $m['head']);
    echo(" <span class=\"header\">Leg:</span> " . $m['leg'] . "<br>\n");
    echo("</div>\n");

    echo("<span class=\"header\">Objects:</span><br>\n");
    echo("<div class=\"sub\">\n");
    echo("<span class=\"header\">Treasure:</span> " . $m['treasure']);
    echo(" <span class=\"header\">Combat:</span> "  . $m['combat']);
    echo(" <span class=\"header\">Magic:</span> " . $m['magic']);
    echo(" <span class=\"header\">Tool:</span> " . $m['tool'] . "<br>\n");
    echo("</div>\n");

    echo("<span class=\"header\">Attack methods:</span><br>\n");
    foreach ($m['attacks'] as $a) {
        echo("\t<div style=\"padding-left: 1em;\">");
        echo($a['method'] . ": ");
        echo($a['effect']);
        if (!empty($a['damage'])) echo(" (" . $a['damage'] . ")");
        echo("</div>\n");
    }

    if ($m['spell_chance'] != 0) {
        echo("<span class=\"header\">Spell chance:</span> 1 spell every " . $m['spell_chance'] . " turns (1_IN_" . $m['spell_chance'] . ")<br>\n");
    }
    echo("<span class=\"header\">Spells (and breaths, arrows or missiles): (" . count($m['spells']) . ")</span><br>\n");
    echo("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<tr>\n");
    $i = 0;
    foreach ($m['spells'] as $s) {
        echo("\t\t<td>" . $s . "</td>\n");
        if (!(++$i % 3)) echo("\t</tr>\n\t<tr>\n");
    }
    while ($i % 3) {
        echo("\t\t<td>&nbsp;</td>\n");
        ++$i;
    }
    echo("\t</tr>\n</table>\n");

    echo("<span class=\"header\">Flags: (" . count($m['flags']) . ")</span><br>\n");
    echo("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<col width=\"200\">\n");
    echo("\t<tr>\n");
    $i = 0;
    foreach ($m['flags'] as $f) {
        echo("\t\t<td>" . $f . "</td>\n");
        if (!(++$i % 3)) echo("\t</tr>\n\t<tr>\n");
    }
    while ($i % 3) {
        echo("\t\t<td>&nbsp;</td>\n");
        ++$i;
    }
    echo("\t</tr>\n</table>\n");
    echo("</div>\n");
}

if (array_key_exists($_SERVER['QUERY_STRING'], $monsters)) {
    $monster = $monsters[$_SERVER['QUERY_STRING']];
?>
<title><?php echo(htmlentities($monster['name'])) ?> | TomeNET monster search</title>
</head>
<body>
<?php show_info_html($monster); ?>
<a href="javascript: history.go(-1)">Back</a>
</body></html>
<?php
} else {
    header('Location: http://muuttuja.org/tomenet/monsters/');
}
?>

parser.php

<?php

/* TomeNET r_info.txt parser by mikaelh */

/* Some settings */
if (!isset($server_settings)) $server_settings = array('RPG_SERVER', 'ENABLE_MAIA');
if (!isset($use_compression)) $use_compression = TRUE;
if (!isset($use_serialized)) $use_serialized = FALSE;

if (!isset($r_info_file)) {
    $r_info_file = 'r_info.txt';
    if ($use_compression) $r_info_file .= '.gz';
}

$found_serialized = FALSE;

if ($use_serialized) {
    if (substr($r_info_file, -3) == '.gz') {
        $serialized_file = basename($r_info_file, '.gz') . '.serialized.gz';
    } else {
        $serialized_file = $r_info_file . '.serialized';
    }

    if (file_exists($serialized_file)) {
        $found_serialized = TRUE;
        $r_info_file = $serialized_file;
    }
}

if (file_exists($r_info_file)) {
    $r_info_contents = "";
    if ($use_compression) {
        $fp = gzopen($r_info_file, 'rb');

        if ($fp === FALSE) {
            echo('ERROR in ' . __FILE__ . ' on line ' . __LINE__ . ': File ' . $r_info_file . "could not be opened\n");
            die();
        }

        while (!gzeof($fp)) {
            $r_info_contents .= gzread($fp, 8192);
        }

        gzclose($fp);
    } else {
        $fp = fopen($r_info_file, 'rb');

        if ($fp === FALSE) {
            echo('ERROR in ' . __FILE__ . ' on line ' . __LINE__ . ': File ' . $r_info_file . "could not be opened\n");
            die();
        }

        while (!feof($fp)) {
            $r_info_contents .= fread($fp, 8192);
        }

        fclose($fp);
    }
} else {
    echo('ERROR in ' . __FILE__ . ' on line ' . __LINE__ . ': File ' . $r_info_file . " doesn't exists!\n");
    die();
}

$monster_types_enum = array(
    'NONEBAND' => 0,
    'BASEANGBAND' => 1,
    'ZANGBAND' => 2,
    'CTHANGBAND' => 3,
    'JOKEANGBAND' => 4,
    'PERNANGBAND' => 5,
    'BLUEBAND' => 6,
);

$monster_types_bits = array(
    'BASEANGBAND' => 0x01,
    'ZANGBAND' => 0x02,
    'CTHANGBAND' => 0x04,
    'JOKEANGBAND' => 0x08,
    'PERNANGBAND' => 0x10,
    'BLUEBAND' => 0x20,
    'NONEBAND' => 0x40,
);

function parse_r_info($r_info_contents, $server_settings) {
    global $info_version;
    $monster_types_enum = $GLOBALS['monster_types_enum'];
    $monster_types_bits = $GLOBALS['monster_types_bits'];
    $r_info = explode("\n", $r_info_contents);

    $monsters = array();
    $cur_monster = -1;
    $cur_attack = 0;

    // Defaults for monster entries
    // Description is empty
    // No body parts
    // 20% chance for object drop rates
    // Empty arrays for attacks, flags and spells
    $monster_defaults = array(
        'number' => 0,
        'name' => NULL,

        'symbol' => NULL,
        'color' => NULL,

        'speed' => 0,
        'hp' => NULL,
        'vision' => 0,
        'ac' => 0,
        'alertness' => 0,

        'depth' => 0,
        'rarity' => 0,
        'weight' => 0,
        'exp' => 0,

        'treasure' => 20,
        'combat' => 20,
        'magic' => 20,
        'tool' => 20,

        'weapons' => 0,
        'torso' => 0,
        'arms' => 0,
        'finger' => 0,
        'head' => 0,
        'leg' => 0,

        'description' => '',

        'attacks' => array(),
        'flags' => array(),
        'spells' => array(),

        'spell_chance' => 0,
        'type' => 0,
        'type_bit' => $monster_types_bits['NONEBAND'],

        'matches' => TRUE,
    );
    $monster = $monster_defaults;

    foreach ($r_info as $row) {
        $row = trim($row);

        if (empty($row) || $row[0] == '#') {
            continue;
        }

        while ($row[0] == '$') {
            /* Process a server setting condition */
            $next_dol = strpos($row, '$', 1);
            $next_neg = strpos($row, '!', 1);

            if (($next_neg === FALSE || $next_neg > $next_dol) && $next_dol !== FALSE) {
                if ($next_dol === FALSE) {
                    echo('ERROR in ' . __FILE__ . ' on line ' . __LINE__ . ": Invalid server setting condition\n");
                    echo($row . "\n");
                    die();
                }

                $negation = FALSE;
                $server_cond = substr($row, 1, $next_dol - 1);
                $row = substr($row, $next_dol + 1);
            } else {
                $negation = TRUE;
                $server_cond = substr($row, 1, $next_neg - 1);
                $row = substr($row, $next_neg + 1);
            }

            if (!in_array($server_cond, $server_settings) ^ $negation) {
                continue;
            }
        }

        if ($row[1] == ':') {
            $row_type = $row[0];
            $row_data = explode(':', $row);

            switch ($row_type) {
                case 'V':
                    $info_version = substr($row, 2);
                    break;

                case 'N':
                    // store previous monster
                    if ($cur_monster >= 0) {
                        $monsters[$cur_monster] = $monster;
                    }
                    $cur_monster = (int) $row_data[1];
                    $cur_attack = 0;

                    $monster = $monster_defaults;

                    $monster['number'] = (int) $row_data[1];
                    $monster['name'] = $row_data[2];
                    break;

                case 'G':
                    $monster['symbol'] = $row_data[1];
                    $monster['color'] = $row_data[2];
                    break;

                case 'I':
                    $monster['speed'] = (int) $row_data[1];
                    $monster['hp'] = $row_data[2];
                    $monster['vision'] = (int) $row_data[3];
                    $monster['ac'] = (int) $row_data[4];
                    $monster['alertness'] = (int) $row_data[5];
                    break;

                case 'W':
                    $monster['depth'] = (int) $row_data[1];
                    $monster['rarity'] = (int) $row_data[2];
                    $monster['weight'] = (int) $row_data[3];
                    $monster['exp'] = (int) $row_data[4];
                    break;

                case 'E':
                    $monster['weapons'] = (int) $row_data[1];
                    $monster['torso'] = (int) $row_data[2];
                    $monster['arms'] = (int) $row_data[3];
                    $monster['finger'] = (int) $row_data[4];
                    $monster['head'] = (int) $row_data[5];
                    $monster['leg'] = (int) $row_data[6];
                    break;

                case 'O':
                    $monster['treasure'] = (int) $row_data[1];
                    $monster['combat'] = (int) $row_data[2];
                    $monster['magic'] = (int) $row_data[3];
                    $monster['tool'] = (int) $row_data[4];
                    break;

                case 'B':
                    $monster['attacks'][$cur_attack++] = array('method' => $row_data[1], 'effect' => $row_data[2], 'damage' => isset($row_data[3]) ? $row_data[3] : NULL);
                    break;

                case 'F':
                    $flags = explode('|', $row_data[1]);
                    foreach ($flags as $flag)
                    {
                        $flag = trim($flag);
                        if (empty($flag)) continue;
                        $monster['flags'][] = $flag;
                        if (isset($monster_types_enum[$flag])) {
                            $monster['type'] = $monster_types_enum[$flag];
                            $monster['type_bit'] = $monster_types_bits[$flag];
                        }
                    }
                    break;

                case 'S':
                    $spells = explode('|', $row_data[1]);
                    foreach ($spells as $spell)
                    {
                        $spell = trim($spell);
                        if (empty($spell)) continue;
                        if (strpos($spell, '1_IN_') !== FALSE)
                        {
                            $monster['spell_chance'] = (int) substr($spell, 5);
                            continue;
                        }
                        $monster['spells'][] = $spell;
                    }
                    break;

                case 'D':
                    if ($monster['description'] != '')
                    {
                        $monster['description'] .= "\n" . substr($row, 2);
                    } else {
                        $monster['description'] = substr($row, 2);
                    }
                    break;

                default:
                    echo("WARNING: Unknown row type in r_info.txt: $row_type\n");
            }
        }
    }

    // store last monster
    if ($cur_monster >= 0) {
        $monsters[$cur_monster] = $monster;
    }

    // HACK - Replace NULL value for "Player"
    $monsters[0]['hp'] = '1d1';

    return $monsters;
}

if ($use_serialized && $found_serialized) {
    $monsters = unserialize($r_info_contents);
} else {
    $monsters = parse_r_info($r_info_contents, $server_settings);
}

/* Don't pollute the enviroment */
unset($r_info_contents);

?>

r_info_sort.php

<?php
$use_compression = FALSE;

require('parser.php');

function monster_level_sort($a, $b) {
    if (array_key_exists('depth', $a) < array_key_exists('depth', $b)) {
        return -1;
    }
    elseif (array_key_exists('depth', $a) > array_key_exists('depth', $b)) {
        return 1;
    }
    elseif ($a['depth'] < $b['depth']) {
        return -1;
    }
    elseif ($a['depth'] > $b['depth']) {
        return 1;
    }
    elseif ($a['number'] < $b['number']) {
        return -1;
    }
    elseif ($a['number'] > $b['number']) {
        return 1;
    }
    else {
        return 0;
    }
}

usort($monsters, 'monster_level_sort');

$fp = fopen('r_info_sorted.txt', 'w');
fwrite($fp, 'V:' . $info_version . "\n");

foreach ($monsters as $m) {
    fwrite($fp, 'N:' . $m['number'] . ':' . $m['name'] . "\n");
    fwrite($fp, 'G:' . $m['symbol'] . ':' . $m['color'] . "\n");
    if (isset($m['speed'])) fwrite($fp, 'I:' . $m['speed'] . ':' . $m['hp'] . ':' . $m['vision'] . ':' . $m['ac'] . ':' . $m['alertness'] . "\n");
    fwrite($fp, 'W:' . $m['depth'] . ':' . $m['rarity'] . ':' . $m['weight'] . ':' . $m['exp'] . "\n");
    if (isset($m['weapons'])) fwrite($fp, 'E:' . $m['weapons'] . ':' . $m['torso'] . ':' .  $m['arms'] . ':' . $m['finger'] . ':' . $m['head'] . ':' . $m['leg'] . "\n");
    if (isset($m['treasure'])) fwrite($fp, 'O:' . $m['treasure'] . ':' . $m['combat'] . ':' . $m['magic'] . ':' . $m['tool'] . "\n");
    foreach ($m['attacks'] as $a) {
        if (!empty($a['damage'])) {
            fwrite($fp, trim('B:' . $a['method'] . ':' . $a['effect'] . ':' . $a['damage']) . "\n");
        } else {
            fwrite($fp, trim('B:' . $a['method'] . ':' . $a['effect']) . "\n");
        }
    }
    if (count($m['flags']) > 0) fwrite($fp, 'F:' . implode('|', $m['flags']) . "\n");
    if (!empty($m['spell_chance'])) fwrite($fp, 'S:1_IN_' . $m['spell_chance'] . "\n");
    if (count($m['spells']) > 0) fwrite($fp, 'S:' . implode('|', $m['spells']) . "\n");
    if (!empty($m['description'])) {
        foreach (explode("\n", $m['description']) as $d) {
            fwrite($fp, 'D:' . $d . "\n");
        }
    }
}
?>

spells.php

<?php
$spells = array('ARROW_1', 'ARROW_2', 'ARROW_3', 'ARROW_4', 'BA_ACID', 'BA_CHAO', 'BA_COLD', 'BA_DARK', 'BA_ELEC', 'BA_FIRE', 'BA_MANA', 'BA_NETH', 'BA_NUKE', 'BA_POIS', 'BA_WATE', 'BLIND', 'BLINK', 'BOULDER', 'BO_ACID', 'BO_COLD', 'BO_DISE', 'BO_ELEC', 'BO_FIRE', 'BO_ICEE', 'BO_MANA', 'BO_NETH', 'BO_PLAS', 'BO_POIS', 'BO_WATE', 'BRAIN_SMASH', 'BR_ACID', 'BR_CHAO', 'BR_COLD', 'BR_CONF', 'BR_DARK', 'BR_DISE', 'BR_DISI', 'BR_ELEC', 'BR_FIRE', 'BR_GRAV', 'BR_INER', 'BR_LITE', 'BR_MANA', 'BR_NETH', 'BR_NEXU', 'BR_NUKE', 'BR_PLAS', 'BR_POIS', 'BR_SHAR', 'BR_SOUN', 'BR_TIME', 'BR_WALL', 'CONF', 'CURSE', 'DARKNESS', 'DRAIN_MANA', 'FORGET', 'HASTE', 'HEAL', 'HOLD', 'MIND_BLAST', 'MISSILE', 'MOAN', 'RAISE_DEAD', 'ROCKET', 'SCARE', 'SHRIEK', 'SLOW', 'S_ANGEL', 'S_ANIMAL', 'S_ANIMALS', 'S_ANT', 'S_BUG', 'S_DEMON', 'S_DRAGON', 'S_DRAGONRIDER', 'S_HI_DEMON', 'S_HI_DRAGON', 'S_HI_MONSTERS', 'S_HI_UNDEAD', 'S_HI_UNIQUE', 'S_HOUND', 'S_HYDRA', 'S_KIN', 'S_MONSTER', 'S_MONSTERS', 'S_NAZGUL', 'S_RNG', 'S_SPIDER', 'S_UNDEAD', 'S_UNIQUE', 'TELE_AWAY', 'TELE_LEVEL', 'TELE_TO', 'TPORT', 'TRAPS', 'UNMAGIC');
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html><head><title>List of spells | TomeNET monster search</title></head><body bgcolor=#ffffff><h1>List of spells used in r_info.txt</h1><table border=0 cellpadding=0 cellspacing=1 width=100%><tr>
<?php
for ($i = 0, $n = count($spells); $i < $n; $i++)
{
    echo('<td>' . $spells[$i] . '</td>');
    if (($i + 1) % 3 == 0)
    {
        echo ('</tr><tr>');
    }
}
?>
</tr></table>
<p><a href="javascript: history.go(-1)">Back</a></p>
</body></html>