// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// 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 . major_radius = rs . getFloat ( "major_radius" ) ;
this . minor_radius = rs . getFloat ( "minor_radius" ) ;
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 have a size of zero in the table
// allowing the enum CityBoundsType to adjust them.
if ( this . major_radius = = 0 ) {
this . minor_radius = Enum . CityBoundsType . ZONE . halfExtents ;
this . major_radius = Enum . CityBoundsType . ZONE . halfExtents ;
}
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 ( ) ) ;
}
}