• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[data conversion/capture] Monster file conversion to JSON.

jlnunez89

a.k.a BurnMc
Premium User
Joined
Jul 5, 2007
Messages
177
Solutions
1
Reaction score
69
Location
Seattle, WA
Starting bounty: $25 USD

Looking for someone to merge/convert all the monster files from CipSoft (only .mon- there are 159) and complement them with those from a 7.7 distribution like Nostalrius, into Fibula's JSON format (link to example below).

I don't really care how you do it (you could technically do it by hand if you don't know how to code), or the conversion tool you build (if you do), so long the data is consistent + accurate to the source .mon file and the output format is compliant with the JSON schema (I'll be validating the converted files against it). You can validate them using a tool like this JSON Schema Validator.

I don't have a time constraint, I just don't want to invest my time into doing grunt work. I'll just say (for the heck of it), between 1 and 3 weeks should be enough (depending on your method)?

I do expect you to deliver by opening a pull request against the monster_new folder, which by the way serves as proof to this community that you have completed the work, and so that I can leverage GitHub's review tools.

The conversion process is explained in detail here with a glossary here and, at a glance, it looks like this:

JSON:
{
  "article": "a",
  "name": "warlock",
  "blood": "blood",
  "experienceYield": 4000,
  "look": {
    "type": "outfit",
    "id": 130,
    "head": 0,
    "body": 52,
    "legs": 128,
    "feet": 95
  },
  "corpse": "4240",
  "stats": {
    "hitpoints": 3200,
    "baseSpeed": 75,
    "carryStrength": 900
  },
  "flags": [
    "CannotBePushed",
    "CanPushItems",
    "CanPushCreatures",
    "CanSeeInvisible"
  ],
  "combat": {
    "baseAttackPower": 40,
    "baseDefensePower": 50,
    "baseArmor": 32,
    "distance": 4,
    "immunities": {
      "poison": 1.00,
      "fire": 1.00,
      "energy": 1.00,
      "lifeDrain": 1.00,
      "paralysis": 1.00
    },
    "skills": {
      "Fist": {
        "level": 50,
        "targetCount": 100,
        "factor": 1.10,
        "increase": 2
      }
    },
    "strategy": {
      "changeTarget": {
        "chance": 0.5
      },
      "flee": {
        "hitpointThreshold": 1000
      },
      "closest": 100,
      "weakest": 0,
      "strongest": 0,
      "random": 0
    },
    "abilities": [
      { 
        "type": "self",
        "casterEffect": "SparklesBlue",
        "actions": [
          {
            "type": "heal",
            "base": 80,
            "variation": 20
          }
        ],
        "chance": 0.25
      },
      { 
        "type": "self",
        "casterEffect": "SparklesBlue",
        "actions": [
          {
            "type": "lookChange",
            "targetLook": {
              "type": "invisible"
            },
            "duration": 20000
          }
        ],
        "chance": 0.1
      },
      { 
        "type": "target",
        "range": 7,
        "projectileEffect": "Energy",
        "targetEffect": "None",
        "actions": [
          {
            "type": "damage",
            "kind": "Physical",
            "base": 75,
            "variation": 30
          }
        ],
        "chance": 0.5
      },
      { 
        "type": "target",
        "range": 7,
        "projectileEffect": "None",
        "targetEffect": "None",
        "actions": [
          {
            "type": "damage",
            "kind": "ManaDrain",
            "base": 55,
            "variation": 20
          }
        ],
        "chance": 0.166
      },
      { 
        "type": "target",
        "range": 7,
        "projectileEffect": "None",
        "targetEffect": "SparklesRed",
        "actions": [
          {
            "type": "speedChange",
            "base": -80,
            "variation": 20,
            "duration": 40000
          }
        ],
        "chance": 0.111
      },
      { 
        "type": "selfArea",
        "radius": 0,
        "areaEffect": "SparklesBlue",
        "actions": [
          {
            "type": "summon",
            "monsterFile": "stonegolem",
            "maximumCount": 1
          }
        ],
        "chance": 0.1
      },
      {
        "type": "targetArea",
        "range": 7,
        "casterEffect": "None",
        "projectileEffect": "Fire",
        "areaEffect": "AreaFlame",
        "radius": 2,
        "actions": [
          {
            "type": "damage",
            "kind": "Fire",
            "base": 130,
            "variation": 40
          }
        ],
        "chance": 0.333
      },
      {
        "type": "targetArea",
        "range": 7,
        "casterEffect": "None",
        "projectileEffect": "Fire",
        "areaEffect": "None",
        "radius": 0,
        "actions": [
          {
            "type": "magicField",
            "kind": "Fire"
          }
        ],
        "chance": 0.143
      },
      {
        "type": "targetArea",
        "range": 7,
        "casterEffect": "None",
        "projectileEffect": "Fire",
        "areaEffect": "None",
        "radius": 1,
        "actions": [
          {
            "type": "magicField",
            "kind": "Fire"
          }
        ],
        "chance": 0.2
      },
      { 
        "type": "targetDirection",
        "length": 8,
        "spread": 0,
        "areaEffect": "DamageEnergy",
        "actions": [
          {
            "type": "damage",
            "kind": "Energy",
            "base": 175,
            "variation": 30
          }
        ],
        "chance": 0.125
      },
    ]
  },
  "inventory": [
    { "id": "3567", "name": "blue robe", "maximumCount": 1, "chance": 0.02 },
    { "id": "3600", "name": "bread", "maximumCount": 1, "chance": 0.11 },
    { "id": "2917", "name": "candlestick", "maximumCount": 1, "chance": 0.15 },
    { "id": "3590", "name": "cherry", "maximumCount": 4, "chance": 0.20 },
    { "id": "3007", "name": "crystal ring", "maximumCount": 1, "chance": 0.01 },
    { "id": "3728", "name": "dark mushroom", "maximumCount": 1, "chance": 0.03 },
    { "id": "3051", "name": "energy ring", "maximumCount": 1, "chance": 0.03 },
    { "id": "3031", "name": "gold coin", "maximumCount": 80, "chance": 0.30 },
    { "id": "3360", "name": "golden armor", "maximumCount": 1, "chance": 0.003 },
    { "id": "3509", "name": "inkwell", "maximumCount": 1, "chance": 0.13 },
    { "id": "3062", "name": "mind stone", "maximumCount": 1, "chance": 0.025 },
    { "id": "3299", "name": "poison dagger", "maximumCount": 1, "chance": 0.10 },
    { "id": "2852", "name": "red tome", "maximumCount": 1, "chance": 0.004 },
    { "id": "3006", "name": "ring of the sky", "maximumCount": 1, "chance": 0.002 },
    { "id": "3324", "name": "skull staff", "maximumCount": 1, "chance": 0.07 },
    { "id": "3029", "name": "small sapphire", "maximumCount": 1, "chance": 0.014 },
    { "id": "3081", "name": "stone skin amulet", "maximumCount": 1, "chance": 0.005 },
    { "id": "3034", "name": "talon", "maximumCount": 1, "chance": 0.011 }
  ],
  "speech": [
    "Learn the secret of our magic! YOUR death!",
    "Even a rat is a better mage than you.",
    "We don't like intruders!"
  ]
}

Haggle here I guess, or contact me (Discord id in my signature, right side).

Thanks.
 
I wrote a very basic converter decades ago, it writes to XML, but changing it to JSON is pretty straightforward. I've also got a converter from XML to custom JSON schema created for a custom engine, which I can also upload if you want.

I'm attaching the tibiastuff.php which is needed for the converter - it has all the races, items etc extracted to php from cip files.

Here is monconverter.php:
PHP:
<?php
    ini_set("display_errors", "1");
    error_reporting(E_ALL);

    include('tibiastuff.php');

    $ignoreItems = array("worm", "snakebite rod", "moonlight rod", "necrotic rod", "terra rod", "hailstorm rod", "wand of vortex", "wand of dragonbreath", "wand of decay", "wand of cosmic energy", "wand of inferno");
    $intervals = array("melee" => 2000, "attack" => 1000, "defense" => 1000);
    $dropPercentToBag = 15;//Items with drop chance lower than this will be moved into bag
    $monPath = "./mon";//Location with tibia .mon files
    $monsters = new DOMDocument("1.0", "UTF-8");
    $mroot = $monsters->createElement("monsters");
    $monsters->appendChild($mroot);

    foreach(glob($monPath."/*.mon", GLOB_MARK) as $monster)
    {
        $fileName = explode(".", end(@explode("/", $monster)))[0];
        if($fileName == 'human') continue; //Probably used for character creation

        $data = file_get_contents($monster);
        $monsterData = array(
            'info' => array(),
            'immunities' => array(),
            'flags' => array(
                'summonable' => 1,
                'attackable' => 1,
                'hostile' => 1,
                'illusionable' => 1,
                'convinceable' => 1,
                'pushable' => 1,
                'canpushitems' => 0,
                'canpushcreatures' => 0,
                'targetdistance' => 1,
                'staticattack' => 90,
                'runonhealth' => 0
            ),
            'attacks' => array(),
            'defenses' => array(),
            'summons' => array(
                'max' => 0, 'monsters' => array()
            ),
            'voices' => array(),
            'loot' => array()
        );

        //Basic
        preg_match("/RaceNumber(\ +)\= (\d+)/is", $data, $raceNumber);
        preg_match("/Name(\ +)\= \"(.*?)\"/is", $data, $name);
        preg_match("/Article(\ +)\= \"(\S+)\"/is", $data, $article);
        preg_match("/Outfit(\ +)\= \((\d+), (\d+)-(\d+)-(\d+)-(\d+)\)/is", $data, $outfit);
        preg_match("/Corpse.(\ +)\= (\d+)/is", $data, $corpse);
        preg_match("/Blood(\ +)\= (\S+)/is", $data, $race);
        preg_match("/HitPoints, (\d+)/is", $data, $health);
        preg_match("/GoStrength, (\d+)/is", $data, $speed);
        preg_match("/FistFighting, (\d+)/is", $data, $skill);
        preg_match("/Experience(\ +)\= (\d+)/is", $data, $experience);
        preg_match("/SummonCost(\ +)\= (\d+)/is", $data, $summonCost);
        preg_match("/Attack(\ +)\= (\d+)/is", $data, $attack);
        preg_match("/Defend(\ +)\= (\d+)/is", $data, $defense);
        preg_match("/Armor(\ +)\= (\d+)/is", $data, $armor);
        preg_match("/Poison(\ +)\= (\d+)/is", $data, $poison);
        preg_match("/LoseTarget(\ +)\= (\d+)/is", $data, $loseTarget);
        preg_match("/FleeThreshold(\ +)\= (\d+)/is", $data, $fleeThreshold);

        $monsterData['info']['name'] = $name[2];
        $monsterData['info']['article'] = (isset($article[2]) ? $article[2] : '');
        if(count($outfit)) $monsterData['info']['outfit'] = array('type' => $outfit[2], 'head' => $outfit[3], 'body' => $outfit[4], 'legs' => $outfit[5], 'feet' => $outfit[6]);
        $monsterData['info']['corpse'] = (int)$corpse[2];
        $monsterData['info']['race'] = (isset($races[$race[2]]) ? $races[$race[2]] : strtolower($race[2]));
        $monsterData['info']['health'] = (int)$health[1];
        $monsterData['info']['speed'] = (isset($speed[1]) ? round((int)$speed[1] + 120) : 0);
        $monsterData['info']['skill'] = (int)$skill[1];
        $monsterData['info']['experience'] = (int)$experience[2] / 3;
        $monsterData['info']['summoncost'] = (int)$summonCost[2];
        $monsterData['info']['attack'] = (int)$attack[2];
        $monsterData['info']['defense'] = (int)$defense[2];
        $monsterData['info']['armor'] = (int)$armor[2];
        $monsterData['info']['poison'] = (int)$poison[2];
        $monsterData['info']['losetarget'] = (int)$loseTarget[2];

        //Immunities
        if(stripos($data, "seeinvisible")) $monsterData['immunities'][] = 'invisible';
        if(stripos($data, "nohit")) $monsterData['immunities'][] = 'physical';
        if(stripos($data, "noburning")) $monsterData['immunities'][] = 'fire';
        if(stripos($data, "nopoison")) $monsterData['immunities'][] = 'poison';
        if(stripos($data, "noenergy")) $monsterData['immunities'][] = 'energy';
        if(stripos($data, "noparalyze")) $monsterData['immunities'][] = 'paralyze';
        if(stripos($data, "nolifedrain")) $monsterData['immunities'][] = 'lifedrain';

        //Flags
        if(stripos($data, "nosummon")) $monsterData['flags']['summonable'] = 0;
        if(stripos($data, "distancefighting")) $monsterData['flags']['targetdistance'] = 4;
        if($monsterData['info']['attack'] == 0) $monsterData['flags']['hostile'] = 0;
        if(stripos($data, "noillusion")) $monsterData['flags']['illusionable'] = 0;
        if(stripos($data, "noconvince")) $monsterData['flags']['convinceable'] = 0;
        if(stripos($data, "unpushable")) $monsterData['flags']['pushable'] = 0;
        if(stripos($data, "kickboxes")) $monsterData['flags']['canpushitems'] = 1;
        if(stripos($data, "kickcreatures")) $monsterData['flags']['canpushcreatures'] = 1;
        $monsterData['flags']['runonhealth'] = (int)$fleeThreshold[2];

        continue;
        //Spells
        preg_match("/Spells(.*?)\= \{(.*?)\}/is", $data, $spells);
        if(isset($spells[2]))
        {
            preg_match_all("/(\S+) \((.*?)\) -\> (\S+) \((.*?)\) \: (\d+)/is", $spells[2],  $matches);
            for($i = 0; $i < count($matches[1]); $i++)
            {
                $attack = $skip = false;
                $spell = array(
                    'what' => strtolower($matches[1][$i]),
                    'type' => strtolower($matches[3][$i]),
                    'target' => false,
                    'chance' => (int)$matches[5][$i]
                );

                if($spell['what'] == 'actor') $spell['effect'] = $effects[$matches[2][$i]];
                
                $sdata = explode(",", $matches[2][$i]);

                if($spell['what'] == 'victim')
                {
                    $spell['range'] = (int)$sdata[0];
                    $spell['target'] = true;
                    $spell['shooteffect'] = $distanceEffects[(int)$sdata[1]];
                    $spell['areaeffect'] = $effects[(int)$sdata[2]];
                    $attack = true;
                }

                if($spell['what'] == 'destination')
                {
                    $spell['range'] = (int)$sdata[0];
                    $spell['target'] = true;
                    $spell['shooteffect'] = $distanceEffects[(int)$sdata[1]];
                    $spell['radius'] = (int)($sdata[2] + 1);
                    $spell['areaeffect'] = $effects[(int)$sdata[3]];
                    $attack = true;
                }

                if($spell['what'] == 'angle')
                {
                    $spell['spread'] = round((int)($sdata[0] / 10));
                    $spell['length'] = (int)$sdata[1];
                    $spell['areaeffect'] = $effects[(int)$sdata[2]];
                }

                if($spell['what'] == 'origin')
                {
                    $spell['radius'] = (int)$sdata[0] + 1;
                    $spell['areaeffect'] = $effects[(int)$sdata[1]];
                }

                $sdata = explode(",", $matches[4][$i]);

                if($spell['type'] == 'speed')//OT does not support random speed value
                {
                    $spell['name'] = $spell['type'];
                    $spell['value'] = (int)$sdata[0];
                    $spell['duration'] = (int)$sdata[2] * 1000;
                    if($spell['value'] < 0) $attack = true;
                }

                if($spell['type'] == 'healing')
                {
                    $spell['name'] = "healing";
                    $spell['minvalue'] = (int)($sdata[0] - $sdata[1]);
                    $spell['maxvalue'] = (int)($sdata[0] + $sdata[1]);
                }

                if($spell['type'] == 'damage')
                {
                    $spell['name'] = $damageTypes[(int)$sdata[0]];
                    $spell['minvalue'] = (int)(- $sdata[1] + $sdata[2]);
                    $spell['maxvalue'] = (int)(- $sdata[1] - $sdata[2]);
                    $attack = true;
                }

                if($spell['type'] == 'summon')
                {
                    $monsterData['summons']['monsters'][] = array($racenumbers[(int)$sdata[0]], (int)$sdata[1], $spell['chance']);
                    $monsterData['summons']['max'] += (int)$sdata[1];
                    $skip = true;
                }

                if($spell['type'] == 'strength')
                {
                    //Missing in OT
                    $skip = true;
                    //This is probably not right
                    /*$spell['name'] = $damageTypes[(int)$sdata[0]];
                    $spell['minvalue'] = (int)($sdata[1] - $sdata[2]);
                    $spell['maxvalue'] = (int)($sdata[1] + $sdata[2]);
                    $spell['duration'] = (int)$sdata[3] * 1000;*/
                }

                if($spell['type'] == 'outfit')
                {
                    unset($spell['target']);
                    $outfit = explode(",", str_replace(array("(", ")"), array("", ""), $matches[4][$i]));
                    $spell['name'] = 'outfit';
                    if((int)$outfit[0] == 0 && (int)$outfit[1] == 0) $spell['name'] = 'invisible';
                    elseif((int)$outfit[0] > 0)
                    {
                        //TFS does not support custom outfits
                        /*if(stripos("-", $outfit[1]) !== false)
                        {
                            $colors = explode("-", $outfit[1]);
                            $spell['custom'] = array(
                                'looktype' => $outfit[0],
                                'head' => $color[0],
                                'body' => $color[1],
                                'legs' => $color[2],
                                'feet' => $color[3]
                            );
                        }
                        else */$spell['monster'] = $racenumbers[(int)$outfit[0]];
                    }
                    elseif((int)$outfit[1] > 0) $spell['item'] = $items[(int)$outfit[1]][0];
                    $spell['duration'] = $outfit[2] * 1000;
                }

                if($spell['type'] == 'drunken')
                {
                    $spell['name'] = "drunk";
                    $spell['duration'] = (int)$sdata[2] * 1000;
                    $attack = true;
                }

                if($spell['type'] == 'field')
                {
                    $spell['name'] = $fields[(int)$sdata[0]];
                    $attack = true;
                }

                if(!$skip) $monsterData[($attack ? 'attacks' : 'defenses')][] = $spell;
            }
        }

        //Voices
        preg_match("/Talk(.*?)\= \{(.*?)\}/is", $data, $voices);
        if(isset($voices[2]))
        {
            preg_match_all("/\"(.*?)\"/is", $voices[2], $matches);
            foreach($matches[1] as $voice)
            {
                $yell = false;

                if(stripos($voice, "#Y") !== false)
                {
                    $voice = str_ireplace("#Y ", "", $voice);
                    $yell = true;
                }

                $monsterData['voices'][] = array('text' => str_replace('"', '', $voice), 'yell' => $yell);
            }
        }

        //Loot
        preg_match("/Inventory(.*?)\= \{(.*?)\}/is", $data, $loot);
        if(isset($loot[2]))
        {
            preg_match_all("/\((\d+), (\d+), (\d+)\)/is", $loot[2], $matches);
            for($i = 0; $i < count($matches[1]); $i++)
                $monsterData['loot'][] = array(
                    'id' => (int)$matches[1][$i],
                    'count' => (int)$matches[2][$i],
                    'chance' => (int)$matches[3][$i] / 2
                );
        }

        $monsterEntry = $monsters->createElement("monster");
        $monsterEntry->setAttribute("name", ucwords($monsterData['info']['name']));
        $monsterEntry->setAttribute("file", $fileName.".xml");

        $mroot->appendChild($monsterEntry);
        $otMonster = new DOMDocument("1.0", "UTF-8");

        $root = $otMonster->createElement("monster");
        $root->setAttribute("name", ucwords($monsterData['info']['name']));
        $root->setAttribute("nameDescription", trim(trim($monsterData['info']['article'])." ".$monsterData['info']['name']));
        $root->setAttribute("race", $monsterData['info']['race']);
        $root->setAttribute("experience", $monsterData['info']['experience']);
        $root->setAttribute("speed", $monsterData['info']['speed']);
        $root->setAttribute("manacost", $monsterData['info']['summoncost']);
        $otMonster->appendChild($root);

        $health = $otMonster->createElement("health");
        $health->setAttribute("now", $monsterData['info']['health']);
        $health->setAttribute("max", $monsterData['info']['health']);
        $root->appendChild($health);

        $outfit = $otMonster->createElement("look");
        if(isset($monsterData['info']['outfit']))
        {   
            foreach($monsterData['info']['outfit'] as $k => $v)
            {
                if($v == 0) continue;
                $outfit->setAttribute($k, $v);
            }
        }

        $outfit->setAttribute("corpse", $items[$monsterData['info']['corpse']][0]);
        $root->appendChild($outfit);
        
        $targetchange = $otMonster->createElement("targetchange");
        $targetchange->setAttribute("interval", 5000);
        $targetchange->setAttribute("chance", $monsterData['info']['losetarget']);
        $root->appendChild($targetchange);

        //This is not used in TFS
        $strategy = $otMonster->createElement("strategy");
        $strategy->setAttribute("attack", 100);
        $strategy->setAttribute("defense", 0);
        $root->appendChild($strategy);

        $flags = $otMonster->createElement("flags");
        foreach($monsterData['flags'] as $k => $v)
        {
            $flag = $otMonster->createElement("flag");
            $flag->setAttribute($k, $v);
            $flags->appendChild($flag);
        }

        $root->appendChild($flags);

        if($monsterData['info']['attack'] || $monsterData['info']['skill'])
        {
            $attacks = $root->appendChild($otMonster->createElement("attacks"));
            $melee = $otMonster->createElement("attack");
            $melee->setAttribute("name", "melee");
            $melee->setAttribute("interval", $intervals['melee']);
            $melee->setAttribute("skill", $monsterData['info']['skill']);
            $melee->setAttribute("attack", $monsterData['info']['attack']);
            $attacks->appendChild($melee);
        }

        if(count($monsterData['attacks']))
        {
            if(!isset($attacks))
                $attacks = $root->appendChild($otMonster->createElement("attacks"));

            foreach($monsterData['attacks'] as $att)
            {
                $attack = $otMonster->createElement("attack");
                $attack->setAttribute("name", $att['name']);
                $attack->setAttribute("interval", $intervals['attack']);
                $attack->setAttribute("chance", $att['chance']);
                if(isset($att['length'])) $attack->setAttribute("length", $att['length']);
                if(isset($att['spread'])) $attack->setAttribute("spread", $att['spread']);
                if(isset($att['range'])) $attack->setAttribute("range", $att['range']);
                if(isset($att['radius'])) $attack->setAttribute("radius", $att['radius']);
                if(isset($att['target'])) $attack->setAttribute("target", 1);
                if($att['type'] == 'speed') $attack->setAttribute("speedchange", $att['value']);
                if(isset($att['monster'])) $attack->setAttribute("monster", $att['monster']);
                if(isset($att['item'])) $attack->setAttribute("item", $att['item']);
                if(isset($att['duration'])) $attack->setAttribute("duration", $att['duration']);
                if(isset($att['minvalue'])) $attack->setAttribute("min", $att['minvalue']);
                if(isset($att['maxvalue'])) $attack->setAttribute("max", $att['maxvalue']);
                if(isset($att['shooteffect']) && strlen($att['shooteffect']))
                {
                    $attribute = $otMonster->createElement("attribute");
                    $attribute->setAttribute("key", "shootEffect");
                    $attribute->setAttribute("value", $att['shooteffect']);
                    $attack->appendChild($attribute);
                }
                if(isset($att['areaeffect']) && strlen($att['areaeffect']) > 0)
                {
                    $attribute = $otMonster->createElement("attribute");
                    $attribute->setAttribute("key", "areaEffect");
                    $attribute->setAttribute("value", $att['areaeffect']);
                    $attack->appendChild($attribute);
                }
                $attacks->appendChild($attack);
            }
        }
        if(isset($attacks)) $root->appendChild($attacks);
        unset($attacks);

        $defenses = $otMonster->createElement("defenses");
        $defenses->setAttribute("armor", $monsterData['info']['armor']);
        $defenses->setAttribute("defense", $monsterData['info']['defense']);

        if(count($monsterData['defenses']))
        {
            foreach($monsterData['defenses'] as $def)
            {
                $defense = $otMonster->createElement("defense");
                $defense->setAttribute("name", $def['name']);
                $defense->setAttribute("interval", $intervals['defense']);
                $defense->setAttribute("chance", $def['chance']);
                if($def['type'] == 'speed') $defense->setAttribute("speedchange", $def['value']);
                if(isset($def['duration'])) $defense->setAttribute("duration", $def['duration']);
                if(isset($def['minvalue'])) $defense->setAttribute("min", $def['minvalue']);
                if(isset($def['maxvalue'])) $defense->setAttribute("max", $def['maxvalue']);

                $defenses->appendChild($defense);
            }
        }
        $root->appendChild($defenses);
        unset($defenses);

        if(count($monsterData['immunities']))
        {
            $immunities = $otMonster->createElement("immunities");
            foreach($monsterData['immunities'] as $k)
            {
                $immunity = $otMonster->createElement("immunity");
                $immunity->setAttribute($k, 1);
                $immunities->appendChild($immunity);
            }
            $root->appendChild($immunities);
            unset($immunities);
        }

        if($monsterData['summons']['max'])
        {
            $summons = $otMonster->createElement("summons");
            $summons->setAttribute("maxsummons", $monsterData['summons']['max']);
            foreach($monsterData['summons']['monsters'] as $s)
            {
                $summon = $otMonster->createElement("summon");
                $summon->setAttribute("name", $s[0]);
                $summon->setAttribute("interval", 2000);
                $summon->setAttribute("chance", $s[2]);
                $summon->setAttribute("max", $s[1]);
                $summons->appendChild($summon);
            }
            $root->appendChild($summons);
            unset($summons);
        }

        if(count($monsterData['voices']))
        {
            $voices = $otMonster->createElement("voices");
            $voices->setAttribute("interval", 10000);
            $voices->setAttribute("chance", 10);
            foreach($monsterData['voices'] as $v)
            {
                $voice = $otMonster->createElement("voice");
                $voice->setAttribute("sentence", $v['text']);
                if($v['yell']) $voice->setAttribute("yell", 1);
                $voices->appendChild($voice);
            }
            $root->appendChild($voices);
            unset($voices);
        }

        if(count($monsterData['loot']))
        {
            $inBag = array();

            $loot = $otMonster->createElement("loot");
            foreach($monsterData['loot'] as $it)
            {
                if(in_array($items[$it['id']][1], $ignoreItems)) continue;
                if($it['chance'] / 10 < $dropPercentToBag) $inBag[] = $it;
                else
                {
                    $item = $otMonster->createElement("item");
                    $item->setAttribute("id",$items[$it['id']][0]);
                    if($it['count'] > 1) $item->setAttribute("countmax", $it['count']);
                    $item->setAttribute("chance", $it['chance'] * 100);
                    $item->setAttribute("name", $items[$it['id']][1]);
                    $loot->appendChild($item);
                }
            }

            if(count($inBag))
            {
                $bag = $otMonster->createElement("item");
                $bag->setAttribute("id", 1987);
                $bag->setAttribute("chance", 100000);
                foreach($inBag as $it)
                {
                    $item = $otMonster->createElement("item");
                    $item->setAttribute("id", $items[$it['id']][0]);
                    if($it['count'] > 1) $item->setAttribute("countmax", $it['count']);
                    $item->setAttribute("chance", $it['chance'] * 100);
                    $item->setAttribute("name", $items[$it['id']][1]);
                    $bag->appendChild($item);
                }
                $loot->appendChild($bag);
                unset($bag);
            }
            $root->appendChild($loot);
            unset($loot);
        }
        $otMonster->formatOutput = true;
        $otMonster->save("monster/{$fileName}.xml");
    }

    $monsters->formatOutput = true;
    $monsters->save("monster/monsters.xml");
?>
 

Attachments

  • tibiastuff.php.zip
    31.6 KB · Views: 13 · VirusTotal
For anyone who wants to do this job. I would accept PR's to soul4soul/ot-monster-converter (https://github.com/soul4soul/ot-monster-converter). "OT Monster Converter" would be a terrific starting point for this work as the whole intent of the tool is to aid in converting monsters between different tibia monster file formats. Adding support for parsing cip monster format is even one of the open issues Add support for cip monster format · Issue #12 · soul4soul/ot-monster-converter (https://github.com/soul4soul/ot-monster-converter/issues/12) so I'm definitely interested in that being contributed.
 
Last edited:
I wrote a very basic converter decades ago, it writes to XML, but changing it to JSON is pretty straightforward. I've also got a converter from XML to custom JSON schema created for a custom engine, which I can also upload if you want.

I'm attaching the tibiastuff.php which is needed for the converter - it has all the races, items etc extracted to php from cip files.

Here is monconverter.php:
PHP:
<?php
    ini_set("display_errors", "1");
    error_reporting(E_ALL);

    include('tibiastuff.php');

    $ignoreItems = array("worm", "snakebite rod", "moonlight rod", "necrotic rod", "terra rod", "hailstorm rod", "wand of vortex", "wand of dragonbreath", "wand of decay", "wand of cosmic energy", "wand of inferno");
    $intervals = array("melee" => 2000, "attack" => 1000, "defense" => 1000);
    $dropPercentToBag = 15;//Items with drop chance lower than this will be moved into bag
    $monPath = "./mon";//Location with tibia .mon files
    $monsters = new DOMDocument("1.0", "UTF-8");
    $mroot = $monsters->createElement("monsters");
    $monsters->appendChild($mroot);

    foreach(glob($monPath."/*.mon", GLOB_MARK) as $monster)
    {
        $fileName = explode(".", end(@explode("/", $monster)))[0];
        if($fileName == 'human') continue; //Probably used for character creation

        $data = file_get_contents($monster);
        $monsterData = array(
            'info' => array(),
            'immunities' => array(),
            'flags' => array(
                'summonable' => 1,
                'attackable' => 1,
                'hostile' => 1,
                'illusionable' => 1,
                'convinceable' => 1,
                'pushable' => 1,
                'canpushitems' => 0,
                'canpushcreatures' => 0,
                'targetdistance' => 1,
                'staticattack' => 90,
                'runonhealth' => 0
            ),
            'attacks' => array(),
            'defenses' => array(),
            'summons' => array(
                'max' => 0, 'monsters' => array()
            ),
            'voices' => array(),
            'loot' => array()
        );

        //Basic
        preg_match("/RaceNumber(\ +)\= (\d+)/is", $data, $raceNumber);
        preg_match("/Name(\ +)\= \"(.*?)\"/is", $data, $name);
        preg_match("/Article(\ +)\= \"(\S+)\"/is", $data, $article);
        preg_match("/Outfit(\ +)\= \((\d+), (\d+)-(\d+)-(\d+)-(\d+)\)/is", $data, $outfit);
        preg_match("/Corpse.(\ +)\= (\d+)/is", $data, $corpse);
        preg_match("/Blood(\ +)\= (\S+)/is", $data, $race);
        preg_match("/HitPoints, (\d+)/is", $data, $health);
        preg_match("/GoStrength, (\d+)/is", $data, $speed);
        preg_match("/FistFighting, (\d+)/is", $data, $skill);
        preg_match("/Experience(\ +)\= (\d+)/is", $data, $experience);
        preg_match("/SummonCost(\ +)\= (\d+)/is", $data, $summonCost);
        preg_match("/Attack(\ +)\= (\d+)/is", $data, $attack);
        preg_match("/Defend(\ +)\= (\d+)/is", $data, $defense);
        preg_match("/Armor(\ +)\= (\d+)/is", $data, $armor);
        preg_match("/Poison(\ +)\= (\d+)/is", $data, $poison);
        preg_match("/LoseTarget(\ +)\= (\d+)/is", $data, $loseTarget);
        preg_match("/FleeThreshold(\ +)\= (\d+)/is", $data, $fleeThreshold);

        $monsterData['info']['name'] = $name[2];
        $monsterData['info']['article'] = (isset($article[2]) ? $article[2] : '');
        if(count($outfit)) $monsterData['info']['outfit'] = array('type' => $outfit[2], 'head' => $outfit[3], 'body' => $outfit[4], 'legs' => $outfit[5], 'feet' => $outfit[6]);
        $monsterData['info']['corpse'] = (int)$corpse[2];
        $monsterData['info']['race'] = (isset($races[$race[2]]) ? $races[$race[2]] : strtolower($race[2]));
        $monsterData['info']['health'] = (int)$health[1];
        $monsterData['info']['speed'] = (isset($speed[1]) ? round((int)$speed[1] + 120) : 0);
        $monsterData['info']['skill'] = (int)$skill[1];
        $monsterData['info']['experience'] = (int)$experience[2] / 3;
        $monsterData['info']['summoncost'] = (int)$summonCost[2];
        $monsterData['info']['attack'] = (int)$attack[2];
        $monsterData['info']['defense'] = (int)$defense[2];
        $monsterData['info']['armor'] = (int)$armor[2];
        $monsterData['info']['poison'] = (int)$poison[2];
        $monsterData['info']['losetarget'] = (int)$loseTarget[2];

        //Immunities
        if(stripos($data, "seeinvisible")) $monsterData['immunities'][] = 'invisible';
        if(stripos($data, "nohit")) $monsterData['immunities'][] = 'physical';
        if(stripos($data, "noburning")) $monsterData['immunities'][] = 'fire';
        if(stripos($data, "nopoison")) $monsterData['immunities'][] = 'poison';
        if(stripos($data, "noenergy")) $monsterData['immunities'][] = 'energy';
        if(stripos($data, "noparalyze")) $monsterData['immunities'][] = 'paralyze';
        if(stripos($data, "nolifedrain")) $monsterData['immunities'][] = 'lifedrain';

        //Flags
        if(stripos($data, "nosummon")) $monsterData['flags']['summonable'] = 0;
        if(stripos($data, "distancefighting")) $monsterData['flags']['targetdistance'] = 4;
        if($monsterData['info']['attack'] == 0) $monsterData['flags']['hostile'] = 0;
        if(stripos($data, "noillusion")) $monsterData['flags']['illusionable'] = 0;
        if(stripos($data, "noconvince")) $monsterData['flags']['convinceable'] = 0;
        if(stripos($data, "unpushable")) $monsterData['flags']['pushable'] = 0;
        if(stripos($data, "kickboxes")) $monsterData['flags']['canpushitems'] = 1;
        if(stripos($data, "kickcreatures")) $monsterData['flags']['canpushcreatures'] = 1;
        $monsterData['flags']['runonhealth'] = (int)$fleeThreshold[2];

        continue;
        //Spells
        preg_match("/Spells(.*?)\= \{(.*?)\}/is", $data, $spells);
        if(isset($spells[2]))
        {
            preg_match_all("/(\S+) \((.*?)\) -\> (\S+) \((.*?)\) \: (\d+)/is", $spells[2],  $matches);
            for($i = 0; $i < count($matches[1]); $i++)
            {
                $attack = $skip = false;
                $spell = array(
                    'what' => strtolower($matches[1][$i]),
                    'type' => strtolower($matches[3][$i]),
                    'target' => false,
                    'chance' => (int)$matches[5][$i]
                );

                if($spell['what'] == 'actor') $spell['effect'] = $effects[$matches[2][$i]];
               
                $sdata = explode(",", $matches[2][$i]);

                if($spell['what'] == 'victim')
                {
                    $spell['range'] = (int)$sdata[0];
                    $spell['target'] = true;
                    $spell['shooteffect'] = $distanceEffects[(int)$sdata[1]];
                    $spell['areaeffect'] = $effects[(int)$sdata[2]];
                    $attack = true;
                }

                if($spell['what'] == 'destination')
                {
                    $spell['range'] = (int)$sdata[0];
                    $spell['target'] = true;
                    $spell['shooteffect'] = $distanceEffects[(int)$sdata[1]];
                    $spell['radius'] = (int)($sdata[2] + 1);
                    $spell['areaeffect'] = $effects[(int)$sdata[3]];
                    $attack = true;
                }

                if($spell['what'] == 'angle')
                {
                    $spell['spread'] = round((int)($sdata[0] / 10));
                    $spell['length'] = (int)$sdata[1];
                    $spell['areaeffect'] = $effects[(int)$sdata[2]];
                }

                if($spell['what'] == 'origin')
                {
                    $spell['radius'] = (int)$sdata[0] + 1;
                    $spell['areaeffect'] = $effects[(int)$sdata[1]];
                }

                $sdata = explode(",", $matches[4][$i]);

                if($spell['type'] == 'speed')//OT does not support random speed value
                {
                    $spell['name'] = $spell['type'];
                    $spell['value'] = (int)$sdata[0];
                    $spell['duration'] = (int)$sdata[2] * 1000;
                    if($spell['value'] < 0) $attack = true;
                }

                if($spell['type'] == 'healing')
                {
                    $spell['name'] = "healing";
                    $spell['minvalue'] = (int)($sdata[0] - $sdata[1]);
                    $spell['maxvalue'] = (int)($sdata[0] + $sdata[1]);
                }

                if($spell['type'] == 'damage')
                {
                    $spell['name'] = $damageTypes[(int)$sdata[0]];
                    $spell['minvalue'] = (int)(- $sdata[1] + $sdata[2]);
                    $spell['maxvalue'] = (int)(- $sdata[1] - $sdata[2]);
                    $attack = true;
                }

                if($spell['type'] == 'summon')
                {
                    $monsterData['summons']['monsters'][] = array($racenumbers[(int)$sdata[0]], (int)$sdata[1], $spell['chance']);
                    $monsterData['summons']['max'] += (int)$sdata[1];
                    $skip = true;
                }

                if($spell['type'] == 'strength')
                {
                    //Missing in OT
                    $skip = true;
                    //This is probably not right
                    /*$spell['name'] = $damageTypes[(int)$sdata[0]];
                    $spell['minvalue'] = (int)($sdata[1] - $sdata[2]);
                    $spell['maxvalue'] = (int)($sdata[1] + $sdata[2]);
                    $spell['duration'] = (int)$sdata[3] * 1000;*/
                }

                if($spell['type'] == 'outfit')
                {
                    unset($spell['target']);
                    $outfit = explode(",", str_replace(array("(", ")"), array("", ""), $matches[4][$i]));
                    $spell['name'] = 'outfit';
                    if((int)$outfit[0] == 0 && (int)$outfit[1] == 0) $spell['name'] = 'invisible';
                    elseif((int)$outfit[0] > 0)
                    {
                        //TFS does not support custom outfits
                        /*if(stripos("-", $outfit[1]) !== false)
                        {
                            $colors = explode("-", $outfit[1]);
                            $spell['custom'] = array(
                                'looktype' => $outfit[0],
                                'head' => $color[0],
                                'body' => $color[1],
                                'legs' => $color[2],
                                'feet' => $color[3]
                            );
                        }
                        else */$spell['monster'] = $racenumbers[(int)$outfit[0]];
                    }
                    elseif((int)$outfit[1] > 0) $spell['item'] = $items[(int)$outfit[1]][0];
                    $spell['duration'] = $outfit[2] * 1000;
                }

                if($spell['type'] == 'drunken')
                {
                    $spell['name'] = "drunk";
                    $spell['duration'] = (int)$sdata[2] * 1000;
                    $attack = true;
                }

                if($spell['type'] == 'field')
                {
                    $spell['name'] = $fields[(int)$sdata[0]];
                    $attack = true;
                }

                if(!$skip) $monsterData[($attack ? 'attacks' : 'defenses')][] = $spell;
            }
        }

        //Voices
        preg_match("/Talk(.*?)\= \{(.*?)\}/is", $data, $voices);
        if(isset($voices[2]))
        {
            preg_match_all("/\"(.*?)\"/is", $voices[2], $matches);
            foreach($matches[1] as $voice)
            {
                $yell = false;

                if(stripos($voice, "#Y") !== false)
                {
                    $voice = str_ireplace("#Y ", "", $voice);
                    $yell = true;
                }

                $monsterData['voices'][] = array('text' => str_replace('"', '', $voice), 'yell' => $yell);
            }
        }

        //Loot
        preg_match("/Inventory(.*?)\= \{(.*?)\}/is", $data, $loot);
        if(isset($loot[2]))
        {
            preg_match_all("/\((\d+), (\d+), (\d+)\)/is", $loot[2], $matches);
            for($i = 0; $i < count($matches[1]); $i++)
                $monsterData['loot'][] = array(
                    'id' => (int)$matches[1][$i],
                    'count' => (int)$matches[2][$i],
                    'chance' => (int)$matches[3][$i] / 2
                );
        }

        $monsterEntry = $monsters->createElement("monster");
        $monsterEntry->setAttribute("name", ucwords($monsterData['info']['name']));
        $monsterEntry->setAttribute("file", $fileName.".xml");

        $mroot->appendChild($monsterEntry);
        $otMonster = new DOMDocument("1.0", "UTF-8");

        $root = $otMonster->createElement("monster");
        $root->setAttribute("name", ucwords($monsterData['info']['name']));
        $root->setAttribute("nameDescription", trim(trim($monsterData['info']['article'])." ".$monsterData['info']['name']));
        $root->setAttribute("race", $monsterData['info']['race']);
        $root->setAttribute("experience", $monsterData['info']['experience']);
        $root->setAttribute("speed", $monsterData['info']['speed']);
        $root->setAttribute("manacost", $monsterData['info']['summoncost']);
        $otMonster->appendChild($root);

        $health = $otMonster->createElement("health");
        $health->setAttribute("now", $monsterData['info']['health']);
        $health->setAttribute("max", $monsterData['info']['health']);
        $root->appendChild($health);

        $outfit = $otMonster->createElement("look");
        if(isset($monsterData['info']['outfit']))
        {  
            foreach($monsterData['info']['outfit'] as $k => $v)
            {
                if($v == 0) continue;
                $outfit->setAttribute($k, $v);
            }
        }

        $outfit->setAttribute("corpse", $items[$monsterData['info']['corpse']][0]);
        $root->appendChild($outfit);
       
        $targetchange = $otMonster->createElement("targetchange");
        $targetchange->setAttribute("interval", 5000);
        $targetchange->setAttribute("chance", $monsterData['info']['losetarget']);
        $root->appendChild($targetchange);

        //This is not used in TFS
        $strategy = $otMonster->createElement("strategy");
        $strategy->setAttribute("attack", 100);
        $strategy->setAttribute("defense", 0);
        $root->appendChild($strategy);

        $flags = $otMonster->createElement("flags");
        foreach($monsterData['flags'] as $k => $v)
        {
            $flag = $otMonster->createElement("flag");
            $flag->setAttribute($k, $v);
            $flags->appendChild($flag);
        }

        $root->appendChild($flags);

        if($monsterData['info']['attack'] || $monsterData['info']['skill'])
        {
            $attacks = $root->appendChild($otMonster->createElement("attacks"));
            $melee = $otMonster->createElement("attack");
            $melee->setAttribute("name", "melee");
            $melee->setAttribute("interval", $intervals['melee']);
            $melee->setAttribute("skill", $monsterData['info']['skill']);
            $melee->setAttribute("attack", $monsterData['info']['attack']);
            $attacks->appendChild($melee);
        }

        if(count($monsterData['attacks']))
        {
            if(!isset($attacks))
                $attacks = $root->appendChild($otMonster->createElement("attacks"));

            foreach($monsterData['attacks'] as $att)
            {
                $attack = $otMonster->createElement("attack");
                $attack->setAttribute("name", $att['name']);
                $attack->setAttribute("interval", $intervals['attack']);
                $attack->setAttribute("chance", $att['chance']);
                if(isset($att['length'])) $attack->setAttribute("length", $att['length']);
                if(isset($att['spread'])) $attack->setAttribute("spread", $att['spread']);
                if(isset($att['range'])) $attack->setAttribute("range", $att['range']);
                if(isset($att['radius'])) $attack->setAttribute("radius", $att['radius']);
                if(isset($att['target'])) $attack->setAttribute("target", 1);
                if($att['type'] == 'speed') $attack->setAttribute("speedchange", $att['value']);
                if(isset($att['monster'])) $attack->setAttribute("monster", $att['monster']);
                if(isset($att['item'])) $attack->setAttribute("item", $att['item']);
                if(isset($att['duration'])) $attack->setAttribute("duration", $att['duration']);
                if(isset($att['minvalue'])) $attack->setAttribute("min", $att['minvalue']);
                if(isset($att['maxvalue'])) $attack->setAttribute("max", $att['maxvalue']);
                if(isset($att['shooteffect']) && strlen($att['shooteffect']))
                {
                    $attribute = $otMonster->createElement("attribute");
                    $attribute->setAttribute("key", "shootEffect");
                    $attribute->setAttribute("value", $att['shooteffect']);
                    $attack->appendChild($attribute);
                }
                if(isset($att['areaeffect']) && strlen($att['areaeffect']) > 0)
                {
                    $attribute = $otMonster->createElement("attribute");
                    $attribute->setAttribute("key", "areaEffect");
                    $attribute->setAttribute("value", $att['areaeffect']);
                    $attack->appendChild($attribute);
                }
                $attacks->appendChild($attack);
            }
        }
        if(isset($attacks)) $root->appendChild($attacks);
        unset($attacks);

        $defenses = $otMonster->createElement("defenses");
        $defenses->setAttribute("armor", $monsterData['info']['armor']);
        $defenses->setAttribute("defense", $monsterData['info']['defense']);

        if(count($monsterData['defenses']))
        {
            foreach($monsterData['defenses'] as $def)
            {
                $defense = $otMonster->createElement("defense");
                $defense->setAttribute("name", $def['name']);
                $defense->setAttribute("interval", $intervals['defense']);
                $defense->setAttribute("chance", $def['chance']);
                if($def['type'] == 'speed') $defense->setAttribute("speedchange", $def['value']);
                if(isset($def['duration'])) $defense->setAttribute("duration", $def['duration']);
                if(isset($def['minvalue'])) $defense->setAttribute("min", $def['minvalue']);
                if(isset($def['maxvalue'])) $defense->setAttribute("max", $def['maxvalue']);

                $defenses->appendChild($defense);
            }
        }
        $root->appendChild($defenses);
        unset($defenses);

        if(count($monsterData['immunities']))
        {
            $immunities = $otMonster->createElement("immunities");
            foreach($monsterData['immunities'] as $k)
            {
                $immunity = $otMonster->createElement("immunity");
                $immunity->setAttribute($k, 1);
                $immunities->appendChild($immunity);
            }
            $root->appendChild($immunities);
            unset($immunities);
        }

        if($monsterData['summons']['max'])
        {
            $summons = $otMonster->createElement("summons");
            $summons->setAttribute("maxsummons", $monsterData['summons']['max']);
            foreach($monsterData['summons']['monsters'] as $s)
            {
                $summon = $otMonster->createElement("summon");
                $summon->setAttribute("name", $s[0]);
                $summon->setAttribute("interval", 2000);
                $summon->setAttribute("chance", $s[2]);
                $summon->setAttribute("max", $s[1]);
                $summons->appendChild($summon);
            }
            $root->appendChild($summons);
            unset($summons);
        }

        if(count($monsterData['voices']))
        {
            $voices = $otMonster->createElement("voices");
            $voices->setAttribute("interval", 10000);
            $voices->setAttribute("chance", 10);
            foreach($monsterData['voices'] as $v)
            {
                $voice = $otMonster->createElement("voice");
                $voice->setAttribute("sentence", $v['text']);
                if($v['yell']) $voice->setAttribute("yell", 1);
                $voices->appendChild($voice);
            }
            $root->appendChild($voices);
            unset($voices);
        }

        if(count($monsterData['loot']))
        {
            $inBag = array();

            $loot = $otMonster->createElement("loot");
            foreach($monsterData['loot'] as $it)
            {
                if(in_array($items[$it['id']][1], $ignoreItems)) continue;
                if($it['chance'] / 10 < $dropPercentToBag) $inBag[] = $it;
                else
                {
                    $item = $otMonster->createElement("item");
                    $item->setAttribute("id",$items[$it['id']][0]);
                    if($it['count'] > 1) $item->setAttribute("countmax", $it['count']);
                    $item->setAttribute("chance", $it['chance'] * 100);
                    $item->setAttribute("name", $items[$it['id']][1]);
                    $loot->appendChild($item);
                }
            }

            if(count($inBag))
            {
                $bag = $otMonster->createElement("item");
                $bag->setAttribute("id", 1987);
                $bag->setAttribute("chance", 100000);
                foreach($inBag as $it)
                {
                    $item = $otMonster->createElement("item");
                    $item->setAttribute("id", $items[$it['id']][0]);
                    if($it['count'] > 1) $item->setAttribute("countmax", $it['count']);
                    $item->setAttribute("chance", $it['chance'] * 100);
                    $item->setAttribute("name", $items[$it['id']][1]);
                    $bag->appendChild($item);
                }
                $loot->appendChild($bag);
                unset($bag);
            }
            $root->appendChild($loot);
            unset($loot);
        }
        $otMonster->formatOutput = true;
        $otMonster->save("monster/{$fileName}.xml");
    }

    $monsters->formatOutput = true;
    $monsters->save("monster/monsters.xml");
?>
short correction to your code to avoid notice error:
change $fileName = explode(".", end(@explode("/", $monster)))[0];
to
$lol= explode("/", $monster);
$fileName = explode(".", end($lol))[0];
btw
for some reason the code doesnt work. it created an empty file monsters.xml with an xml version only but doesnt create any monsters. it also doesnt validate if the path to the mon folder is correct
 
I have a php to json converter from monsters.xml, would that be it? I use it to create the monsters.php page

[
{
"name": "Trainer Rat",
"experience": 0,
"health": 50,
"speed": 134,
"lookType": 21,
"lookHead": 0,
"lookBody": 0,
"lookLegs": 0,
"lookFeet": 0,
"defArmor": "15",
"summonable": "",
"convinceable": "",
"targetDistance": "",
"physicalPercent": "",
"earthPercent": "",
"energyPercent": "",
"deathPercent": "",
"icePercent": "",
"firePercent": "",
"immunityPhysical": "0",
"immunityFire": "",
"immunityEarth": "",
"immunityInvisible": "",
"immunityEnergy": "",
"immunityDeath": "",
"immunityIce": "",
"loot": []
}
]
 
Last edited:
short correction to your code to avoid notice error:

to

btw
for some reason the code doesnt work. it created an empty file monsters.xml with an xml version only but doesnt create any monsters. it also doesnt validate if the path to the mon folder is correct
There is a wild continue above //Spells, remove it
 
Back
Top