Parse the map zone tree file (maps/<map>.tree)

Reads uid, parent, template, name, isPlayerCity, offset, peaceZone and
the three icons. Skips childCount (derived), objectId (live NEWWORLD ID,
not needed yet) and extents (player-city only). A non-zero yaw, a missing
offset or a map ID that differs from MB_WORLD_MAPID fails the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-27 20:07:21 -04:00
1 parent 9419d02f42
commit f7b81f46a5
2 files changed
+116

No files matched your search

@@ -0,0 +1,87 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.map;
import engine.gameManager.ConfigManager;
import engine.parsers.cache.CacheParserUtil;
import engine.parsers.objects.ZoneTreeEntry;
import org.json.JSONArray;
import org.json.JSONObject;
import org.pmw.tinylog.Logger;
import java.io.File;
import java.util.ArrayList;
// Loads the zone tree for a map (maps/<map>.tree), in file order: parents always precede their children.
public class ZoneTreeParser {
private static final String mapPath = ConfigManager.DEFAULT_DATA_DIR + "maps/";
public static ArrayList<ZoneTreeEntry> parseZoneTree(int mapID) {
ArrayList<ZoneTreeEntry> zoneTree = new ArrayList<>();
File treeFile = new File(mapPath + mapID + ".tree");
JSONObject treeJson;
JSONArray zonesJson;
if (!treeFile.exists()) {
Logger.error("Zone tree not found: " + treeFile.getAbsolutePath());
return null;
}
try {
treeJson = CacheParserUtil.readJsonFile(treeFile);
if (treeJson.getInt("map") != mapID) {
Logger.error(treeFile.getName() + ": map " + treeJson.getInt("map") + " does not match MB_WORLD_MAPID " + mapID);
return null;
}
zonesJson = treeJson.getJSONArray("zones");
for (int i = 0; i < zonesJson.length(); i++)
zoneTree.add(parseZoneEntry(zonesJson.getJSONObject(i)));
} catch (Exception e) {
Logger.error(treeFile.getName() + ": " + e);
return null;
}
Logger.info("Loaded " + zoneTree.size() + " zones from " + treeFile.getName());
return zoneTree;
}
private static ZoneTreeEntry parseZoneEntry(JSONObject zoneJson) {
ZoneTreeEntry zone = new ZoneTreeEntry();
zone.uid = zoneJson.getInt("uid");
zone.parent = zoneJson.getInt("parent");
zone.template = zoneJson.getInt("template");
zone.name = zoneJson.getString("name");
zone.isPlayerCity = zoneJson.getBoolean("isPlayerCity");
zone.offset = CacheParserUtil.readVector3(zoneJson.getJSONArray("offset"));
zone.peaceZone = zoneJson.getBoolean("peaceZone");
zone.terrain = zoneJson.getString("terrain");
zone.biome = zoneJson.getString("biome");
zone.height = zoneJson.getString("height");
// The server places zones by offset alone; a rotated zone would put everything in it out of place
if (zoneJson.getFloat("yaw") != 0.0f)
throw new IllegalStateException("zone " + zone.uid + " has a non-zero yaw");
if (zone.offset == null)
throw new IllegalStateException("zone " + zone.uid + " has no offset");
return zone;
}
}
@@ -0,0 +1,29 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.objects;
import engine.math.Vector3fImmutable;
// One zone of a <map>.tree file, exported by mbScryer from a recording's NEWWORLD message.
// Field names match the JSON keys; terrain, biome and height are the zone's three icons.
public class ZoneTreeEntry {
public int uid;
public int parent;
public int template;
public String name;
public boolean isPlayerCity;
public Vector3fImmutable offset;
public boolean peaceZone;
public String terrain;
public String biome;
public String height;
}