Map data parser and utilities algorithms for maps and cells.
For installing using maven, add this dependency into the pom.xml
:
<dependency>
<groupId>fr.arakne</groupId>
<artifactId>arakne-map</artifactId>
<version>0.8-alpha</version>
</dependency>
The first step for use Dofus map is to parse the map data to extract the cells information.
// Create the serializer
MapDataSerializer serializer = new DefaultMapDataSerializer();
// A cell cache can also be enabled to improve performance when loading multiple maps (like on a server)
DefaultMapDataSerializer serializer = new DefaultMapDataSerializer();
serializer.enableCache();
// Parse the cells data
CellData[] data = serializer.deserialize(mapEntity.mapData());
data[15].layer2().interactive(); // Check if the cell 15 has an interactive object
// Serialize map data
String mapData = serializer.serialize(data);
Encrypted cells are also supported, using the EncryptedMapDataSerializer :
DefaultMapDataSerializer serializer = new DefaultMapDataSerializer();
// A key is given on the GDM packet : use this key to decrypt map
if (!gdm.key().isEmpty()) {
// Deserialize the map data
CellData[] data = serializer.withKey(gdm.key()).deserialize(encryptedMapData);
} else {
// No key : simply parse the map
CellData[] data = serializer.deserialize(encryptedMapData);
}
To use map algorithms and utilities, the maps must implement the map interfaces.
Implements the MapCell :
// Simply extends AbstractCellDataAdapter.
// For custom implementation, implements MapCell instead.
public class MyCell extends AbstractCellDataAdapter<MyMap, MyCell> {
public MyCell(MyMap map, CellData data, int id) {
super(map, data, id);
}
// Implements other methods here...
}
Implements the DofusMap :
// Must implements DofusMap with the cell type as template parameter
public class MyMap implements DofusMap<MyCell> {
private final int id;
private final MyCell[] cell;
private final Dimensions dimensions;
// Other map fields...
public MyMap(int id, CellData[] data, Dimensions dimensions) {
this.id = id;
this.dimensions = dimensions;
this.cells = createCells(data);
}
@Override
public int size() {
return cells.length;
}
@Override
public Dimensions dimensions() {
return dimensions;
}
@Override
public MyCell get(int id) {
return dimensions;
}
// Other map methods...
/**
* Create cells from map data
*/
private MyCell[] createCells(CellData[] data) {
// The cells count is same as data length
MyCell[] cells = new MyCell[data.length];
// Wrap all cell data into a cell
for (int cellId = 0; cellId < data.length; ++cellId) {
cells[cellId] = new MyCell(this, data[cellId], id);
}
return cells;
}
}
Now, you can use the cell data to create the dofus map :
// Parse the map data
MapDataSerializer serializer = new DefaultMapDataSerizlizer();
CellData[] data = serializer.deserialize(entity.mapData());
// Create the map instance
MyMap map = new MyMap(entity.id(), data, entity.dimensions());
map.get(15).walkable(); // Check if the cell 15 is walkable
Once the map is created you can compute movement path on it :
// Decorate the map with the Decoder
Decoder<MyCell> decoder = new Decoder<>(map);
// decode the movement game action
// Server side : the start cell is not sent by the client,
// so it must be provided on the decode method
Path<MyCell> path = decoder.decode(gameAction.argument(), character.cell());
// Client side : because the first cell is on the path, only the path should be provider
Path<MyCell> path = decoder.decode(gameAction.argument());
// Now, you can validate the path or perform operations on it
path.target(); // Get the target cell
path = path.keepWhile(step -> step.cell().walkable()); // Truncate the path at the first unwalkable cell
// You can encode the path for sending to the client or server
path.encodeWithStartCell(); // Encode the path and force to set the start cell. This is required by the client.
path.encode(); // Simple encode the path. This is the method used by the client to send to the server.
You can also generate a path using the pathfinder :
// Get the pathfinder for the map
Decoder<MyCell> decoder = new Decoder<>(map);
Pathfinder<MyCell> pathfinder = decoder.pathfinder();
// Find the path
Path<MyCell> path = pathfinder.findPath(character.cell(), targetCell);
// You can configure the pathfinding algorithm :
pathfinding
.targetDistance(5) // Stop at 5 cells of the target
.addFirstCell(false) // Do not add the first cell (for client to server)
.findPath(character.cell(), targetCell)
;
Helper class for get cell coordinates (i.e. x, y) and some utilities methods.
MyMap map = getMap();
// Decorate the cell 15 with CoordinateCell
CoordinateCell<MyCell> cell15 = new CoordinateCell<>(map.get(15));
// Or use the helper method on the cell (preferred way : this method allows to cache the CoordinateCell instance)
CoordinateCell<MyCell> cell15 = map.get(15).coordinate();
// Get coordinates
cell15.x();
cell15.y();
CoordinateCell<MyCell> target = new CoordinateCell<>(map.get(42));
cell15.directionTo(target); // Compute the direction between cells 15 and 42
cell15.distance(target); // Compute the distance between the two cells
Helper for check if a cell is accessible following the line of sight.
Note: To works, the cells must implement BattlefieldCell.
MyMap map = getMap();
// Decorate the map
BattlefieldSight<MyMap> mapSight = new BattlefieldSight<>(map);
// Check the line of sight between two cells
if (mapSight.between(fighter.cell(), map.get(targetCell))) {
// The target cell is accessible from the fighter cell
} else {
// Error : cell is not accessible
}
// Same as above
if (mapSight.from(fighter.cell()).isFree(map.get(targetCell))) {
// The target cell is accessible from the fighter cell
} else {
// Error : cell is not accessible
}
// You can also use helper method on cell objects :
if (fighter.cell().sight().isFree(map.get(targetCell))) {
// ...
}
// Get all accessible cells from the current cell :
for (MyCell cell : mapSight.from(fighter.cell()).available()) {
// All cells are accessible
}
See:
- Direction : Enum of all available direction with extract information
- CellMovement : Enum of cell movement values