Parse CZONE zone templates at world server boot
ZoneParser reads mb.data/CZONE/<id>.json into CZoneTemplate with its props, child props, mobiles and point sets (World-Boot-Plan Task 1.1). Only the agreed server-needed fields are parsed; textures, water, weather, sound and always-default fields are skipped. Boot aborts if any file fails. Nothing uses the data yet (Task 1.2). New mbEnums: ZoneShapeType, SeaLevelType, TerrainGenType, ZonePropType, ZoneMobileType, PointSetType. root FSM maps to MobBehaviourType and monster types to MonsterType. Value changes: none (load only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a8b8858f0e
commit
ac1fcaecf5
9 files changed
+622
No files matched your search
@@ -3147,6 +3147,41 @@ public class mbEnums {
|
||||
MULTIPLY
|
||||
}
|
||||
|
||||
// CZONE value sets; only the values present in the shipped cache.
|
||||
|
||||
public enum ZoneShapeType {
|
||||
ELLIPTICAL,
|
||||
RECTANGULAR
|
||||
}
|
||||
|
||||
public enum SeaLevelType {
|
||||
PARENT,
|
||||
SELF,
|
||||
WORLD
|
||||
}
|
||||
|
||||
// NONE is the server's marker for a zone with no zone_terrain_gen block
|
||||
|
||||
public enum TerrainGenType {
|
||||
NONE,
|
||||
TARGA,
|
||||
PLANAR
|
||||
}
|
||||
|
||||
public enum ZonePropType {
|
||||
PROP,
|
||||
CITY
|
||||
}
|
||||
|
||||
public enum ZoneMobileType {
|
||||
MOBILE,
|
||||
TRAINER
|
||||
}
|
||||
|
||||
public enum PointSetType {
|
||||
POINTS_ORDEREDREV
|
||||
}
|
||||
|
||||
public enum ServerConfig {
|
||||
MBDB_108("AERYNTH", "ARAC", "MOURNING", 300000),
|
||||
MBDB_192("DALGOTH", "ARAC", "GOOK", 300001),
|
||||
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.cache;
|
||||
|
||||
import engine.gameManager.ConfigManager;
|
||||
import engine.mbEnums;
|
||||
import engine.parsers.objects.CZoneDrop;
|
||||
import engine.parsers.objects.CZoneMobile;
|
||||
import engine.parsers.objects.CZonePlacement;
|
||||
import engine.parsers.objects.CZonePointSet;
|
||||
import engine.parsers.objects.CZoneProp;
|
||||
import engine.parsers.objects.CZoneTemplate;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
// Loads zone templates from the client cache JSON (CZONE/<id>.json).
|
||||
|
||||
public class ZoneParser {
|
||||
|
||||
private static final String zonePath = ConfigManager.DEFAULT_DATA_DIR + "CZONE/";
|
||||
|
||||
public static HashMap<Integer, CZoneTemplate> parseZoneFolder() {
|
||||
|
||||
HashMap<Integer, CZoneTemplate> zoneTemplates = new HashMap<>();
|
||||
File[] zoneFiles = CacheParserUtil.listJsonFiles(zonePath);
|
||||
int templateId;
|
||||
JSONObject zoneJson;
|
||||
int failedCount = 0;
|
||||
|
||||
for (File zoneFile : zoneFiles) {
|
||||
try {
|
||||
templateId = CacheParserUtil.templateIdFromFile(zoneFile);
|
||||
zoneJson = CacheParserUtil.readJsonFile(zoneFile);
|
||||
zoneTemplates.put(templateId, parseZoneEntry(templateId, zoneJson));
|
||||
} catch (Exception e) {
|
||||
Logger.error(zoneFile.getName() + ": " + e);
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.info("Loaded " + zoneTemplates.size() + " of " + zoneFiles.length + " zone templates");
|
||||
logCounts(zoneTemplates);
|
||||
|
||||
// Partial zone data is never usable; null tells the caller to abort
|
||||
if (failedCount > 0)
|
||||
return null;
|
||||
|
||||
return zoneTemplates;
|
||||
}
|
||||
|
||||
private static CZoneTemplate parseZoneEntry(int templateId, JSONObject zoneJson) {
|
||||
|
||||
CZoneTemplate zone = new CZoneTemplate();
|
||||
JSONObject terrainJson;
|
||||
JSONArray propsJson;
|
||||
JSONArray mobilesJson;
|
||||
JSONArray pointSetsJson;
|
||||
|
||||
zone.template_id = templateId;
|
||||
zone.zone_type = CacheParserUtil.readEnum(mbEnums.ZoneShapeType.class, zoneJson.getString("zone_type"), templateId, "zone_type");
|
||||
zone.zone_name = zoneJson.getString("zone_name");
|
||||
zone.zone_peace_zone = zoneJson.getBoolean("zone_peace_zone");
|
||||
zone.zone_major_radius = zoneJson.getFloat("zone_major_radius");
|
||||
zone.zone_minor_radius = zoneJson.getFloat("zone_minor_radius");
|
||||
zone.zone_min_blend = zoneJson.getFloat("zone_min_blend");
|
||||
zone.zone_max_blend = zoneJson.getFloat("zone_max_blend");
|
||||
zone.zone_sea_level = zoneJson.getFloat("zone_sea_level");
|
||||
zone.zone_sea_level_type = CacheParserUtil.readEnum(mbEnums.SeaLevelType.class, zoneJson.getString("zone_sea_level_type"),
|
||||
templateId, "zone_sea_level_type");
|
||||
zone.zone_has_water = zoneJson.getBoolean("zone_has_water");
|
||||
|
||||
zone.zone_has_terrain_gen = zoneJson.getBoolean("zone_has_terrain_gen");
|
||||
zone.terrain_type = mbEnums.TerrainGenType.NONE;
|
||||
|
||||
if (zone.zone_has_terrain_gen) {
|
||||
terrainJson = zoneJson.getJSONObject("zone_terrain_gen");
|
||||
zone.terrain_type = CacheParserUtil.readEnum(mbEnums.TerrainGenType.class, terrainJson.getString("terrain_type"),
|
||||
templateId, "terrain_type");
|
||||
|
||||
if (zone.terrain_type == mbEnums.TerrainGenType.TARGA) {
|
||||
zone.terrain_max_y = terrainJson.getFloat("terrain_max_y");
|
||||
zone.terrain_image = terrainJson.getInt("terrain_image");
|
||||
}
|
||||
}
|
||||
|
||||
propsJson = zoneJson.getJSONArray("zone_prop_info");
|
||||
|
||||
for (int i = 0; i < propsJson.length(); i++)
|
||||
zone.zone_prop_info.add(parseProp(propsJson.getJSONObject(i), templateId));
|
||||
|
||||
mobilesJson = zoneJson.getJSONArray("zone_mobile_info");
|
||||
|
||||
for (int i = 0; i < mobilesJson.length(); i++)
|
||||
zone.zone_mobile_info.add(parseMobile(mobilesJson.getJSONObject(i), templateId));
|
||||
|
||||
pointSetsJson = zoneJson.getJSONArray("zone_point_sets");
|
||||
|
||||
for (int i = 0; i < pointSetsJson.length(); i++)
|
||||
zone.zone_point_sets.add(parsePointSet(pointSetsJson.getJSONObject(i), templateId));
|
||||
|
||||
return zone;
|
||||
}
|
||||
|
||||
private static CZonePlacement parsePlacement(JSONObject placementJson, int templateId) {
|
||||
|
||||
CZonePlacement placement = new CZonePlacement();
|
||||
JSONObject sparseJson;
|
||||
|
||||
placement.basic_zone_spawn_location = CacheParserUtil.readVector3(placementJson.getJSONArray("basic_zone_spawn_location"));
|
||||
placement.basic_zone_name_override = placementJson.getString("basic_zone_name_override");
|
||||
placement.basic_zone_time_to_respawn = placementJson.getFloat("basic_zone_time_to_respawn");
|
||||
placement.basic_zone_spawn_radius = placementJson.getFloat("basic_zone_spawn_radius");
|
||||
placement.basic_zone_y_rot = placementJson.getFloat("basic_zone_y_rot");
|
||||
placement.basic_zone_template_id = (int) placementJson.getLong("basic_zone_template_id");
|
||||
placement.basic_zone_level_number = placementJson.getInt("basic_zone_level_number");
|
||||
placement.basic_zone_room_number = placementJson.getInt("basic_zone_room_number");
|
||||
|
||||
sparseJson = placementJson.getJSONObject("basic_zone_sparse_data");
|
||||
|
||||
for (String key : sparseJson.keySet()) {
|
||||
if (!key.equals("IMMUNE_TO_STEAL"))
|
||||
throw new IllegalArgumentException(templateId + ": basic_zone_sparse_data has unknown key '" + key + "'");
|
||||
|
||||
placement.basic_zone_immune_to_steal = sparseJson.getBoolean(key);
|
||||
}
|
||||
|
||||
return placement;
|
||||
}
|
||||
|
||||
private static CZoneProp parseProp(JSONObject propJson, int templateId) {
|
||||
|
||||
CZoneProp prop = new CZoneProp();
|
||||
JSONObject dataJson;
|
||||
JSONArray childrenJson;
|
||||
JSONArray mobilesJson;
|
||||
|
||||
prop.prop_type = CacheParserUtil.readEnum(mbEnums.ZonePropType.class, propJson.getString("prop_type"), templateId, "prop_type");
|
||||
|
||||
dataJson = propJson.getJSONObject("prop_data");
|
||||
prop.prop_basic_zone = parsePlacement(dataJson.getJSONObject("prop_basic_zone"), templateId);
|
||||
|
||||
childrenJson = dataJson.getJSONArray("prop_content_props");
|
||||
|
||||
for (int i = 0; i < childrenJson.length(); i++)
|
||||
prop.prop_content_props.add(parseProp(childrenJson.getJSONObject(i), templateId));
|
||||
|
||||
mobilesJson = dataJson.getJSONArray("prop_content_mobiles");
|
||||
|
||||
for (int i = 0; i < mobilesJson.length(); i++)
|
||||
prop.prop_content_mobiles.add(parseMobile(mobilesJson.getJSONObject(i), templateId));
|
||||
|
||||
prop.prop_registry = dataJson.getInt("prop_registry");
|
||||
|
||||
if (prop.prop_registry != 0)
|
||||
prop.prop_registry_name = dataJson.getString("prop_registry_name");
|
||||
|
||||
prop.prop_has_teleporter = dataJson.getBoolean("prop_has_teleporter");
|
||||
|
||||
if (prop.prop_has_teleporter)
|
||||
prop.prop_teleporter = (int) dataJson.getLong("prop_teleporter");
|
||||
|
||||
if (prop.prop_type == mbEnums.ZonePropType.CITY) {
|
||||
prop.city_prop_gm_guild = (int) dataJson.getLong("city_prop_gm_guild");
|
||||
prop.city_prop_gm_nation = (int) dataJson.getLong("city_prop_gm_nation");
|
||||
prop.city_prop_asset_rank = dataJson.getInt("city_prop_asset_rank");
|
||||
prop.city_prop_asset_funds = dataJson.getInt("city_prop_asset_funds");
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
private static CZoneMobile parseMobile(JSONObject mobileJson, int templateId) {
|
||||
|
||||
CZoneMobile mobile = new CZoneMobile();
|
||||
JSONObject dataJson;
|
||||
JSONArray equipmentJson;
|
||||
JSONArray equipJson;
|
||||
JSONArray bootiesJson;
|
||||
JSONObject bootyJson;
|
||||
JSONArray itemsJson;
|
||||
String rootFsm;
|
||||
|
||||
mobile.mobile_type = CacheParserUtil.readEnum(mbEnums.ZoneMobileType.class, mobileJson.getString("mobile_type"), templateId, "mobile_type");
|
||||
|
||||
dataJson = mobileJson.getJSONObject("mobile_data");
|
||||
mobile.mobile_base_zone = parsePlacement(dataJson.getJSONObject("mobile_base_zone"), templateId);
|
||||
|
||||
mobile.mobile_number_in_group = dataJson.getInt("mobile_number_in_group");
|
||||
mobile.mobile_level = dataJson.getInt("mobile_level");
|
||||
mobile.mobile_dialog_override = (int) dataJson.getLong("mobile_dialog_override");
|
||||
mobile.mobile_rune_stone_ids.addAll(CacheParserUtil.readIntList(dataJson.getJSONArray("mobile_rune_stone_ids")));
|
||||
mobile.mobile_spawn_hps.addAll(CacheParserUtil.readIntList(dataJson.getJSONArray("mobile_spawn_hps")));
|
||||
|
||||
// Equipment entries are [item id, chance]
|
||||
|
||||
equipmentJson = dataJson.getJSONArray("mobile_equipment");
|
||||
|
||||
for (int i = 0; i < equipmentJson.length(); i++) {
|
||||
equipJson = equipmentJson.getJSONArray(i);
|
||||
mobile.mobile_equipment.add(new CZoneDrop((int) equipJson.getLong(0), equipJson.getFloat(1)));
|
||||
}
|
||||
|
||||
// Booty items are [chance, item id] pairs, the reverse of equipment
|
||||
|
||||
bootiesJson = dataJson.getJSONArray("mobile_booties");
|
||||
|
||||
for (int i = 0; i < bootiesJson.length(); i++) {
|
||||
bootyJson = bootiesJson.getJSONObject(i);
|
||||
|
||||
if (!bootyJson.getString("type").equals("ITEM"))
|
||||
throw new IllegalArgumentException(templateId + ": mobile_booties has unknown type '" + bootyJson.getString("type") + "'");
|
||||
|
||||
itemsJson = bootyJson.getJSONArray("items");
|
||||
|
||||
for (int j = 0; j + 1 < itemsJson.length(); j += 2)
|
||||
mobile.mobile_booties.add(new CZoneDrop((int) itemsJson.getLong(j + 1), itemsJson.getFloat(j)));
|
||||
}
|
||||
|
||||
rootFsm = dataJson.getString("mobile_root_fs_mid");
|
||||
|
||||
if (!rootFsm.isEmpty())
|
||||
mobile.mobile_root_fs_mid = CacheParserUtil.readEnum(mbEnums.MobBehaviourType.class, rootFsm, templateId, "mobile_root_fs_mid");
|
||||
|
||||
mobile.mobile_has_home_goal = dataJson.getBoolean("mobile_has_home_goal");
|
||||
|
||||
if (mobile.mobile_has_home_goal) {
|
||||
mobile.mobile_home_goal = (int) dataJson.getLong("mobile_home_goal");
|
||||
mobile.mobile_spawn_home_goal = dataJson.getBoolean("mobile_spawn_home_goal");
|
||||
}
|
||||
|
||||
mobile.mobile_has_target_goal = dataJson.getBoolean("mobile_has_target_goal");
|
||||
|
||||
if (mobile.mobile_has_target_goal) {
|
||||
mobile.mobile_target_goal = (int) dataJson.getLong("mobile_target_goal");
|
||||
mobile.mobile_spawn_target_goal = dataJson.getBoolean("mobile_spawn_target_goal");
|
||||
}
|
||||
|
||||
mobile.mobile_has_group_tactics = dataJson.getBoolean("mobile_has_group_tactics");
|
||||
|
||||
if (mobile.mobile_has_group_tactics)
|
||||
mobile.mobile_group_tactics = (int) dataJson.getLong("mobile_group_tactics");
|
||||
|
||||
mobile.mobile_enemy_monster_types.addAll(CacheParserUtil.readEnumSet(mbEnums.MonsterType.class,
|
||||
dataJson.getJSONArray("mobile_enemy_monster_types"), templateId, "mobile_enemy_monster_types"));
|
||||
mobile.mobile_not_enemy_monster_types.addAll(CacheParserUtil.readEnumSet(mbEnums.MonsterType.class,
|
||||
dataJson.getJSONArray("mobile_not_enemy_monster_types"), templateId, "mobile_not_enemy_monster_types"));
|
||||
mobile.mobile_friendly_charters.addAll(CacheParserUtil.readIntList(dataJson.getJSONArray("mobile_friendly_charters")));
|
||||
mobile.mobile_parley_name = dataJson.getString("mobile_parley_name");
|
||||
|
||||
if (mobile.mobile_type == mbEnums.ZoneMobileType.TRAINER) {
|
||||
mobile.trainer_std_profit_margin = dataJson.getFloat("trainer_std_profit_margin");
|
||||
mobile.trainer_profit_margin = dataJson.getFloat("trainer_profit_margin");
|
||||
}
|
||||
|
||||
return mobile;
|
||||
}
|
||||
|
||||
private static CZonePointSet parsePointSet(JSONObject pointSetJson, int templateId) {
|
||||
|
||||
CZonePointSet pointSet = new CZonePointSet();
|
||||
JSONArray pointsJson;
|
||||
JSONObject pointJson;
|
||||
CZonePointSet.Point point;
|
||||
|
||||
pointSet.zone_goal_type = CacheParserUtil.readEnum(mbEnums.PointSetType.class, pointSetJson.getString("zone_goal_type"),
|
||||
templateId, "zone_goal_type");
|
||||
pointSet.zone_goal_value = (int) pointSetJson.getLong("zone_goal_value");
|
||||
pointSet.zone_goal_name = pointSetJson.getString("zone_goal_name");
|
||||
|
||||
pointsJson = pointSetJson.getJSONArray("zone_goal_points");
|
||||
|
||||
for (int i = 0; i < pointsJson.length(); i++) {
|
||||
pointJson = pointsJson.getJSONObject(i);
|
||||
point = new CZonePointSet.Point();
|
||||
point.zone_goal_point_location = CacheParserUtil.readVector3(pointJson.getJSONArray("zone_goal_point_location"));
|
||||
point.zone_goal_point_vectors = CacheParserUtil.readIntArray(pointJson.getJSONArray("zone_goal_point_vectors"));
|
||||
pointSet.zone_goal_points.add(point);
|
||||
}
|
||||
|
||||
return pointSet;
|
||||
}
|
||||
|
||||
// Counts to check against World-Boot-Plan findings §1
|
||||
|
||||
private static void logCounts(HashMap<Integer, CZoneTemplate> zoneTemplates) {
|
||||
|
||||
int topProps = 0;
|
||||
int childProps = 0;
|
||||
int cityProps = 0;
|
||||
int zoneMobiles = 0;
|
||||
int zoneTrainers = 0;
|
||||
int propMobiles = 0;
|
||||
int propTrainers = 0;
|
||||
int pointSets = 0;
|
||||
|
||||
for (CZoneTemplate zone : zoneTemplates.values()) {
|
||||
topProps += zone.zone_prop_info.size();
|
||||
pointSets += zone.zone_point_sets.size();
|
||||
|
||||
for (CZoneMobile mobile : zone.zone_mobile_info) {
|
||||
if (mobile.mobile_type == mbEnums.ZoneMobileType.TRAINER)
|
||||
zoneTrainers++;
|
||||
else
|
||||
zoneMobiles++;
|
||||
}
|
||||
|
||||
for (CZoneProp prop : zone.zone_prop_info) {
|
||||
childProps += prop.prop_content_props.size();
|
||||
|
||||
if (prop.prop_type == mbEnums.ZonePropType.CITY)
|
||||
cityProps++;
|
||||
|
||||
for (CZoneMobile mobile : prop.prop_content_mobiles) {
|
||||
if (mobile.mobile_type == mbEnums.ZoneMobileType.TRAINER)
|
||||
propTrainers++;
|
||||
else
|
||||
propMobiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.info("Zone props: " + topProps + " top level (" + cityProps + " CITY), " + childProps + " children");
|
||||
Logger.info("Zone mobiles: " + zoneMobiles + " MOBILE, " + zoneTrainers + " TRAINER; prop mobiles: "
|
||||
+ propMobiles + " MOBILE, " + propTrainers + " TRAINER; point sets: " + pointSets);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
// One item with a chance, from a CZONE mobile's mobile_equipment or mobile_booties.
|
||||
// Equipment chances are 0–1 fractions; booty chances are percentages (100.0, 50.0).
|
||||
|
||||
public class CZoneDrop {
|
||||
|
||||
public final int item_id;
|
||||
public final float chance;
|
||||
|
||||
public CZoneDrop(int item_id, float chance) {
|
||||
this.item_id = item_id;
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
import engine.mbEnums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
// A MOBILE or TRAINER entry from a CZONE zone (zone_mobile_info) or from a
|
||||
// top-level prop (prop_content_mobiles). Field names match the JSON keys.
|
||||
// Fields that are default in every shipped file are not parsed (Task 1.1 list).
|
||||
|
||||
public class CZoneMobile {
|
||||
|
||||
public mbEnums.ZoneMobileType mobile_type;
|
||||
public CZonePlacement mobile_base_zone;
|
||||
|
||||
public int mobile_number_in_group;
|
||||
public int mobile_level;
|
||||
public int mobile_dialog_override;
|
||||
public final ArrayList<Integer> mobile_rune_stone_ids = new ArrayList<>();
|
||||
public final ArrayList<CZoneDrop> mobile_equipment = new ArrayList<>();
|
||||
public final ArrayList<CZoneDrop> mobile_booties = new ArrayList<>();
|
||||
public final ArrayList<Integer> mobile_spawn_hps = new ArrayList<>();
|
||||
|
||||
// null when the entry has no root FSM
|
||||
|
||||
public mbEnums.MobBehaviourType mobile_root_fs_mid;
|
||||
|
||||
// Goal values are zone_point_sets zone_goal_value hashes
|
||||
|
||||
public boolean mobile_has_home_goal;
|
||||
public int mobile_home_goal;
|
||||
public boolean mobile_spawn_home_goal;
|
||||
public boolean mobile_has_target_goal;
|
||||
public int mobile_target_goal;
|
||||
public boolean mobile_spawn_target_goal;
|
||||
|
||||
public boolean mobile_has_group_tactics;
|
||||
public int mobile_group_tactics;
|
||||
|
||||
public final EnumSet<mbEnums.MonsterType> mobile_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
|
||||
public final EnumSet<mbEnums.MonsterType> mobile_not_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
|
||||
public final ArrayList<Integer> mobile_friendly_charters = new ArrayList<>();
|
||||
public String mobile_parley_name;
|
||||
|
||||
// TRAINER only
|
||||
|
||||
public float trainer_std_profit_margin;
|
||||
public float trainer_profit_margin;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
import engine.math.Vector3fImmutable;
|
||||
|
||||
// The basic_zone_* block shared by every CZONE prop and mobile
|
||||
// (client BasicZoneTemplateData). Field names match the JSON keys.
|
||||
// spawn_location is relative to the parent; y_rot is not the client yaw (W8: yaw = π − y_rot).
|
||||
|
||||
public class CZonePlacement {
|
||||
|
||||
public Vector3fImmutable basic_zone_spawn_location;
|
||||
public String basic_zone_name_override;
|
||||
public float basic_zone_time_to_respawn;
|
||||
public float basic_zone_spawn_radius;
|
||||
public float basic_zone_y_rot;
|
||||
public int basic_zone_template_id;
|
||||
public int basic_zone_level_number;
|
||||
public int basic_zone_room_number;
|
||||
|
||||
// IMMUNE_TO_STEAL is the only sparse key in the shipped cache
|
||||
|
||||
public boolean basic_zone_immune_to_steal;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
import engine.math.Vector3fImmutable;
|
||||
import engine.mbEnums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// A zone_point_sets entry: a named path that mobile home/target goals point at
|
||||
// through zone_goal_value. Field names match the JSON keys.
|
||||
|
||||
public class CZonePointSet {
|
||||
|
||||
public mbEnums.PointSetType zone_goal_type;
|
||||
public int zone_goal_value;
|
||||
public String zone_goal_name;
|
||||
public final ArrayList<Point> zone_goal_points = new ArrayList<>();
|
||||
|
||||
public static class Point {
|
||||
|
||||
public Vector3fImmutable zone_goal_point_location;
|
||||
|
||||
// Six ints; the first is the point's index, the rest are unknown
|
||||
|
||||
public int[] zone_goal_point_vectors;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
import engine.mbEnums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// A PROP or CITY entry from a CZONE zone (zone_prop_info) or a child prop
|
||||
// (prop_content_props). Field names match the JSON keys.
|
||||
// Children never have children or mobiles in the shipped cache.
|
||||
|
||||
public class CZoneProp {
|
||||
|
||||
public mbEnums.ZonePropType prop_type;
|
||||
public CZonePlacement prop_basic_zone;
|
||||
|
||||
public final ArrayList<CZoneProp> prop_content_props = new ArrayList<>();
|
||||
public final ArrayList<CZoneMobile> prop_content_mobiles = new ArrayList<>();
|
||||
|
||||
public int prop_registry;
|
||||
public String prop_registry_name = "";
|
||||
public boolean prop_has_teleporter;
|
||||
public int prop_teleporter;
|
||||
|
||||
// CITY only. Guild and nation are keys into the map's guild seed (W7).
|
||||
|
||||
public int city_prop_gm_guild;
|
||||
public int city_prop_gm_nation;
|
||||
public int city_prop_asset_rank;
|
||||
public int city_prop_asset_funds;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.objects;
|
||||
|
||||
import engine.mbEnums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
// Zone template as defined by the client's cache data (MBDATA/data/cache/CZONE).
|
||||
// Field names match the JSON keys. Only server-needed fields are parsed (Task 1.1 list);
|
||||
// textures, water, weather, sound and fields default in every file are skipped.
|
||||
// A template is reused by many zones; placement lives in the zone tree.
|
||||
//
|
||||
// Read only once loaded.
|
||||
|
||||
public class CZoneTemplate {
|
||||
|
||||
public static HashMap<Integer, CZoneTemplate> _zones = new HashMap<>();
|
||||
|
||||
public int template_id;
|
||||
|
||||
public mbEnums.ZoneShapeType zone_type;
|
||||
public String zone_name;
|
||||
public boolean zone_peace_zone;
|
||||
public float zone_major_radius;
|
||||
public float zone_minor_radius;
|
||||
public float zone_min_blend;
|
||||
public float zone_max_blend;
|
||||
public float zone_sea_level;
|
||||
public mbEnums.SeaLevelType zone_sea_level_type;
|
||||
public boolean zone_has_water;
|
||||
|
||||
// zone_terrain_gen; max_y and image exist only on TARGA terrain
|
||||
|
||||
public boolean zone_has_terrain_gen;
|
||||
public mbEnums.TerrainGenType terrain_type;
|
||||
public float terrain_max_y;
|
||||
public int terrain_image;
|
||||
|
||||
public final ArrayList<CZoneProp> zone_prop_info = new ArrayList<>();
|
||||
public final ArrayList<CZoneMobile> zone_mobile_info = new ArrayList<>();
|
||||
public final ArrayList<CZonePointSet> zone_point_sets = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -36,6 +36,8 @@ import engine.parsers.cache.DeedParser;
|
||||
import engine.parsers.cache.ItemParser;
|
||||
import engine.parsers.cache.ParityCheck;
|
||||
import engine.parsers.cache.RuneParser;
|
||||
import engine.parsers.cache.ZoneParser;
|
||||
import engine.parsers.objects.CZoneTemplate;
|
||||
import engine.parsers.objects.DeedTemplate;
|
||||
import engine.parsers.objects.ItemTemplate;
|
||||
import engine.parsers.objects.RuneTemplate;
|
||||
@@ -430,6 +432,14 @@ public class WorldServer {
|
||||
Logger.info("Indexing Cache Templates");
|
||||
CObjectIndex.build();
|
||||
|
||||
Logger.info("Loading Cache Zone Templates");
|
||||
CZoneTemplate._zones = ZoneParser.parseZoneFolder();
|
||||
|
||||
if (CZoneTemplate._zones == null || CZoneTemplate._zones.isEmpty()) {
|
||||
Logger.error("ABORT! Cache zone templates failed to load!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger.info("Loading Rune Power Requirements");
|
||||
PowerReq.loadRunePowers();
|
||||
|
||||
|
||||
Reference in new issue
Block a user