-
Notifications
You must be signed in to change notification settings - Fork 6
Map file
Based on these Original findings:
Write it better so its easyer to understand:
The MPD format serves as a container that holds all the map's information, including the placement of tiles and enemies. Tiles are organized into chunks, each containing a certain number of tiles, but with a fixed number of objects (models) and event tile markings. Textures and the geometry of the object themself are stored in separate MPP files. One containing the original low-resolution (SD) textures, and another, prefixed with "NEW_", that includes the high-definition (HD) textures.
Name | Format | Info |
---|---|---|
Header | mpdfileHeader | |
Chunks | vector<mpdfileChunk> | |
├── Chunk header | mpdfileChunkHeader | |
├── Objects | std::array<mpdfileChunkObject, 32> | Maps Models |
├── Event tiles | std::array<mpdfileChunkEventTile, 16> | |
└── Base tile | mpdfileTile | Not used by the game |
Tiles | vector<mpdfileTile> | |
└── Tile Texture | std::array<mpdfileTexdata, 12> | |
Actors | vector<mpdfileActopr> |
The file header mainly contains the number of chunks and actors, the rest doesn't seam to be read by the game at all.
struct mpdfileHeader// 16 bytes
{
unsigned short number_of_chunks; //number of chunks in map
unsigned short number_of_actors; //number of actors (enemies) in map
unsigned short field_0x4;
unsigned short field_0x6; //the rest is kinda 0
unsigned short field_0x8;
unsigned short field_0xA;
unsigned short field_0xC;
unsigned short field_0xE;
}
A chunk contains basic positional information, up to 32 objects (models), and 16 event tiles. The number of tiles within the chunk is listed by size (i.e. how many tiles are included), but the actual tile list appears only after all chunks have been defined. While more than one chunk can exist, 99% of the time only one is used. Rotation and translation data is applied only from the first chunk. For additional chunks, this data is ignored. Also while rotation works the developers seam to never used it, and produces rather strange results in game anyway, but its fun regardless.
Additionally, there's a "base tile", its purpose its rather dubious. It seems to duplicate the data from the tile where the player spawns, but the game appears to never actually read or use this information.
struct mpdfileChunkDef{
mpdfileChunkHeader header;
std::array<mpdfileChunkObject, 32> objects; //80 - (80 + 1152 bytes)
std::array<mpdfileChunkEventTile, 16> eventTiles; // 1232 - (1232+ 64)
mpdfileTile baseTile; // Never used by the game?
};
Why the chunks are needed in the first place, don’t ask me. As basically all maps only use one. But you can rotate and transform a chunk and all of its content inside freely, which never was used in game and creates strange results, but its fun regardless.
Additionally, there's a base tile that duplicates all the data from the tile where the player spawns. Why they choose to copy everything instead of simply saving the coordinates (x,z) of its location on the map, I have no clue, but I suppose it gets the job done. And yes, if there are multiple chunks, only the first one is utilized; the others appear to be dead data.
struct mpdfileChunkHeader { // 64 bytes
float map_offset_x, map_offset_y, map_offset_z, map_offset_w;
float xRot, yRot, zRot, wRot; //works but totaly broken ingame
float map_offset2_x, map_offset2_y, map_offset2_z; //why there are two offsets I don't know
unsigned short number_of_tiles;
unsigned short unknown2;
unsigned short index;
unsigned char unknown_data2[14];
};
So first come the chunks that are fixed size, the tiles are stored separately after the chunks in a big list. The amount of them for each chunk you can find in it's header.
Objects refer to models, and each chunk can hold a maximum of 32 of them. If an object is not used, it's set to be invisible. The MPD file doesn't actually contain any geometry; instead, it's stored inside the MPP file.
In addition to transformations, the texture scroll speed for certain textures, like water, is also stored. Transformation may seem straightforward at first, but for some reason, it's overly complicated and over-engineered.
struct mpdfileChunkObject //36 bytes
{
short x, y, z; // y is negative for some reason?
short xRot, yRot, zRot; // in degree
short xScale, yScale, zScale; // 0-100
short unk10;
unsigned char unk11, model_id, visible, unk12;
char textureScrollX, textureScrollY; //this defines the speed and direction of texture scroll for flaged textures
unsigned char pad[10];
};
How the transformation is applied is very weird. Instead of applying the values directly to a single node in the scene graph, the translation plus a fixed scale is applied to the object node, while rotation and scale are applied to each root node of the object itself.
I use OpenGl, so my up axis is Y.
Event tiles are (x,z) positions to mark tiles with additional information.
truct mpdfileChunkEventTile // 4 byte
{
char x, z;
unsigned short eventID;
};
Only one event ID is currently known: ID 1 is used to mark where the teleporter will spawn.
Each tile consists of:
struct mpdfileTile {
mpdfileTexdata textures[12]; // Base color: N, E, S, W, Top; Decal layer: N, E, S, W, Top; Shadowmap; Unknown (is always zero?)
/*
* NW2-----NE3
* | |
* | |
* SW0-----SE1
*/
std::array<char, 4> cornersTop; // Height in each corner of the tile, negative = up; (SW, SE, NW, NE)
std::array<char, 2> sideN; //North bottom points, are usualy 0 if side face is hidden or starts from ground
std::array<char, 2> sideE; //East bottom points, are usualy 0 if side face is hidden or starts from ground
std::array<char, 2> sideS; //South bottom points, are usualy 0 if side face is hidden or starts from ground
std::array<char, 2> sideW; //West bottom points, are usualy 0 if side face is hidden or starts from ground
unsigned char unk2[4]; // TBD
unsigned char visible;
char x, z; //position of tile
unsigned char unk4, unk5, unk6, unk7;
unsigned char mobility; // 0 = Unlimited, 1 = Fly only, 2 = Impassable
unsigned char geo_color;
unsigned char geo_mark; // 0 = No geopanel, 100 = Has geopanel
unsigned char pad2[6];
};
struct mpdfileTexdata textures[12];
The faces of a tile can have a up to two sets of UV Maps, one for a Base Color and one for a Decal Layer. Usage is indicated if textureIndex is zero or not. The top face of the tile can additionally have a shadow map, which functions the same as a decal. (The shadow map seams to be drawn by hand)
Base color:[N, W, S, E, Top], Decal:[N, W, S, E, Top], Shadowmap:[Top], [Unknown (is always zero?) ]
Each entry consists of:
struct mpdfileTexdata{ //8 bytes
unsigned char u;
unsigned char v;
unsigned char vRight;
unsigned char width;
unsigned char height;
unsigned char heightRight;
unsigned char textureIndex;
unsigned char transform; //rotation(0b11) - mirrorX(0b100) - mirrorY(0b1000) - decalmode(0b110000)
};
The four texture corners are computed as:
(u,v) (u + width, vRight)
A---------B
| |
| |
| |
| |
C---------D
(u,v + height) (u + width, vRight + heightRight)
struct mpdfileActor
{
unsigned short id; // Class ID
unsigned short level;
unsigned char alliance; //0 = User controlled, 1 = Enemy, 2 = Enemy, third party (will have turn first), 3 = Passive (will do nothing), 4 = Neutral (will do nothing, and not agro enemies)
char x, z;
char rotation; // -1 = W, 0 = N, 1 = E, 2 = S
unsigned char visible;
unsigned char ai; // 0 = Disabled, 1 = Guard, 2 = Attack
unsigned char unk5, unk6; //health multiplyer? speed?
unsigned short items[4];
unsigned char appearance; // 0 = Appear Normal, 1 = Absent 2nd, 2 = Appear 2nd
unsigned char unk7;
unsigned short geo_effect; // Only used for geosymbols, val%10 = color, val/10 = effect
unsigned short unk8;
unsigned short magic[4];
char unk[30]; // TBD stat modifiers?
};
Each actor can carry up to 4 items, stored as an array of item IDs. The first item in this array must always be a weapon, with its ID taken from the list in MITEM.DAT. However, not all items are compatible with all actors: for instance, monsters can only use weapons designated for monsters. Using incompatible items will cause the game to break (it plays wrong animations causing a softlock).
Only used by Geo Symbols with id 2800 and is a compound number: Is structured so that the last digit determines the color (val % 10), while the remaining digits (from integer division by 10) specify the effect id. The effect descriptions are defined in GE.DAT and the colors follow binary counting.