// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.objects; import engine.mbEnums; import engine.InterestManagement.Terrain; import engine.db.archive.DataWarehouse; import engine.gameManager.ZoneManager; import engine.math.Bounds; import engine.math.Vector2f; import engine.math.Vector3fImmutable; import engine.net.ByteBufferWriter; import engine.server.MBServerStatics; import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class Zone extends AbstractWorldObject { public final Set zoneBuildingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); public final Set zoneNPCSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); public final Set zoneMobSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); public ZoneTemplate template; public final int playerCityUUID; public final String zoneName; public float major_radius; public float minor_radius; public final float xOffset; public final float zOffset; public final float yOffset; public final int templateID; public final byte peace_zone; public final String icon1; public final String icon2; public final String icon3; public float absX = 0.0f; public float absY = 0.0f; public float absZ = 0.0f; public int min_level; public int max_level; public boolean wasHotzonw = false; public ArrayList nodes = new ArrayList<>(); public int parentZoneID; public Zone parent = null; public Bounds bounds; public boolean isNPCCity = false; public boolean guild_zone; public String hash; public float global_height = 0; public float sea_level; public Terrain terrain = null; /** * ResultSet Constructor */ public Zone(ResultSet rs) throws SQLException { super(rs); this.parentZoneID = rs.getInt("parent"); this.templateID = rs.getInt("template"); this.zoneName = rs.getString("zone_name"); this.peace_zone = rs.getByte("peace_zone"); this.xOffset = rs.getFloat("xOffset"); this.zOffset = rs.getFloat("zOffset"); this.yOffset = rs.getFloat("yOffset"); this.playerCityUUID = rs.getInt("playerCityUUID"); this.guild_zone = this.playerCityUUID != 0; this.icon1 = rs.getString("icon1"); this.icon2 = rs.getString("icon2"); this.icon3 = rs.getString("icon3"); this.min_level = rs.getInt("min_level"); this.max_level = rs.getInt("max_level"); // If zone doesn't yet hava a hash then write it back to the zone table if (hash == null) setHash(); } public static void serializeForClientMsg(Zone zone, ByteBufferWriter writer) { if (zone.templateID == 0 && zone.playerCityUUID == 0) Logger.warn("Warning! WorldServerMap with ID " + zone.getObjectUUID() + " has a loadnum of 0 (player city) and no city linked. This will probably crash the client!"); // Player City Terraform values serialized here. if (zone.playerCityUUID > 0) { writer.put((byte) 1); // Player City - True writer.putFloat(mbEnums.CityBoundsType.ZONE.halfExtents); writer.putFloat(mbEnums.CityBoundsType.ZONE.halfExtents); } else writer.put((byte) 0); // Player City - False writer.putFloat(zone.xOffset); writer.putFloat(zone.zOffset); writer.putFloat(zone.yOffset); writer.putInt(0); writer.putInt(0); writer.putInt(zone.templateID); if (zone.playerCityUUID > 0) { City k = City.getCity(zone.playerCityUUID); if (k != null) { writer.putInt(k.getObjectType().ordinal()); writer.putInt(k.getObjectUUID()); } else writer.putLong(0x0); } else { writer.putInt(zone.getObjectType().ordinal()); writer.putInt(zone.getObjectUUID()); } writer.putInt(zone.nodes.size()); City city = City.getCity(zone.playerCityUUID); if (city != null) writer.putString(city.getCityName()); else writer.putString(zone.zoneName); writer.put(zone.peace_zone); writer.putString(zone.icon1); writer.putString(zone.icon2); writer.putString(zone.icon3); writer.put((byte) 0); // Pad for (Zone child : zone.nodes) { Zone.serializeForClientMsg(child, writer); } } @Override public void runAfterLoad() { this.template = ZoneManager._zone_templates.get(this.templateID); // First zone is always the seafloor if (ZoneManager.seaFloor == null) ZoneManager.seaFloor = this; // Guild zones use the enum CityBoundsType to adjust // city size. All other zones derive from the JSON // stored within its template. if (this.template == null) { this.minor_radius = mbEnums.CityBoundsType.ZONE.halfExtents; this.major_radius = mbEnums.CityBoundsType.ZONE.halfExtents; } else { this.minor_radius = this.template.minor_radius; this.major_radius = this.template.major_radius; } this.setParent(); this.setBounds(); if (this.template.terrain_type.equals("NONE")) this.terrain = null; else this.terrain = new Terrain(this); this.global_height = ZoneManager.calculateGlobalZoneHeight(this); setSeaLevel(); if (this.min_level == 0 && this.parent != null) { this.min_level = this.parent.min_level; this.max_level = this.parent.max_level; } ZoneManager.populateZoneCollections(this); } /* Method sets a default value for player cities * otherwise using values derived from the loadnum * field in the obj_zone database table. */ public void setBounds() { // Set initial bounds object this.bounds = Bounds.borrow(); this.bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(this.major_radius, this.minor_radius), 0.0f); } public void setParent() { this.parent = ZoneManager.getZoneByUUID(parentZoneID); if (parent != null) parent.addNode(this); // Seafloor if (ZoneManager.seaFloor.equals(this)) { this.absX = this.xOffset; this.absY = MBServerStatics.SEA_FLOOR_ALTITUDE; this.global_height = MBServerStatics.SEA_FLOOR_ALTITUDE; this.absZ = this.zOffset; this.sea_level = 0; this.setBounds(); return; } this.absX = this.xOffset + parent.absX; this.absY = this.yOffset + parent.absY; this.absZ = this.zOffset + parent.absZ; if (this.min_level == 0 || this.max_level == 0) { this.min_level = this.parent.min_level; this.max_level = this.parent.max_level; } } private void setSeaLevel() { int world_sea_level = 0; if (this.parent == null) { this.sea_level = world_sea_level; return; } switch (this.template.sea_level_type) { case "WORLD": this.sea_level = world_sea_level + this.template.sea_level; break; case "PARENT": this.sea_level = this.parent.sea_level + this.template.sea_level; break; case "SELF": this.sea_level = this.global_height + this.template.sea_level; break; } } public boolean isMacroZone() { // Macro zones have icons. if (this.guild_zone) return false; if (this.parent == null) return false; return !icon1.equals(""); } public Vector3fImmutable getLoc() { return new Vector3fImmutable(this.absX, this.absY, this.absZ); } public int getParentZoneID() { return this.parentZoneID; } /* * Serializing */ public void addNode(Zone child) { this.nodes.add(child); } @Override public void updateDatabase() { // TODO Auto-generated method stub } public boolean isContinent() { if (this.equals(ZoneManager.seaFloor)) return false; if (this.nodes.isEmpty()) return false; if (this.nodes.get(0).isMacroZone()) return true; return this.parent.equals(ZoneManager.seaFloor); } public void setHash() { this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); // Write hash to player character table DataWarehouse.writeHash(mbEnums.DataRecordType.ZONE, this.getObjectUUID()); } }