You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							426 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							426 lines
						
					
					
						
							12 KiB
						
					
					
				| // • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ . | |
| // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· | |
| // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ | |
| // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ | |
| // ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀ | |
| //      Magicbane Emulator Project © 2013 - 2022 | |
| //                www.magicbane.com | |
|  | |
|  | |
| package engine.objects; | |
|  | |
| import engine.Enum; | |
| import engine.InterestManagement.HeightMap; | |
| 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<Building> zoneBuildingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); | |
|     public final Set<NPC> zoneNPCSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); | |
|     public final Set<Mob> zoneMobSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); | |
|     private final int playerCityID; | |
|     private final String zoneName; | |
|     private final float xCoord; | |
|     private final float zCoord; | |
|     private final float yCoord; | |
|     private final int loadNum; | |
|     private final byte safeZone; | |
|     private final String Icon1; | |
|     private final String Icon2; | |
|     private final String Icon3; | |
|     public float absX = 0.0f; | |
|     public float absY = 0.0f; | |
|     public float absZ = 0.0f; | |
|     public int minLvl; | |
|     public int maxLvl; | |
|     public boolean hasBeenHotzone = false; | |
|     private ArrayList<Zone> nodes = null; | |
|     private int parentZoneID; | |
|     private Zone parent = null; | |
|     private Bounds bounds; | |
|     private boolean isNPCCity = false; | |
|     private boolean isPlayerCity = false; | |
|     private String hash; | |
|     private float worldAltitude = 0; | |
|     private float seaLevel = 0; | |
|     //public static ArrayList<Mob> respawnQue = new ArrayList<>(); | |
|     public static final Set<Mob> respawnQue = Collections.newSetFromMap(new ConcurrentHashMap<>()); | |
|     public static long lastRespawn = 0; | |
|     /** | |
|      * ResultSet Constructor | |
|      */ | |
|     public Zone(ResultSet rs) throws SQLException { | |
|         super(rs); | |
|         this.parentZoneID = rs.getInt("parent"); | |
|         this.playerCityID = rs.getInt("isPlayerCity"); | |
|         this.isPlayerCity = this.playerCityID != 0; | |
|         this.zoneName = rs.getString("Name"); | |
|         this.xCoord = rs.getFloat("XCoord"); | |
|         this.zCoord = rs.getFloat("ZCoord"); | |
|         this.yCoord = rs.getFloat("YOffset"); | |
|         this.loadNum = rs.getInt("LoadNum"); | |
|         this.safeZone = rs.getByte("SafeZone"); | |
|         this.Icon1 = rs.getString("Icon1"); | |
|         this.Icon2 = rs.getString("Icon2"); | |
|         this.Icon3 = rs.getString("Icon3"); | |
|         this.hash = rs.getString("hash"); | |
|  | |
|         this.minLvl = rs.getInt("minLvl"); | |
|         this.maxLvl = rs.getInt("maxLvl"); | |
|  | |
|         //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.minLvl == 0 && parentZone != null) { | |
|             this.minLvl = parentZone.minLvl; | |
|             this.maxLvl = parentZone.maxLvl; | |
|         } | |
|  | |
|         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.loadNum == 0 && zone.playerCityID == 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.playerCityID > 0) { | |
|             writer.put((byte) 1); // Player City - True | |
|             writer.putFloat(Enum.CityBoundsType.ZONE.extents); | |
|             writer.putFloat(Enum.CityBoundsType.ZONE.extents); | |
|         } else | |
|             writer.put((byte) 0); // Player City - False | |
|  | |
|         writer.putFloat(zone.xCoord); | |
|         writer.putFloat(zone.zCoord); | |
|         writer.putFloat(zone.yCoord); | |
|  | |
|         writer.putInt(0); | |
|         writer.putInt(0); | |
|         writer.putInt(zone.loadNum); | |
|  | |
|         if (zone.playerCityID > 0) { | |
|             City k = City.getCity(zone.playerCityID); | |
|  | |
|             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.playerCityID); | |
|  | |
|         if (city != null) | |
|             writer.putString(city.getCityName()); | |
|         else | |
|             writer.putString(zone.zoneName); | |
|         writer.put(zone.safeZone); | |
|         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() { | |
|  | |
|         float halfExtentX; | |
|         float halfExtentY; | |
|  | |
|         // Set initial bounds object | |
|  | |
|         this.bounds = Bounds.borrow(); | |
|  | |
|         // Player cities are assigned default value | |
|  | |
|         if (this.loadNum == 0) { | |
|             bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents), 0.0f); | |
|             return; | |
|         } | |
|  | |
|         Vector2f zoneSize = ZoneManager._zone_size_data.get(this.loadNum); | |
|  | |
|         // 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.extents, Enum.CityBoundsType.ZONE.extents), 0.0f); | |
|  | |
|     } | |
|  | |
|     public int getPlayerCityUUID() { | |
|         return this.playerCityID; | |
|     } | |
|  | |
|     public String getName() { | |
|         return zoneName; | |
|     } | |
|  | |
|     public float getXCoord() { | |
|         return xCoord; | |
|     } | |
|  | |
|     public float getYCoord() { | |
|         return yCoord; | |
|     } | |
|  | |
|     public float getZCoord() { | |
|         return zCoord; | |
|     } | |
|  | |
|     public int getLoadNum() { | |
|         return loadNum; | |
|     } | |
|  | |
|     public byte getSafeZone() { | |
|         return safeZone; | |
|     } | |
|  | |
|     public String getIcon1() { | |
|         return Icon1; | |
|     } | |
|  | |
|     public void generateWorldAltitude() { | |
|  | |
|         if (ZoneManager.getSeaFloor().getObjectUUID() == this.getObjectUUID()) { | |
|             this.worldAltitude = MBServerStatics.SEA_FLOOR_ALTITUDE; | |
|             return; | |
|         } | |
|  | |
|         Zone parentZone = this.parent; | |
|  | |
|         Zone currentZone = this; | |
|         float altitude = this.absY; | |
|  | |
|         //seafloor only zone with null parent; | |
|  | |
|         while (parentZone != ZoneManager.getSeaFloor()) { | |
|  | |
|             if (parentZone.getHeightMap() != null) { | |
|  | |
|                 Vector2f zoneLoc = ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone); | |
|                 altitude += parentZone.getHeightMap().getInterpolatedTerrainHeight(zoneLoc); | |
|  | |
|             } | |
|             currentZone = parentZone; | |
|             parentZone = parentZone.parent; | |
|  | |
|         } | |
|  | |
|         this.worldAltitude = altitude; | |
|  | |
|         if (ZoneManager.getSeaFloor().equals(this)) | |
|             this.seaLevel = 0; | |
|         else if | |
|         (this.getHeightMap() != null && this.getHeightMap().getSeaLevel() == 0) { | |
|             this.seaLevel = this.parent.seaLevel; | |
|  | |
|         } else if (this.getHeightMap() != null) { | |
|             this.seaLevel = this.worldAltitude + this.getHeightMap().getSeaLevel(); | |
|         } else { | |
|             this.seaLevel = this.parent.seaLevel; | |
|         } | |
|  | |
|     } | |
|  | |
|     public Zone getParent() { | |
|         return this.parent; | |
|     } | |
|  | |
|     public void setParent(final Zone value) { | |
|  | |
|         this.parent = value; | |
|         this.parentZoneID = (this.parent != null) ? this.parent.getObjectUUID() : 0; | |
|  | |
|         if (this.parent != null) { | |
|             this.absX = this.xCoord + parent.absX; | |
|             this.absY = this.yCoord + parent.absY; | |
|             this.absZ = this.zCoord + parent.absZ; | |
|  | |
|             if (this.minLvl == 0 || this.maxLvl == 0) { | |
|                 this.minLvl = this.parent.minLvl; | |
|                 this.maxLvl = this.parent.maxLvl; | |
|             } | |
|         } else {  //only the Sea Floor zone does not have a parent | |
|             this.absX = this.xCoord; | |
|             this.absY = MBServerStatics.SEA_FLOOR_ALTITUDE; | |
|             this.absZ = this.zCoord; | |
|         } | |
|  | |
|         // Zone AABB is set here as it's coordinate space is world requiring a parent. | |
|         this.setBounds(); | |
|  | |
|         if (this.getHeightMap() != null && this.getHeightMap().getSeaLevel() != 0) | |
|             this.seaLevel = this.getHeightMap().getSeaLevel(); | |
|  | |
|     } | |
|  | |
|     public float getAbsX() { | |
|         return this.absX; | |
|     } | |
|  | |
|     public float getAbsY() { | |
|         return this.absY; | |
|     } | |
|  | |
|     public float getAbsZ() { | |
|         return this.absZ; | |
|     } | |
|  | |
|     public boolean isMacroZone() { | |
|  | |
|         // Macro zones have icons. | |
|  | |
|         if (this.isPlayerCity == true) | |
|             return false; | |
|  | |
|         if (this.parent == null) | |
|             return false; | |
|  | |
|         return !this.getIcon1().equals(""); | |
|     } | |
|  | |
|     public boolean isNPCCity() { | |
|         return this.isNPCCity; | |
|     } | |
|  | |
|     public void setNPCCity(boolean value) { | |
|         this.isNPCCity = value; | |
|     } | |
|  | |
|     public boolean isPlayerCity() { | |
|         return this.isPlayerCity; | |
|     } | |
|  | |
|     public void setPlayerCity(boolean value) { | |
|         this.isPlayerCity = value; | |
|     } | |
|  | |
|     public Vector3fImmutable getLoc() { | |
|         return new Vector3fImmutable(this.absX, this.absY, this.absZ); | |
|     } | |
|  | |
|     public int getParentZoneID() { | |
|         return this.parentZoneID; | |
|     } | |
|  | |
|     public ArrayList<Zone> 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.getParent().equals(ZoneManager.getSeaFloor()); | |
|  | |
|     } | |
|  | |
|     /** | |
|      * @return the bounds | |
|      */ | |
|     public Bounds getBounds() { | |
|         return bounds; | |
|     } | |
|  | |
|     public String getHash() { | |
|         return hash; | |
|     } | |
|  | |
|     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. | |
|  | |
|     public HeightMap getHeightMap() { | |
|  | |
|         if (this.isPlayerCity) | |
|             return HeightMap.PlayerCityHeightMap; | |
|  | |
|         return HeightMap.heightmapByLoadNum.get(this.loadNum); | |
|     } | |
|  | |
|     public float getSeaLevel() { | |
|         return seaLevel; | |
|     } | |
|  | |
|     public float getWorldAltitude() { | |
|         return worldAltitude; | |
|     } | |
|  | |
| }
 | |
| 
 |