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.
318 lines
9.5 KiB
318 lines
9.5 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// 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.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<Building> zoneBuildingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); |
|
public final Set<NPC> zoneNPCSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); |
|
public final Set<Mob> 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<Zone> 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(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.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 = Enum.CityBoundsType.ZONE.halfExtents; |
|
this.major_radius = Enum.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(Enum.DataRecordType.ZONE, this.getObjectUUID()); |
|
} |
|
|
|
}
|
|
|