From dd0d74a8e644afa594f154c690608b43a3bb791f Mon Sep 17 00:00:00 2001 From: MobiusDevelopment <8391001+MobiusDevelopment@users.noreply.github.com> Date: Sat, 20 Feb 2021 07:48:16 +0000 Subject: [PATCH] Reverted back to l2j geoengine. --- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../AbstractNodeLoc.java} | 26 +- .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_1.0_Ertheia/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 26 +- .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_2.5_Underground/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 26 +- .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_3.0_Helios/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 26 +- .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_4.0_GrandCrusade/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_5.0_Salvation/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_5.5_EtinasFate/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_6.0_Fafurion/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_7.0_PreludeOfWar/readme.txt | 12 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_8.0_Homunculus/readme.txt | 11 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/main/GeoEngine.ini | 36 +- .../java/org/l2jmobius/Config.java | 32 +- .../gameserver/geoengine/GeoEngine.java | 1382 +++++++--------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../admincommandhandlers/AdminGeodata.java | 12 +- .../gameserver/model/actor/Creature.java | 6 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../l2jmobius/gameserver/util/GeoUtils.java | 77 +- .../geodataconverter/GeoDataConverter.java | 367 ----- L2J_Mobius_C4_ScionsOfDestiny/readme.txt | 2 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/main/GeoEngine.ini | 36 +- .../java/org/l2jmobius/Config.java | 32 +- .../gameserver/geoengine/GeoEngine.java | 1382 +++++++--------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../admincommandhandlers/AdminGeodata.java | 12 +- .../gameserver/model/actor/Creature.java | 6 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../l2jmobius/gameserver/util/GeoUtils.java | 77 +- .../geodataconverter/GeoDataConverter.java | 367 ----- L2J_Mobius_C6_Interlude/readme.txt | 2 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 14 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../org/l2jmobius/gameserver/ai/SummonAI.java | 3 +- .../gameserver/geoengine/GeoEngine.java | 1383 +++++++--------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../gameserver/model/actor/Summon.java | 3 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_CT_2.4_Epilogue/readme.txt | 3 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 14 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../org/l2jmobius/gameserver/ai/SummonAI.java | 3 +- .../gameserver/geoengine/GeoEngine.java | 1383 +++++++--------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../gameserver/model/actor/Summon.java | 3 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_CT_2.6_HighFive/readme.txt | 6 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_2.0_Saviors/readme.txt | 3 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_2.1_Zaken/readme.txt | 3 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_2.2_Antharas/readme.txt | 3 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_2.3_SevenSigns/readme.txt | 3 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- .../readme.txt | 2 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_3.0_TheKamael/readme.txt | 2 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- L2J_Mobius_Classic_Interlude/readme.txt | 4 +- .../dist/game/GeoDataConverter.bat | 6 - .../dist/game/GeoDataConverter.sh | 4 - .../dist/game/config/GeoEngine.ini | 36 +- .../admincommandhandlers/AdminGeodata.java | 10 +- .../admincommandhandlers/AdminPathNode.java | 31 +- .../java/org/l2jmobius/Config.java | 31 +- .../gameserver/geoengine/GeoEngine.java | 1403 +++++++---------- .../geoengine/GeoEnginePathfinding.java | 336 ++-- .../gameserver/geoengine/geodata/ABlock.java | 197 --- .../geoengine/geodata/BlockComplex.java | 252 --- .../geoengine/geodata/BlockFlat.java | 176 --- .../geoengine/geodata/BlockMultilayer.java | 465 ------ .../geoengine/geodata/BlockNull.java | 150 -- .../gameserver/geoengine/geodata/Cell.java | 48 + .../geoengine/geodata/ComplexBlock.java | 77 + .../geoengine/geodata/FlatBlock.java | 56 + .../geoengine/geodata/GeoFormat.java | 39 - .../geoengine/geodata/GeoLocation.java | 67 - .../geoengine/geodata/GeoStructure.java | 70 - .../gameserver/geoengine/geodata/IBlock.java | 42 + .../geoengine/geodata/IGeoObject.java | 53 - .../gameserver/geoengine/geodata/IRegion.java | 47 + .../geoengine/geodata/MultilayerBlock.java | 183 +++ .../geoengine/geodata/NullRegion.java | 55 + .../gameserver/geoengine/geodata/Region.java | 92 ++ .../geoengine/pathfinding/AbstractNode.java | 87 + .../pathfinding/AbstractNodeLoc.java | 33 + .../geoengine/pathfinding/CellNode.java | 67 + .../geoengine/pathfinding/CellNodeBuffer.java | 348 ++++ .../geoengine/pathfinding/Node.java | 87 - .../geoengine/pathfinding/NodeBuffer.java | 306 ---- .../geoengine/pathfinding/NodeLoc.java | 183 +++ .../gameserver/model/actor/Creature.java | 22 +- .../model/actor/instance/PlayerInstance.java | 2 +- .../clientpackets/MoveBackwardToLocation.java | 8 - .../l2jmobius/gameserver/util/GeoUtils.java | 76 +- .../geodataconverter/GeoDataConverter.java | 386 ----- .../readme.txt | 5 +- 794 files changed, 45461 insertions(+), 70442 deletions(-) delete mode 100644 L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java rename L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/{geodata/GeoFormat.java => pathfinding/AbstractNodeLoc.java} (67%) create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java rename L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java => L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (67%) create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java rename L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java => L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (67%) create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java rename L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java => L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (67%) create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.bat delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.sh delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java delete mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.bat b/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.sh b/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_1.0_Ertheia/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java index e8ced2ddef..7b07bfc413 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -929,17 +930,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2470,17 +2468,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java similarity index 67% rename from L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java rename to L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java index 793b847f35..7223b5b2be 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -14,26 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.geodata; +package org.l2jmobius.gameserver.geoengine.pathfinding; /** - * @author Hasha + * @author -Nemesiss- */ -public enum GeoFormat +public abstract class AbstractNodeLoc { - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); + public abstract int getX(); - private final String _filename; + public abstract int getY(); - private GeoFormat(String filename) - { - _filename = filename; - } + public abstract int getZ(); - public String getFilename() - { - return _filename; - } -} \ No newline at end of file + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java index 0cf37f5c04..ac5089ff54 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5475,16 +5471,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 73855cdba9..bfb54b8225 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12704,7 +12704,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/readme.txt b/L2J_Mobius_1.0_Ertheia/readme.txt index dd443d00a2..b93cbd653f 100644 --- a/L2J_Mobius_1.0_Ertheia/readme.txt +++ b/L2J_Mobius_1.0_Ertheia/readme.txt @@ -7,7 +7,7 @@ System: https://mega.nz/#!s8lghARA!f7Hi1Yx5JWrNjT3kMRaGK_StI1Tnrxnhia8kaG8edSs JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/muge7mydml1en8t/L2J_Mobius_1.0_Ertheia_Geodata.zip +Geodata: http://www.mediafire.com/file/pzykzq2vpy4po2u/L2J_Mobius_1.0_Ertheia_Geodata.zip Working features... @@ -109,14 +109,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Modify all skill levels/sublevels based on client diff --git a/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.bat b/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.sh b/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_2.5_Underground/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java index ec1def3779..f6400bf61b 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -939,17 +940,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2492,17 +2490,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java similarity index 67% rename from L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java rename to L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java index 793b847f35..7223b5b2be 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -14,26 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.geodata; +package org.l2jmobius.gameserver.geoengine.pathfinding; /** - * @author Hasha + * @author -Nemesiss- */ -public enum GeoFormat +public abstract class AbstractNodeLoc { - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); + public abstract int getX(); - private final String _filename; + public abstract int getY(); - private GeoFormat(String filename) - { - _filename = filename; - } + public abstract int getZ(); - public String getFilename() - { - return _filename; - } -} \ No newline at end of file + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java index 0cf37f5c04..ac5089ff54 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5475,16 +5471,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index d23fe2ccea..05682537d8 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12711,7 +12711,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/readme.txt b/L2J_Mobius_2.5_Underground/readme.txt index be326b6727..25fe9b9bb9 100644 --- a/L2J_Mobius_2.5_Underground/readme.txt +++ b/L2J_Mobius_2.5_Underground/readme.txt @@ -6,7 +6,7 @@ System: https://mega.nz/#!Rx1SCIBC!W5pM7xljB_6UwXaHp_aW8sqY1SJXp5LFa121YGRrb74 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/p6d27anpotr33zl/L2J_Mobius_2.5_Underground_Geodata.zip +Geodata: http://www.mediafire.com/file/jme7lm3imhu97m5/L2J_Mobius_2.5_Underground_Geodata.zip Working features... @@ -122,14 +122,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.bat b/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.sh b/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_3.0_Helios/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java index 9a7d8d6fc2..252f8ba0bd 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -952,17 +953,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2514,17 +2512,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java similarity index 67% rename from L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java rename to L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java index 793b847f35..7223b5b2be 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -14,26 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.geodata; +package org.l2jmobius.gameserver.geoengine.pathfinding; /** - * @author Hasha + * @author -Nemesiss- */ -public enum GeoFormat +public abstract class AbstractNodeLoc { - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); + public abstract int getX(); - private final String _filename; + public abstract int getY(); - private GeoFormat(String filename) - { - _filename = filename; - } + public abstract int getZ(); - public String getFilename() - { - return _filename; - } -} \ No newline at end of file + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java index a102dd449b..c5fbce130c 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5475,16 +5471,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 7e609b0425..7be0986165 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12713,7 +12713,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/readme.txt b/L2J_Mobius_3.0_Helios/readme.txt index 01078f9dc2..136fa5f120 100644 --- a/L2J_Mobius_3.0_Helios/readme.txt +++ b/L2J_Mobius_3.0_Helios/readme.txt @@ -6,7 +6,7 @@ System: https://mega.nz/#!Fp8QBYYD!jLmMH7wisNk198-A7AYqhA1VMDru_OXpJbunXpBh4ww JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/7zych8rwf4fw3hd/L2J_Mobius_3.0_Helios_Geodata.zip +Geodata: http://www.mediafire.com/file/sviy5wsbdcgf5ve/L2J_Mobius_3.0_Helios_Geodata.zip Working features... @@ -131,14 +131,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.bat b/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.sh b/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java index 2e1ffb2bd6..5b0dfb4ff4 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -939,17 +940,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2489,17 +2487,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java similarity index 67% rename from L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java rename to L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java index 793b847f35..7223b5b2be 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -14,26 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.geodata; +package org.l2jmobius.gameserver.geoengine.pathfinding; /** - * @author Hasha + * @author -Nemesiss- */ -public enum GeoFormat +public abstract class AbstractNodeLoc { - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); + public abstract int getX(); - private final String _filename; + public abstract int getY(); - private GeoFormat(String filename) - { - _filename = filename; - } + public abstract int getZ(); - public String getFilename() - { - return _filename; - } -} \ No newline at end of file + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java index a102dd449b..c5fbce130c 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5475,16 +5471,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 3e501645d8..31eae4c958 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12693,7 +12693,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/readme.txt b/L2J_Mobius_4.0_GrandCrusade/readme.txt index 6d89e8f19a..b4fb3965e5 100644 --- a/L2J_Mobius_4.0_GrandCrusade/readme.txt +++ b/L2J_Mobius_4.0_GrandCrusade/readme.txt @@ -6,7 +6,7 @@ System: https://mega.nz/#!Fk0SAK6L!v3JSzKN6QuNzINgel0wp7zXovh0DOYzqxcm7etQOSLI JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/gxjyde9ji7w0zs7/L2J_Mobius_4.0_GrandCrusade_Geodata.zip +Geodata: http://www.mediafire.com/file/3wru8ll42xoqzn4/L2J_Mobius_4.0_GrandCrusade_Geodata.zip Working features... @@ -135,14 +135,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.bat b/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.sh b/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_5.0_Salvation/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java index 8537119ea4..867c3d8dba 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -934,17 +935,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2489,17 +2487,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java index 6b3388d4e1..321f95d137 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5484,16 +5480,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 13ad7bbe9c..8bab7b04b7 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12680,7 +12680,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/readme.txt b/L2J_Mobius_5.0_Salvation/readme.txt index f972157c46..32ed73ba1d 100644 --- a/L2J_Mobius_5.0_Salvation/readme.txt +++ b/L2J_Mobius_5.0_Salvation/readme.txt @@ -7,7 +7,7 @@ System: https://mega.nz/#!ZgtSUQKR!CNGdlcf2TRiDKvH8NJowEQUfafoJyzsv-g2SM9kpOB4 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/iwtdjt1vfs7hq12/L2J_Mobius_5.0_Salvation_Geodata.zip +Geodata: http://www.mediafire.com/file/arc7w3cbiukz0wj/L2J_Mobius_5.0_Salvation_Geodata.zip Working features... @@ -141,14 +141,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.bat b/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.sh b/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java index c93f84b637..fbf09e7329 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -941,17 +942,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2501,17 +2499,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java index 6b3388d4e1..321f95d137 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5484,16 +5480,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index fa43a94182..a6bf20c463 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12683,7 +12683,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/readme.txt b/L2J_Mobius_5.5_EtinasFate/readme.txt index a49366ec48..129be44258 100644 --- a/L2J_Mobius_5.5_EtinasFate/readme.txt +++ b/L2J_Mobius_5.5_EtinasFate/readme.txt @@ -5,7 +5,7 @@ System: https://mega.nz/#!B10W0QSR!xon-5i8Z8g5-VoJPTt16leYVIVV8GosQE_OSH1j7pnM JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/9zm5dal5jyj313w/L2J_Mobius_5.5_EtinasFate_Geodata.zip +Geodata: http://www.mediafire.com/file/3cjttcj5ij8fsxl/L2J_Mobius_5.5_EtinasFate_Geodata.zip Working features... @@ -142,14 +142,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.bat b/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.sh b/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_6.0_Fafurion/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java index 31c0174f51..2960dd258b 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -953,17 +954,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2544,17 +2542,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java index 6b3388d4e1..321f95d137 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5484,16 +5480,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 40294c5c2c..fc230d3d25 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12689,7 +12689,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/readme.txt b/L2J_Mobius_6.0_Fafurion/readme.txt index 11d1d682b7..2de47111c6 100644 --- a/L2J_Mobius_6.0_Fafurion/readme.txt +++ b/L2J_Mobius_6.0_Fafurion/readme.txt @@ -8,7 +8,7 @@ System: https://mega.nz/#!xolCkYjD!TpdBo8SwYpPYJN86H_r5AxZljTZAfyj-o2H9cfy2bNc JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/w6j8cphwoji06is/L2J_Mobius_6.0_Fafurion_Geodata.zip +Geodata: http://www.mediafire.com/file/dcpw8w5u60alw14/L2J_Mobius_6.0_Fafurion_Geodata.zip Working features... @@ -153,14 +153,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.bat b/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.sh b/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java index a19230d974..fb2422eade 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -964,17 +965,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2568,17 +2566,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java index 4f916e55e7..ba10981eef 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3017,7 +3018,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3258,10 +3258,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3391,7 +3387,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5483,16 +5479,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index c3f115c065..9c99e3519f 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12697,7 +12697,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/readme.txt b/L2J_Mobius_7.0_PreludeOfWar/readme.txt index cd54258e46..e8bca15590 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/readme.txt +++ b/L2J_Mobius_7.0_PreludeOfWar/readme.txt @@ -5,7 +5,8 @@ System: https://mega.nz/#!98likSra!vP3uaDwJs8oiY5q60UzBSXAbgNELWX_dycWb98Mz96U JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/ef4gn45tic0kjmm/L2J_Mobius_7.0_PreludeOfWar_Geodata.zip +Geodata: http://www.mediafire.com/file/94ekkgj1xmr5rb1/L2J_Mobius_7.0_PreludeOfWar_Geodata.zip + Working features... @@ -161,14 +162,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.bat b/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.sh b/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_8.0_Homunculus/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java index 92c1eaaa69..2f7f69a20f 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -964,17 +965,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2568,17 +2566,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java index 4f916e55e7..ba10981eef 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3017,7 +3018,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3258,10 +3258,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3391,7 +3387,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5483,16 +5479,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 496d434fc7..91dd35c793 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12738,7 +12738,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/readme.txt b/L2J_Mobius_8.0_Homunculus/readme.txt index e7b304c52f..45541d3229 100644 --- a/L2J_Mobius_8.0_Homunculus/readme.txt +++ b/L2J_Mobius_8.0_Homunculus/readme.txt @@ -4,7 +4,7 @@ System: https://mega.nz/file/xl1njaTY#BdVHadGs9bTDyrDE3QgBGvAqkmqSqPtn8hLpcCil1F JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/ea7d7zy8y13dta3/L2J_Mobius_8.0_Homunculus_Geodata.zip +Geodata: http://www.mediafire.com/file/muw8zo6v7cl7ujt/L2J_Mobius_8.0_Homunculus_Geodata.zip Working features... @@ -164,14 +164,5 @@ Customs: -Vote reward -Automated database backups -Others: --Reworked tax system --Reworked quest system --Reworked spawn system --Reworked drop system --Reworked L2World, L2WorldRegion and L2Object --Skill system from L2jUnity --GeoEngine from aCis - TODO: -Mystic Tavern instanced zones diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.bat b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.sh b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini index c579cd8f12..51dd237ca8 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java index e13ef61787..bcb2b4ff66 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java @@ -27,6 +27,8 @@ import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStream; import java.math.BigInteger; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -927,17 +929,14 @@ public class Config public static boolean LEAVE_BUFFS_ON_DIE; public static boolean ALT_RAIDS_STATS_BONUS; - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; public static boolean FALL_DAMAGE; public static boolean ALLOW_WATER; @@ -2494,17 +2493,14 @@ public class Config public static void loadgeodataConfig() { final PropertiesParser geoengineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = geoengineConfig.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = geoengineConfig.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = geoengineConfig.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = geoengineConfig.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = geoengineConfig.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = geoengineConfig.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); + LOW_WEIGHT = geoengineConfig.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = geoengineConfig.getFloat("MediumWeight", 2); + HIGH_WEIGHT = geoengineConfig.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = geoengineConfig.getBoolean("CorrectPlayerZ", false); FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index b1cda6273d..9f7c44a9bb 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,89 +16,98 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -112,827 +121,580 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact()) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, Location worldPosition) { - // 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 (DoorData.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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instanceId - * @return {@code boolean} : True, when target can be seen. + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tInstanceId the target's instanceId + * @return */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) + public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param instanceId + * @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 instanceId, int tx, int ty, int tz) + { + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instanceId the instance id + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instanceId the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 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 (DoorData.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.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 (DoorData.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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -940,6 +702,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 09c482a29a..43ac4d89b8 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,83 +20,87 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -120,147 +124,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java index b2121fa6a9..893d3ef03a 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java @@ -47,11 +47,11 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(geoX, geoY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { @@ -63,11 +63,11 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(worldX, worldY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java index 22153e61f5..404c58675b 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -43,6 +43,8 @@ import org.l2jmobius.gameserver.data.xml.MapRegionData; import org.l2jmobius.gameserver.data.xml.ZoneData; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.ISkillHandler; import org.l2jmobius.gameserver.handler.SkillHandler; import org.l2jmobius.gameserver.handler.itemhandlers.Potions; @@ -4293,7 +4295,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder public int _heading; public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -5523,7 +5525,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index aacb93ca35..829f3edcc6 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -15482,7 +15482,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ), false); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java index d179c87bad..aa60d1a6b4 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,33 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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); - // west arrow - col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index 59c5ee5476..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,367 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - final static String GEODATA_PATH = "./data/geodata/"; - - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/readme.txt b/L2J_Mobius_C4_ScionsOfDestiny/readme.txt index 0cf3e395da..65dc4a5730 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/readme.txt +++ b/L2J_Mobius_C4_ScionsOfDestiny/readme.txt @@ -6,7 +6,7 @@ System: https://mega.nz/file/c09XTS7Y#MoIzoOZ8q-aAiKuE4XzB4JpJU67Eql61SHD4VhqP9M JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/n0pj5tn0v8z3e8s/L2J_Mobius_C4_ScionsOfDestiny_Geodata.zip +Geodata: http://www.mediafire.com/file/q0j6uawuq5fjo0k/L2J_Mobius_C4_ScionsOfDestiny_Geodata.zip Prelude: http://legacy.lineage2.com/news/career.html diff --git a/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.bat b/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.sh b/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_C6_Interlude/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini index c579cd8f12..51dd237ca8 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java index d97610d206..54eca364a5 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java @@ -28,6 +28,8 @@ 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; @@ -961,17 +963,14 @@ public class Config public static boolean LEAVE_BUFFS_ON_DIE; public static boolean ALT_RAIDS_STATS_BONUS; - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; public static boolean FALL_DAMAGE; public static boolean ALLOW_WATER; @@ -2559,17 +2558,14 @@ public class Config public static void loadgeodataConfig() { final PropertiesParser geoengineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = geoengineConfig.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = geoengineConfig.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = geoengineConfig.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = geoengineConfig.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = geoengineConfig.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = geoengineConfig.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); + LOW_WEIGHT = geoengineConfig.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = geoengineConfig.getFloat("MediumWeight", 2); + HIGH_WEIGHT = geoengineConfig.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = geoengineConfig.getBoolean("CorrectPlayerZ", false); FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index b1cda6273d..9f7c44a9bb 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,89 +16,98 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -112,827 +121,580 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact()) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, Location worldPosition) { - // 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 (DoorData.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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instanceId - * @return {@code boolean} : True, when target can be seen. + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tInstanceId the target's instanceId + * @return */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) + public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param instanceId + * @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 instanceId, int tx, int ty, int tz) + { + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instanceId the instance id + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instanceId the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 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 (DoorData.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.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 (DoorData.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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -940,6 +702,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 09c482a29a..43ac4d89b8 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,83 +20,87 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -120,147 +124,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java index b2121fa6a9..893d3ef03a 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java @@ -47,11 +47,11 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(geoX, geoY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { @@ -63,11 +63,11 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeightNearest(worldX, worldY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java index 6a13b5b031..a54fc409a7 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -43,6 +43,8 @@ import org.l2jmobius.gameserver.data.xml.MapRegionData; import org.l2jmobius.gameserver.data.xml.ZoneData; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.ISkillHandler; import org.l2jmobius.gameserver.handler.SkillHandler; import org.l2jmobius.gameserver.handler.itemhandlers.Potions; @@ -4339,7 +4341,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder public int _heading; public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -5569,7 +5571,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 35c94c643d..028716aee2 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -15848,7 +15848,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ), false); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java index d179c87bad..aa60d1a6b4 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,33 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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); - // west arrow - col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index 59c5ee5476..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,367 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - final static String GEODATA_PATH = "./data/geodata/"; - - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/readme.txt b/L2J_Mobius_C6_Interlude/readme.txt index 876e66538d..53dbe390b5 100644 --- a/L2J_Mobius_C6_Interlude/readme.txt +++ b/L2J_Mobius_C6_Interlude/readme.txt @@ -5,7 +5,7 @@ System: https://mega.nz/#!t49wiKgZ!PzVAcxcg2o8gRkAiMjH7CUO6lKrBG27npg2JPL1uEq8 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/d8t02akasoeth2i/L2J_Mobius_C6_Interlude_Geodata_v2.zip +Geodata: http://www.mediafire.com/file/4k2pi3qa8rqt299/L2J_Mobius_C6_Interlude_Geodata.zip Prelude: http://legacy.lineage2.com/news/career.html diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.bat b/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.sh b/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index d68b91bd85..524652cbc1 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,11 +55,12 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(geoX, geoY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -133,7 +135,7 @@ public class AdminGeodata implements IAdminCommandHandler { final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; - BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y); + BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index ce584bb866..a29a86b4f4 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java index 40ab86f996..80260c21d1 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -1057,17 +1058,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; // -------------------------------------------------- @@ -2586,17 +2584,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java index 217d288f9d..d56be32b4c 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java @@ -26,6 +26,7 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.model.WorldObject; import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Summon; @@ -50,7 +51,7 @@ public class SummonAI extends PlayableAI implements Runnable @Override protected void onIntentionAttack(Creature target) { - if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) + if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) { return; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7d02c58864..2c424b1f5a 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,89 +16,99 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -112,827 +122,580 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instanceId - * @return {@code boolean} : True, when target can be seen. + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tInstanceId the target's instanceId + * @return */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) + public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param instanceId + * @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 instanceId, int tx, int ty, int tz) + { + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instanceId the instance id + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instanceId the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) - { - 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.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instanceId, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instanceId)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -940,6 +703,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 09c482a29a..43ac4d89b8 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,83 +20,87 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -120,147 +124,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java index 52a53a192f..a1aaa8131b 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -56,6 +56,8 @@ import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.InstanceManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; @@ -258,7 +260,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -3357,7 +3358,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3966,7 +3967,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -4205,10 +4205,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -4338,7 +4334,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -6708,16 +6704,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java index d95fcc5334..f9ff199335 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -29,6 +29,7 @@ import org.l2jmobius.gameserver.enums.Race; import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.handler.IItemHandler; import org.l2jmobius.gameserver.handler.ItemHandler; import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; @@ -645,7 +646,7 @@ public abstract class Summon extends Playable return false; } - if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) + if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); return false; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 9baf658ade..c8db23101d 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -13512,7 +13512,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index df3444c5c7..f121db3a17 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -176,10 +176,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -198,10 +194,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final int teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/readme.txt b/L2J_Mobius_CT_2.4_Epilogue/readme.txt index f73720ec62..259b961b79 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/readme.txt +++ b/L2J_Mobius_CT_2.4_Epilogue/readme.txt @@ -5,7 +5,8 @@ System: https://mega.nz/#!ppkUFIJQ!oat-UGQsf2liToQu19nCljGO5BAUInwfzln-ERBBB28 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/j126h41s6jl9dyn/L2J_Mobius_CT_2.4_Epilogue_Geodata.zip +Geodata: http://www.mediafire.com/file/q515p4hl6j0ivxm/L2J_Mobius_CT_2.4_Epilogue_Geodata.zip + What is done -Removed HighFive packets. diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.bat b/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.sh b/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index d68b91bd85..524652cbc1 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,11 +55,12 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(geoX, geoY, worldZ)); + BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); } else { @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -133,7 +135,7 @@ public class AdminGeodata implements IAdminCommandHandler { final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; - BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y); + BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } } diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index ce584bb866..a29a86b4f4 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java index 5d311fcfc8..da038c82ec 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -1057,17 +1058,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; // -------------------------------------------------- @@ -2595,17 +2593,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java index 217d288f9d..d56be32b4c 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java @@ -26,6 +26,7 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.model.WorldObject; import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Summon; @@ -50,7 +51,7 @@ public class SummonAI extends PlayableAI implements Runnable @Override protected void onIntentionAttack(Creature target) { - if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) + if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) { return; } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7d02c58864..2c424b1f5a 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,89 +16,99 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -112,827 +122,580 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instanceId - * @return {@code boolean} : True, when target can be seen. + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tInstanceId the target's instanceId + * @return */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) + public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param instanceId + * @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 instanceId, int tx, int ty, int tz) + { + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instanceId the instance id + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instanceId the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) - { - 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.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instanceId, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instanceId)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -940,6 +703,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 09c482a29a..43ac4d89b8 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,83 +20,87 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -120,147 +124,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java index 39d95f05fa..dbdcbe7976 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -56,6 +56,8 @@ import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.InstanceManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; @@ -259,7 +261,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -3359,7 +3360,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3968,7 +3969,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -4207,10 +4207,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -4340,7 +4336,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -6710,16 +6706,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java index f1c8b41712..d4542d0165 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -29,6 +29,7 @@ import org.l2jmobius.gameserver.enums.Race; import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.handler.IItemHandler; import org.l2jmobius.gameserver.handler.ItemHandler; import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; @@ -645,7 +646,7 @@ public abstract class Summon extends Playable return false; } - if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) + if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); return false; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 64cf2a0c5f..029520ebd9 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -13405,7 +13405,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index df3444c5c7..f121db3a17 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -176,10 +176,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -198,10 +194,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final int teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/readme.txt b/L2J_Mobius_CT_2.6_HighFive/readme.txt index 7f7a9a9126..c72efbf35d 100644 --- a/L2J_Mobius_CT_2.6_HighFive/readme.txt +++ b/L2J_Mobius_CT_2.6_HighFive/readme.txt @@ -6,7 +6,8 @@ System: https://mega.nz/file/B5sVlTgJ#a19JJWZ7llbAZF9XnU91Z98ZbO_Rb56s9AvQ_QyDz- JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/ega48fmtx35t6iy/L2J_Mobius_CT_2.6_HighFive_Geodata.zip +Geodata: http://www.mediafire.com/file/92t2tdjni4rux06/L2J_Mobius_CT_2.6_HighFive_Geodata.zip + What is done -Dropped Python / Javolution / Trove @@ -29,7 +30,6 @@ What is done -Premium System -User command expon/expoff -Reworked drop system --GeoEngine from aCis -Retail system messages and NPC strings -New script loader -New ThreadPool @@ -41,4 +41,4 @@ What is done -Faction System (Good vs Evil) -Fake players -Automated database backups --Reworked L2World, L2WorldRegion and L2Object +-Reworked World, WorldRegion and WorldObject diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java index d05c2df9a6..20cd961600 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -886,17 +887,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2387,17 +2385,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java index 87e36471eb..ffd4f9b9c9 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5474,16 +5470,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index fe168964ed..cd65511c96 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12488,7 +12488,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/readme.txt b/L2J_Mobius_Classic_2.0_Saviors/readme.txt index 95ca013b18..894ef9162a 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/readme.txt +++ b/L2J_Mobius_Classic_2.0_Saviors/readme.txt @@ -5,7 +5,8 @@ System: https://mega.nz/#!Fp8QBYYD!jLmMH7wisNk198-A7AYqhA1VMDru_OXpJbunXpBh4ww JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/oxk8o5tiq0rdmz5/L2J_Mobius_Classic_2.0_Saviors_Geodata.zip +Geodata: http://www.mediafire.com/file/jstiuq52oq0h3wk/L2J_Mobius_Classic_2.0_Saviors_Geodata.zip + Saviors: https://eu.4game.com/lineage2classic/play/saviors/ -Classic packet compatibility diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java index b631692c93..77f5b95dd8 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -890,17 +891,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2393,17 +2391,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java index 87e36471eb..ffd4f9b9c9 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5474,16 +5470,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 173075c8ed..4ae07d0694 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12488,7 +12488,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/readme.txt b/L2J_Mobius_Classic_2.1_Zaken/readme.txt index bd7c07df3a..c133440650 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/readme.txt +++ b/L2J_Mobius_Classic_2.1_Zaken/readme.txt @@ -5,7 +5,8 @@ System: https://mega.nz/#!os8HRShC!5JbEmWEugMyssgE-lWgBxJj_zC63gXX-ogBh-A3arX8 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/cqrcj73pntv82d8/L2J_Mobius_Classic_2.1_Zaken_Geodata.zip +Geodata: http://www.mediafire.com/file/38yzztwmmdq0r07/L2J_Mobius_Classic_2.1_Zaken_Geodata.zip + Saviors: https://eu.4game.com/lineage2classic/play/saviors/ -Classic packet compatibility diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java index b631692c93..77f5b95dd8 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -890,17 +891,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2393,17 +2391,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java index 64f2bf87d7..fc9aa8684e 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5483,16 +5479,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 13100dbdec..63a252496d 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12474,7 +12474,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/readme.txt b/L2J_Mobius_Classic_2.2_Antharas/readme.txt index 94c44039c4..cba5041175 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/readme.txt +++ b/L2J_Mobius_Classic_2.2_Antharas/readme.txt @@ -7,7 +7,8 @@ System: https://mega.nz/#!ZgtSUQKR!CNGdlcf2TRiDKvH8NJowEQUfafoJyzsv-g2SM9kpOB4 JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/tas8fp9ee2r6mn9/L2J_Mobius_Classic_2.2_Antharas_Geodata.zip +Geodata: http://www.mediafire.com/file/zzpl37z8nqzj7mj/L2J_Mobius_Classic_2.2_Antharas_Geodata.zip + Saviors: https://eu.4game.com/lineage2classic/play/saviors/ -Classic packet compatibility diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java index 07ed6d17c9..75c200e47a 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -890,17 +891,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2394,17 +2392,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java index 8879f37ec9..8ce2f08d0c 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -64,6 +64,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -251,7 +253,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2558,7 +2559,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3031,7 +3032,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3272,10 +3272,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3405,7 +3401,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5508,16 +5504,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index bdc4d156c7..5a26e12a0c 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12523,7 +12523,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/readme.txt b/L2J_Mobius_Classic_2.3_SevenSigns/readme.txt index 91f7e9ac4c..eb83b47843 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/readme.txt +++ b/L2J_Mobius_Classic_2.3_SevenSigns/readme.txt @@ -5,7 +5,8 @@ System: https://mega.nz/#!B10W0QSR!xon-5i8Z8g5-VoJPTt16leYVIVV8GosQE_OSH1j7pnM JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/liyo9ibulkp3l9g/L2J_Mobius_Classic_2.3_SevenSigns_Geodata.zip +Geodata: http://www.mediafire.com/file/i0irxa6tda1np71/L2J_Mobius_Classic_2.3_SevenSigns_Geodata.zip + Saviors: https://eu.4game.com/lineage2classic/play/saviors/ -Classic packet compatibility diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java index 0b0a6bca37..22a4aa9eca 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -895,17 +896,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2403,17 +2401,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java index 8879f37ec9..8ce2f08d0c 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -64,6 +64,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -251,7 +253,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2558,7 +2559,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3031,7 +3032,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3272,10 +3272,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3405,7 +3401,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5508,16 +5504,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 2cef52490a..21a33155fa 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12523,7 +12523,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/readme.txt b/L2J_Mobius_Classic_2.4_SecretOfEmpire/readme.txt index 209a458829..cb6e2466a8 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/readme.txt +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/readme.txt @@ -8,7 +8,7 @@ System: https://mega.nz/#!xolCkYjD!TpdBo8SwYpPYJN86H_r5AxZljTZAfyj-o2H9cfy2bNc JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/liyo9ibulkp3l9g/L2J_Mobius_Classic_2.3_SevenSigns_Geodata.zip +Geodata: http://www.mediafire.com/file/0lcde1bxjg7bayz/L2J_Mobius_Classic_2.4_SecretOfEmpire_Geodata.zip Saviors: https://eu.4game.com/lineage2classic/play/saviors/ diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java index 7b73d9e0af..2df79fdec5 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -903,17 +904,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2420,17 +2418,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java index ad28349dc5..c362e0d1a9 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -64,6 +64,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -251,7 +253,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2558,7 +2559,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3030,7 +3031,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3271,10 +3271,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3404,7 +3400,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5507,16 +5503,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 0add727e56..1780193b84 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12501,7 +12501,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/readme.txt b/L2J_Mobius_Classic_3.0_TheKamael/readme.txt index 2a2e24e761..f202574e16 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/readme.txt +++ b/L2J_Mobius_Classic_3.0_TheKamael/readme.txt @@ -5,7 +5,7 @@ System: https://mega.nz/#!98likSra!vP3uaDwJs8oiY5q60UzBSXAbgNELWX_dycWb98Mz96U JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/fzw7fplavow88wb/L2J_Mobius_Classic_3.0_TheKamael_Geodata.zip +Geodata: http://www.mediafire.com/file/67p627w8qmalsbs/L2J_Mobius_Classic_3.0_TheKamael_Geodata.zip Saviors: https://eu.4game.com/lineage2classic/play/saviors/ diff --git a/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.bat b/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.sh b/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Classic_Interlude/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java index a79381b01b..bf466231d2 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -904,17 +905,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2418,17 +2416,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java index 87e36471eb..ffd4f9b9c9 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5474,16 +5470,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index ebb8436d7d..ab8d8f48a5 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12509,7 +12509,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/readme.txt b/L2J_Mobius_Classic_Interlude/readme.txt index 7c6c8bb775..10699d144f 100644 --- a/L2J_Mobius_Classic_Interlude/readme.txt +++ b/L2J_Mobius_Classic_Interlude/readme.txt @@ -6,7 +6,8 @@ System: https://www.mediafire.com/file/mvergqghqz4jzpq/L2J_Mobius_Classic_Interl JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: https://www.mediafire.com/file/5ebydcm5ipckpoa/L2J_Mobius_Classic_Interlude_Geodata_v5.zip +Geodata: http://www.mediafire.com/file/prx3sls7lts5p1f/L2J_Mobius_Classic_Interlude_Geodata.zip + This is a Classic server based on the Grand Crusade client. The goal is to make a better approximation of what Classic is to older chronicles, like Interlude. @@ -17,6 +18,7 @@ Who knows? Maybe some day it will be a pure Interlude version. A lot of things can go wrong while using this project, if you do not know what you are doing, it is best not to use it. + Tools that might be helpful (use with Java 1.8) L2ClientDat: https://github.com/MobiusDevelopment/l2clientdat XdatEditor: https://github.com/MobiusDevelopment/xdat_editor diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.bat b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.bat deleted file mode 100644 index fc833fc2ff..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title L2D geodata converter - -java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter - -pause diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.sh b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.sh deleted file mode 100644 index 02f5ac665a..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/GeoDataConverter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 - diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini index 3eee183747..a76d42edd1 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini @@ -1,10 +1,6 @@ # ================================================================= # 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/ @@ -16,16 +12,6 @@ GeoDataPath = ./data/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 # ================================================================= @@ -37,22 +23,18 @@ 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 +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 -# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -DiagonalWeight = 14 +# Weight for nodes near walls +MediumWeight = 2 -# 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 for nodes with obstacles +HighWeight = 3 -# 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 +# Weight for diagonal movement. +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 # ================================================================= # Other diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index e636650c23..524652cbc1 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler final int worldX = activeChar.getX(); final int worldY = activeChar.getY(); final int worldZ = activeChar.getZ(); - final int geoX = GeoEngine.getGeoX(worldX); - final int geoY = GeoEngine.getGeoY(worldY); + final int geoX = GeoEngine.getInstance().getGeoX(worldX); + final int geoY = GeoEngine.getInstance().getGeoY(worldY); + if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index 4d519d1956..fdb2319138 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -18,18 +18,18 @@ package handlers.admincommandhandlers; import java.util.List; -import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.Config; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.util.BuilderUtil; public class AdminPathNode implements IAdminCommandHandler { private static final String[] ADMIN_COMMANDS = { - "admin_path_find", + "admin_path_find" }; @Override @@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler { if (command.equals("admin_path_find")) { + if (!Config.PATHFINDING) + { + BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); + return true; + } if (activeChar.getTarget() != null) { - final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { - BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); + BuilderUtil.sendSysMessage(activeChar, "No Route!"); + return true; } - else + for (AbstractNodeLoc a : path) { - for (Location point : path) - { - BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); - } + BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } } else { - activeChar.sendPacket(SystemMessageId.INVALID_TARGET); + BuilderUtil.sendSysMessage(activeChar, "No Target!"); } } - else - { - return false; - } return true; } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java index 6c544911de..8e2b33d541 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java @@ -32,6 +32,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -925,17 +926,14 @@ public class Config // -------------------------------------------------- // GeoEngine // -------------------------------------------------- - public static String GEODATA_PATH; - public static int COORD_SYNCHRONIZE; - public static int PART_OF_CHARACTER_HEIGHT; - public static int MAX_OBSTACLE_HEIGHT; + public static Path GEODATA_PATH; 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 float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static float DIAGONAL_WEIGHT; + public static int COORD_SYNCHRONIZE; public static boolean CORRECT_PLAYER_Z; /** Attribute System */ @@ -2468,17 +2466,14 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); - GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); - COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); - PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); - MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); + GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); - DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); - OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); - HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); - MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); + MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); + HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); + DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); // Load AllowedPlayerRaces config file (if exists) diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7691e5b35a..08fc6402d1 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,91 +16,101 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.logging.Level; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.IRegion; +import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; +import org.l2jmobius.gameserver.geoengine.geodata.Region; import org.l2jmobius.gameserver.instancemanager.WarpedSpaceManager; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.util.MathUtil; +import org.l2jmobius.gameserver.model.interfaces.ILocational; +import org.l2jmobius.gameserver.util.GeoUtils; +import org.l2jmobius.gameserver.util.LinePointIterator; +import org.l2jmobius.gameserver.util.LinePointIterator3D; /** - * @author Hasha + * @author -Nemesiss-, HorridoJoho */ public class GeoEngine { - protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); + private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - private final ABlock[][] _blocks; - private final BlockNull _nullBlock; + private static final int WORLD_MIN_X = -655360; + private static final int WORLD_MIN_Y = -589824; + private static final int WORLD_MIN_Z = -16384; - /** - * GeoEngine constructor. Loads all geodata files of chosen geodata format. - */ - public GeoEngine() + /** 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; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + 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; + + protected 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; - long fileSize = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) + for (int i = 0; i < _regions.length(); i++) { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) + _regions.set(i, NullRegion.INSTANCE); + } + + int loaded = 0; + try + { + for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) { - final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); - if (f.exists() && !f.isDirectory()) + for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) { - // region file is load-able, try to load it - if (loadGeoBlocks(rx, ry)) + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + if (Files.exists(geoFilePath)) { - loaded++; - fileSize += f.length(); + try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); + loaded++; + } } } - else - { - // region file is not load-able, load null blocks - loadNullBlocks(rx, ry); - } } } - - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); - if (loaded > 0) + catch (Exception e) { - LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); + LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + System.exit(1); } - // avoid wrong configs when no files are loaded + LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + + // Avoid wrong configs when no files are loaded. if (loaded == 0) { if (Config.PATHFINDING) @@ -114,839 +124,588 @@ public class GeoEngine 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 - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // get block indexes - final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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()); - - // 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 - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; - final int blockY = (regionY - World.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, World.MAP_MIN_X, World.MAP_MAX_X) - World.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, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.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) + World.MAP_MIN_X + 8; - } - - /** - * Converts geodata Y to world Y. * @param geoY - * @return int : World Y + * @return the region */ - public static int getWorldY(int geoY) + private IRegion getRegion(int geoX, int geoY) { - return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; + return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y)); } /** - * Returns block of geodata on given coordinates. - * @param geoX : Geodata X - * @param geoY : Geodata Y - * @return {@link ABlock} : Block of geodata. + * @param filePath + * @param regionX + * @param regionY + * @throws IOException */ - private final ABlock getBlock(int geoX, int geoY) + public void loadRegion(Path filePath, int regionX, int regionY) throws IOException { - 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 > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) + final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY; + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r")) { - return _blocks[x][y]; + _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); } - 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 + * @param regionX + * @param regionY + */ + public void unloadRegion(int regionX, int regionY) + { + _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); + } + + /** + * @param geoX + * @param geoY + * @return if geodata exist */ public boolean hasGeoPos(int geoX, int geoY) { - final ABlock block = getBlock(geoX, geoY); - if (block == null) // null block check + return getRegion(geoX, geoY).hasGeo(); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe check + */ + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @param nswe + * @return the nearest nswe anti-corner cut + */ + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) { - // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) - // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); - return false; + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); } - 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 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 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 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 short getHeight(int worldX, int worldY, int worldZ) - { - return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - - // PATHFINDING - - /** - * Check line of sight from {@link WorldObject} to {@link WorldObject}. - * @param origin : The origin object. - * @param target : The target object. - * @return {@code boolean} : True if origin can see target - */ - public boolean canSeeTarget(WorldObject origin, WorldObject target) - { - if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) { + 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 = 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 = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the nearest Z value + */ + public int getNearestZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNearestZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next lower Z value + */ + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextLowerZ(geoX, geoY, worldZ); + } + + /** + * @param geoX + * @param geoY + * @param worldZ + * @return the next higher Z value + */ + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + final IRegion region = getRegion(geoX, geoY); + if (region == null) + { + return worldZ; + } + return region.getNextHigherZ(geoX, geoY, worldZ); + } + + /** + * @param worldX + * @return the geo X + */ + public int getGeoX(int worldX) + { + return (worldX - WORLD_MIN_X) / 16; + } + + /** + * @param worldY + * @return the geo Y + */ + public int getGeoY(int worldY) + { + return (worldY - WORLD_MIN_Y) / 16; + } + + /** + * @param worldZ + * @return the geo Z + */ + public int getGeoZ(int worldZ) + { + return (worldZ - WORLD_MIN_Z) / 16; + } + + /** + * @param geoX + * @return the world X + */ + public int getWorldX(int geoX) + { + return (geoX * 16) + WORLD_MIN_X + 8; + } + + /** + * @param geoY + * @return the world Y + */ + public int getWorldY(int geoY) + { + return (geoY * 16) + WORLD_MIN_Y + 8; + } + + /** + * @param geoZ + * @return the world Z + */ + public int getWorldZ(int geoZ) + { + return (geoZ * 16) + WORLD_MIN_Z + 8; + } + + /** + * Gets the Z height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the nearest Z height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * 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(WorldObject cha, WorldObject target) + { + if (target.isDoor()) + { + // Can always see doors. 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getCollisionHeight() * 2; - } - - double theight = 0; - if (target.isCreature()) - { - theight = ((Creature) target).getCollisionHeight() * 2; - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); } /** - * Check line of sight from {@link WorldObject} to {@link Location}. - * @param origin : The origin object. - * @param position : The target position. - * @return {@code boolean} : True if object can see position + * Can see target. Checks doors between. + * @param cha the character + * @param worldPosition the world position + * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise */ - public boolean canSeeTarget(WorldObject origin, Location position) + public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) { - // 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 (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - return false; - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { - 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.isCreature()) - { - oheight = ((Creature) origin).getTemplate().getCollisionHeight(); - } - - // perform geodata check - return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); } /** - * Simple check for origin to target visibility. - * @param goxValue : origin X geodata coordinate - * @param goyValue : origin Y geodata coordinate - * @param gozValue : origin Z geodata coordinate - * @param oheight : origin height (if instance of {@link Character}) - * @param gtxValue : target X geodata coordinate - * @param gtyValue : target Y geodata coordinate - * @param gtzValue : target Z geodata coordinate - * @param theight : target height (if instance of {@link Character}) + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param world + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @param tworld the target's instanceId + * @return + */ + public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) + { + return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); + } + + /** + * Can see target. Checks doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @param instance - * @return {@code boolean} : True, when target can be seen. + * @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 */ - private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) + public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) { - int goz = gozValue; - int gtz = gtzValue; - int gox = goxValue; - int goy = goyValue; - int gtx = gtxValue; - int gty = gtyValue; - - // 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++) + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) { - // 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 - final int e2 = 2 * d; - if ((e2 > -dy) && (e2 < dx)) + return false; + } + return canSeeTarget(x, y, z, tx, ty, tz); + } + + /** + * @param prevX + * @param prevY + * @param prevGeoZ + * @param curX + * @param curY + * @param nswe + * @return the LOS Z value + */ + 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 xValue the x coordinate + * @param yValue the y coordinate + * @param zValue the z coordinate + * @param txValue the target's x coordinate + * @param tyValue the target's y coordinate + * @param tzValue 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 xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) + { + int x = xValue; + int y = yValue; + int tx = txValue; + int ty = tyValue; + + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + int z = getNearestZ(geoX, geoY, zValue); + int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + // Fastpath. + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) { - // calculate next point XY coordinates - d -= dy; - d += dx; - nox += sx; - ntx -= sx; - noy += sy; - nty -= sy; - diro |= diroxy; - dirt |= dirtxy; + return z == tz; } - else if (e2 > -dy) + 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)) { - // 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; + continue; } + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // Check if the position has geodata. + if (hasGeoPos(curX, curY)) { - // get block of the next cell - block = getBlock(nox, noy); - - // get index of particular layer, based on movement conditions - if ((nsweo & diro) == 0) + 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) { - index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = z + MAX_SEE_OVER_HEIGHT; } else { - index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; } - // layer does not exist, return - if (index == -1) + 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; } - - // 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) + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param zValue the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tzValue the target's z coordinate + * @param instance the instance + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + final int z = getNearestZ(geoX, geoY, zValue); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + final int tz = getNearestZ(tGeoX, tGeoY, tzValue); + + if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) + { + return new Location(x, y, getHeight(x, y, z)); + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(x, y, z, tx, ty, tz, instance)) + { + 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 fromZvalue 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 toZvalue the Z coordinate to end checking at + * @param instance the instance + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + final int fromZ = getNearestZ(geoX, geoY, fromZvalue); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); + + if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + { + return false; + } + if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + return false; + } + if (WarpedSpaceManager.getInstance().checkForWarpedSpace(fromX, fromY, fromZ, toX, toY, toZ, instance)) + { + 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; } - - // 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; + prevX = curX; + prevY = curY; + prevZ = curZ; } - // when iteration is completed, compare final Z coordinates - return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // Different floors. + return false; + } + + return true; } /** - * 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 instance - * @return {code boolean} : True if target coordinates are reachable from origin coordinates + * 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 canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean hasGeo(int x, int y) { - // 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 - final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); - return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); + return hasGeoPos(getGeoX(x), getGeoY(y)); } - /** - * 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 instance - * @return {@link Location} : Last point where object can walk (just before wall) - */ - public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. - if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) - { - return new Location(ox, oy, oz); - } - if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) - { - 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, instance); - } - - /** - * With this method you can check if a position is visible or can be reached by beeline movement.
- * Target X and Y reachable and Z is on same floor: - *
    - *
  • Location of the target with corrected Z value from geodata.
  • - *
- * Target X and Y reachable but Z is on another floor: - *
    - *
  • Location of the origin with corrected Z value from geodata.
  • - *
- * Target X and Y not reachable: - *
    - *
  • Last accessible location in destination to target.
  • - *
- * @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 instance - * @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, Instance instance) - { - if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { - return new GeoLocation(gox, goy, goz); - } - if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { - return new GeoLocation(gox, goy, goz); - } - if (WarpedSpaceManager.getInstance().checkForWarpedSpace(gox, goy, goz, gtx, gty, gtz, instance)) - { - 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 - int count = 0; - while (count++ < Config.MAX_ITERATIONS) - { - direction = 0; - - // calculate next point coordinates - final 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 last good point - return new GeoLocation(gpx, gpy, gpz); - } - } - - return new GeoLocation(gox, goy, goz); - } - - /** - * 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 instance - * @return {@code List} : complete path from nodes - */ - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { - return null; - } - - /** - * Returns the instance of the {@link GeoEngine}. - * @return {@link GeoEngine} : The instance. - */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -954,6 +713,6 @@ public class GeoEngine private static class SingletonHolder { - protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); + protected static final GeoEngine INSTANCE = new GeoEngine(); } -} \ No newline at end of file +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java index 9c7ab9a042..cc76b7523a 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java @@ -20,84 +20,88 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -import org.l2jmobius.gameserver.model.Location; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; +import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; +import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.instancezone.Instance; /** - * @author Hasha + * @author -Nemesiss- */ -final class GeoEnginePathfinding extends GeoEngine +public class GeoEnginePathfinding { - // pre-allocated buffers - private final BufferHolder[] _buffers; + private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); + + private BufferInfo[] _buffers; protected GeoEnginePathfinding() { - super(); - - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - _buffers = new BufferHolder[array.length]; - int count = 0; - for (int i = 0; i < array.length; i++) + try { - final String buf = array[i]; - final String[] args = buf.split("x"); + final String[] array = Config.PATHFIND_BUFFERS.split(";"); - try + _buffers = new BufferInfo[array.length]; + + String buf; + String[] args; + for (int i = 0; i < array.length; i++) { - final 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); + buf = array[i]; + args = buf.split("x"); + if (args.length != 2) + { + throw new Exception("Invalid buffer definition: " + buf); + } + + _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } } - - LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); + catch (Exception e) + { + LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); + throw new Error("CellPathFinding: load aborted"); + } } - @Override - public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + public boolean pathNodesExist(short regionoffset) { - // get origin and check existing geo coords - final int gox = getGeoX(ox); - final int goy = getGeoY(oy); - if (!hasGeoPos(gox, goy)) + return false; + } + + public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) + { + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); + if (!GeoEngine.getInstance().hasGeo(x, y)) { return null; } - - final short goz = getHeightNearest(gox, goy, oz); - - // get target and check existing geo coords - final int gtx = getGeoX(tx); - final int gty = getGeoY(ty); - if (!hasGeoPos(gtx, gty)) + final int gz = GeoEngine.getInstance().getHeight(x, y, z); + final int gtx = GeoEngine.getInstance().getGeoX(tx); + final int gty = GeoEngine.getInstance().getGeoY(ty); + if (!GeoEngine.getInstance().hasGeo(tx, ty)) { return null; } - - final 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)))); + final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz); + final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)))); if (buffer == null) { return null; } - // find path - List path = null; + List path = null; try { - final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); + if (result == null) { return null; @@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine return path; } - // get path list iterator - final ListIterator point = path.listIterator(); + int currentX, currentY, currentZ; + ListIterator middlePoint; - // get node A (origin) - int nodeAx = gox; - int nodeAy = goy; - short nodeAz = goz; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; - // get node B - GeoLocation nodeB = (GeoLocation) point.next(); - - // iterate thought the path to optimize it - int count = 0; - while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) + while (middlePoint.hasNext()) { - // get node C - final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - - // check movement from node A to node C - final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); - if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) { - // can move from node A to node C - - // remove node B - point.remove(); + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) + { + middlePoint.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(); + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); } - - // set node B - nodeB = (GeoLocation) point.next(); } return path; } /** - * Create list of node locations as result of calculated buffer node tree. - * @param node : the entry point - * @return List : list of node location + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position */ - private static List constructPath(Node node) + public short getNodePos(int geo_pos) { - // create empty list - final LinkedList list = new LinkedList<>(); - - // set direction X/Y - int dx = 0; - int dy = 0; - - // get target parent - Node target = node; - Node parent = target.getParent(); - - // while parent exists - int count = 0; - while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { - // 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; + return (short) (geo_pos >> 3); // OK? } /** - * 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 + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) */ - private final NodeBuffer getBuffer(int size) + public short getNodeBlock(int node_pos) { - NodeBuffer current = null; - for (BufferHolder holder : _buffers) + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + World.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 World.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 World.MAP_MIN_Y + (node_y * 128) + 48; + } + + private List constructPath(AbstractNode nodeValue) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + AbstractNode node = nodeValue; + while (node.getParent() != null) { - // Find proper size of buffer - if (holder._size < size) + 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)) { - continue; + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); } - // Find unlocked NodeBuffer - for (NodeBuffer buffer : holder._buffer) + node = node.getParent(); + } + + return path; + } + + private CellNodeBuffer alloc(int size) + { + CellNodeBuffer current = null; + for (BufferInfo i : _buffers) + { + if (i.mapSize >= size) { - if (!buffer.isLocked()) + for (CellNodeBuffer buf : i.buffer) { - continue; + if (buf.lock()) + { + current = buf; + break; + } + } + if (current != null) + { + break; } - return buffer; + // not found, allocate temporary buffer + current = new CellNodeBuffer(i.mapSize); + current.lock(); + if (i.buffer.size() < i.count) + { + i.buffer.add(current); + break; + } } - - // 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 + private static final class BufferInfo { - final int _size; - List _buffer; + final int mapSize; + final int count; + ArrayList buffer; - public BufferHolder(int size, int count) + public BufferInfo(int size, int cnt) { - _size = size; - _buffer = new ArrayList<>(count); - for (int i = 0; i < count; i++) - { - _buffer.add(new NodeBuffer(size)); - } + mapSize = size; + count = cnt; + buffer = new ArrayList<>(count); } } -} \ No newline at end of file + + public static GeoEnginePathfinding getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java deleted file mode 100644 index f97f28b3e4..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java +++ /dev/null @@ -1,197 +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 . - */ -package org.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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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.
- * 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; -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java deleted file mode 100644 index fed251c03c..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java +++ /dev/null @@ -1,252 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - _buffer[index] = nswe; - } - - @Override - public 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); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java deleted file mode 100644 index ee93ee0450..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java +++ /dev/null @@ -1,176 +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 . - */ -package org.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 boolean hasGeoPos() - { - return true; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return _height; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - // check and return height - return _height > worldZ ? _height : Short.MIN_VALUE; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - // check and return height - return _height < worldZ ? _height : Short.MAX_VALUE; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height > worldZ ? _nswe : 0; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - // check height and return nswe - return _height < worldZ ? _nswe : 0; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height > worldZ ? 0 : -1; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return getIndexAbove(geoX, geoY, worldZ); - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - // check height and return index - return _height < worldZ ? 0 : -1; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return getIndexBelow(geoX, geoY, worldZ); - } - - @Override - public short getHeight(int index) - { - return _height; - } - - @Override - public short getHeightOriginal(int index) - { - return _height; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - _nswe = nswe; - } - - @Override - public 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)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java deleted file mode 100644 index e0f36426e8..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java +++ /dev/null @@ -1,465 +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 . - */ -package org.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 - final 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 boolean hasGeoPos() - { - return true; - } - - @Override - public 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 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 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 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 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 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 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 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 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 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 byte getNswe(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public byte getNsweOriginal(int index) - { - // get nswe - return _buffer[index]; - } - - @Override - public void setNswe(int index, byte nswe) - { - // set nswe - _buffer[index] = nswe; - } - - @Override - public 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 - final byte layers = _buffer[index++]; - stream.write(layers); - - // write cell data - stream.write(_buffer, index, layers * 3); - - // move index to next cell - index += layers * 3; - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java deleted file mode 100644 index 4202950b58..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java +++ /dev/null @@ -1,150 +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 . - */ -package org.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 boolean hasGeoPos() - { - return false; - } - - @Override - public short getHeightNearest(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightAbove(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public short getHeightBelow(int geoX, int geoY, int worldZ) - { - return (short) worldZ; - } - - @Override - public byte getNsweNearest(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweAbove(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public byte getNsweBelow(int geoX, int geoY, int worldZ) - { - return _nswe; - } - - @Override - public int getIndexNearest(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAbove(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelow(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) - { - return 0; - } - - @Override - public short getHeight(int index) - { - return 0; - } - - @Override - public short getHeightOriginal(int index) - { - return 0; - } - - @Override - public byte getNswe(int index) - { - return _nswe; - } - - @Override - public byte getNsweOriginal(int index) - { - return _nswe; - } - - @Override - public void setNswe(int index, byte nswe) - { - } - - @Override - public void saveBlock(BufferedOutputStream stream) - { - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java new file mode 100644 index 0000000000..61ea4fcf82 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java new file mode 100644 index 0000000000..3c8de1a7f7 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java new file mode 100644 index 0000000000..1ccdefe3da --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java deleted file mode 100644 index 793b847f35..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java +++ /dev/null @@ -1,39 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author Hasha - */ -public enum GeoFormat -{ - L2J("%d_%d.l2j"), - L2OFF("%d_%d_conv.dat"), - L2D("%d_%d.l2d"); - - private final String _filename; - - private GeoFormat(String filename) - { - _filename = filename; - } - - public String getFilename() - { - return _filename; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java deleted file mode 100644 index d5721a6151..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.model.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java deleted file mode 100644 index 0edc18235e..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ /dev/null @@ -1,70 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public 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 = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); - private static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.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; -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java new file mode 100644 index 0000000000..25eafc5a04 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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); +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java deleted file mode 100644 index 06d2eba7c4..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java +++ /dev/null @@ -1,53 +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 . - */ -package org.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(); -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java new file mode 100644 index 0000000000..0d2c765002 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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(); +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java new file mode 100644 index 0000000000..52df457360 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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 (byte) (layer & 0x000F); + } + + private int _extractLayerHeight(short layer) + { + return ((short) (layer & 0x0fff0)) >> 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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java new file mode 100644 index 0000000000..72a5a635c7 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java new file mode 100644 index 0000000000..fc924cb875 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +import java.nio.ByteBuffer; + +/** + * @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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java new file mode 100644 index 0000000000..5087513ed9 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java new file mode 100644 index 0000000000..7223b5b2be --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java new file mode 100644 index 0000000000..3425234e0e --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java @@ -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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +public class CellNode extends AbstractNode +{ + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java new file mode 100644 index 0000000000..522610bb7c --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.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 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) + { + _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(); + } + + public final List debugPath() + { + final List 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); + } + + // 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; + // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16 + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java deleted file mode 100644 index fb87282d8a..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ /dev/null @@ -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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java deleted file mode 100644 index 8f2586c4fc..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ /dev/null @@ -1,306 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import java.util.concurrent.locks.ReentrantLock; - -import org.l2jmobius.Config; -import org.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; - - 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 Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) - { - // 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 boolean isLocked() - { - return _lock.tryLock(); - } - - public void free() - { - _current = null; - - for (Node[] nodes : _buffer) - { - for (Node node : nodes) - { - if (node.getLoc() != null) - { - node.free(); - } - } - } - - _lock.unlock(); - } - - /** - * Check _current Node and add its neighbors to the buffer. - */ - private final void expand() - { - // can't move anywhere, don't expand - final 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 - final 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 - final 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 - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java new file mode 100644 index 0000000000..e3bad04b25 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; + +/** + * @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 = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoEngine.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 canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoEngine.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoEngine.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 (canGoNorth()) + { + nswe |= Cell.NSWE_NORTH; + } + if (canGoEast()) + { + nswe |= Cell.NSWE_EAST; + } + if (canGoSouth()) + { + nswe |= Cell.NSWE_SOUTH; + } + if (canGoWest()) + { + nswe |= Cell.NSWE_WEST; + } + + result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); + return result; + // return super.hashCode(); + } + + @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; + } +} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java index 36d2cdd61b..fc7d9fe51d 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -64,6 +64,8 @@ import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.geoengine.GeoEngine; +import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; +import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -251,7 +253,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** Movement data of this Creature */ protected MoveData _move; private boolean _cursorKeyMovement = false; - private boolean _cursorKeyMovementActive = true; /** This creature's target. */ private WorldObject _target; @@ -2561,7 +2562,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3033,7 +3034,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe { _move.onGeodataPathIndex = -1; stopMove(getActingPlayer().getLastServerPosition()); - _cursorKeyMovementActive = false; return false; } } @@ -3274,10 +3274,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe double dy = (y - curY); double dz = (z - curZ); double distance = Math.hypot(dx, dy); - if (!_cursorKeyMovementActive && (distance > 200)) - { - return; - } final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0); if (verticalMovementOnly) @@ -3407,7 +3403,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { m.disregardingGeodata = true; @@ -5521,16 +5517,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe _cursorKeyMovement = value; } - public void setCursorKeyMovementActive(boolean value) - { - _cursorKeyMovementActive = value; - } - - public boolean isCursorKeyMovementActive() - { - return _cursorKeyMovementActive; - } - public List getFakePlayerDrops() { return _fakePlayerDrops; diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 8e24ce0392..927dab7075 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -12692,7 +12692,7 @@ public class PlayerInstance extends Playable { if (Config.CORRECT_PLAYER_Z) { - final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); + final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ()); if (getZ() < nearestZ) { teleToLocation(new Location(getX(), getY(), nearestZ)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 0d0d8fb280..c63a8bfcf4 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket // sort of incompatibility fix. // Validate position packets sends head level. _targetZ += player.getTemplate().getCollisionHeight(); - if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ)))) - { - player.setCursorKeyMovementActive(true); - } if (_movementMode == 1) { @@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket return; } player.setCursorKeyMovement(true); - if (!player.isCursorKeyMovementActive()) - { - return; - } } final AdminTeleportType teleMode = player.getTeleMode(); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java index 333dcbf9bf..4600a0fc53 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util; import java.awt.Color; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.geodata.Cell; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** * @author HorridoJoho */ -public class GeoUtils +public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getWorldX(iter.x()); - final int wy = GeoEngine.getWorldY(iter.y()); + final int wx = GeoEngine.getInstance().getWorldX(iter.x()); + final int wy = GeoEngine.getInstance().getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getGeoX(x); - final int gy = GeoEngine.getGeoY(y); + final int gx = GeoEngine.getInstance().getGeoX(x); + final int gy = GeoEngine.getInstance().getGeoY(y); - final int tgx = GeoEngine.getGeoX(tx); - final int tgy = GeoEngine.getGeoY(ty); + final int tgx = GeoEngine.getInstance().getGeoX(tx); + final int tgy = GeoEngine.getInstance().getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().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 = GeoEngine.getWorldX(prevX); - int wy = GeoEngine.getWorldY(prevY); + int wx = GeoEngine.getInstance().getWorldX(prevX); + int wy = GeoEngine.getInstance().getWorldY(prevY); int wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -78,8 +78,8 @@ public class GeoUtils if ((curX != prevX) || (curY != prevY)) { - wx = GeoEngine.getWorldX(curX); - wy = GeoEngine.getWorldY(curY); + wx = GeoEngine.getInstance().getWorldX(curX); + wy = GeoEngine.getInstance().getWorldY(curY); wz = iter.z(); prim.addPoint(Color.RED, wx, wy, wz); @@ -93,7 +93,7 @@ public class GeoUtils private static Color getDirectionColor(int x, int y, int z, int nswe) { - if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) + if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) { return Color.GREEN; } @@ -109,8 +109,9 @@ public class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final int playerGx = GeoEngine.getGeoX(player.getX()); - final int playerGy = GeoEngine.getGeoY(player.getY()); + final GeoEngine ge = GeoEngine.getInstance(); + final int playerGx = ge.getGeoX(player.getX()); + final int playerGy = ge.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -134,32 +135,32 @@ public class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = GeoEngine.getWorldX(gx); - final int y = GeoEngine.getWorldY(gy); - final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); + final int x = ge.getWorldX(gx); + final int y = ge.getWorldY(gy); + final int z = ge.getNearestZ(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); + Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); 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, GeoStructure.CELL_FLAG_E); + col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); 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, GeoStructure.CELL_FLAG_S); + col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); 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, GeoStructure.CELL_FLAG_W); + col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST); 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); @@ -187,42 +188,41 @@ public class GeoUtils { if (y > lastY) { - return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; } else { - return GeoStructure.CELL_FLAG_E; // Direction.EAST; + return Cell.NSWE_EAST; } } else if (x < lastX) // west { if (y > lastY) { - return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; } else { - return GeoStructure.CELL_FLAG_W; // Direction.WEST; + return Cell.NSWE_WEST; } } - else - // unchanged x + else // unchanged x { if (y > lastY) { - return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; + return Cell.NSWE_SOUTH; } else if (y < lastY) { - return GeoStructure.CELL_FLAG_N; // Direction.NORTH; + return Cell.NSWE_NORTH; } else { diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java deleted file mode 100644 index a9f98b02a5..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java +++ /dev/null @@ -1,386 +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 . - */ -package org.l2jmobius.tools.geodataconverter; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Scanner; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.util.PropertiesParser; -import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -import org.l2jmobius.gameserver.model.World; - -/** - * @author Hasha - */ -public class GeoDataConverter -{ - private static GeoFormat _format; - private static ABlock[][] _blocks; - - public static void main(String[] args) - { - // initialize config - loadGeoengineConfigs(); - - // get geodata format - String type = ""; - try (Scanner scn = new Scanner(System.in)) - { - while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) - { - System.out.println("GeoDataConverter: Select source geodata type:"); - System.out.println(" J: L2J (e.g. 23_22.l2j)"); - System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); - System.out.println(" E: Exit"); - System.out.print("Choice: "); - type = scn.next(); - } - } - if (type.equalsIgnoreCase("E")) - { - System.exit(0); - } - - _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; - - // start conversion - System.out.println("GeoDataConverter: Converting all " + _format + " files."); - - // initialize geodata container - _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; - - // initialize multilayer temporarily buffer - BlockMultilayer.initialize(); - - // load geo files - int converted = 0; - for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { - for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { - final String input = String.format(_format.getFilename(), rx, ry); - final String filepath = Config.GEODATA_PATH; - final File f = new File(filepath + input); - if (f.exists() && !f.isDirectory()) - { - // load geodata - if (!loadGeoBlocks(input)) - { - System.out.println("GeoDataConverter: Unable to load " + input + " region file."); - continue; - } - - // recalculate nswe - if (!recalculateNswe()) - { - System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); - continue; - } - - // save geodata - final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); - if (!saveGeoBlocks(output)) - { - System.out.println("GeoDataConverter: Unable to save " + output + " region file."); - continue; - } - - converted++; - System.out.println("GeoDataConverter: Created " + output + " region file."); - } - } - } - System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); - - // release multilayer block temporarily buffer - BlockMultilayer.release(); - } - - /** - * Loads geo blocks from buffer of the region file. - * @param filename : The name of the to load. - * @return boolean : True when successful. - */ - private static boolean loadGeoBlocks(String filename) - { - // region file is load-able, try to load it - try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); - FileChannel fc = raf.getChannel()) - { - final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) - if (_format == GeoFormat.L2OFF) - { - for (int i = 0; i < 18; i++) - { - buffer.get(); - } - } - - // loop over region blocks - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - if (_format == GeoFormat.L2J) - { - // get block type - final byte type = buffer.get(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2J: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - case GeoStructure.TYPE_MULTILAYER_L2J: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - default: - { - throw new IllegalArgumentException("Unknown block type: " + type); - } - } - } - else - { - // get block type - final short type = buffer.getShort(); - - // load block according to block type - switch (type) - { - case GeoStructure.TYPE_FLAT_L2J_L2OFF: - { - _blocks[ix][iy] = new BlockFlat(buffer, _format); - break; - } - case GeoStructure.TYPE_COMPLEX_L2OFF: - { - _blocks[ix][iy] = new BlockComplex(buffer, _format); - break; - } - default: - { - _blocks[ix][iy] = new BlockMultilayer(buffer, _format); - break; - } - } - } - } - } - - if (buffer.remaining() > 0) - { - System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); - return false; - } - - return true; - } - catch (Exception e) - { - System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); - return false; - } - } - - /** - * Recalculate diagonal flags for the region file. - * @return boolean : True when successful. - */ - private static boolean recalculateNswe() - { - try - { - for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) - { - for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) - { - // get block - final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; - - // skip flat blocks - if (block instanceof BlockFlat) - { - continue; - } - - // for complex and multilayer blocks go though all layers - short height = Short.MAX_VALUE; - int index; - while ((index = block.getIndexBelow(x, y, height)) != -1) - { - // get height and nswe - height = block.getHeight(index); - byte nswe = block.getNswe(index); - - // update nswe with diagonal flags - nswe = updateNsweBelow(x, y, height, nswe); - - // set nswe of the cell - block.setNswe(index, nswe); - } - } - } - - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Updates the NSWE flag with diagonal flags. - * @param x : Geodata X coordinate. - * @param y : Geodata Y coordinate. - * @param z : Geodata Z coordinate. - * @param nsweValue : NSWE flag to be updated. - * @return byte : Updated NSWE flag. - */ - private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) - { - byte nswe = nsweValue; - - // calculate virtual layer height - final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); - - // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) - final byte nsweN = getNsweBelow(x, y - 1, height); - final byte nsweS = getNsweBelow(x, y + 1, height); - final byte nsweW = getNsweBelow(x - 1, y, height); - final byte nsweE = getNsweBelow(x + 1, y, height); - - // north-west - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NW; - } - - // north-east - if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_NE; - } - - // south-west - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SW; - } - - // south-east - if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) - { - nswe |= GeoStructure.CELL_FLAG_SE; - } - - return nswe; - } - - private static byte getNsweBelow(int geoX, int geoY, short worldZ) - { - // out of geo coordinates - if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) - { - return 0; - } - - // out of geo coordinates - if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) - { - return 0; - } - - // get block - final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; - - // get index, when valid, return nswe - final int index = block.getIndexBelow(geoX, geoY, worldZ); - return index == -1 ? 0 : block.getNswe(index); - } - - /** - * Save region file to file. - * @param filename : The name of file to save. - * @return boolean : True when successful. - */ - private static boolean saveGeoBlocks(String filename) - { - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) - { - // loop over region blocks and save each block - for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { - for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) - { - _blocks[ix][iy].saveBlock(bos); - } - } - - // flush data to file - bos.flush(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private static void loadGeoengineConfigs() - { - final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); - Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); - Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); - Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); - Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); - Config.PATHFINDING = geoData.getBoolean("PathFinding", true); - Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); - Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); - Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); - Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); - Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/readme.txt b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/readme.txt index e370417a39..ca05703000 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/readme.txt +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/readme.txt @@ -4,7 +4,8 @@ System: https://mega.nz/file/xl1njaTY#BdVHadGs9bTDyrDE3QgBGvAqkmqSqPtn8hLpcCil1F JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip -Geodata: http://www.mediafire.com/file/d1o5j1871mfgk5j/L2J_Mobius_Essence_4.0_DwellingOfSpirits_Geodata.zip +Geodata: http://www.mediafire.com/file/ctithbrur1cevrd/L2J_Mobius_Essence_4.0_DwellingOfSpirits_Geodata.zip + Saviors: https://eu.4game.com/lineage2classic/play/saviors/ -Classic packet compatibility @@ -119,7 +120,7 @@ Fluffy Reinforcement: https://eu.4game.com/patchnotes/lineage2essence/204/ -Teleport favorites system Dwelling of Spirits: https://eu.4game.com/patchnotes/lineage2essence/261/ --Login support. +-Login support Customs: -Newbie Helper NPC location info