forked from MagicBane/Server
338 lines
11 KiB
Java
338 lines
11 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
// 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<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 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<Zone> 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<Mob> 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_offset = 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");
|
|
|
|
if (this.guild_zone) {
|
|
this.max_blend = 128;
|
|
this.min_blend = 128;
|
|
this.major_radius = (int) Enum.CityBoundsType.ZONE.halfExtents;
|
|
this.minor_radius = (int) Enum.CityBoundsType.ZONE.halfExtents;
|
|
this.terrain_type = "PLANAR";
|
|
}
|
|
|
|
if (this.terrain_type.equals("NONE"))
|
|
this.terrain = null;
|
|
else
|
|
this.terrain = new Terrain(this);
|
|
|
|
//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<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.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());
|
|
}
|
|
|
|
}
|