Addition of diagonal GeoEngine.

This commit is contained in:
MobiusDev 2018-09-24 02:31:21 +00:00
parent 0f36a0da37
commit 19e467cfbf
62 changed files with 3447 additions and 4122 deletions

View File

@ -0,0 +1,67 @@
# =================================================================
# Geodata
# =================================================================
# Because of real-time performance we are using geodata files only in
# diagonal L2D format now (using filename e.g. 22_16.l2d).
# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata.
# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion.
# Specifies the path to geodata files. For example, when using geodata files located
# at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/
GeoDataPath = ./data/geodata/
# Player coordinates synchronization, default: 2
# 1 - partial synchronization Client --> Server ; don't use it with geodata
# 2 - partial synchronization Server --> Client ; use this setting with geodata
# -1 - Old system: will synchronize Z only
CoordSynchronize = 2
# =================================================================
# Path checking
# =================================================================
# Line of sight start at X percent of the character height, default: 75
PartOfCharacterHeight = 75
# Maximum height of an obstacle, which can exceed the line of sight, default: 32
MaxObstacleHeight = 32
# =================================================================
# Path finding
# =================================================================
# When line of movement check fails, the pathfinding algoritm is performed to look for
# an alternative path (e.g. walk around obstacle), default: true
PathFinding = true
# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2
PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# Base path weight, when moving from one node to another on axis direction, default: 10
BaseWeight = 10
# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14
DiagonalWeight = 14
# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier.
# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10
ObstacleMultiplier = 10
# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20
# For proper function must be higher than BaseWeight and/or DiagonalWeight.
HeuristicWeight = 20
# Maximum number of generated nodes per one path-finding process, default 3500
MaxIterations = 3500
# =================================================================
# Other
# =================================================================
# When a character falls, he takes damages.
# Default: True
FallDamage = True
# The zones with water are enabled.
# Default: True
AllowWater = True

View File

@ -1,87 +0,0 @@
# ---------------------------------------------------------------------------
# GeoData
# ---------------------------------------------------------------------------
# Pathfinding options:
# 0 = Disabled
# 1 = Enabled using path node files
# 2 = Enabled using geodata cells at runtime
# Default: 0
PathFinding = 2
# Pathfinding array buffers configuration
PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# Weight for nodes without obstacles far from walls
LowWeight = 0.5
# Weight for nodes near walls
MediumWeight = 2
# Weight for nodes with obstacles
HighWeight = 3
# Angle paths will be more "smart", but in cost of higher CPU utilization
AdvancedDiagonalStrategy = True
# Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True
# Default: LowWeight * sqrt(2)
DiagonalWeight = 0.707
# Maximum number of LOS postfilter passes, 0 will disable postfilter.
# Default: 3
MaxPostfilterPasses = 3
# Path debug function.
# Nodes known to pathfinder will be displayed as adena, constructed path as antidots.
# Number of the items show node cost * 10
# Potions display path after first stage filter
# Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter
# This function FOR DEBUG PURPOSES ONLY, never use it on the live server !
DebugPath = False
# True = Loads GeoData buffer's content into physical memory.
# False = Does not necessarily imply that the GeoData buffer's content is not resident in physical memory.
# Default: True
ForceGeoData = True
# This setting controls Client <--> Server Player coordinates synchronization:
# -1 - Will synchronize only Z from Client --> Server. Default when no geodata.
# 1 - Synchronization Client --> Server only. Using this option (without geodata) makes it more difficult for players to bypass obstacles.
# 2 - Intended for geodata (at least with cell-level pathfinding, otherwise can you try -1).
# Server sends validation packet if client goes too far from server calculated coordinates.
# Default: -1
CoordSynchronize = -1
# Default: False
AcceptGeoeditorConn = False
GeoEditorPort = 9011
# When a character falls, he takes damages.
# Default: True
FallDamage = True
# The zones with water are enabled.
# Default: True
AllowWater = True
# Geodata files folder
GeoDataPath = ./data/geodata
# Pathnode directory
# Default: data/pathnode
PathnodeDirectory = data/pathnode
# True: Try to load regions not specified below(won't disturb server startup when file does not exist)
# False: Don't load any regions other than the ones specified with True below
TryLoadUnspecifiedRegions = True
# List of regions to be required to load
# eg.:
# Both regions required
# 22_22 = True
# 19_20 = True
# Exclude region from loading
# 25_26 = False
# True: Region is required for the server to startup
# False: Region is not considered to be loaded

View File

@ -1,3 +1,41 @@
################################################
# L2j Geodata #
################################################
##############################################
GEODATA COMPENDIUM
##############################################
Comprehensive guide for geodata, by Tryskell and Hasha.
I - How to configure it
a - Prerequisites
b - Make it work
c - L2D format
II - Addendum
##############################################
I - How to configure it
##############################################
----------------------------------------------
a - Prerequisites
----------------------------------------------
* A 64bits Windows/Java JDK is a must-have to run server with geodata. Linux servers don't have the issue.
* The server can start (hardly) with -Xmx3000m. -Xmx4g is recommended.
----------------------------------------------
b - Make it work
----------------------------------------------
To make geodata working:
* unpack your geodata files into "/data/geodata" folder
* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config:
- CoordSynchronize = 2
* If you do not use any geodata files, the server will automatically change this setting to -1.
----------------------------------------------
c - L2D format
----------------------------------------------
* L2D is a new geodata file format. It holds diagonal movement informations, in addition to regular NSWE flags.
* Heavier file weight (+30%), but the pathfinding algorithms are processed way faster (-35% calculation times).
* L2D files can be converted from L2OFF/L2J formats without losing any information. Converter is part of the gameserver.
* Keep in mind to convert new geodata files, once you update your L2OFF/L2J ones.

View File

@ -1,3 +0,0 @@
################################################
# L2j pathnode #
################################################

View File

@ -31,7 +31,7 @@ import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.datatables.sql.NpcTable;
import com.l2jmobius.gameserver.datatables.sql.SpawnTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.L2World;
@ -621,7 +621,8 @@ public class Antharas extends Quest
final int rx = Rnd.get(175000, 179900);
final int ry = Rnd.get(112400, 116000);
final int rdt = ((_antharas.getX() - rx) * (_antharas.getX() - rx)) + ((_antharas.getY() - ry) * (_antharas.getY() - ry));
if (GeoData.getInstance().canSeeTarget(_antharas.getX(), _antharas.getY(), -7704, rx, ry, -7704))
final Location randomLocation = new Location(rx, ry, -7704);
if (GeoEngine.getInstance().canSeeTarget(_antharas, randomLocation))
{
if (rdt < dt)
{

View File

@ -29,7 +29,7 @@ import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.L2Object;
@ -487,7 +487,7 @@ public class Baium extends Quest
{
if (obj instanceof L2Character)
{
if (((((L2Character) obj).getZ() < (npc.getZ() - 100)) && (((L2Character) obj).getZ() > (npc.getZ() + 100))) || !GeoData.getInstance().canSeeTarget(obj, npc))
if (((((L2Character) obj).getZ() < (npc.getZ() - 100)) && (((L2Character) obj).getZ() > (npc.getZ() + 100))) || !GeoEngine.getInstance().canSeeTarget(obj, npc))
{
continue;
}

View File

@ -29,8 +29,6 @@ import java.io.LineNumberReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -42,7 +40,6 @@ import java.util.logging.Logger;
import com.l2jmobius.commons.util.ClassMasterSettings;
import com.l2jmobius.commons.util.L2Properties;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.entity.olympiad.OlympiadPeriod;
import com.l2jmobius.gameserver.util.FloodProtectorConfig;
import com.l2jmobius.loginserver.LoginController;
@ -63,7 +60,7 @@ public final class Config
private static final String CLANHALL_CONFIG_FILE = "./config/main/clanhall.ini";
private static final String ENCHANT_CONFIG_FILE = "./config/main/enchant.ini";
public static final String FORTSIEGE_CONFIG_FILE = "./config/main/fort.ini";
private static final String GEODATA_CONFIG_FILE = "./config/main/geodata.ini";
private static final String GEODATA_CONFIG_FILE = "./config/main/GeoEngine.ini";
private static final String OLYMP_CONFIG_FILE = "./config/main/olympiad.ini";
private static final String OPTIONS_CONFIG_FILE = "./config/main/options.ini";
private static final String OTHER_CONFIG_FILE = "./config/main/other.ini";
@ -1078,23 +1075,17 @@ public final class Config
public static boolean LEAVE_BUFFS_ON_DIE;
public static boolean ALT_RAIDS_STATS_BONUS;
public static int PATHFINDING;
public static File PATHNODE_DIR;
public static String PATHFIND_BUFFERS;
public static float LOW_WEIGHT;
public static float MEDIUM_WEIGHT;
public static float HIGH_WEIGHT;
public static boolean ADVANCED_DIAGONAL_STRATEGY;
public static float DIAGONAL_WEIGHT;
public static int MAX_POSTFILTER_PASSES;
public static boolean DEBUG_PATH;
public static boolean FORCE_GEODATA;
public static String GEODATA_PATH;
public static int COORD_SYNCHRONIZE;
public static Path GEODATA_PATH;
public static boolean TRY_LOAD_UNSPECIFIED_REGIONS;
public static Map<String, Boolean> GEODATA_REGIONS;
public static boolean ACCEPT_GEOEDITOR_CONN;
public static int GEOEDITOR_PORT;
public static int PART_OF_CHARACTER_HEIGHT;
public static int MAX_OBSTACLE_HEIGHT;
public static boolean PATHFINDING;
public static String PATHFIND_BUFFERS;
public static int BASE_WEIGHT;
public static int DIAGONAL_WEIGHT;
public static int HEURISTIC_WEIGHT;
public static int OBSTACLE_MULTIPLIER;
public static int MAX_ITERATIONS;
public static boolean FALL_DAMAGE;
public static boolean ALLOW_WATER;
@ -3388,42 +3379,19 @@ public final class Config
geodataSetting.load(is);
is.close();
PATHFINDING = Integer.parseInt(geodataSetting.getProperty("PathFinding", "0"));
try
{
PATHNODE_DIR = new File(geodataSetting.getProperty("PathnodeDirectory", "data/pathnode").replaceAll("\\\\", "/")).getCanonicalFile();
}
catch (IOException e)
{
PATHNODE_DIR = new File("data/pathnode");
}
PATHFIND_BUFFERS = geodataSetting.getProperty("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
LOW_WEIGHT = Float.parseFloat(geodataSetting.getProperty("LowWeight", "0.5f"));
MEDIUM_WEIGHT = Float.parseFloat(geodataSetting.getProperty("MediumWeight", "2"));
HIGH_WEIGHT = Float.parseFloat(geodataSetting.getProperty("HighWeight", "3"));
ADVANCED_DIAGONAL_STRATEGY = Boolean.parseBoolean(geodataSetting.getProperty("AdvancedDiagonalStrategy", "true"));
DIAGONAL_WEIGHT = Float.parseFloat(geodataSetting.getProperty("DiagonalWeight", "0.707f"));
MAX_POSTFILTER_PASSES = Integer.parseInt(geodataSetting.getProperty("MaxPostfilterPasses", "3"));
DEBUG_PATH = Boolean.parseBoolean(geodataSetting.getProperty("DebugPath", "false"));
FORCE_GEODATA = Boolean.parseBoolean(geodataSetting.getProperty("ForceGeoData", "true"));
GEODATA_PATH = geodataSetting.getProperty("GeoDataPath", "./data/geodata/");
COORD_SYNCHRONIZE = Integer.parseInt(geodataSetting.getProperty("CoordSynchronize", "-1"));
GEODATA_PATH = Paths.get(geodataSetting.getProperty("GeoDataPath", "./data/geodata"));
TRY_LOAD_UNSPECIFIED_REGIONS = Boolean.parseBoolean(geodataSetting.getProperty("TryLoadUnspecifiedRegions", "true"));
GEODATA_REGIONS = new HashMap<>();
for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++)
{
for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++)
{
final String key = regionX + "_" + regionY;
if (geodataSetting.containsKey(regionX + "_" + regionY))
{
GEODATA_REGIONS.put(key, Boolean.parseBoolean(geodataSetting.getProperty(key, "false")));
}
}
}
ACCEPT_GEOEDITOR_CONN = Boolean.parseBoolean(geodataSetting.getProperty("AcceptGeoeditorConn", "false"));
GEOEDITOR_PORT = Integer.parseInt(geodataSetting.getProperty("GeoEditorPort", "9011"));
PART_OF_CHARACTER_HEIGHT = Integer.parseInt(geodataSetting.getProperty("PartOfCharacterHeight", "75"));
MAX_OBSTACLE_HEIGHT = Integer.parseInt(geodataSetting.getProperty("MaxObstacleHeight", "32"));
PATHFINDING = Boolean.parseBoolean(geodataSetting.getProperty("PathFinding", "true"));
PATHFIND_BUFFERS = geodataSetting.getProperty("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
BASE_WEIGHT = Integer.parseInt(geodataSetting.getProperty("BaseWeight", "10"));
DIAGONAL_WEIGHT = Integer.parseInt(geodataSetting.getProperty("DiagonalWeight", "14"));
OBSTACLE_MULTIPLIER = Integer.parseInt(geodataSetting.getProperty("ObstacleMultiplier", "10"));
HEURISTIC_WEIGHT = Integer.parseInt(geodataSetting.getProperty("HeuristicWeight", "20"));
MAX_ITERATIONS = Integer.parseInt(geodataSetting.getProperty("MaxIterations", "3500"));
FALL_DAMAGE = Boolean.parseBoolean(geodataSetting.getProperty("FallDamage", "false"));
ALLOW_WATER = Boolean.valueOf(geodataSetting.getProperty("AllowWater", "false"));

View File

@ -76,9 +76,7 @@ import com.l2jmobius.gameserver.datatables.xml.ExperienceData;
import com.l2jmobius.gameserver.datatables.xml.FenceData;
import com.l2jmobius.gameserver.datatables.xml.ItemTable;
import com.l2jmobius.gameserver.datatables.xml.ZoneData;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.geoeditorcon.GeoEditorListener;
import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.AdminCommandHandler;
import com.l2jmobius.gameserver.handler.AutoAnnouncementHandler;
import com.l2jmobius.gameserver.handler.AutoChatHandler;
@ -300,11 +298,7 @@ public class GameServer
}
Util.printSection("Geodata");
GeoData.getInstance();
if (Config.PATHFINDING > 0)
{
PathFinding.getInstance();
}
GeoEngine.getInstance();
Util.printSection("Economy");
TradeController.getInstance();
@ -357,10 +351,6 @@ public class GameServer
CursedWeaponsManager.getInstance();
TaskManager.getInstance();
L2PetDataTable.getInstance().loadPetsData();
if (Config.ACCEPT_GEOEDITOR_CONN)
{
GeoEditorListener.getInstance();
}
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance();

View File

@ -27,7 +27,7 @@ import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.datatables.sql.TerritoryTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.instancemanager.DimensionalRiftManager;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.L2Object;
@ -249,7 +249,7 @@ public class L2AttackableAI extends L2CharacterAI
if ((target instanceof L2PcInstance) && (((L2PcInstance) target).getKarma() > 0))
{
// Los Check
return GeoData.getInstance().canSeeTarget(me, target);
return GeoEngine.getInstance().canSeeTarget(me, target);
}
// if (target instanceof L2Summon)
@ -257,7 +257,7 @@ public class L2AttackableAI extends L2CharacterAI
// Check if the L2MonsterInstance target is aggressive
if (target instanceof L2MonsterInstance)
{
return ((L2MonsterInstance) target).isAggressive() && GeoData.getInstance().canSeeTarget(me, target);
return ((L2MonsterInstance) target).isAggressive() && GeoEngine.getInstance().canSeeTarget(me, target);
}
return false;
@ -276,7 +276,7 @@ public class L2AttackableAI extends L2CharacterAI
if ((target instanceof L2PcInstance) && (((L2PcInstance) target).getKarma() > 0))
{
// Los Check
return GeoData.getInstance().canSeeTarget(me, target);
return GeoEngine.getInstance().canSeeTarget(me, target);
}
return false;
}
@ -298,7 +298,7 @@ public class L2AttackableAI extends L2CharacterAI
}
// Check if the actor is Aggressive
return me.isAggressive() && GeoData.getInstance().canSeeTarget(me, target);
return me.isAggressive() && GeoEngine.getInstance().canSeeTarget(me, target);
}
}
@ -678,7 +678,7 @@ public class L2AttackableAI extends L2CharacterAI
* "c_dungeon_nephi".equals(npcfaction))) sevenSignFaction = true; //Lilim mobs should assist other Lilim and catacomb mobs else if ("c_dungeon_lilim".equals(faction_id) && "c_dungeon_clan".equals(npcfaction)) sevenSignFaction = true; //Nephilim mobs should assist other Nephilim and catacomb
* mobs else if ("c_dungeon_nephi".equals(faction_id) && "c_dungeon_clan".equals(npcfaction)) sevenSignFaction = true; if (!faction_id.equals(npc.getFactionId()) && !sevenSignFaction) continue; // Check if the L2Object is inside the Faction Range of // the actor if
* (_actor.isInsideRadius(npc, npc.getFactionRange() + npc.getTemplate().collisionRadius, true, false) && npc.getAI() != null) { if (Math.abs(originalAttackTarget.getZ() - npc.getZ()) < 600 && _actor.getAttackByList().contains(originalAttackTarget) && (npc.getAI()._intention ==
* CtrlIntention.AI_INTENTION_IDLE || npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE) && GeoData.getInstance().canSeeTarget(_actor, npc)) { if ((originalAttackTarget instanceof L2PcInstance) || (originalAttackTarget instanceof L2Summon)) { if
* CtrlIntention.AI_INTENTION_IDLE || npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE) && GeoEngine.getInstance().canSeeTarget(_actor, npc)) { if ((originalAttackTarget instanceof L2PcInstance) || (originalAttackTarget instanceof L2Summon)) { if
* (npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_FACTION_CALL) != null) { L2PcInstance player = (originalAttackTarget instanceof L2PcInstance) ? (L2PcInstance) originalAttackTarget : ((L2Summon) originalAttackTarget).getOwner(); for (Quest quest :
* npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_FACTION_CALL)) quest.notifyFactionCall(npc, (L2NpcInstance) _actor, player, (originalAttackTarget instanceof L2Summon)); } } else if (npc instanceof L2Attackable && getAttackTarget() != null && npc.getAI()._intention !=
* CtrlIntention.AI_INTENTION_ATTACK) { ((L2Attackable) npc).addDamageHate(getAttackTarget(), 0, ((L2Attackable) _actor).getHating(getAttackTarget())); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, getAttackTarget()); } } } } } } catch (NullPointerException e) { LOGGER.warning(
@ -706,7 +706,7 @@ public class L2AttackableAI extends L2CharacterAI
{
if ((npc.getAI().getIntention() == AI_INTENTION_IDLE) || (npc.getAI().getIntention() == AI_INTENTION_ACTIVE))
{
if (GeoData.getInstance().canSeeTarget(_actor, npc) && (Math.abs(originalAttackTarget.getZ() - npc.getZ()) < 600))
if (GeoEngine.getInstance().canSeeTarget(_actor, npc) && (Math.abs(originalAttackTarget.getZ() - npc.getZ()) < 600))
{
if ((originalAttackTarget instanceof L2PcInstance) && originalAttackTarget.isInParty() && originalAttackTarget.getParty().isInDimensionalRift())
{
@ -723,7 +723,7 @@ public class L2AttackableAI extends L2CharacterAI
}
}
if (GeoData.getInstance().canSeeTarget(_actor, npc) && (Math.abs(originalAttackTarget.getZ() - npc.getZ()) < 500))
if (GeoEngine.getInstance().canSeeTarget(_actor, npc) && (Math.abs(originalAttackTarget.getZ() - npc.getZ()) < 500))
{
if ((originalAttackTarget instanceof L2PcInstance) || (originalAttackTarget instanceof L2Summon))
{
@ -800,7 +800,7 @@ public class L2AttackableAI extends L2CharacterAI
if (!_actor.isInsideRadius(newX, newY, collision, false))
{
final int newZ = _actor.getZ() + 30;
if ((Config.PATHFINDING == 0) || GeoData.getInstance().canMove(_actor, newX, newY, newZ))
if (!Config.PATHFINDING || GeoEngine.getInstance().canMoveToTarget(_actor.getX(), _actor.getY(), _actor.getZ(), newX, newY, newZ, _actor.getInstanceId()))
{
moveTo(newX, newY, newZ);
}
@ -816,9 +816,6 @@ public class L2AttackableAI extends L2CharacterAI
final double distance2 = _actor.getPlanDistanceSq(originalAttackTarget.getX(), originalAttackTarget.getY());
if (Math.sqrt(distance2) <= (60 + combinedCollision))
{
// double distance2 = _actor.getPlanDistanceSq(originalAttackTarget.getX(), originalAttackTarget.getY());
// if(distance2 <= 10000)
// {
final int chance = 5;
if (chance >= Rnd.get(100))
{
@ -992,7 +989,7 @@ public class L2AttackableAI extends L2CharacterAI
}
}
// GeoData Los Check here
if (!useSkillSelf && !GeoData.getInstance().canSeeTarget(_actor, _actor.getTarget()))
if (!useSkillSelf && !GeoEngine.getInstance().canSeeTarget(_actor, _actor.getTarget()))
{
return;
}

View File

@ -29,7 +29,7 @@ import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
@ -194,7 +194,7 @@ public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
}
}
// Los Check Here
return _actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target);
return _actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target);
}
/**
@ -531,7 +531,7 @@ public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, cha))
if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
{
break;
}
@ -561,7 +561,7 @@ public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
// && _actor.getAttackByList().contains(getAttackTarget())
&& ((npc.getAI().getIntention() == AI_INTENTION_IDLE) || (npc.getAI().getIntention() == AI_INTENTION_ACTIVE))
// limiting aggro for siege guards
&& target.isInsideRadius(npc, 1500, true, false) && GeoData.getInstance().canSeeTarget(npc, target))
&& target.isInsideRadius(npc, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
{
// Notify the L2Object AI with EVT_AGGRESSION
final L2CharacterAI ai = npc.getAI();
@ -593,7 +593,7 @@ public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, npc))
if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
{
break;
}
@ -648,7 +648,7 @@ public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
return;
}
if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget))
if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
{
// Siege guards differ from normal mobs currently:
// If target cannot seen, don't attack any more

View File

@ -26,7 +26,7 @@ import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
@ -150,7 +150,7 @@ public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
}
}
// Los Check Here
return _actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target);
return _actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target);
}
/**
@ -360,7 +360,7 @@ public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
return;
}
if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget))
if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
{
// Siege guards differ from normal mobs currently:
// If target cannot seen, don't attack any more
@ -654,9 +654,9 @@ public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
// Check if the L2Object is inside the Faction Range of the actor
if ((npc.getAI() != null) && ((npc.getAI().getIntention() == AI_INTENTION_IDLE) || (npc.getAI().getIntention() == AI_INTENTION_ACTIVE)) && actor.isInsideRadius(npc, npc.getFactionRange(), false, true) && target.isInsideRadius(npc, npc.getFactionRange(), false, true))
{
if (Config.PATHFINDING > 0)
if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(npc, target))
if (GeoEngine.getInstance().canSeeTarget(npc, target))
{
// Notify the L2Object AI with EVT_AGGRESSION
final L2CharacterAI ai = npc.getAI();

View File

@ -28,10 +28,10 @@ import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.instancemanager.ClanHallManager;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.templates.StatsSet;
import com.l2jmobius.gameserver.templates.chars.L2CharTemplate;
@ -332,7 +332,7 @@ public class DoorTable
}
}
public boolean checkIfDoorsBetween(AbstractNodeLoc start, AbstractNodeLoc end)
public boolean checkIfDoorsBetween(Location start, Location end)
{
return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ());
}

View File

@ -1,567 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.datatables.csv.DoorTable;
import com.l2jmobius.gameserver.datatables.xml.FenceData;
import com.l2jmobius.gameserver.geodata.geodriver.Cell;
import com.l2jmobius.gameserver.geodata.geodriver.GeoDriver;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.util.GeoUtils;
import com.l2jmobius.gameserver.util.LinePointIterator;
import com.l2jmobius.gameserver.util.LinePointIterator3D;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class GeoData
{
protected static final Logger LOGGER = Logger.getLogger(GeoData.class.getName());
private static final String FILE_NAME_FORMAT = "%d_%d.l2j";
private static final int ELEVATED_SEE_OVER_DISTANCE = 2;
private static final int MAX_SEE_OVER_HEIGHT = 48;
private static final int SPAWN_Z_DELTA_LIMIT = 100;
private final GeoDriver _driver = new GeoDriver();
protected GeoData()
{
int loadedRegions = 0;
try
{
for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++)
{
for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++)
{
final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY));
final Boolean loadFile = Config.GEODATA_REGIONS.get(regionX + "_" + regionY);
if (loadFile != null)
{
if (loadFile)
{
LOGGER.info(getClass().getSimpleName() + ": Loading " + geoFilePath.getFileName() + "...");
_driver.loadRegion(geoFilePath, regionX, regionY);
loadedRegions++;
}
}
else if (Config.TRY_LOAD_UNSPECIFIED_REGIONS && Files.exists(geoFilePath))
{
try
{
LOGGER.info(getClass().getSimpleName() + ": Loading " + geoFilePath.getFileName() + "...");
_driver.loadRegion(geoFilePath, regionX, regionY);
loadedRegions++;
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Failed to load " + geoFilePath.getFileName() + "! " + e);
}
}
}
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Failed to load geodata! " + e);
System.exit(1);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + loadedRegions + " regions.");
}
public boolean hasGeoPos(int geoX, int geoY)
{
return _driver.hasGeoPos(geoX, geoY);
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return _driver.checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe)
{
boolean can = true;
if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST)
{
// can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.NORTH);
can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH);
}
if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST))
{
// can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.NORTH);
can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH);
}
if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST))
{
// can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.SOUTH);
can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH);
}
if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST))
{
// can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.SOUTH);
can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH);
}
return can && checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _driver.getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _driver.getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _driver.getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
return _driver.getGeoX(worldX);
}
public int getGeoY(int worldY)
{
return _driver.getGeoY(worldY);
}
public int getGeoZ(int worldZ)
{
return _driver.getGeoZ(worldZ);
}
public int getWorldX(int geoX)
{
return _driver.getWorldX(geoX);
}
public int getWorldY(int geoY)
{
return _driver.getWorldY(geoY);
}
public int getWorldZ(int geoZ)
{
return _driver.getWorldZ(geoZ);
}
// ///////////////////
// L2J METHODS
/**
* Gets the height.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return the height
*/
public int getHeight(int x, int y, int z)
{
return getNearestZ(getGeoX(x), getGeoY(y), z);
}
/**
* Gets the spawn height.
* @param x the x coordinate
* @param y the y coordinate
* @param z the the z coordinate
* @return the spawn height
*/
public int getSpawnHeight(int x, int y, int z)
{
final int geoX = getGeoX(x);
final int geoY = getGeoY(y);
if (!hasGeoPos(geoX, geoY))
{
return z;
}
final int nextLowerZ = getNextLowerZ(geoX, geoY, z + 100);
return Math.abs(nextLowerZ - z) <= SPAWN_Z_DELTA_LIMIT ? nextLowerZ : z;
}
/**
* Gets the spawn height.
* @param location the location
* @return the spawn height
*/
public int getSpawnHeight(Location location)
{
return getSpawnHeight(location.getX(), location.getY(), location.getZ());
}
/**
* Can see target. Doors as target always return true. Checks doors between.
* @param cha the character
* @param target the target
* @return {@code true} if the character can see the target (LOS), {@code false} otherwise
*/
public boolean canSeeTarget(L2Object cha, L2Object target)
{
if (target instanceof L2DoorInstance)
{
// can always see doors :o
return true;
}
return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ());
}
private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe)
{
if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0)))
{
throw new RuntimeException("Multiple directions!");
}
if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe))
{
return getNearestZ(curX, curY, prevGeoZ);
}
return getNextHigherZ(curX, curY, prevGeoZ);
}
/**
* Can see target. Does not check doors between.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise
*/
public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz)
{
int geoX = getGeoX(x);
int geoY = getGeoY(y);
int tGeoX = getGeoX(tx);
int tGeoY = getGeoY(ty);
z = getNearestZ(geoX, geoY, z);
tz = getNearestZ(tGeoX, tGeoY, tz);
// fastpath
if ((geoX == tGeoX) && (geoY == tGeoY))
{
if (hasGeoPos(tGeoX, tGeoY))
{
return z == tz;
}
return true;
}
if (tz > z)
{
int tmp = tx;
tx = x;
x = tmp;
tmp = ty;
ty = y;
y = tmp;
tmp = tz;
tz = z;
z = tmp;
tmp = tGeoX;
tGeoX = geoX;
geoX = tmp;
tmp = tGeoY;
tGeoY = geoY;
geoY = tmp;
}
final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz);
// first point is guaranteed to be available, skip it, we can always see our own position
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
final int prevZ = pointIter.z();
int prevGeoZ = prevZ;
int ptIndex = 0;
while (pointIter.next())
{
final int curX = pointIter.x();
final int curY = pointIter.y();
if ((curX == prevX) && (curY == prevY))
{
continue;
}
final int beeCurZ = pointIter.z();
int curGeoZ = prevGeoZ;
// check if the position has geodata
if (hasGeoPos(curX, curY))
{
final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ);
final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY);
curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe);
int maxHeight;
if (ptIndex < ELEVATED_SEE_OVER_DISTANCE)
{
maxHeight = z + MAX_SEE_OVER_HEIGHT;
}
else
{
maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT;
}
boolean canSeeThrough = false;
if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ))
{
if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST)
{
final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST);
final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH);
canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
}
else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)
{
final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST);
final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH);
canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
}
else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)
{
final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST);
final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH);
canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
}
else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)
{
final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST);
final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH);
canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
}
else
{
canSeeThrough = true;
}
}
if (!canSeeThrough)
{
return false;
}
}
prevX = curX;
prevY = curY;
prevGeoZ = curGeoZ;
++ptIndex;
}
return true;
}
/**
* Move check.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @return the last Location (x,y,z) where player can walk - just before wall
*/
public Location moveCheck(int x, int y, int z, int tx, int ty, int tz)
{
final int geoX = getGeoX(x);
final int geoY = getGeoY(y);
z = getNearestZ(geoX, geoY, z);
final int tGeoX = getGeoX(tx);
final int tGeoY = getGeoY(ty);
tz = getNearestZ(tGeoX, tGeoY, tz);
if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz))
{
return new Location(x, y, getHeight(x, y, z));
}
if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz))
{
return new Location(x, y, getHeight(x, y, z));
}
final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY);
// first point is guaranteed to be available
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
int prevZ = z;
while (pointIter.next())
{
final int curX = pointIter.x();
final int curY = pointIter.y();
final int curZ = getNearestZ(curX, curY, prevZ);
if (hasGeoPos(prevX, prevY))
{
final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY);
if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe))
{
// can't move, return previous location
return new Location(getWorldX(prevX), getWorldY(prevY), prevZ);
}
}
prevX = curX;
prevY = curY;
prevZ = curZ;
}
if (hasGeoPos(prevX, prevY) && (prevZ != tz))
{
// different floors, return start location
return new Location(x, y, z);
}
return new Location(tx, ty, tz);
}
/**
* Checks if its possible to move from one location to another.
* @param fromX the X coordinate to start checking from
* @param fromY the Y coordinate to start checking from
* @param fromZ the Z coordinate to start checking from
* @param toX the X coordinate to end checking at
* @param toY the Y coordinate to end checking at
* @param toZ the Z coordinate to end checking at
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(int fromX, int fromY, int fromZ, int toX, int toY, int toZ)
{
final int geoX = getGeoX(fromX);
final int geoY = getGeoY(fromY);
fromZ = getNearestZ(geoX, geoY, fromZ);
final int tGeoX = getGeoX(toX);
final int tGeoY = getGeoY(toY);
toZ = getNearestZ(tGeoX, tGeoY, toZ);
if (DoorTable.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ))
{
return false;
}
if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ))
{
return false;
}
final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY);
// first point is guaranteed to be available
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
int prevZ = fromZ;
while (pointIter.next())
{
final int curX = pointIter.x();
final int curY = pointIter.y();
final int curZ = getNearestZ(curX, curY, prevZ);
if (hasGeoPos(prevX, prevY))
{
final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY);
if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe))
{
return false;
}
}
prevX = curX;
prevY = curY;
prevZ = curZ;
}
if (hasGeoPos(prevX, prevY) && (prevZ != toZ))
{
// different floors
return false;
}
return true;
}
/**
* Checks if its possible to move from one location to another.
* @param from the {@code WorldObject} to start checking from
* @param toX the X coordinate to end checking at
* @param toY the Y coordinate to end checking at
* @param toZ the Z coordinate to end checking at
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(L2Object from, int toX, int toY, int toZ)
{
return canMove(from.getX(), from.getY(), from.getZ(), toX, toY, toZ);
}
/**
* Checks if its possible to move from one location to another.
* @param from the {@code WorldObject} to start checking from
* @param to the {@code WorldObject} to end checking at
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(L2Object from, L2Object to)
{
return canMove(from, to.getX(), to.getY(), to.getZ());
}
/**
* Checks the specified position for available geodata.
* @param x the X coordinate
* @param y the Y coordinate
* @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise
*/
public boolean hasGeo(int x, int y)
{
return hasGeoPos(getGeoX(x), getGeoY(y));
}
public static GeoData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final GeoData _instance = new GeoData();
}
}

View File

@ -1,48 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver;
/**
* @author HorridoJoho
*/
public final class Cell
{
/** East NSWE flag */
public static final byte NSWE_EAST = 1 << 0;
/** West NSWE flag */
public static final byte NSWE_WEST = 1 << 1;
/** South NSWE flag */
public static final byte NSWE_SOUTH = 1 << 2;
/** North NSWE flag */
public static final byte NSWE_NORTH = 1 << 3;
/** North-East NSWE flags */
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
/** North-West NSWE flags */
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
/** South-East NSWE flags */
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
/** South-West NSWE flags */
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
/** All directions NSWE flags */
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
private Cell()
{
}
}

View File

@ -1,189 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReferenceArray;
import com.l2jmobius.gameserver.geodata.geodriver.regions.NullRegion;
import com.l2jmobius.gameserver.geodata.geodriver.regions.Region;
/**
* @author HorridoJoho
*/
public final class GeoDriver
{
// world dimensions: 1048576 * 1048576 = 1099511627776
private static final int WORLD_MIN_X = -655360;
private static final int WORLD_MAX_X = 393215;
private static final int WORLD_MIN_Y = -589824;
private static final int WORLD_MAX_Y = 458751;
private static final int WORLD_MIN_Z = -16384;
private static final int WORLD_MAX_Z = 16384;
/** Regions in the world on the x axis */
public static final int GEO_REGIONS_X = 32;
/** Regions in the world on the y axis */
public static final int GEO_REGIONS_Y = 32;
/** Region in the world */
public static final int GEO_REGIONS = GEO_REGIONS_X * GEO_REGIONS_Y;
/** Blocks in the world on the x axis */
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * IRegion.REGION_BLOCKS_X;
/** Blocks in the world on the y axis */
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * IRegion.REGION_BLOCKS_Y;
/** Blocks in the world */
public static final int GEO_BLOCKS = GEO_REGIONS * IRegion.REGION_BLOCKS;
/** Cells in the world on the x axis */
public static final int GEO_CELLS_X = GEO_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in the world in the y axis */
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** Cells in the world in the z axis */
public static final int GEO_CELLS_Z = (Math.abs(WORLD_MIN_Z) + Math.abs(WORLD_MAX_Z)) / 16;
/** The regions array */
private final AtomicReferenceArray<IRegion> _regions = new AtomicReferenceArray<>(GEO_REGIONS);
public GeoDriver()
{
for (int i = 0; i < _regions.length(); i++)
{
_regions.set(i, NullRegion.INSTANCE);
}
}
private void checkGeoX(int geoX)
{
if ((geoX < 0) || (geoX >= GEO_CELLS_X))
{
throw new IllegalArgumentException();
}
}
private void checkGeoY(int geoY)
{
if ((geoY < 0) || (geoY >= GEO_CELLS_Y))
{
throw new IllegalArgumentException();
}
}
private void checkGeoZ(int geoZ)
{
if ((geoZ < 0) || (geoZ >= GEO_CELLS_Z))
{
throw new IllegalArgumentException();
}
}
private IRegion getRegion(int geoX, int geoY)
{
checkGeoX(geoX);
checkGeoY(geoY);
return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y));
}
public void loadRegion(Path filePath, int regionX, int regionY) throws IOException
{
final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY;
try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r"))
{
_regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN)));
}
}
public void unloadRegion(int regionX, int regionY)
{
_regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE);
}
public boolean hasGeoPos(int geoX, int geoY)
{
return getRegion(geoX, geoY).hasGeo();
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
if ((worldX < WORLD_MIN_X) || (worldX > WORLD_MAX_X))
{
throw new IllegalArgumentException();
}
return (worldX - WORLD_MIN_X) / 16;
}
public int getGeoY(int worldY)
{
if ((worldY < WORLD_MIN_Y) || (worldY > WORLD_MAX_Y))
{
throw new IllegalArgumentException();
}
return (worldY - WORLD_MIN_Y) / 16;
}
public int getGeoZ(int worldZ)
{
if ((worldZ < WORLD_MIN_Z) || (worldZ > WORLD_MAX_Z))
{
throw new IllegalArgumentException();
}
return (worldZ - WORLD_MIN_Z) / 16;
}
public int getWorldX(int geoX)
{
checkGeoX(geoX);
return (geoX * 16) + WORLD_MIN_X + 8;
}
public int getWorldY(int geoY)
{
checkGeoY(geoY);
return (geoY * 16) + WORLD_MIN_Y + 8;
}
public int getWorldZ(int geoZ)
{
checkGeoZ(geoZ);
return (geoZ * 16) + WORLD_MIN_Z + 8;
}
}

View File

@ -1,42 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver;
/**
* @author HorridoJoho
*/
public interface IBlock
{
int TYPE_FLAT = 0;
int TYPE_COMPLEX = 1;
int TYPE_MULTILAYER = 2;
/** Cells in a block on the x axis */
int BLOCK_CELLS_X = 8;
/** Cells in a block on the y axis */
int BLOCK_CELLS_Y = 8;
/** Cells in a block */
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
}

View File

@ -1,47 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver;
/**
* @author HorridoJoho
*/
public interface IRegion
{
/** Blocks in a region on the x axis. */
int REGION_BLOCKS_X = 256;
/** Blocks in a region on the y axis. */
int REGION_BLOCKS_Y = 256;
/** Blocks in a region. */
int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
/** Cells in a region on the x axis. */
int REGION_CELLS_X = REGION_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in a regioin on the y axis. */
int REGION_CELLS_Y = REGION_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** Cells in a region. */
int REGION_CELLS = REGION_CELLS_X * REGION_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
boolean hasGeo();
}

View File

@ -1,79 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver.blocks;
import java.nio.ByteBuffer;
import com.l2jmobius.gameserver.geodata.geodriver.IBlock;
/**
* @author HorridoJoho
*/
public final class ComplexBlock implements IBlock
{
private final short[] _data;
public ComplexBlock(ByteBuffer bb)
{
_data = new short[IBlock.BLOCK_CELLS];
for (int cellOffset = 0; cellOffset < IBlock.BLOCK_CELLS; cellOffset++)
{
_data[cellOffset] = bb.getShort();
}
}
private short _getCellData(int geoX, int geoY)
{
return _data[((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y)];
}
private byte _getCellNSWE(int geoX, int geoY)
{
return (byte) (_getCellData(geoX, geoY) & 0x000F);
}
private int _getCellHeight(int geoX, int geoY)
{
return (short) (_getCellData(geoX, geoY) & 0x0FFF0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (_getCellNSWE(geoX, geoY) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _getCellHeight(geoX, geoY);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = _getCellHeight(geoX, geoY);
return cellHeight <= worldZ ? cellHeight : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = _getCellHeight(geoX, geoY);
return cellHeight >= worldZ ? cellHeight : worldZ;
}
}

View File

@ -1,58 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver.blocks;
import java.nio.ByteBuffer;
import com.l2jmobius.gameserver.geodata.geodriver.IBlock;
/**
* @author HorridoJoho
*/
public class FlatBlock implements IBlock
{
private final short _height;
public FlatBlock(ByteBuffer bb)
{
_height = bb.getShort();
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _height <= worldZ ? _height : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _height >= worldZ ? _height : worldZ;
}
}

View File

@ -1,186 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver.blocks;
import java.nio.ByteBuffer;
import com.l2jmobius.gameserver.geodata.geodriver.IBlock;
/**
* @author HorridoJoho
*/
public class MultilayerBlock implements IBlock
{
private final byte[] _data;
/**
* Initializes a new instance of this block reading the specified buffer.
* @param bb the buffer
*/
public MultilayerBlock(ByteBuffer bb)
{
final int start = bb.position();
for (int blockCellOffset = 0; blockCellOffset < IBlock.BLOCK_CELLS; blockCellOffset++)
{
final byte nLayers = bb.get();
if ((nLayers <= 0) || (nLayers > 125))
{
throw new RuntimeException("L2JGeoDriver: Geo file corrupted! Invalid layers count!");
}
bb.position(bb.position() + (nLayers * 2));
}
_data = new byte[bb.position() - start];
bb.position(start);
bb.get(_data);
}
private short _getNearestLayer(int geoX, int geoY, int worldZ)
{
final int startOffset = _getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
// 1 layer at least was required on loading so this is set at least once on the loop below
int nearestDZ = 0;
short nearestData = 0;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = _extractLayerData(offset);
final int layerZ = _extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// exact z
return layerData;
}
final int layerDZ = Math.abs(layerZ - worldZ);
if ((offset == (startOffset + 1)) || (layerDZ < nearestDZ))
{
nearestDZ = layerDZ;
nearestData = layerData;
}
}
return nearestData;
}
private int _getCellDataOffset(int geoX, int geoY)
{
final int cellLocalOffset = ((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y);
int cellDataOffset = 0;
// move index to cell, we need to parse on each request, OR we parse on creation and save indexes
for (int i = 0; i < cellLocalOffset; i++)
{
cellDataOffset += 1 + (_data[cellDataOffset] * 2);
}
// now the index points to the cell we need
return cellDataOffset;
}
private short _extractLayerData(int dataOffset)
{
return (short) ((_data[dataOffset] & 0xFF) | (_data[dataOffset + 1] << 8));
}
private int _getNearestNSWE(int geoX, int geoY, int worldZ)
{
return _extractLayerNswe(_getNearestLayer(geoX, geoY, worldZ));
}
private int _extractLayerNswe(short layer)
{
return (layer & 0x000F);
}
private int _extractLayerHeight(short layer)
{
layer = (short) (layer & 0x0fff0);
return layer >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (_getNearestNSWE(geoX, geoY, worldZ) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _extractLayerHeight(_getNearestLayer(geoX, geoY, worldZ));
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int startOffset = _getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int lowerZ = Integer.MIN_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = _extractLayerData(offset);
final int layerZ = _extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// exact z
return layerZ;
}
if ((layerZ < worldZ) && (layerZ > lowerZ))
{
lowerZ = layerZ;
}
}
return lowerZ == Integer.MIN_VALUE ? worldZ : lowerZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int startOffset = _getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int higherZ = Integer.MAX_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = _extractLayerData(offset);
final int layerZ = _extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// exact z
return layerZ;
}
if ((layerZ > worldZ) && (layerZ < higherZ))
{
higherZ = layerZ;
}
}
return higherZ == Integer.MAX_VALUE ? worldZ : higherZ;
}
}

View File

@ -1,57 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver.regions;
import com.l2jmobius.gameserver.geodata.geodriver.IRegion;
/**
* @author HorridoJoho
*/
public final class NullRegion implements IRegion
{
public static final NullRegion INSTANCE = new NullRegion();
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public boolean hasGeo()
{
return false;
}
}

View File

@ -1,98 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geodriver.regions;
import java.nio.ByteBuffer;
import com.l2jmobius.gameserver.geodata.geodriver.IBlock;
import com.l2jmobius.gameserver.geodata.geodriver.IRegion;
import com.l2jmobius.gameserver.geodata.geodriver.blocks.ComplexBlock;
import com.l2jmobius.gameserver.geodata.geodriver.blocks.FlatBlock;
import com.l2jmobius.gameserver.geodata.geodriver.blocks.MultilayerBlock;
/**
* @author HorridoJoho
*/
public final class Region implements IRegion
{
private final IBlock[] _blocks = new IBlock[IRegion.REGION_BLOCKS];
public Region(ByteBuffer bb)
{
for (int blockOffset = 0; blockOffset < IRegion.REGION_BLOCKS; blockOffset++)
{
final int blockType = bb.get();
switch (blockType)
{
case IBlock.TYPE_FLAT:
{
_blocks[blockOffset] = new FlatBlock(bb);
break;
}
case IBlock.TYPE_COMPLEX:
{
_blocks[blockOffset] = new ComplexBlock(bb);
break;
}
case IBlock.TYPE_MULTILAYER:
{
_blocks[blockOffset] = new MultilayerBlock(bb);
break;
}
default:
{
throw new RuntimeException("Invalid block type " + blockType + "!");
}
}
}
}
private IBlock getBlock(int geoX, int geoY)
{
return _blocks[(((geoX / IBlock.BLOCK_CELLS_X) % IRegion.REGION_BLOCKS_X) * IRegion.REGION_BLOCKS_Y) + ((geoY / IBlock.BLOCK_CELLS_Y) % IRegion.REGION_BLOCKS_Y)];
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getBlock(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
@Override
public boolean hasGeo()
{
return true;
}
}

View File

@ -1,121 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geoeditorcon;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;
import com.l2jmobius.Config;
public class GeoEditorListener extends Thread
{
protected static final Logger LOGGER = Logger.getLogger(GeoEditorListener.class.getName());
private static final int PORT = Config.GEOEDITOR_PORT;
private static final class SingletonHolder
{
protected static final GeoEditorListener INSTANCE = new GeoEditorListener();
}
public static GeoEditorListener getInstance()
{
return SingletonHolder.INSTANCE;
}
private ServerSocket _serverSocket;
private GeoEditorThread _geoEditor;
protected GeoEditorListener()
{
try
{
_serverSocket = new ServerSocket(PORT);
}
catch (IOException e)
{
LOGGER.warning("Error creating geoeditor listener! " + e);
System.exit(1);
}
start();
LOGGER.info("GeoEditorListener Initialized.");
}
public GeoEditorThread getThread()
{
return _geoEditor;
}
public String getStatus()
{
if ((_geoEditor != null) && _geoEditor.isWorking())
{
return "Geoeditor connected.";
}
return "Geoeditor not connected.";
}
@Override
public void run()
{
Socket connection = null;
try
{
while (true)
{
connection = _serverSocket.accept();
if ((_geoEditor != null) && _geoEditor.isWorking())
{
LOGGER.warning("Geoeditor already connected!");
connection.close();
continue;
}
LOGGER.info("Received geoeditor connection from: " + connection.getInetAddress().getHostAddress());
_geoEditor = new GeoEditorThread(connection);
_geoEditor.start();
}
}
catch (Exception e)
{
LOGGER.warning("GeoEditorListener: " + e);
try
{
if (connection != null)
{
connection.close();
}
}
catch (Exception e2)
{
}
}
finally
{
try
{
_serverSocket.close();
}
catch (IOException io)
{
LOGGER.warning(io.getMessage());
}
LOGGER.warning("GeoEditorListener Closed!");
}
}
}

View File

@ -1,283 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.geoeditorcon;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class GeoEditorThread extends Thread
{
protected static final Logger LOGGER = Logger.getLogger(GeoEditorThread.class.getName());
private boolean _working = false;
private int _mode = 0; // 0 - don't send coords, 1 - send each
// validateposition from client, 2 - send in intervals of _sendDelay ms.
private int _sendDelay = 1000; // default - once in second
private final Socket _geSocket;
private OutputStream _out;
private final List<L2PcInstance> _gms;
public GeoEditorThread(Socket ge)
{
_geSocket = ge;
_working = true;
_gms = new ArrayList<>();
}
@Override
public void interrupt()
{
try
{
_geSocket.close();
}
catch (Exception e)
{
}
super.interrupt();
}
@Override
public void run()
{
try
{
_out = _geSocket.getOutputStream();
int timer = 0;
while (_working)
{
if (!isConnected())
{
_working = false;
}
if ((_mode == 2) && (timer > _sendDelay))
{
for (L2PcInstance gm : _gms)
{
if (gm.isOnline() == 1)
{
sendGmPosition(gm);
}
else
{
_gms.remove(gm);
}
}
timer = 0;
}
try
{
sleep(100);
if (_mode == 2)
{
timer += 100;
}
}
catch (Exception e)
{
}
}
}
catch (SocketException e)
{
LOGGER.warning("GeoEditor disconnected. " + e);
}
catch (Exception e)
{
LOGGER.warning(e.getMessage());
}
finally
{
try
{
_geSocket.close();
}
catch (Exception e)
{
}
_working = false;
}
}
public void sendGmPosition(int gx, int gy, short z)
{
if (!isConnected())
{
return;
}
try
{
synchronized (_out)
{
writeC(0x0b); // length 11 bytes!
writeC(0x01); // Cmd = save cell;
writeD(gx); // Global coord X;
writeD(gy); // Global coord Y;
writeH(z); // Coord Z;
_out.flush();
}
}
catch (SocketException e)
{
LOGGER.warning("GeoEditor disconnected. " + e);
_working = false;
}
catch (Exception e)
{
LOGGER.warning(e.getMessage());
try
{
_geSocket.close();
}
catch (Exception ex)
{
}
_working = false;
}
}
public void sendGmPosition(L2PcInstance _gm)
{
sendGmPosition(_gm.getX(), _gm.getY(), (short) _gm.getZ());
}
public void sendPing()
{
if (!isConnected())
{
return;
}
try
{
synchronized (_out)
{
writeC(0x01); // length 1 byte!
writeC(0x02); // Cmd = ping (dummy packet for connection test);
_out.flush();
}
}
catch (SocketException e)
{
LOGGER.warning("GeoEditor disconnected. " + e);
_working = false;
}
catch (Exception e)
{
LOGGER.warning(e.getMessage() + e);
try
{
_geSocket.close();
}
catch (Exception ex)
{
}
_working = false;
}
}
private void writeD(int value) throws IOException
{
_out.write(value & 0xff);
_out.write((value >> 8) & 0xff);
_out.write((value >> 16) & 0xff);
_out.write((value >> 24) & 0xff);
}
private void writeH(int value) throws IOException
{
_out.write(value & 0xff);
_out.write((value >> 8) & 0xff);
}
private void writeC(int value) throws IOException
{
_out.write(value & 0xff);
}
public void setMode(int value)
{
_mode = value;
}
public void setTimer(int value)
{
if (value < 500)
{
_sendDelay = 500; // maximum - 2 times per second!
}
else if (value > 60000)
{
_sendDelay = 60000; // Minimum - 1 time per minute.
}
else
{
_sendDelay = value;
}
}
public void addGM(L2PcInstance gm)
{
if (!_gms.contains(gm))
{
_gms.add(gm);
}
}
public void removeGM(L2PcInstance gm)
{
if (_gms.contains(gm))
{
_gms.remove(gm);
}
}
public boolean isSend(L2PcInstance gm)
{
return (_mode == 1) && _gms.contains(gm);
}
private boolean isConnected()
{
return _geSocket.isConnected() && !_geSocket.isClosed();
}
public boolean isWorking()
{
sendPing();
return _working;
}
public int getMode()
{
return _mode;
}
}

View File

@ -1,87 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding;
public abstract class AbstractNode<T extends AbstractNodeLoc>
{
private T _loc;
private AbstractNode<T> _parent;
public AbstractNode(T loc)
{
_loc = loc;
}
public void setParent(AbstractNode<T> p)
{
_parent = p;
}
public AbstractNode<T> getParent()
{
return _parent;
}
public T getLoc()
{
return _loc;
}
public void setLoc(T l)
{
_loc = l;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof AbstractNode))
{
return false;
}
final AbstractNode<?> other = (AbstractNode<?>) obj;
if (_loc == null)
{
if (other._loc != null)
{
return false;
}
}
else if (!_loc.equals(other._loc))
{
return false;
}
return true;
}
}

View File

@ -1,105 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding;
import java.util.List;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geodata.pathfinding.cellnodes.CellPathFinding;
import com.l2jmobius.gameserver.geodata.pathfinding.geonodes.GeoPathFinding;
import com.l2jmobius.gameserver.model.L2World;
/**
* @author -Nemesiss-
*/
public abstract class PathFinding
{
public static PathFinding getInstance()
{
if (Config.PATHFINDING == 1)
{
// Higher Memory Usage, Smaller Cpu Usage
return GeoPathFinding.getInstance();
}
// Cell pathfinding, calculated directly from geodata files
return CellPathFinding.getInstance();
}
public abstract boolean pathNodesExist(short regionoffset);
public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable);
/**
* Convert geodata position to pathnode position
* @param geo_pos
* @return pathnode position
*/
public short getNodePos(int geo_pos)
{
return (short) (geo_pos >> 3); // OK?
}
/**
* Convert node position to pathnode block position
* @param node_pos
* @return pathnode block position (0...255)
*/
public short getNodeBlock(int node_pos)
{
return (short) (node_pos % 256);
}
public byte getRegionX(int node_pos)
{
return (byte) ((node_pos >> 8) + L2World.TILE_X_MIN);
}
public byte getRegionY(int node_pos)
{
return (byte) ((node_pos >> 8) + L2World.TILE_Y_MIN);
}
public short getRegionOffset(byte rx, byte ry)
{
return (short) ((rx << 5) + ry);
}
/**
* Convert pathnode x to World x position
* @param node_x rx
* @return
*/
public int calculateWorldX(short node_x)
{
return L2World.MAP_MIN_X + (node_x * 128) + 48;
}
/**
* Convert pathnode y to World y position
* @param node_y
* @return
*/
public int calculateWorldY(short node_y)
{
return L2World.MAP_MIN_Y + (node_y * 128) + 48;
}
public String[] getStat()
{
return null;
}
}

View File

@ -1,69 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode;
public class CellNode extends AbstractNode<NodeLoc>
{
private CellNode _next = null;
private boolean _isInUse = true;
private float _cost = -1000;
public CellNode(NodeLoc loc)
{
super(loc);
}
public boolean isInUse()
{
return _isInUse;
}
public void setInUse()
{
_isInUse = true;
}
public CellNode getNext()
{
return _next;
}
public void setNext(CellNode next)
{
_next = next;
}
public float getCost()
{
return _cost;
}
public void setCost(double cost)
{
_cost = (float) cost;
}
public void free()
{
setParent(null);
_cost = -1000;
_isInUse = false;
_next = null;
}
}

View File

@ -1,360 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.l2jmobius.Config;
/**
* @author DS Credits to Diamond
*/
public class CellNodeBuffer
{
private static final int MAX_ITERATIONS = 3500;
private final ReentrantLock _lock = new ReentrantLock();
private final int _mapSize;
private final CellNode[][] _buffer;
private int _baseX = 0;
private int _baseY = 0;
private int _targetX = 0;
private int _targetY = 0;
private int _targetZ = 0;
private long _timeStamp = 0;
private long _lastElapsedTime = 0;
private CellNode _current = null;
public CellNodeBuffer(int size)
{
_mapSize = size;
_buffer = new CellNode[_mapSize][_mapSize];
}
public final boolean lock()
{
return _lock.tryLock();
}
public final CellNode findPath(int x, int y, int z, int tx, int ty, int tz)
{
_timeStamp = System.currentTimeMillis();
_baseX = x + ((tx - x - _mapSize) / 2); // middle of the line (x,y) - (tx,ty)
_baseY = y + ((ty - y - _mapSize) / 2); // will be in the center of the buffer
_targetX = tx;
_targetY = ty;
_targetZ = tz;
_current = getNode(x, y, z);
_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
for (int count = 0; count < MAX_ITERATIONS; count++)
{
if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
{
return _current; // found
}
getNeighbors();
if (_current.getNext() == null)
{
return null; // no more ways
}
_current = _current.getNext();
}
return null;
}
public final void free()
{
_current = null;
CellNode node;
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
node = _buffer[i][j];
if (node != null)
{
node.free();
}
}
}
_lock.unlock();
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
}
public final long getElapsedTime()
{
return _lastElapsedTime;
}
public final List<CellNode> debugPath()
{
final List<CellNode> result = new LinkedList<>();
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
{
result.add(n);
n.setCost(-n.getCost());
}
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
final CellNode n = _buffer[i][j];
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
{
continue;
}
result.add(n);
}
}
return result;
}
private void getNeighbors()
{
if (!_current.getLoc().canGoAll())
{
return;
}
final int x = _current.getLoc().getNodeX();
final int y = _current.getLoc().getNodeY();
final int z = _current.getLoc().getZ();
CellNode nodeE = null;
CellNode nodeS = null;
CellNode nodeW = null;
CellNode nodeN = null;
// East
if (_current.getLoc().canGoEast())
{
nodeE = addNode(x + 1, y, z, false);
}
// South
if (_current.getLoc().canGoSouth())
{
nodeS = addNode(x, y + 1, z, false);
}
// West
if (_current.getLoc().canGoWest())
{
nodeW = addNode(x - 1, y, z, false);
}
// North
if (_current.getLoc().canGoNorth())
{
nodeN = addNode(x, y - 1, z, false);
}
if (Config.ADVANCED_DIAGONAL_STRATEGY)
{
// SouthEast
if ((nodeE != null) && (nodeS != null))
{
if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
{
addNode(x + 1, y + 1, z, true);
}
}
// SouthWest
if ((nodeS != null) && (nodeW != null))
{
if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
{
addNode(x - 1, y + 1, z, true);
}
}
// NorthEast
if ((nodeN != null) && (nodeE != null))
{
if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
{
addNode(x + 1, y - 1, z, true);
}
}
// NorthWest
if ((nodeN != null) && (nodeW != null))
{
if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
{
addNode(x - 1, y - 1, z, true);
}
}
}
}
private CellNode getNode(int x, int y, int z)
{
final int aX = x - _baseX;
if ((aX < 0) || (aX >= _mapSize))
{
return null;
}
final int aY = y - _baseY;
if ((aY < 0) || (aY >= _mapSize))
{
return null;
}
CellNode result = _buffer[aX][aY];
if (result == null)
{
result = new CellNode(new NodeLoc(x, y, z));
_buffer[aX][aY] = result;
}
else if (!result.isInUse())
{
result.setInUse();
// reinit node if needed
if (result.getLoc() != null)
{
result.getLoc().set(x, y, z);
}
else
{
result.setLoc(new NodeLoc(x, y, z));
}
}
return result;
}
private CellNode addNode(int x, int y, int z, boolean diagonal)
{
final CellNode newNode = getNode(x, y, z);
if (newNode == null)
{
return null;
}
if (newNode.getCost() >= 0)
{
return newNode;
}
final int geoZ = newNode.getLoc().getZ();
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
if (!newNode.getLoc().canGoAll() || (stepZ > 16))
{
weight = Config.HIGH_WEIGHT;
}
else if (isHighWeight(x + 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x - 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y + 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y - 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
newNode.setParent(_current);
newNode.setCost(getCost(x, y, geoZ, weight));
CellNode node = _current;
int count = 0;
while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
{
count++;
if (node.getNext().getCost() > newNode.getCost())
{
// insert node into a chain
newNode.setNext(node.getNext());
break;
}
node = node.getNext();
}
if (count == (MAX_ITERATIONS * 4))
{
System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
}
node.setNext(newNode); // add last
return newNode;
}
private boolean isHighWeight(int x, int y, int z)
{
final CellNode result = getNode(x, y, z);
if (result == null)
{
return true;
}
if (!result.getLoc().canGoAll())
{
return true;
}
if (Math.abs(result.getLoc().getZ() - z) > 16)
{
return true;
}
return false;
}
private double getCost(int x, int y, int z, float weight)
{
final int dX = x - _targetX;
final int dY = y - _targetY;
final int dZ = z - _targetZ;
double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256.0));
if (result > weight)
{
result += weight;
}
if (result > Float.MAX_VALUE)
{
result = Float.MAX_VALUE;
}
return result;
}
}

View File

@ -1,441 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.model.PcInventory;
import com.l2jmobius.gameserver.model.actor.instance.L2ItemInstance;
/**
* @author Sami, DS Credits to Diamond
*/
public class CellPathFinding extends PathFinding
{
private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName());
private BufferInfo[] _allBuffers;
private int _findSuccess = 0;
private int _findFails = 0;
private int _postFilterUses = 0;
private int _postFilterPlayableUses = 0;
private int _postFilterPasses = 0;
private long _postFilterElapsed = 0;
private List<L2ItemInstance> _debugItems = null;
public static CellPathFinding getInstance()
{
return SingletonHolder._instance;
}
protected CellPathFinding()
{
try
{
final String[] array = Config.PATHFIND_BUFFERS.split(";");
_allBuffers = new BufferInfo[array.length];
String buf;
String[] args;
for (int i = 0; i < array.length; i++)
{
buf = array[i];
args = buf.split("x");
if (args.length != 2)
{
throw new Exception("Invalid buffer definition: " + buf);
}
_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage());
throw new Error("CellPathFinding: load aborted");
}
}
@Override
public boolean pathNodesExist(short regionoffset)
{
return false;
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable)
{
final int gx = GeoData.getInstance().getGeoX(x);
final int gy = GeoData.getInstance().getGeoY(y);
if (!GeoData.getInstance().hasGeo(x, y))
{
return null;
}
final int gz = GeoData.getInstance().getHeight(x, y, z);
final int gtx = GeoData.getInstance().getGeoX(tx);
final int gty = GeoData.getInstance().getGeoY(ty);
if (!GeoData.getInstance().hasGeo(tx, ty))
{
return null;
}
final int gtz = GeoData.getInstance().getHeight(tx, ty, tz);
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
if (buffer == null)
{
return null;
}
final boolean debug = playable && Config.DEBUG_PATH;
if (debug)
{
if (_debugItems == null)
{
_debugItems = new CopyOnWriteArrayList<>();
}
else
{
for (L2ItemInstance item : _debugItems)
{
if (item == null)
{
continue;
}
item.decayMe();
}
_debugItems.clear();
}
}
List<AbstractNodeLoc> path = null;
try
{
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
if (debug)
{
for (CellNode n : buffer.debugPath())
{
if (n.getCost() < 0)
{
dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc());
}
else
{
// known nodes
dropDebugItem(PcInventory.ADENA_ID, (int) (n.getCost() * 10), n.getLoc());
}
}
}
if (result == null)
{
_findFails++;
return null;
}
path = constructPath(result);
}
catch (Exception e)
{
_log.log(Level.WARNING, "", e);
return null;
}
finally
{
buffer.free();
}
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
{
_findSuccess++;
return path;
}
final long timeStamp = System.currentTimeMillis();
_postFilterUses++;
if (playable)
{
_postFilterPlayableUses++;
}
int currentX;
int currentY;
int currentZ;
ListIterator<AbstractNodeLoc> middlePoint;
boolean remove;
int pass = 0;
do
{
pass++;
_postFilterPasses++;
remove = false;
middlePoint = path.listIterator();
currentX = x;
currentY = y;
currentZ = z;
while (middlePoint.hasNext())
{
final AbstractNodeLoc locMiddle = middlePoint.next();
if (!middlePoint.hasNext())
{
break;
}
final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex());
if (GeoData.getInstance().canMove(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ()))
{
middlePoint.remove();
remove = true;
if (debug)
{
dropDebugItem(735, 1, locMiddle);
}
}
else
{
currentX = locMiddle.getX();
currentY = locMiddle.getY();
currentZ = locMiddle.getZ();
}
}
}
// only one postfilter pass for AI
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
if (debug)
{
path.forEach(n -> dropDebugItem(65, 1, n));
}
_findSuccess++;
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
return path;
}
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
{
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
int previousDirectionX = Integer.MIN_VALUE;
int previousDirectionY = Integer.MIN_VALUE;
int directionX;
int directionY;
while (node.getParent() != null)
{
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (node.getParent().getParent() != null))
{
final int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
final int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
if (Math.abs(tmpX) == Math.abs(tmpY))
{
directionX = tmpX;
directionY = tmpY;
}
else
{
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
}
}
else
{
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
}
// only add a new route point if moving direction changes
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.addFirst(node.getLoc());
node.setLoc(null);
}
node = node.getParent();
}
return path;
}
private CellNodeBuffer alloc(int size, boolean playable)
{
CellNodeBuffer current = null;
for (BufferInfo i : _allBuffers)
{
if (i.mapSize >= size)
{
for (CellNodeBuffer buf : i.bufs)
{
if (buf.lock())
{
i.uses++;
if (playable)
{
i.playableUses++;
}
i.elapsed += buf.getElapsedTime();
current = buf;
break;
}
}
if (current != null)
{
break;
}
// not found, allocate temporary buffer
current = new CellNodeBuffer(i.mapSize);
current.lock();
if (i.bufs.size() < i.count)
{
i.bufs.add(current);
i.uses++;
if (playable)
{
i.playableUses++;
}
break;
}
i.overflows++;
if (playable)
{
i.playableOverflows++;
}
}
}
return current;
}
private void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
{
final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
item.setCount(num);
item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
_debugItems.add(item);
}
private static final class BufferInfo
{
final int mapSize;
final int count;
ArrayList<CellNodeBuffer> bufs;
int uses = 0;
int playableUses = 0;
int overflows = 0;
int playableOverflows = 0;
long elapsed = 0;
public BufferInfo(int size, int cnt)
{
mapSize = size;
count = cnt;
bufs = new ArrayList<>(count);
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder(100);
sb.append(mapSize);
sb.append("x");
sb.append(mapSize);
sb.append(" num:");
sb.append(bufs.size());
sb.append("/");
sb.append(count);
sb.append(" uses:");
sb.append(uses);
sb.append("/");
sb.append(playableUses);
if (uses > 0)
{
sb.append(" total/avg(ms):");
sb.append(elapsed);
sb.append("/");
sb.append(String.format("%1.2f", (double) elapsed / uses));
}
sb.append(" ovf:");
sb.append(overflows);
sb.append("/");
sb.append(playableOverflows);
return sb.toString();
}
}
@Override
public String[] getStat()
{
final String[] result = new String[_allBuffers.length + 1];
for (int i = 0; i < _allBuffers.length; i++)
{
result[i] = _allBuffers[i].toString();
}
final StringBuilder sb = new StringBuilder(128);
sb.append("LOS postfilter uses:");
sb.append(_postFilterUses);
sb.append("/");
sb.append(_postFilterPlayableUses);
if (_postFilterUses > 0)
{
sb.append(" total/avg(ms):");
sb.append(_postFilterElapsed);
sb.append("/");
sb.append(String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses));
sb.append(" passes total/avg:");
sb.append(_postFilterPasses);
sb.append("/");
sb.append(String.format("%1.1f", (double) _postFilterPasses / _postFilterUses));
sb.append(Config.EOL);
}
sb.append("Pathfind success/fail:");
sb.append(_findSuccess);
sb.append("/");
sb.append(_findFails);
result[result.length - 1] = sb.toString();
return result;
}
private static class SingletonHolder
{
protected static final CellPathFinding _instance = new CellPathFinding();
}
}

View File

@ -1,183 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.geodriver.Cell;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class NodeLoc extends AbstractNodeLoc
{
private int _x;
private int _y;
private boolean _goNorth;
private boolean _goEast;
private boolean _goSouth;
private boolean _goWest;
private int _geoHeight;
public NodeLoc(int x, int y, int z)
{
set(x, y, z);
}
public void set(int x, int y, int z)
{
_x = x;
_y = y;
_goNorth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
_goEast = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
_goSouth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
_goWest = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
_geoHeight = GeoData.getInstance().getNearestZ(x, y, z);
}
public boolean canGoNorth()
{
return _goNorth;
}
public boolean canGoEast()
{
return _goEast;
}
public boolean canGoSouth()
{
return _goSouth;
}
public boolean canGoWest()
{
return _goWest;
}
public boolean canGoAll()
{
return _goNorth && _goEast && _goSouth && _goWest;
}
@Override
public int getX()
{
return GeoData.getInstance().getWorldX(_x);
}
@Override
public int getY()
{
return GeoData.getInstance().getWorldY(_y);
}
@Override
public int getZ()
{
return _geoHeight;
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
int nswe = 0;
if (_goNorth)
{
nswe |= Cell.NSWE_NORTH;
}
if (_goEast)
{
nswe |= Cell.NSWE_EAST;
}
if (_goSouth)
{
nswe |= Cell.NSWE_SOUTH;
}
if (_goWest)
{
nswe |= Cell.NSWE_WEST;
}
result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe);
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof NodeLoc))
{
return false;
}
final NodeLoc other = (NodeLoc) obj;
if (_x != other._x)
{
return false;
}
if (_y != other._y)
{
return false;
}
if (_goNorth != other._goNorth)
{
return false;
}
if (_goEast != other._goEast)
{
return false;
}
if (_goSouth != other._goSouth)
{
return false;
}
if (_goWest != other._goWest)
{
return false;
}
if (_geoHeight != other._geoHeight)
{
return false;
}
return true;
}
}

View File

@ -1,62 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.geonodes;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class GeoNode extends AbstractNode<GeoNodeLoc>
{
private final int _neighborsIdx;
private short _cost;
private GeoNode[] _neighbors;
public GeoNode(GeoNodeLoc Loc, int Neighbors_idx)
{
super(Loc);
_neighborsIdx = Neighbors_idx;
}
public short getCost()
{
return _cost;
}
public void setCost(int cost)
{
_cost = (short) cost;
}
public GeoNode[] getNeighbors()
{
return _neighbors;
}
public void attachNeighbors()
{
if (getLoc() == null)
{
_neighbors = null;
}
else
{
_neighbors = GeoPathFinding.getInstance().readNeighbors(this, _neighborsIdx);
}
}
}

View File

@ -1,109 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.geonodes;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.model.L2World;
/**
* @author -Nemesiss-
*/
public class GeoNodeLoc extends AbstractNodeLoc
{
private final short _x;
private final short _y;
private final short _z;
public GeoNodeLoc(short x, short y, short z)
{
_x = x;
_y = y;
_z = z;
}
@Override
public int getX()
{
return L2World.MAP_MIN_X + (_x * 128) + 48;
}
@Override
public int getY()
{
return L2World.MAP_MIN_Y + (_y * 128) + 48;
}
@Override
public int getZ()
{
return _z;
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
result = (prime * result) + _z;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof GeoNodeLoc))
{
return false;
}
final GeoNodeLoc other = (GeoNodeLoc) obj;
if (_x != other._x)
{
return false;
}
if (_y != other._y)
{
return false;
}
if (_z != other._z)
{
return false;
}
return true;
}
}

View File

@ -1,473 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.geonodes;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.util.Util;
/**
* @author -Nemesiss-
*/
public class GeoPathFinding extends PathFinding
{
private static Logger _log = Logger.getLogger(GeoPathFinding.class.getName());
private static Map<Short, ByteBuffer> _pathNodes = new ConcurrentHashMap<>();
private static Map<Short, IntBuffer> _pathNodesIndex = new ConcurrentHashMap<>();
public static GeoPathFinding getInstance()
{
return SingletonHolder._instance;
}
@Override
public boolean pathNodesExist(short regionoffset)
{
return _pathNodesIndex.containsKey(regionoffset);
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable)
{
final int gx = (x - L2World.MAP_MIN_X) >> 4;
final int gy = (y - L2World.MAP_MIN_Y) >> 4;
final short gz = (short) z;
final int gtx = (tx - L2World.MAP_MIN_X) >> 4;
final int gty = (ty - L2World.MAP_MIN_Y) >> 4;
final short gtz = (short) tz;
final GeoNode start = readNode(gx, gy, gz);
final GeoNode end = readNode(gtx, gty, gtz);
if ((start == null) || (end == null))
{
return null;
}
if (Math.abs(start.getLoc().getZ() - z) > 55)
{
return null; // not correct layer
}
if (Math.abs(end.getLoc().getZ() - tz) > 55)
{
return null; // not correct layer
}
if (start == end)
{
return null;
}
// TODO: Find closest path node we CAN access. Now only checks if we can not reach the closest
Location temp = GeoData.getInstance().moveCheck(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ());
if ((temp.getX() != start.getLoc().getX()) || (temp.getY() != start.getLoc().getY()))
{
return null; // cannot reach closest...
}
// TODO: Find closest path node around target, now only checks if final location can be reached
temp = GeoData.getInstance().moveCheck(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ());
if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY()))
{
return null; // cannot reach closest...
}
// return searchAStar(start, end);
return searchByClosest2(start, end);
}
public List<AbstractNodeLoc> searchByClosest2(GeoNode start, GeoNode end)
{
// Always continues checking from the closest to target non-blocked
// node from to_visit list. There's extra length in path if needed
// to go backwards/sideways but when moving generally forwards, this is extra fast
// and accurate. And can reach insane distances (try it with 800 nodes..).
// Minimum required node count would be around 300-400.
// Generally returns a bit (only a bit) more intelligent looking routes than
// the basic version. Not a true distance image (which would increase CPU
// load) level of intelligence though.
// List of Visited Nodes
final List<GeoNode> visited = new ArrayList<>(550);
// List of Nodes to Visit
final LinkedList<GeoNode> to_visit = new LinkedList<>();
to_visit.add(start);
final int targetX = end.getLoc().getNodeX();
final int targetY = end.getLoc().getNodeY();
int dx;
int dy;
boolean added;
int i = 0;
while (i < 550)
{
GeoNode node;
try
{
node = to_visit.removeFirst();
}
catch (Exception e)
{
// No Path found
return null;
}
if (node.equals(end))
{
return constructPath2(node);
}
i++;
visited.add(node);
node.attachNeighbors();
final GeoNode[] neighbors = node.getNeighbors();
if (neighbors == null)
{
continue;
}
for (GeoNode n : neighbors)
{
if ((visited.lastIndexOf(n) == -1) && !to_visit.contains(n))
{
added = false;
n.setParent(node);
dx = targetX - n.getLoc().getNodeX();
dy = targetY - n.getLoc().getNodeY();
n.setCost((dx * dx) + (dy * dy));
for (int index = 0; index < to_visit.size(); index++)
{
// supposed to find it quite early..
if (to_visit.get(index).getCost() > n.getCost())
{
to_visit.add(index, n);
added = true;
break;
}
}
if (!added)
{
to_visit.addLast(n);
}
}
}
}
// No Path found
return null;
}
public List<AbstractNodeLoc> constructPath2(AbstractNode<GeoNodeLoc> node)
{
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
int previousDirectionX = -1000;
int previousDirectionY = -1000;
int directionX;
int directionY;
while (node.getParent() != null)
{
// only add a new route point if moving direction changes
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.addFirst(node.getLoc());
}
node = node.getParent();
}
return path;
}
public GeoNode[] readNeighbors(GeoNode n, int idx)
{
final int node_x = n.getLoc().getNodeX();
final int node_y = n.getLoc().getNodeY();
// short node_z = n.getLoc().getZ();
final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y));
final ByteBuffer pn = _pathNodes.get(regoffset);
final List<AbstractNode<GeoNodeLoc>> Neighbors = new ArrayList<>(8);
GeoNode newNode;
short new_node_x;
short new_node_y;
// Region for sure will change, we must read from correct file
byte neighbor = pn.get(idx++); // N
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) node_x;
new_node_y = (short) (node_y - 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NE
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x + 1);
new_node_y = (short) (node_y - 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // E
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x + 1);
new_node_y = (short) node_y;
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SE
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x + 1);
new_node_y = (short) (node_y + 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // S
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) node_x;
new_node_y = (short) (node_y + 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SW
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x - 1);
new_node_y = (short) (node_y + 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // W
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x - 1);
new_node_y = (short) node_y;
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NW
if (neighbor > 0)
{
neighbor--;
new_node_x = (short) (node_x - 1);
new_node_y = (short) (node_y - 1);
newNode = readNode(new_node_x, new_node_y, neighbor);
if (newNode != null)
{
Neighbors.add(newNode);
}
}
final GeoNode[] result = new GeoNode[Neighbors.size()];
return Neighbors.toArray(result);
}
// Private
private GeoNode readNode(short node_x, short node_y, byte layer)
{
final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(node_x);
final short nby = getNodeBlock(node_y);
int idx = _pathNodesIndex.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = _pathNodes.get(regoffset);
// reading
final byte nodes = pn.get(idx);
idx += (layer * 10) + 1; // byte + layer*10byte
if (nodes < layer)
{
_log.warning("SmthWrong!");
}
final short node_z = pn.getShort(idx);
idx += 2;
return new GeoNode(new GeoNodeLoc(node_x, node_y, node_z), idx);
}
private GeoNode readNode(int gx, int gy, short z)
{
final short node_x = getNodePos(gx);
final short node_y = getNodePos(gy);
final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(node_x);
final short nby = getNodeBlock(node_y);
int idx = _pathNodesIndex.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = _pathNodes.get(regoffset);
// reading
byte nodes = pn.get(idx++);
int idx2 = 0; // create index to nearlest node by z
short last_z = Short.MIN_VALUE;
while (nodes > 0)
{
final short node_z = pn.getShort(idx);
if (Math.abs(last_z - z) > Math.abs(node_z - z))
{
last_z = node_z;
idx2 = idx + 2;
}
idx += 10; // short + 8 byte
nodes--;
}
return new GeoNode(new GeoNodeLoc(node_x, node_y, last_z), idx2);
}
protected GeoPathFinding()
{
try
{
_log.info("Path Engine: - Loading Path Nodes...");
//@formatter:off
Files.lines(Paths.get(Config.PATHNODE_DIR.getPath(), "pn_index.txt"), StandardCharsets.UTF_8)
.filter(l -> !l.isEmpty())
.forEach(line ->
{
final String[] parts = line.split("_");
if ((parts.length != 2)
|| !Util.isDigit(parts[0])
|| !Util.isDigit(parts[1]))
{
_log.warning("Invalid pathnode entry: '" + line + "', must be in format 'XX_YY', where X and Y - integers");
return;
}
final byte rx = Byte.parseByte(parts[0]);
final byte ry = Byte.parseByte(parts[1]);
LoadPathNodeFile(rx, ry);
});
//@formatter:on
}
catch (IOException e)
{
_log.log(Level.WARNING, "", e);
throw new Error("Failed to read pn_index file.");
}
}
private void LoadPathNodeFile(byte rx, byte ry)
{
if ((rx < L2World.TILE_X_MIN) || (rx > L2World.TILE_X_MAX) || (ry < L2World.TILE_Y_MIN) || (ry > L2World.TILE_Y_MAX))
{
_log.warning("Failed to Load PathNode File: invalid region " + rx + "," + ry + Config.EOL);
return;
}
final short regionoffset = getRegionOffset(rx, ry);
final File file = new File(Config.PATHNODE_DIR, rx + "_" + ry + ".pn");
_log.info("Path Engine: - Loading: " + file.getName() + " -> region offset: " + regionoffset + " X: " + rx + " Y: " + ry);
int node = 0;
int size;
int index = 0;
// Create a read-only memory-mapped file
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel roChannel = raf.getChannel())
{
size = (int) roChannel.size();
MappedByteBuffer nodes;
if (Config.FORCE_GEODATA)
{
// it is not guarantee, because the underlying operating system may have paged out some of the buffer's data
nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
}
else
{
nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
}
// Indexing pathnode files, so we will know where each block starts
final IntBuffer indexs = IntBuffer.allocate(65536);
while (node < 65536)
{
final byte layer = nodes.get(index);
indexs.put(node++, index);
index += (layer * 10) + 1;
}
_pathNodesIndex.put(regionoffset, indexs);
_pathNodes.put(regionoffset, nodes);
}
catch (Exception e)
{
_log.log(Level.WARNING, "Failed to Load PathNode File: " + file.getAbsolutePath() + " : " + e.getMessage());
}
}
private static class SingletonHolder
{
protected static final GeoPathFinding _instance = new GeoPathFinding();
}
}

View File

@ -1,124 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding.utils;
import com.l2jmobius.gameserver.geodata.pathfinding.geonodes.GeoNode;
/**
* @author -Nemesiss-
*/
public class BinaryNodeHeap
{
private final GeoNode[] _list;
private int _size;
public BinaryNodeHeap(int size)
{
_list = new GeoNode[size + 1];
_size = 0;
}
public void add(GeoNode n)
{
_size++;
int pos = _size;
_list[pos] = n;
while (pos != 1)
{
final int p2 = pos / 2;
if (_list[pos].getCost() <= _list[p2].getCost())
{
final GeoNode temp = _list[p2];
_list[p2] = _list[pos];
_list[pos] = temp;
pos = p2;
}
else
{
break;
}
}
}
public GeoNode removeFirst()
{
final GeoNode first = _list[1];
_list[1] = _list[_size];
_list[_size] = null;
_size--;
int pos = 1;
int cpos;
int dblcpos;
GeoNode temp;
while (true)
{
cpos = pos;
dblcpos = cpos * 2;
if ((dblcpos + 1) <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
if (_list[pos].getCost() >= _list[dblcpos + 1].getCost())
{
pos = dblcpos + 1;
}
}
else if (dblcpos <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
}
if (cpos != pos)
{
temp = _list[cpos];
_list[cpos] = _list[pos];
_list[pos] = temp;
}
else
{
break;
}
}
return first;
}
public boolean contains(GeoNode n)
{
if (_size == 0)
{
return false;
}
for (int i = 1; i <= _size; i++)
{
if (_list[i].equals(n))
{
return true;
}
}
return false;
}
public boolean isEmpty()
{
return _size == 0;
}
}

View File

@ -0,0 +1,931 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.datatables.csv.DoorTable;
import com.l2jmobius.gameserver.datatables.xml.FenceData;
import com.l2jmobius.gameserver.geoengine.geodata.ABlock;
import com.l2jmobius.gameserver.geoengine.geodata.BlockComplex;
import com.l2jmobius.gameserver.geoengine.geodata.BlockFlat;
import com.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer;
import com.l2jmobius.gameserver.geoengine.geodata.BlockNull;
import com.l2jmobius.gameserver.geoengine.geodata.GeoFormat;
import com.l2jmobius.gameserver.geoengine.geodata.GeoLocation;
import com.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.util.MathUtil;
/**
* @author Hasha
*/
public class GeoEngine
{
protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName());
private final ABlock[][] _blocks;
private final BlockNull _nullBlock;
/**
* Returns the instance of the {@link GeoEngine}.
* @return {@link GeoEngine} : The instance.
*/
public static GeoEngine getInstance()
{
return SingletonHolder._instance;
}
/**
* GeoEngine constructor. Loads all geodata files of chosen geodata format.
*/
public GeoEngine()
{
LOGGER.info("GeoEngine: Initializing...");
// initialize block container
_blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y];
// load null block
_nullBlock = new BlockNull();
// initialize multilayer temporarily buffer
BlockMultilayer.initialize();
// load geo files according to geoengine config setup
int loaded = 0;
for (int rx = L2World.TILE_X_MIN; rx <= L2World.TILE_X_MAX; rx++)
{
for (int ry = L2World.TILE_Y_MIN; ry <= L2World.TILE_Y_MAX; ry++)
{
final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry));
if (f.exists() && !f.isDirectory())
{
// region file is load-able, try to load it
if (loadGeoBlocks(rx, ry))
{
loaded++;
}
}
else
{
// region file is not load-able, load null blocks
loadNullBlocks(rx, ry);
}
}
}
LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files.");
// avoid wrong configs when no files are loaded
if (loaded == 0)
{
if (Config.PATHFINDING)
{
Config.PATHFINDING = false;
LOGGER.info("GeoEngine: Forcing PathFinding setting to false.");
}
if (Config.COORD_SYNCHRONIZE == 2)
{
Config.COORD_SYNCHRONIZE = -1;
LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1.");
}
}
// release multilayer block temporarily buffer
BlockMultilayer.release();
}
/**
* Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata.
* @param regionX : Geodata file region X coordinate.
* @param regionY : Geodata file region Y coordinate.
* @return boolean : True, when geodata file was loaded without problem.
*/
private final boolean loadGeoBlocks(int regionX, int regionY)
{
final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY);
// standard load
try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r");
FileChannel fc = raf.getChannel())
{
// initialize file buffer
MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load();
buffer.order(ByteOrder.LITTLE_ENDIAN);
// get block indexes
final int blockX = (regionX - L2World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X;
final int blockY = (regionY - L2World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y;
// loop over region blocks
for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++)
{
for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++)
{
// get block type
final byte type = buffer.get();
// load block according to block type
switch (type)
{
case GeoStructure.TYPE_FLAT_L2D:
{
_blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D);
break;
}
case GeoStructure.TYPE_COMPLEX_L2D:
{
_blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D);
break;
}
case GeoStructure.TYPE_MULTILAYER_L2D:
{
_blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D);
break;
}
default:
{
throw new IllegalArgumentException("Unknown block type: " + type);
}
}
}
}
// check data consistency
if (buffer.remaining() > 0)
{
LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read.");
}
// loading was successful
return true;
}
catch (Exception e)
{
// an error occured while loading, load null blocks
LOGGER.warning("GeoEngine: Error while loading " + filename + " region file.");
LOGGER.warning(e.getMessage());
e.printStackTrace();
// replace whole region file with null blocks
loadNullBlocks(regionX, regionY);
// loading was not successful
return false;
}
}
/**
* Loads null blocks. Used when no region file is detected or an error occurs during loading.
* @param regionX : Geodata file region X coordinate.
* @param regionY : Geodata file region Y coordinate.
*/
private final void loadNullBlocks(int regionX, int regionY)
{
// get block indexes
final int blockX = (regionX - L2World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X;
final int blockY = (regionY - L2World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y;
// load all null blocks
for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++)
{
for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++)
{
_blocks[blockX + ix][blockY + iy] = _nullBlock;
}
}
}
// GEODATA - GENERAL
/**
* Converts world X to geodata X.
* @param worldX
* @return int : Geo X
*/
public static int getGeoX(int worldX)
{
return (MathUtil.limit(worldX, L2World.MAP_MIN_X, L2World.MAP_MAX_X) - L2World.MAP_MIN_X) >> 4;
}
/**
* Converts world Y to geodata Y.
* @param worldY
* @return int : Geo Y
*/
public static int getGeoY(int worldY)
{
return (MathUtil.limit(worldY, L2World.MAP_MIN_Y, L2World.MAP_MAX_Y) - L2World.MAP_MIN_Y) >> 4;
}
/**
* Converts geodata X to world X.
* @param geoX
* @return int : World X
*/
public static int getWorldX(int geoX)
{
return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + L2World.MAP_MIN_X + 8;
}
/**
* Converts geodata Y to world Y.
* @param geoY
* @return int : World Y
*/
public static int getWorldY(int geoY)
{
return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + L2World.MAP_MIN_Y + 8;
}
/**
* Returns block of geodata on given coordinates.
* @param geoX : Geodata X
* @param geoY : Geodata Y
* @return {@link ABlock} : Block of geodata.
*/
private final ABlock getBlock(int geoX, int geoY)
{
final int x = geoX / GeoStructure.BLOCK_CELLS_X;
final int y = geoY / GeoStructure.BLOCK_CELLS_Y;
// if x or y is out of array return null
if ((x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y))
{
return _blocks[x][y];
}
return null;
}
/**
* Check if geo coordinates has geo.
* @param geoX : Geodata X
* @param geoY : Geodata Y
* @return boolean : True, if given geo coordinates have geodata
*/
public final boolean hasGeoPos(int geoX, int geoY)
{
final ABlock block = getBlock(geoX, geoY);
if (block == null) // null block check
{
// TODO: Find when this can be null. (Bad geodata? Check L2World getRegion method.)
// LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + ".");
return false;
}
return block.hasGeoPos();
}
/**
* Returns the height of cell, which is closest to given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, closest to given coordinates.
*/
public final short getHeightNearest(int geoX, int geoY, int worldZ)
{
final ABlock block = getBlock(geoX, geoY);
return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ;
}
/**
* Returns the NSWE flag byte of cell, which is closes to given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte coordinate, closest to given coordinates.
*/
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
{
final ABlock block = getBlock(geoX, geoY);
return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF;
}
/**
* Check if world coordinates has geo.
* @param worldX : World X
* @param worldY : World Y
* @return boolean : True, if given world coordinates have geodata
*/
public final boolean hasGeo(int worldX, int worldY)
{
return hasGeoPos(getGeoX(worldX), getGeoY(worldY));
}
/**
* Returns closest Z coordinate according to geodata.
* @param worldX : world x
* @param worldY : world y
* @param worldZ : world z
* @return short : nearest Z coordinates according to geodata
*/
public final short getHeight(int worldX, int worldY, int worldZ)
{
return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ);
}
// PATHFINDING
/**
* Check line of sight from {@link L2Object} to {@link L2Object}.
* @param origin : The origin object.
* @param target : The target object.
* @return {@code boolean} : True if origin can see target
*/
public final boolean canSeeTarget(L2Object origin, L2Object target)
{
if (target.isDoor())
{
return true;
}
// get origin and target world coordinates
final int ox = origin.getX();
final int oy = origin.getY();
final int oz = origin.getZ();
final int tx = target.getX();
final int ty = target.getY();
final int tz = target.getZ();
if (DoorTable.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz))
{
return false;
}
if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz))
{
return false;
}
// get origin and check existing geo coordinates
final int gox = getGeoX(ox);
final int goy = getGeoY(oy);
if (!hasGeoPos(gox, goy))
{
return true;
}
final short goz = getHeightNearest(gox, goy, oz);
// get target and check existing geo coordinates
final int gtx = getGeoX(tx);
final int gty = getGeoY(ty);
if (!hasGeoPos(gtx, gty))
{
return true;
}
final short gtz = getHeightNearest(gtx, gty, tz);
// origin and target coordinates are same
if ((gox == gtx) && (goy == gty))
{
return goz == gtz;
}
// get origin and target height, real height = collision height * 2
double oheight = 0;
if (origin.isCharacter())
{
oheight = ((L2Character) origin).getTemplate().collisionHeight * 2;
}
double theight = 0;
if (target.isCharacter())
{
theight = ((L2Character) target).getTemplate().collisionHeight * 2;
}
// perform geodata check
return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId());
}
/**
* Check line of sight from {@link L2Object} to {@link Location}.
* @param origin : The origin object.
* @param position : The target position.
* @return {@code boolean} : True if object can see position
*/
public final boolean canSeeTarget(L2Object origin, Location position)
{
// get origin and target world coordinates
final int ox = origin.getX();
final int oy = origin.getY();
final int oz = origin.getZ();
final int tx = position.getX();
final int ty = position.getY();
final int tz = position.getZ();
if (DoorTable.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz))
{
return false;
}
if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz))
{
return false;
}
// get origin and check existing geo coordinates
final int gox = getGeoX(ox);
final int goy = getGeoY(oy);
if (!hasGeoPos(gox, goy))
{
return true;
}
final short goz = getHeightNearest(gox, goy, oz);
// get target and check existing geo coordinates
final int gtx = getGeoX(tx);
final int gty = getGeoY(ty);
if (!hasGeoPos(gtx, gty))
{
return true;
}
final short gtz = getHeightNearest(gtx, gty, tz);
// origin and target coordinates are same
if ((gox == gtx) && (goy == gty))
{
return goz == gtz;
}
// get origin and target height, real height = collision height * 2
double oheight = 0;
if (origin.isCharacter())
{
oheight = ((L2Character) origin).getTemplate().collisionHeight;
}
// perform geodata check
return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId());
}
/**
* Simple check for origin to target visibility.
* @param gox : origin X geodata coordinate
* @param goy : origin Y geodata coordinate
* @param goz : origin Z geodata coordinate
* @param oheight : origin height (if instance of {@link Character})
* @param gtx : target X geodata coordinate
* @param gty : target Y geodata coordinate
* @param gtz : target Z geodata coordinate
* @param theight : target height (if instance of {@link Character})
* @param instanceId
* @return {@code boolean} : True, when target can be seen.
*/
private final boolean checkSee(int gox, int goy, int goz, double oheight, int gtx, int gty, int gtz, double theight, int instanceId)
{
// get line of sight Z coordinates
double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100);
double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100);
// get X delta and signum
final int dx = Math.abs(gtx - gox);
final int sx = gox < gtx ? 1 : -1;
final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W;
final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E;
// get Y delta and signum
final int dy = Math.abs(gty - goy);
final int sy = goy < gty ? 1 : -1;
final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N;
final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S;
// get Z delta
final int dm = Math.max(dx, dy);
final double dz = (lostz - losoz) / dm;
// get direction flag for diagonal movement
final byte diroxy = getDirXY(dirox, diroy);
final byte dirtxy = getDirXY(dirtx, dirty);
// delta, determines axis to move on (+..X axis, -..Y axis)
int d = dx - dy;
// NSWE direction of movement
byte diro;
byte dirt;
// clearDebugItems();
// dropDebugItem(728, 0, new GeoLocation(gox, goy, goz)); // blue potion
// dropDebugItem(728, 0, new GeoLocation(gtx, gty, gtz)); // blue potion
// initialize node values
int nox = gox;
int noy = goy;
int ntx = gtx;
int nty = gty;
byte nsweo = getNsweNearest(gox, goy, goz);
byte nswet = getNsweNearest(gtx, gty, gtz);
// loop
ABlock block;
int index;
for (int i = 0; i < ((dm + 1) / 2); i++)
{
// dropDebugItem(57, 0, new GeoLocation(gox, goy, goz)); // antidote
// dropDebugItem(1831, 0, new GeoLocation(gtx, gty, gtz)); // adena
// reset direction flag
diro = 0;
dirt = 0;
// calculate next point coordinates
int e2 = 2 * d;
if ((e2 > -dy) && (e2 < dx))
{
// calculate next point XY coordinates
d -= dy;
d += dx;
nox += sx;
ntx -= sx;
noy += sy;
nty -= sy;
diro |= diroxy;
dirt |= dirtxy;
}
else if (e2 > -dy)
{
// calculate next point X coordinate
d -= dy;
nox += sx;
ntx -= sx;
diro |= dirox;
dirt |= dirtx;
}
else if (e2 < dx)
{
// calculate next point Y coordinate
d += dx;
noy += sy;
nty -= sy;
diro |= diroy;
dirt |= dirty;
}
{
// get block of the next cell
block = getBlock(nox, noy);
// get index of particular layer, based on movement conditions
if ((nsweo & diro) == 0)
{
index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT);
}
else
{
index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT);
}
// layer does not exist, return
if (index == -1)
{
return false;
}
// get layer and next line of sight Z coordinate
goz = block.getHeight(index);
losoz += dz;
// perform line of sight check, return when fails
if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT)
{
return false;
}
// get layer nswe
nsweo = block.getNswe(index);
}
{
// get block of the next cell
block = getBlock(ntx, nty);
// get index of particular layer, based on movement conditions
if ((nswet & dirt) == 0)
{
index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT);
}
else
{
index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT);
}
// layer does not exist, return
if (index == -1)
{
return false;
}
// get layer and next line of sight Z coordinate
gtz = block.getHeight(index);
lostz -= dz;
// perform line of sight check, return when fails
if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT)
{
return false;
}
// get layer nswe
nswet = block.getNswe(index);
}
// update coords
gox = nox;
goy = noy;
gtx = ntx;
gty = nty;
}
// when iteration is completed, compare final Z coordinates
return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4);
}
/**
* Check movement from coordinates to coordinates.
* @param ox : origin X coordinate
* @param oy : origin Y coordinate
* @param oz : origin Z coordinate
* @param tx : target X coordinate
* @param ty : target Y coordinate
* @param tz : target Z coordinate
* @param instanceId
* @return {code boolean} : True if target coordinates are reachable from origin coordinates
*/
public final boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId)
{
// get origin and check existing geo coordinates
final int gox = getGeoX(ox);
final int goy = getGeoY(oy);
if (!hasGeoPos(gox, goy))
{
return true;
}
final short goz = getHeightNearest(gox, goy, oz);
// get target and check existing geo coordinates
final int gtx = getGeoX(tx);
final int gty = getGeoY(ty);
if (!hasGeoPos(gtx, gty))
{
return true;
}
final short gtz = getHeightNearest(gtx, gty, tz);
// target coordinates reached
if ((gox == gtx) && (goy == gty) && (goz == gtz))
{
return true;
}
// perform geodata check
GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId);
return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty);
}
/**
* Check movement from origin to target. Returns last available point in the checked path.
* @param ox : origin X coordinate
* @param oy : origin Y coordinate
* @param oz : origin Z coordinate
* @param tx : target X coordinate
* @param ty : target Y coordinate
* @param tz : target Z coordinate
* @param instanceId
* @return {@link Location} : Last point where object can walk (just before wall)
*/
public final Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId)
{
// Mobius: Double check for doors before normal checkMove to avoid exploiting key movement.
if (DoorTable.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz))
{
return new Location(ox, oy, oz);
}
if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz))
{
return new Location(ox, oy, oz);
}
// get origin and check existing geo coordinates
final int gox = getGeoX(ox);
final int goy = getGeoY(oy);
if (!hasGeoPos(gox, goy))
{
return new Location(tx, ty, tz);
}
final short goz = getHeightNearest(gox, goy, oz);
// get target and check existing geo coordinates
final int gtx = getGeoX(tx);
final int gty = getGeoY(ty);
if (!hasGeoPos(gtx, gty))
{
return new Location(tx, ty, tz);
}
final short gtz = getHeightNearest(gtx, gty, tz);
// target coordinates reached
if ((gox == gtx) && (goy == gty) && (goz == gtz))
{
return new Location(tx, ty, tz);
}
// perform geodata check
return checkMove(gox, goy, goz, gtx, gty, gtz, instanceId);
}
/**
* With this method you can check if a position is visible or can be reached by beeline movement.<br>
* Target X and Y reachable and Z is on same floor:
* <ul>
* <li>Location of the target with corrected Z value from geodata.</li>
* </ul>
* Target X and Y reachable but Z is on another floor:
* <ul>
* <li>Location of the origin with corrected Z value from geodata.</li>
* </ul>
* Target X and Y not reachable:
* <ul>
* <li>Last accessible location in destination to target.</li>
* </ul>
* @param gox : origin X geodata coordinate
* @param goy : origin Y geodata coordinate
* @param goz : origin Z geodata coordinate
* @param gtx : target X geodata coordinate
* @param gty : target Y geodata coordinate
* @param gtz : target Z geodata coordinate
* @param instanceId
* @return {@link GeoLocation} : The last allowed point of movement.
*/
protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, int instanceId)
{
if (DoorTable.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz))
{
return new GeoLocation(gox, goy, goz);
}
if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz))
{
return new GeoLocation(gox, goy, goz);
}
// get X delta, signum and direction flag
final int dx = Math.abs(gtx - gox);
final int sx = gox < gtx ? 1 : -1;
final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W;
// get Y delta, signum and direction flag
final int dy = Math.abs(gty - goy);
final int sy = goy < gty ? 1 : -1;
final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N;
// get direction flag for diagonal movement
final byte dirXY = getDirXY(dirX, dirY);
// delta, determines axis to move on (+..X axis, -..Y axis)
int d = dx - dy;
// NSWE direction of movement
byte direction;
// load pointer coordinates
int gpx = gox;
int gpy = goy;
int gpz = goz;
// load next pointer
int nx = gpx;
int ny = gpy;
// loop
do
{
direction = 0;
// calculate next point coordinates
int e2 = 2 * d;
if ((e2 > -dy) && (e2 < dx))
{
d -= dy;
d += dx;
nx += sx;
ny += sy;
direction |= dirXY;
}
else if (e2 > -dy)
{
d -= dy;
nx += sx;
direction |= dirX;
}
else if (e2 < dx)
{
d += dx;
ny += sy;
direction |= dirY;
}
// obstacle found, return
if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0)
{
return new GeoLocation(gpx, gpy, gpz);
}
// update pointer coordinates
gpx = nx;
gpy = ny;
gpz = getHeightNearest(nx, ny, gpz);
// target coordinates reached
if ((gpx == gtx) && (gpy == gty))
{
if (gpz == gtz)
{
// path found, Z coordinates are okay, return target point
return new GeoLocation(gtx, gty, gtz);
}
// path found, Z coordinates are not okay, return origin point
return new GeoLocation(gox, goy, goz);
}
}
while (true);
}
/**
* Returns diagonal NSWE flag format of combined two NSWE flags.
* @param dirX : X direction NSWE flag
* @param dirY : Y direction NSWE flag
* @return byte : NSWE flag of combined direction
*/
private static byte getDirXY(byte dirX, byte dirY)
{
// check axis directions
if (dirY == GeoStructure.CELL_FLAG_N)
{
if (dirX == GeoStructure.CELL_FLAG_W)
{
return GeoStructure.CELL_FLAG_NW;
}
return GeoStructure.CELL_FLAG_NE;
}
if (dirX == GeoStructure.CELL_FLAG_W)
{
return GeoStructure.CELL_FLAG_SW;
}
return GeoStructure.CELL_FLAG_SE;
}
/**
* Returns the list of location objects as a result of complete path calculation.
* @param ox : origin x
* @param oy : origin y
* @param oz : origin z
* @param tx : target x
* @param ty : target y
* @param tz : target z
* @param instanceId
* @return {@code List<Location>} : complete path from nodes
*/
public List<Location> findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId)
{
return null;
}
private static class SingletonHolder
{
protected static final GeoEngine _instance = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine();
}
}

View File

@ -0,0 +1,266 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geoengine.geodata.GeoLocation;
import com.l2jmobius.gameserver.geoengine.pathfinding.Node;
import com.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer;
import com.l2jmobius.gameserver.model.actor.position.Location;
/**
* @author Hasha
*/
final class GeoEnginePathfinding extends GeoEngine
{
// pre-allocated buffers
private final BufferHolder[] _buffers;
protected GeoEnginePathfinding()
{
super();
String[] array = Config.PATHFIND_BUFFERS.split(";");
_buffers = new BufferHolder[array.length];
int count = 0;
for (int i = 0; i < array.length; i++)
{
String buf = array[i];
String[] args = buf.split("x");
try
{
int size = Integer.parseInt(args[1]);
count += size;
_buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size);
}
catch (Exception e)
{
LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf);
}
}
LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers.");
}
@Override
public List<Location> findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instance)
{
// get origin and check existing geo coords
int gox = getGeoX(ox);
int goy = getGeoY(oy);
if (!hasGeoPos(gox, goy))
{
return null;
}
short goz = getHeightNearest(gox, goy, oz);
// get target and check existing geo coords
int gtx = getGeoX(tx);
int gty = getGeoY(ty);
if (!hasGeoPos(gtx, gty))
{
return null;
}
short gtz = getHeightNearest(gtx, gty, tz);
// Prepare buffer for pathfinding calculations
final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty))));
if (buffer == null)
{
return null;
}
// find path
List<Location> path = null;
try
{
Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz);
if (result == null)
{
return null;
}
path = constructPath(result);
}
catch (Exception e)
{
LOGGER.warning(e.getMessage());
return null;
}
finally
{
buffer.free();
}
// check path
if (path.size() < 3)
{
return path;
}
// get path list iterator
ListIterator<Location> point = path.listIterator();
// get node A (origin)
int nodeAx = gox;
int nodeAy = goy;
short nodeAz = goz;
// get node B
GeoLocation nodeB = (GeoLocation) point.next();
// iterate thought the path to optimize it
while (point.hasNext())
{
// get node C
GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex());
// check movement from node A to node C
GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance);
if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY()))
{
// can move from node A to node C
// remove node B
point.remove();
}
else
{
// can not move from node A to node C
// set node A (node B is part of path, update A coordinates)
nodeAx = nodeB.getGeoX();
nodeAy = nodeB.getGeoY();
nodeAz = (short) nodeB.getZ();
}
// set node B
nodeB = (GeoLocation) point.next();
}
return path;
}
/**
* Create list of node locations as result of calculated buffer node tree.
* @param target : the entry point
* @return List<NodeLoc> : list of node location
*/
private static List<Location> constructPath(Node target)
{
// create empty list
LinkedList<Location> list = new LinkedList<>();
// set direction X/Y
int dx = 0;
int dy = 0;
// get target parent
Node parent = target.getParent();
// while parent exists
while (parent != null)
{
// get parent <> target direction X/Y
final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX();
final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY();
// direction has changed?
if ((dx != nx) || (dy != ny))
{
// add node to the beginning of the list
list.addFirst(target.getLoc());
// update direction X/Y
dx = nx;
dy = ny;
}
// move to next node, set target and get its parent
target = parent;
parent = target.getParent();
}
// return list
return list;
}
/**
* Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer.
* @param size : pre-calculated minimal required size
* @return NodeBuffer : buffer
*/
private final NodeBuffer getBuffer(int size)
{
NodeBuffer current = null;
for (BufferHolder holder : _buffers)
{
// Find proper size of buffer
if (holder._size < size)
{
continue;
}
// Find unlocked NodeBuffer
for (NodeBuffer buffer : holder._buffer)
{
if (!buffer.isLocked())
{
continue;
}
return buffer;
}
// NodeBuffer not found, allocate temporary buffer
current = new NodeBuffer(holder._size);
current.isLocked();
}
return current;
}
/**
* NodeBuffer container with specified size and count of separate buffers.
*/
private static final class BufferHolder
{
final int _size;
List<NodeBuffer> _buffer;
public BufferHolder(int size, int count)
{
_size = size;
_buffer = new ArrayList<>(count);
for (int i = 0; i < count; i++)
{
_buffer.add(new NodeBuffer(size));
}
}
}
}

View File

@ -0,0 +1,197 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import java.io.BufferedOutputStream;
import java.io.IOException;
/**
* @author Hasha
*/
public abstract class ABlock
{
/**
* Checks the block for having geodata.
* @return boolean : True, when block has geodata (Flat, Complex, Multilayer).
*/
public abstract boolean hasGeoPos();
/**
* Returns the height of cell, which is closest to given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearest(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell, which is closest to given coordinates.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearestOriginal(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell, which is first above given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, above given coordinates.
*/
public abstract short getHeightAbove(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell, which is first below given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeightBelow(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearest(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearestOriginal(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is first above given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweAbove(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is first below given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweBelow(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is closes layer to given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index.
*/
public abstract int getIndexNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAbove(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAboveOriginal(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelow(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelowOriginal(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell given by cell index.
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeight(int index);
/**
* Returns the height of cell given by cell index.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeightOriginal(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNswe(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.<br>
* Geodata without {@link IGeoObject} are taken in consideration.
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNsweOriginal(int index);
/**
* Sets the NSWE flag byte of cell given by cell index.
* @param index : Index of the cell.
* @param nswe : New NSWE flag byte.
*/
public abstract void setNswe(int index, byte nswe);
/**
* Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion.
* @param stream : The stream.
* @throws IOException : Can't save the block to steam.
*/
public abstract void saveBlock(BufferedOutputStream stream) throws IOException;
}

View File

@ -0,0 +1,252 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* @author Hasha
*/
public class BlockComplex extends ABlock
{
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockComplex()
{
// buffer is initialized in children class
_buffer = null;
}
/**
* Creates ComplexBlock.
* @param bb : Input byte buffer.
* @param format : GeoFormat specifying format of loaded data.
*/
public BlockComplex(ByteBuffer bb, GeoFormat format)
{
// initialize buffer
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
// load data
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
if (format != GeoFormat.L2D)
{
// get data
short data = bb.getShort();
// get nswe
_buffer[i * 3] = (byte) (data & 0x000F);
// get height
data = (short) ((short) (data & 0xFFF0) >> 1);
_buffer[(i * 3) + 1] = (byte) (data & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (data >> 8);
}
else
{
// get nswe
final byte nswe = bb.get();
_buffer[i * 3] = nswe;
// get height
final short height = bb.getShort();
_buffer[(i * 3) + 1] = (byte) (height & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (height >> 8);
}
}
}
@Override
public final boolean hasGeoPos()
{
return true;
}
@Override
public final short getHeightNearest(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
{
return getHeightNearest(geoX, geoY, worldZ);
}
@Override
public final short getHeightAbove(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
// check and return height
return height > worldZ ? height : Short.MIN_VALUE;
}
@Override
public final short getHeightBelow(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
// check and return height
return height < worldZ ? height : Short.MAX_VALUE;
}
@Override
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get nswe
return _buffer[index];
}
@Override
public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
{
return getNsweNearest(geoX, geoY, worldZ);
}
@Override
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// check height and return nswe
return height > worldZ ? _buffer[index] : 0;
}
@Override
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// check height and return nswe
return height < worldZ ? _buffer[index] : 0;
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
}
@Override
public final int getIndexAbove(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// check height and return nswe
return height > worldZ ? index : -1;
}
@Override
public int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
{
return getIndexAbove(geoX, geoY, worldZ);
}
@Override
public final int getIndexBelow(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// get height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// check height and return nswe
return height < worldZ ? index : -1;
}
@Override
public int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
{
return getIndexBelow(geoX, geoY, worldZ);
}
@Override
public final short getHeight(int index)
{
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public short getHeightOriginal(int index)
{
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public final byte getNswe(int index)
{
return _buffer[index];
}
@Override
public byte getNsweOriginal(int index)
{
return _buffer[index];
}
@Override
public final void setNswe(int index, byte nswe)
{
_buffer[index] = nswe;
}
@Override
public final void saveBlock(BufferedOutputStream stream) throws IOException
{
// write block type
stream.write(GeoStructure.TYPE_COMPLEX_L2D);
// write block data
stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3);
}
}

View File

@ -0,0 +1,177 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* @author Hasha
*/
public class BlockFlat extends ABlock
{
protected final short _height;
protected byte _nswe;
/**
* Creates FlatBlock.
* @param bb : Input byte buffer.
* @param format : GeoFormat specifying format of loaded data.
*/
public BlockFlat(ByteBuffer bb, GeoFormat format)
{
_height = bb.getShort();
_nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF);
if (format == GeoFormat.L2OFF)
{
bb.getShort();
}
}
@Override
public final boolean hasGeoPos()
{
return true;
}
@Override
public final short getHeightNearest(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public final short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public final short getHeightAbove(int geoX, int geoY, int worldZ)
{
// check and return height
return _height > worldZ ? _height : Short.MIN_VALUE;
}
@Override
public final short getHeightBelow(int geoX, int geoY, int worldZ)
{
// check and return height
return _height < worldZ ? _height : Short.MAX_VALUE;
}
@Override
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
{
// check height and return nswe
return _height > worldZ ? _nswe : 0;
}
@Override
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
{
// check height and return nswe
return _height < worldZ ? _nswe : 0;
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final int getIndexAbove(int geoX, int geoY, int worldZ)
{
// check height and return index
return _height > worldZ ? 0 : -1;
}
@Override
public final int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
{
return getIndexAbove(geoX, geoY, worldZ);
}
@Override
public final int getIndexBelow(int geoX, int geoY, int worldZ)
{
// check height and return index
return _height < worldZ ? 0 : -1;
}
@Override
public final int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
{
return getIndexBelow(geoX, geoY, worldZ);
}
@Override
public final short getHeight(int index)
{
return _height;
}
@Override
public final short getHeightOriginal(int index)
{
return _height;
}
@Override
public final byte getNswe(int index)
{
return _nswe;
}
@Override
public final byte getNsweOriginal(int index)
{
return _nswe;
}
@Override
public final void setNswe(int index, byte nswe)
{
_nswe = nswe;
}
@Override
public final void saveBlock(BufferedOutputStream stream) throws IOException
{
// write block type
stream.write(GeoStructure.TYPE_FLAT_L2D);
// write height
stream.write((byte) (_height & 0x00FF));
stream.write((byte) (_height >> 8));
}
}

View File

@ -0,0 +1,466 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* @author Hasha
*/
public class BlockMultilayer extends ABlock
{
private static final int MAX_LAYERS = Byte.MAX_VALUE;
private static ByteBuffer _temp;
/**
* Initializes the temporarily buffer.
*/
public static void initialize()
{
// initialize temporarily buffer and sorting mechanism
_temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3);
_temp.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Releases temporarily buffer.
*/
public static void release()
{
_temp = null;
}
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockMultilayer()
{
_buffer = null;
}
/**
* Creates MultilayerBlock.
* @param bb : Input byte buffer.
* @param format : GeoFormat specifying format of loaded data.
*/
public BlockMultilayer(ByteBuffer bb, GeoFormat format)
{
// move buffer pointer to end of MultilayerBlock
for (int cell = 0; cell < GeoStructure.BLOCK_CELLS; cell++)
{
// get layer count for this cell
final byte layers = format != GeoFormat.L2OFF ? bb.get() : (byte) bb.getShort();
if ((layers <= 0) || (layers > MAX_LAYERS))
{
throw new RuntimeException("Invalid layer count for MultilayerBlock");
}
// add layers count
_temp.put(layers);
// loop over layers
for (byte layer = 0; layer < layers; layer++)
{
if (format != GeoFormat.L2D)
{
// get data
short data = bb.getShort();
// add nswe and height
_temp.put((byte) (data & 0x000F));
_temp.putShort((short) ((short) (data & 0xFFF0) >> 1));
}
else
{
// add nswe
_temp.put(bb.get());
// add height
_temp.putShort(bb.getShort());
}
}
}
// initialize buffer
_buffer = Arrays.copyOf(_temp.array(), _temp.position());
// clear temp buffer
_temp.clear();
}
@Override
public final boolean hasGeoPos()
{
return true;
}
@Override
public final short getHeightNearest(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = getIndexNearest(geoX, geoY, worldZ);
// get height
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
{
return getHeightNearest(geoX, geoY, worldZ);
}
@Override
public final short getHeightAbove(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to last layer data (first from bottom)
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// loop though all layers, find first layer above worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is higher than worldZ, return layer height
if (height > worldZ)
{
return (short) height;
}
// move index to next layer
index -= 3;
}
// none layer found, return minimum value
return Short.MIN_VALUE;
}
@Override
public final short getHeightBelow(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to first layer data (first from top)
byte layers = _buffer[index++];
// loop though all layers, find first layer below worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is lower than worldZ, return layer height
if (height < worldZ)
{
return (short) height;
}
// move index to next layer
index += 3;
}
// none layer found, return maximum value
return Short.MAX_VALUE;
}
@Override
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// get cell index
final int index = getIndexNearest(geoX, geoY, worldZ);
// get nswe
return _buffer[index];
}
@Override
public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
{
return getNsweNearest(geoX, geoY, worldZ);
}
@Override
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to last layer data (first from bottom)
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// loop though all layers, find first layer above worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is higher than worldZ, return layer nswe
if (height > worldZ)
{
return _buffer[index];
}
// move index to next layer
index -= 3;
}
// none layer found, block movement
return 0;
}
@Override
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to first layer data (first from top)
byte layers = _buffer[index++];
// loop though all layers, find first layer below worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is lower than worldZ, return layer nswe
if (height < worldZ)
{
return _buffer[index];
}
// move index to next layer
index += 3;
}
// none layer found, block movement
return 0;
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to first layer data (first from bottom)
byte layers = _buffer[index++];
// loop though all cell layers, find closest layer
int limit = Integer.MAX_VALUE;
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// get Z distance and compare with limit
// note: When 2 layers have same distance to worldZ (worldZ is in the middle of them):
// > returns bottom layer
// >= returns upper layer
final int distance = Math.abs(height - worldZ);
if (distance > limit)
{
break;
}
// update limit and move to next layer
limit = distance;
index += 3;
}
// return layer index
return index - 3;
}
@Override
public final int getIndexAbove(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to last layer data (first from bottom)
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// loop though all layers, find first layer above worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is higher than worldZ, return layer index
if (height > worldZ)
{
return index;
}
// move index to next layer
index -= 3;
}
// none layer found
return -1;
}
@Override
public int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
{
return getIndexAbove(geoX, geoY, worldZ);
}
@Override
public final int getIndexBelow(int geoX, int geoY, int worldZ)
{
// move index to the cell given by coordinates
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// move index by amount of layers for this cell
index += (_buffer[index] * 3) + 1;
}
// get layers count and shift to first layer data (first from top)
byte layers = _buffer[index++];
// loop though all layers, find first layer below worldZ
while (layers-- > 0)
{
// get layer height
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// layer height is lower than worldZ, return layer index
if (height < worldZ)
{
return index;
}
// move index to next layer
index += 3;
}
// none layer found
return -1;
}
@Override
public int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
{
return getIndexBelow(geoX, geoY, worldZ);
}
@Override
public final short getHeight(int index)
{
// get height
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public short getHeightOriginal(int index)
{
// get height
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public final byte getNswe(int index)
{
// get nswe
return _buffer[index];
}
@Override
public byte getNsweOriginal(int index)
{
// get nswe
return _buffer[index];
}
@Override
public final void setNswe(int index, byte nswe)
{
// set nswe
_buffer[index] = nswe;
}
@Override
public final void saveBlock(BufferedOutputStream stream) throws IOException
{
// write block type
stream.write(GeoStructure.TYPE_MULTILAYER_L2D);
// for each cell
int index = 0;
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
// write layers count
byte layers = _buffer[index++];
stream.write(layers);
// write cell data
stream.write(_buffer, index, layers * 3);
// move index to next cell
index += layers * 3;
}
}
}

View File

@ -0,0 +1,150 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import java.io.BufferedOutputStream;
/**
* @author Hasha
*/
public class BlockNull extends ABlock
{
private final byte _nswe;
public BlockNull()
{
_nswe = (byte) 0xFF;
}
@Override
public final boolean hasGeoPos()
{
return false;
}
@Override
public final short getHeightNearest(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public final short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public final short getHeightAbove(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public final short getHeightBelow(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final int getIndexAbove(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final int getIndexBelow(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public final short getHeight(int index)
{
return 0;
}
@Override
public final short getHeightOriginal(int index)
{
return 0;
}
@Override
public final byte getNswe(int index)
{
return _nswe;
}
@Override
public final byte getNsweOriginal(int index)
{
return _nswe;
}
@Override
public final void setNswe(int index, byte nswe)
{
}
@Override
public final void saveBlock(BufferedOutputStream stream)
{
}
}

View File

@ -14,20 +14,26 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geodata.pathfinding;
package com.l2jmobius.gameserver.geoengine.geodata;
/**
* @author -Nemesiss-
* @author Hasha
*/
public abstract class AbstractNodeLoc
public enum GeoFormat
{
public abstract int getX();
L2J("%d_%d.l2j"),
L2OFF("%d_%d_conv.dat"),
L2D("%d_%d.l2d");
public abstract int getY();
private final String _filename;
public abstract int getZ();
private GeoFormat(String filename)
{
_filename = filename;
}
public abstract int getNodeX();
public abstract int getNodeY();
public String getFilename()
{
return _filename;
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.actor.position.Location;
/**
* @author Hasha
*/
public class GeoLocation extends Location
{
private byte _nswe;
public GeoLocation(int x, int y, int z)
{
super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z));
_nswe = GeoEngine.getInstance().getNsweNearest(x, y, z);
}
public void set(int x, int y, short z)
{
super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z));
_nswe = GeoEngine.getInstance().getNsweNearest(x, y, z);
}
public int getGeoX()
{
return _x;
}
public int getGeoY()
{
return _y;
}
@Override
public int getX()
{
return GeoEngine.getWorldX(_x);
}
@Override
public int getY()
{
return GeoEngine.getWorldY(_y);
}
public byte getNSWE()
{
return _nswe;
}
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
import com.l2jmobius.gameserver.model.L2World;
/**
* @author Hasha
*/
public final class GeoStructure
{
// cells
public static final byte CELL_FLAG_E = 1 << 0;
public static final byte CELL_FLAG_W = 1 << 1;
public static final byte CELL_FLAG_S = 1 << 2;
public static final byte CELL_FLAG_N = 1 << 3;
public static final byte CELL_FLAG_SE = 1 << 4;
public static final byte CELL_FLAG_SW = 1 << 5;
public static final byte CELL_FLAG_NE = 1 << 6;
public static final byte CELL_FLAG_NW = (byte) (1 << 7);
public static final int CELL_HEIGHT = 8;
public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6;
// blocks
public static final byte TYPE_FLAT_L2J_L2OFF = 0;
public static final byte TYPE_FLAT_L2D = (byte) 0xD0;
public static final byte TYPE_COMPLEX_L2J = 1;
public static final byte TYPE_COMPLEX_L2OFF = 0x40;
public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1;
public static final byte TYPE_MULTILAYER_L2J = 2;
// public static final byte TYPE_MULTILAYER_L2OFF = 0x41; // officially not does exist, is anything above complex block (0x41 - 0xFFFF)
public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2;
public static final int BLOCK_CELLS_X = 8;
public static final int BLOCK_CELLS_Y = 8;
public static final int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
// regions
public static final int REGION_BLOCKS_X = 256;
public static final int REGION_BLOCKS_Y = 256;
public static final int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
public static final int REGION_CELLS_X = REGION_BLOCKS_X * BLOCK_CELLS_X;
public static final int REGION_CELLS_Y = REGION_BLOCKS_Y * BLOCK_CELLS_Y;
// global geodata
private static final int GEO_REGIONS_X = ((L2World.TILE_X_MAX - L2World.TILE_X_MIN) + 1);
private static final int GEO_REGIONS_Y = ((L2World.TILE_Y_MAX - L2World.TILE_Y_MIN) + 1);
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.geodata;
/**
* @author Hasha
*/
public interface IGeoObject
{
/**
* Returns geodata X coordinate of the {@link IGeoObject}.
* @return int : Geodata X coordinate.
*/
int getGeoX();
/**
* Returns geodata Y coordinate of the {@link IGeoObject}.
* @return int : Geodata Y coordinate.
*/
int getGeoY();
/**
* Returns geodata Z coordinate of the {@link IGeoObject}.
* @return int : Geodata Z coordinate.
*/
int getGeoZ();
/**
* Returns height of the {@link IGeoObject}.
* @return int : Height.
*/
int getHeight();
/**
* Returns {@link IGeoObject} data.
* @return byte[][] : {@link IGeoObject} data.
*/
byte[][] getObjectGeoData();
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.pathfinding;
import com.l2jmobius.gameserver.geoengine.geodata.GeoLocation;
/**
* @author Hasha
*/
public class Node
{
// node coords and nswe flag
private GeoLocation _loc;
// node parent (for reverse path construction)
private Node _parent;
// node child (for moving over nodes during iteration)
private Node _child;
// node G cost (movement cost = parent movement cost + current movement cost)
private double _cost = -1000;
public void setLoc(int x, int y, int z)
{
_loc = new GeoLocation(x, y, z);
}
public GeoLocation getLoc()
{
return _loc;
}
public void setParent(Node parent)
{
_parent = parent;
}
public Node getParent()
{
return _parent;
}
public void setChild(Node child)
{
_child = child;
}
public Node getChild()
{
return _child;
}
public void setCost(double cost)
{
_cost = cost;
}
public double getCost()
{
return _cost;
}
public void free()
{
// reset node location
_loc = null;
// reset node parent, child and cost
_parent = null;
_child = null;
_cost = -1000;
}
}

View File

@ -0,0 +1,319 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.concurrent.locks.ReentrantLock;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
/**
* @author DS, Hasha; Credits to Diamond
*/
public class NodeBuffer
{
private final ReentrantLock _lock = new ReentrantLock();
private final int _size;
private final Node[][] _buffer;
// center coordinates
private int _cx = 0;
private int _cy = 0;
// target coordinates
private int _gtx = 0;
private int _gty = 0;
private short _gtz = 0;
// pathfinding statistics
private long _timeStamp = 0;
private long _lastElapsedTime = 0;
private Node _current = null;
/**
* Constructor of NodeBuffer.
* @param size : one dimension size of buffer
*/
public NodeBuffer(int size)
{
// set size
_size = size;
// initialize buffer
_buffer = new Node[size][size];
for (int x = 0; x < size; x++)
{
for (int y = 0; y < size; y++)
{
_buffer[x][y] = new Node();
}
}
}
/**
* Find path consisting of Nodes. Starts at origin coordinates, ends in target coordinates.
* @param gox : origin point x
* @param goy : origin point y
* @param goz : origin point z
* @param gtx : target point x
* @param gty : target point y
* @param gtz : target point z
* @return Node : first node of path
*/
public final Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz)
{
// load timestamp
_timeStamp = System.currentTimeMillis();
// set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer)
_cx = gox + ((gtx - gox - _size) / 2);
_cy = goy + ((gty - goy - _size) / 2);
_gtx = gtx;
_gty = gty;
_gtz = gtz;
_current = getNode(gox, goy, goz);
_current.setCost(getCostH(gox, goy, goz));
int count = 0;
do
{
// reached target?
if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8))
{
return _current;
}
// expand current node
expand();
// move pointer
_current = _current.getChild();
}
while ((_current != null) && (++count < Config.MAX_ITERATIONS));
return null;
}
public final boolean isLocked()
{
return _lock.tryLock();
}
public final void free()
{
_current = null;
for (Node[] nodes : _buffer)
{
for (Node node : nodes)
{
if (node.getLoc() != null)
{
node.free();
}
}
}
_lock.unlock();
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
}
public final long getElapsedTime()
{
return _lastElapsedTime;
}
/**
* Check _current Node and add its neighbors to the buffer.
*/
private final void expand()
{
// can't move anywhere, don't expand
byte nswe = _current.getLoc().getNSWE();
if (nswe == 0)
{
return;
}
// get geo coords of the node to be expanded
final int x = _current.getLoc().getGeoX();
final int y = _current.getLoc().getGeoY();
final short z = (short) _current.getLoc().getZ();
// can move north, expand
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x, y - 1, z, Config.BASE_WEIGHT);
}
// can move south, expand
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x, y + 1, z, Config.BASE_WEIGHT);
}
// can move west, expand
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y, z, Config.BASE_WEIGHT);
}
// can move east, expand
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y, z, Config.BASE_WEIGHT);
}
// can move north-west, expand
if ((nswe & GeoStructure.CELL_FLAG_NW) != 0)
{
addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT);
}
// can move north-east, expand
if ((nswe & GeoStructure.CELL_FLAG_NE) != 0)
{
addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT);
}
// can move south-west, expand
if ((nswe & GeoStructure.CELL_FLAG_SW) != 0)
{
addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT);
}
// can move south-east, expand
if ((nswe & GeoStructure.CELL_FLAG_SE) != 0)
{
addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT);
}
}
/**
* Returns node, if it exists in buffer.
* @param x : node X coord
* @param y : node Y coord
* @param z : node Z coord
* @return Node : node, if exits in buffer
*/
private final Node getNode(int x, int y, short z)
{
// check node X out of coordinates
final int ix = x - _cx;
if ((ix < 0) || (ix >= _size))
{
return null;
}
// check node Y out of coordinates
final int iy = y - _cy;
if ((iy < 0) || (iy >= _size))
{
return null;
}
// get node
Node result = _buffer[ix][iy];
// check and update
if (result.getLoc() == null)
{
result.setLoc(x, y, z);
}
// return node
return result;
}
/**
* Add node given by coordinates to the buffer.
* @param x : geo X coord
* @param y : geo Y coord
* @param z : geo Z coord
* @param weight : weight of movement to new node
*/
private final void addNode(int x, int y, short z, int weight)
{
// get node to be expanded
Node node = getNode(x, y, z);
if (node == null)
{
return;
}
// Z distance between nearby cells is higher than cell size
if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT)))
{
return;
}
// node was already expanded, return
if (node.getCost() >= 0)
{
return;
}
node.setParent(_current);
if (node.getLoc().getNSWE() != (byte) 0xFF)
{
node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER));
}
else
{
node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight);
}
Node current = _current;
int count = 0;
while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4)))
{
count++;
if (current.getChild().getCost() > node.getCost())
{
node.setChild(current.getChild());
break;
}
current = current.getChild();
}
if (count >= (Config.MAX_ITERATIONS * 4))
{
System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost());
}
current.setChild(node);
}
/**
* @param x : node X coord
* @param y : node Y coord
* @param i : node Z coord
* @return double : node cost
*/
private final double getCostH(int x, int y, int i)
{
final int dX = x - _gtx;
final int dY = y - _gty;
final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT;
// return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance
return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance
}
}

View File

@ -16,7 +16,7 @@
*/
package com.l2jmobius.gameserver.handler.admincommandhandlers;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.IAdminCommandHandler;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
@ -47,12 +47,12 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX();
final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ();
final int geoX = GeoData.getInstance().getGeoX(worldX);
final int geoY = GeoData.getInstance().getGeoY(worldY);
final int geoX = GeoEngine.getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY);
if (GeoData.getInstance().hasGeoPos(geoX, geoY))
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoData.getInstance().getNearestZ(geoX, geoY, worldZ));
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(geoX, geoY, worldZ));
}
else
{
@ -64,12 +64,12 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX();
final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ();
final int geoX = GeoData.getInstance().getGeoX(worldX);
final int geoY = GeoData.getInstance().getGeoY(worldY);
final int geoX = GeoEngine.getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY);
if (GeoData.getInstance().hasGeoPos(geoX, geoY))
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoData.getInstance().getSpawnHeight(worldX, worldY, worldZ));
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(worldX, worldY, worldZ));
}
else
{
@ -81,7 +81,7 @@ public class AdminGeodata implements IAdminCommandHandler
final L2Object target = activeChar.getTarget();
if (target != null)
{
if (GeoData.getInstance().canSeeTarget(activeChar, target))
if (GeoEngine.getInstance().canSeeTarget(activeChar, target))
{
BuilderUtil.sendSysMessage(activeChar, "Can move beeline.");
}
@ -100,7 +100,7 @@ public class AdminGeodata implements IAdminCommandHandler
final L2Object target = activeChar.getTarget();
if (target != null)
{
if (GeoData.getInstance().canSeeTarget(activeChar, target))
if (GeoEngine.getInstance().canSeeTarget(activeChar, target))
{
BuilderUtil.sendSysMessage(activeChar, "Can see target.");
}

View File

@ -17,7 +17,7 @@
package com.l2jmobius.gameserver.handler.itemhandlers;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.IItemHandler;
import com.l2jmobius.gameserver.model.actor.L2Playable;
import com.l2jmobius.gameserver.model.actor.instance.L2ItemInstance;
@ -83,7 +83,7 @@ public class RollingDice implements IItemHandler
final int x = activeChar.getX() + x1;
final int y = activeChar.getY() + y1;
final int z = activeChar.getZ();
final Location destination = GeoData.getInstance().moveCheck(activeChar.getX(), activeChar.getY(), activeChar.getZ(), x, y, z);
final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(activeChar.getX(), activeChar.getY(), activeChar.getZ(), x, y, z, activeChar.getInstanceId());
Broadcast.toSelfAndKnownPlayers(activeChar, new Dice(activeChar.getObjectId(), item.getItemId(), number, destination.getX(), destination.getY(), destination.getZ()));

View File

@ -18,7 +18,7 @@ package com.l2jmobius.gameserver.handler.skillhandlers;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.ISkillHandler;
import com.l2jmobius.gameserver.instancemanager.FishingZoneManager;
import com.l2jmobius.gameserver.model.Inventory;
@ -28,6 +28,7 @@ import com.l2jmobius.gameserver.model.L2Skill.SkillType;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.model.zone.type.L2FishingZone;
import com.l2jmobius.gameserver.model.zone.type.L2WaterZone;
import com.l2jmobius.gameserver.network.SystemMessageId;
@ -130,14 +131,18 @@ public class Fishing implements ISkillHandler
// ...and if the spot is in a fishing zone. If it is, it will then position the hook on the water surface. If not, you have to be GM to proceed past here... in that case, the hook will be positioned using the old Z lookup method.
L2FishingZone aimingTo = FishingZoneManager.getInstance().isInsideFishingZone(x, y, z);
L2WaterZone water = FishingZoneManager.getInstance().isInsideWaterZone(x, y, z);
if ((aimingTo != null) && (water != null) && (GeoData.getInstance().canSeeTarget(player.getX(), player.getY(), player.getZ() + 50, x, y, water.getWaterZ() - 50)))
if ((water != null))
{
final Location waterLocation = new Location(x, y, water.getWaterZ() - 50);
if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation))
{
z = water.getWaterZ() + 10;
}
else if ((aimingTo != null) && (water != null) && GeoData.getInstance().canSeeTarget(player.getX(), player.getY(), player.getZ() + 50, x, y, water.getWaterZ() - 50))
else if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation))
{
z = aimingTo.getWaterZ() + 10;
}
}
else
{
// You can't fish here

View File

@ -16,7 +16,7 @@
*/
package com.l2jmobius.gameserver.handler.usercommandhandlers;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.IUserCommandHandler;
import com.l2jmobius.gameserver.model.Inventory;
import com.l2jmobius.gameserver.model.actor.L2Summon;
@ -78,7 +78,7 @@ public class Mount implements IUserCommandHandler
activeChar.sendMessage("Too far away from strider to mount.");
return false;
}
else if (!GeoData.getInstance().canSeeTarget(activeChar, pet))
else if (!GeoEngine.getInstance().canSeeTarget(activeChar, pet))
{
final SystemMessage msg = new SystemMessage(SystemMessageId.CANT_SEE_TARGET);
activeChar.sendPacket(msg);

View File

@ -27,7 +27,7 @@ import com.l2jmobius.Config;
import com.l2jmobius.gameserver.datatables.HeroSkillTable;
import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.datatables.sql.SkillTreeTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.instancemanager.SiegeManager;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
@ -1675,7 +1675,7 @@ public abstract class L2Skill
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(activeChar, obj))
if (!GeoEngine.getInstance().canSeeTarget(activeChar, obj))
{
continue;
}
@ -1816,7 +1816,7 @@ public abstract class L2Skill
continue;
}
target = (L2Character) obj;
if (!GeoData.getInstance().canSeeTarget(activeChar, target))
if (!GeoEngine.getInstance().canSeeTarget(activeChar, target))
{
continue;
}
@ -2514,7 +2514,7 @@ public abstract class L2Skill
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(activeChar, obj))
if (!GeoEngine.getInstance().canSeeTarget(activeChar, obj))
{
continue;
}
@ -2694,7 +2694,7 @@ public abstract class L2Skill
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(activeChar, target))
if (!GeoEngine.getInstance().canSeeTarget(activeChar, target))
{
continue;
}

View File

@ -42,9 +42,7 @@ import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.datatables.csv.MapRegionTable;
import com.l2jmobius.gameserver.datatables.csv.MapRegionTable.TeleportWhereType;
import com.l2jmobius.gameserver.datatables.sql.NpcTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.ISkillHandler;
import com.l2jmobius.gameserver.handler.SkillHandler;
import com.l2jmobius.gameserver.handler.itemhandlers.Potions;
@ -1004,7 +1002,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
}
// GeoData Los Check here (or dz > 1000)
if (!GeoData.getInstance().canSeeTarget(this, target))
if (!GeoEngine.getInstance().canSeeTarget(this, target))
{
sendPacket(new SystemMessage(SystemMessageId.CANT_SEE_TARGET));
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
@ -4764,7 +4762,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
public int _heading;
public boolean disregardingGeodata;
public int onGeodataPathIndex;
public List<AbstractNodeLoc> geoPath;
public List<Location> geoPath;
public int geoPathAccurateTx;
public int geoPathAccurateTy;
public int geoPathGtx;
@ -5618,7 +5616,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
final int y1 = (int) (Math.sin(Math.PI + radian + course) * frontDistance);
final int x = xPrev + x1;
final int y = yPrev + y1;
if (!GeoData.getInstance().canMove(xPrev, yPrev, zPrev, x, y, zPrev))
if (!GeoEngine.getInstance().canMoveToTarget(xPrev, yPrev, zPrev, x, y, zPrev, getInstanceId()))
{
_move.onGeodataPathIndex = -1;
stopMove(getActingPlayer().getLastServerPosition());
@ -5733,7 +5731,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
// All data are contained in a L2CharPosition object
if (pos != null)
{
getPosition().setXYZ(pos.getX(), pos.getY(), GeoData.getInstance().getHeight(pos.getX(), pos.getY(), pos.getZ()));
getPosition().setXYZ(pos.getX(), pos.getY(), GeoEngine.getInstance().getHeight(pos.getX(), pos.getY(), pos.getZ()));
setHeading(pos.getHeading());
if (this instanceof L2PcInstance)
@ -6026,7 +6024,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
}
// Movement checks.
if (Config.PATHFINDING > 0)
if (Config.PATHFINDING)
{
final double originalDistance = distance;
final int originalX = x;
@ -6077,7 +6075,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
&& !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall.
{
// location different if destination wasn't reached (or just z coord is different)
final Location destiny = GeoData.getInstance().moveCheck(curX, curY, curZ, x, y, z);
final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceId());
x = destiny.getX();
y = destiny.getY();
dx = x - curX;
@ -6090,13 +6088,13 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat)
{
// Path calculation -- overrides previous movement check
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, this instanceof L2Playable);
m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId());
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
{
m.disregardingGeodata = true;
// Mobius: Verify destination. Prevents wall collision issues.
final Location newDestination = GeoData.getInstance().moveCheck(curX, curY, curZ, originalX, originalY, originalZ);
final Location newDestination = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId());
x = newDestination.getX();
y = newDestination.getY();
z = newDestination.getZ();
@ -6124,7 +6122,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
}
// If no distance to go through, the movement is cancelled
if ((distance < 1) && ((Config.PATHFINDING > 0) || (this instanceof L2Playable) || _isAfraid || (this instanceof L2RiftInvaderInstance)))
if ((distance < 1) && (Config.PATHFINDING || (this instanceof L2Playable) || _isAfraid || (this instanceof L2RiftInvaderInstance)))
{
if (this instanceof L2Summon)
{
@ -6989,7 +6987,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
}
// GeoData Los Check or dz > 1000
if (!GeoData.getInstance().canSeeTarget(player, this))
if (!GeoEngine.getInstance().canSeeTarget(player, this))
{
player.sendPacket(SystemMessageId.CANT_SEE_TARGET);
player.sendPacket(ActionFailed.STATIC_PACKET);
@ -7863,7 +7861,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
}
// Check if the target is behind a wall
if ((skill.getSkillRadius() > 0) && skill.isOffensive() && (Config.PATHFINDING > 0) && !GeoData.getInstance().canSeeTarget(this, targets[i]))
if ((skill.getSkillRadius() > 0) && skill.isOffensive() && Config.PATHFINDING && !GeoEngine.getInstance().canSeeTarget(this, targets[i]))
{
_skipped++;
continue;

View File

@ -23,7 +23,7 @@ import com.l2jmobius.gameserver.ai.L2CharacterAI;
import com.l2jmobius.gameserver.ai.L2SummonAI;
import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.datatables.xml.ExperienceData;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Party;
import com.l2jmobius.gameserver.model.L2Skill;
@ -106,7 +106,7 @@ public abstract class L2Summon extends L2Playable
final int x = owner.getX();
final int y = owner.getY();
final int z = owner.getZ();
final Location location = GeoData.getInstance().moveCheck(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z);
final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceId());
setXYZInvisible(location.getX(), location.getY(), location.getZ());
}
@ -220,9 +220,9 @@ public abstract class L2Summon extends L2Playable
{
if (isAutoAttackable(player))
{
if (Config.PATHFINDING > 0)
if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
player.onActionRequest();
@ -239,9 +239,9 @@ public abstract class L2Summon extends L2Playable
// This Action Failed packet avoids player getting stuck when clicking three or more times
player.sendPacket(ActionFailed.STATIC_PACKET);
if (Config.PATHFINDING > 0)
if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this);
}

View File

@ -20,7 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.spawn.L2Spawn;
import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
@ -86,8 +86,8 @@ public class L2ControlTowerInstance extends L2NpcInstance
// Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client
player.sendPacket(new ValidateLocation(this));
}
else if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100 // Less then max height difference, delete check when geo
) && GeoData.getInstance().canSeeTarget(player, this))
else if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100) // Less then max height difference, delete check when geo
&& GeoEngine.getInstance().canSeeTarget(player, this))
{
// Notify the L2PcInstance AI with AI_INTENTION_INTERACT
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);

View File

@ -29,7 +29,7 @@ import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.xml.ItemTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.model.DropProtection;
import com.l2jmobius.gameserver.model.L2Augmentation;
@ -1183,9 +1183,9 @@ public final class L2ItemInstance extends L2Object
assert getPosition().getWorldRegion() == null;
}
if ((Config.PATHFINDING > 0) && (dropper != null))
if (Config.PATHFINDING && (dropper != null))
{
Location dropDest = GeoData.getInstance().moveCheck(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z);
Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId());
if ((dropDest != null) && (dropDest.getX() != 0) && (dropDest.getY() != 0))
{

View File

@ -70,7 +70,7 @@ import com.l2jmobius.gameserver.datatables.sql.NpcTable;
import com.l2jmobius.gameserver.datatables.sql.SkillTreeTable;
import com.l2jmobius.gameserver.datatables.xml.ExperienceData;
import com.l2jmobius.gameserver.datatables.xml.ItemTable;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.IItemHandler;
import com.l2jmobius.gameserver.handler.ItemHandler;
import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminEditChar;
@ -5253,9 +5253,9 @@ public final class L2PcInstance extends L2Playable
{
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (Config.PATHFINDING > 0)
else if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
player.onActionRequest();
@ -5267,9 +5267,9 @@ public final class L2PcInstance extends L2Playable
player.onActionRequest();
}
}
else if (Config.PATHFINDING > 0)
else if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this);
}
@ -5441,9 +5441,9 @@ public final class L2PcInstance extends L2Playable
{
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (Config.PATHFINDING > 0)
else if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
// Calculate the distance between the L2PcInstance
// Only archer can hit from long
@ -5479,9 +5479,9 @@ public final class L2PcInstance extends L2Playable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
}
else if (Config.PATHFINDING > 0)
else if (Config.PATHFINDING)
{
if (GeoData.getInstance().canSeeTarget(player, this))
if (GeoEngine.getInstance().canSeeTarget(player, this))
{
// Calculate the distance between the L2PcInstance. Only archer can hit from long.
if ((currentWeapon != null) && (currentWeapon.getItemType() == L2WeaponType.BOW))
@ -11758,7 +11758,7 @@ public final class L2PcInstance extends L2Playable
}
// GeoData Los Check here
if ((skill.getCastRange() > 0) && !GeoData.getInstance().canSeeTarget(this, target))
if ((skill.getCastRange() > 0) && !GeoEngine.getInstance().canSeeTarget(this, target))
{
sendPacket(SystemMessageId.CANT_SEE_TARGET);
sendPacket(ActionFailed.STATIC_PACKET);
@ -16923,7 +16923,7 @@ public final class L2PcInstance extends L2Playable
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if ((GeoData.getInstance().getHeight(getX(), getY(), getZ()) + 300) < getZ())
if ((GeoEngine.getInstance().getHeight(getX(), getY(), getZ()) + 300) < getZ())
{
sendPacket(SystemMessageId.YOU_CANNOT_DISMOUNT_FROM_THIS_ELEVATION);
sendPacket(ActionFailed.STATIC_PACKET);

View File

@ -19,7 +19,7 @@ package com.l2jmobius.gameserver.model.actor.position;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.actor.L2Character;
public final class Location
public class Location
{
public int _x;
public int _y;

View File

@ -17,7 +17,7 @@
package com.l2jmobius.gameserver.skills.effects;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.actor.instance.L2CommanderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2FolkInstance;
@ -120,7 +120,7 @@ final class EffectFear extends L2Effect
posX += signx * FEAR_RANGE;
posY += signy * FEAR_RANGE;
Location destiny = GeoData.getInstance().moveCheck(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ);
Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ, getEffected().getInstanceId());
getEffected().setRunning();
getEffected().getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(destiny.getX(), destiny.getY(), destiny.getZ(), 0));

View File

@ -18,8 +18,8 @@ package com.l2jmobius.gameserver.util;
import java.awt.Color;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.geodriver.Cell;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
@ -30,21 +30,21 @@ public final class GeoUtils
{
public static void debug2DLine(L2PcInstance player, int x, int y, int tx, int ty, int z)
{
final int gx = GeoData.getInstance().getGeoX(x);
final int gy = GeoData.getInstance().getGeoY(y);
final int gx = GeoEngine.getGeoX(x);
final int gy = GeoEngine.getGeoY(y);
final int tgx = GeoData.getInstance().getGeoX(tx);
final int tgy = GeoData.getInstance().getGeoY(ty);
final int tgx = GeoEngine.getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.getInstance().getWorldY(tgy), z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z);
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
while (iter.next())
{
final int wx = GeoData.getInstance().getWorldX(iter.x());
final int wy = GeoData.getInstance().getWorldY(iter.y());
final int wx = GeoEngine.getWorldX(iter.x());
final int wy = GeoEngine.getWorldY(iter.y());
prim.addPoint(Color.RED, wx, wy, z);
}
@ -53,21 +53,21 @@ public final class GeoUtils
public static void debug3DLine(L2PcInstance player, int x, int y, int z, int tx, int ty, int tz)
{
final int gx = GeoData.getInstance().getGeoX(x);
final int gy = GeoData.getInstance().getGeoY(y);
final int gx = GeoEngine.getGeoX(x);
final int gy = GeoEngine.getGeoY(y);
final int tgx = GeoData.getInstance().getGeoX(tx);
final int tgy = GeoData.getInstance().getGeoY(ty);
final int tgx = GeoEngine.getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.getInstance().getWorldY(tgy), tz);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz);
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
iter.next();
int prevX = iter.x();
int prevY = iter.y();
int wx = GeoData.getInstance().getWorldX(prevX);
int wy = GeoData.getInstance().getWorldY(prevY);
int wx = GeoEngine.getWorldX(prevX);
int wy = GeoEngine.getWorldY(prevY);
int wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz);
@ -78,8 +78,8 @@ public final class GeoUtils
if ((curX != prevX) || (curY != prevY))
{
wx = GeoData.getInstance().getWorldX(curX);
wy = GeoData.getInstance().getWorldY(curY);
wx = GeoEngine.getWorldX(curX);
wy = GeoEngine.getWorldY(curY);
wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz);
@ -93,7 +93,7 @@ public final class GeoUtils
private static Color getDirectionColor(int x, int y, int z, int nswe)
{
if (GeoData.getInstance().checkNearestNswe(x, y, z, nswe))
if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe)
{
return Color.GREEN;
}
@ -109,9 +109,8 @@ public final class GeoUtils
int iPacket = 0;
ExServerPrimitive exsp = null;
final GeoData gd = GeoData.getInstance();
final int playerGx = gd.getGeoX(player.getX());
final int playerGy = gd.getGeoY(player.getY());
final int playerGx = GeoEngine.getGeoX(player.getX());
final int playerGy = GeoEngine.getGeoY(player.getY());
for (int dx = -geoRadius; dx <= geoRadius; ++dx)
{
for (int dy = -geoRadius; dy <= geoRadius; ++dy)
@ -135,32 +134,32 @@ public final class GeoUtils
final int gx = playerGx + dx;
final int gy = playerGy + dy;
final int x = gd.getWorldX(gx);
final int y = gd.getWorldY(gy);
final int z = gd.getNearestZ(gx, gy, player.getZ());
final int x = GeoEngine.getWorldX(gx);
final int y = GeoEngine.getWorldY(gy);
final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ());
// north arrow
Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N);
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
// east arrow
col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E);
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
// south arrow
col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S);
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W);
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
@ -188,42 +187,47 @@ public final class GeoUtils
{
if (y > lastY)
{
return Cell.NSWE_SOUTH_EAST; // Direction.SOUTH_EAST;
return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST;
}
else if (y < lastY)
{
return Cell.NSWE_NORTH_EAST; // Direction.NORTH_EAST;
return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST;
}
else
{
return Cell.NSWE_EAST; // Direction.EAST;
return GeoStructure.CELL_FLAG_E; // Direction.EAST;
}
}
else if (x < lastX) // west
{
if (y > lastY)
{
return Cell.NSWE_SOUTH_WEST; // Direction.SOUTH_WEST;
return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST;
}
else if (y < lastY)
{
return Cell.NSWE_NORTH_WEST; // Direction.NORTH_WEST;
return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST;
}
else
{
return Cell.NSWE_WEST; // Direction.WEST;
return GeoStructure.CELL_FLAG_W; // Direction.WEST;
}
} else if (y > lastY)
}
else
// unchanged x
{
return Cell.NSWE_SOUTH; // Direction.SOUTH;
if (y > lastY)
{
return GeoStructure.CELL_FLAG_S; // Direction.SOUTH;
}
else if (y < lastY)
{
return Cell.NSWE_NORTH; // Direction.NORTH;
return GeoStructure.CELL_FLAG_N; // Direction.NORTH;
}
else
{
throw new RuntimeException();
}
}
}
}

View File

@ -0,0 +1,94 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util;
/**
* @author UnAfraid
*/
public class MathUtil
{
public static byte add(byte oldValue, byte value)
{
return (byte) (oldValue + value);
}
public static short add(short oldValue, short value)
{
return (short) (oldValue + value);
}
public static int add(int oldValue, int value)
{
return oldValue + value;
}
public static double add(double oldValue, double value)
{
return oldValue + value;
}
public static byte mul(byte oldValue, byte value)
{
return (byte) (oldValue * value);
}
public static short mul(short oldValue, short value)
{
return (short) (oldValue * value);
}
public static int mul(int oldValue, int value)
{
return oldValue * value;
}
public static double mul(double oldValue, double value)
{
return oldValue * value;
}
public static byte div(byte oldValue, byte value)
{
return (byte) (oldValue / value);
}
public static short div(short oldValue, short value)
{
return (short) (oldValue / value);
}
public static int div(int oldValue, int value)
{
return oldValue / value;
}
public static double div(double oldValue, double value)
{
return oldValue / value;
}
/**
* @param numToTest : The number to test.
* @param min : The minimum limit.
* @param max : The maximum limit.
* @return the number or one of the limit (mininum / maximum).
*/
public static int limit(int numToTest, int min, int max)
{
return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest);
}
}

View File

@ -1,7 +1,7 @@
L2J-Mobius Interlude
Client: https://drive.google.com/uc?id=1tpXwh3FPWEIoWzfOb7-v482spYuQp6JM&export=download
Geodata: http://www.mediafire.com/file/jwivbb2t91a474e/mobius_interlude_geodata_l2j.zip
Client: https://drive.google.com/uc?id=1LcKCQTbRXJvteJcuvc_rnX8i2gT1fcHB&export=download
Geodata: http://www.mediafire.com/file/8m6vosu7h5p23j1/mobius_geodata_interlude_l2d.zip
Prelude: http://legacy.lineage2.com/news/career.html