// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.objects; import engine.Enum; import engine.InterestManagement.Terrain; import engine.db.archive.DataWarehouse; import engine.gameManager.DbManager; 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 AbstractGameObject { 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 final int playerCityUUID; public final String zoneName; public final float xOffset; public final float zOffset; public final float yOffset; public final int template; 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 hasBeenHotzone = false; public ArrayList nodes = null; public int parentZoneID; public Zone parent = null; public Bounds bounds; public boolean isNPCCity = false; public boolean guild_zone; public String hash; public float worldAltitude = 0; public float seaLevel = 0f; public float sea_level_offset = 0; public static final Set respawnQue = Collections.newSetFromMap(new ConcurrentHashMap<>()); public static long lastRespawn = 0; public float major_radius; public float minor_radius; public float min_blend; public float max_blend; public String sea_level_type; public float sea_level; public String terrain_type; public float terrain_max_y; public int terrain_image; public Terrain terrain = null; /** * ResultSet Constructor */ public Zone(ResultSet rs) throws SQLException { super(rs); this.parentZoneID = rs.getInt("parent"); this.playerCityUUID = rs.getInt("playerCityUUID"); this.guild_zone = this.playerCityUUID != 0; this.zoneName = rs.getString("zone_name"); this.xOffset = rs.getFloat("xOffset"); this.zOffset = rs.getFloat("zOffset"); this.yOffset = rs.getFloat("yOffset"); this.template = rs.getInt("template"); this.peace_zone = rs.getByte("peace_zone"); 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"); this.major_radius = rs.getFloat("major_radius"); this.minor_radius = rs.getFloat("minor_radius"); this.min_blend = rs.getFloat("min_blend"); this.max_blend = rs.getFloat("max_blend"); this.sea_level_type = rs.getString("sea_level_type"); this.sea_level = rs.getFloat("sea_level"); this.terrain_type = rs.getString("terrain_type"); this.terrain_max_y = rs.getFloat("terrain_max_y"); this.terrain_image = rs.getInt("terrain_image"); //this needs to be here specifically for new zones created after server boot (e.g. player city zones) Zone parentZone = ZoneManager.getZoneByUUID(parentZoneID); this.setParent(parentZone); if (this.min_level == 0 && parentZone != null) { this.min_level = parentZone.min_level; this.max_level = parentZone.max_level; } if (parentZone != null) parentZone.addNode(this); // 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.template == 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(Enum.CityBoundsType.ZONE.halfExtents); writer.putFloat(Enum.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.template); 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); } } /* 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(); Vector2f zoneSize = ZoneManager._zone_size_data.get(this.template); // Default to player zone size on error? Maybe log this if (zoneSize != null) this.bounds.setBounds(new Vector2f(this.absX, this.absZ), zoneSize, 0.0f); else bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(Enum.CityBoundsType.ZONE.halfExtents, Enum.CityBoundsType.ZONE.halfExtents), 0.0f); } public void setParent(final Zone value) { this.parent = value; this.parentZoneID = (this.parent != null) ? this.parent.getObjectUUID() : 0; // Seafloor if (this.parent == null) { this.absX = this.xOffset; this.absY = MBServerStatics.SEA_FLOOR_ALTITUDE; this.absZ = this.zOffset; this.seaLevel = 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; } this.setBounds(); this.worldAltitude = ZoneManager.caclulateWorldAltitude(this); setSeaLevel(); } private void setSeaLevel() { int world_sea_level = 0; if (this.parent == null) { this.sea_level = world_sea_level; return; } switch (this.sea_level_type) { case "WORLD": this.sea_level = world_sea_level + this.sea_level_offset; break; case "PARENT": this.sea_level = this.parent.sea_level + this.sea_level_offset; break; case "SELF": this.sea_level = this.worldAltitude + this.sea_level_offset; break; } } public boolean isMacroZone() { // Macro zones have icons. if (this.guild_zone == true) 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; } public ArrayList getNodes() { if (this.nodes == null) { this.nodes = DbManager.ZoneQueries.GET_MAP_NODES(super.getObjectUUID()); //Add reverse lookup for child->parent if (this.nodes != null) for (Zone zone : this.nodes) { zone.setParent(this); } } return nodes; } /* * 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.getSeaFloor())) return false; if (this.getNodes().isEmpty()) return false; if (this.getNodes().get(0).isMacroZone()) return true; return this.parent.equals(ZoneManager.getSeaFloor()); } public void setHash() { this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); // Write hash to player character table DataWarehouse.writeHash(Enum.DataRecordType.ZONE, this.getObjectUUID()); } // Return heightmap for this Zone. }