Skip to content

Commit

Permalink
Merge pull request #5050 from kwvanderlinde/feature/3485-wall-based-t…
Browse files Browse the repository at this point in the history
…opology

Add wall-based topology
  • Loading branch information
cwisniew authored Nov 28, 2024
2 parents 5024111 + 09d1015 commit 7f317c8
Show file tree
Hide file tree
Showing 68 changed files with 3,518 additions and 1,135 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,8 @@ dependencies {
implementation 'com.github.jknack:handlebars:4.3.1'
implementation 'com.github.jknack:handlebars-helpers:4.3.1'

implementation 'org.jgrapht:jgrapht-core:1.5.2'


// Built In Add-on Libraries
implementation 'com.github.RPTools:maptool-builtin-addons:1.3'
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/rptools/lib/CodeTimer.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ public static <Ex extends Exception> void using(String name, TimedSection<Ex> ca

stack.addLast(timer);
try {
timer.start("<root>");
callback.call(timer);
} finally {
timer.stop("<root>");

final var lastTimer = stack.removeLast();
assert lastTimer == timer : "Timer stack is corrupted";

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/net/rptools/lib/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import net.rptools.maptool.client.ui.token.BarTokenOverlay;
import net.rptools.maptool.model.AStarCellPointConverter;
import net.rptools.maptool.model.ShapeType;
import net.rptools.maptool.model.converters.WallTopologyConverter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -484,6 +485,7 @@ public static XStream getConfiguredXStream() {
XStream.setupDefaultSecurity(xStream);
xStream.allowTypesByWildcard(new String[] {"net.rptools.**", "java.awt.**", "sun.awt.**"});
xStream.registerConverter(new AStarCellPointConverter());
xStream.registerConverter(new WallTopologyConverter(xStream));
xStream.addImmutableType(ShapeType.class, true);
xStream.addImmutableType(BarTokenOverlay.Side.class, true);
return xStream;
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/net/rptools/lib/GeometryUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,12 @@ public static Collection<Polygon> toJtsPolygons(Area area) {

return toPolygonizer(area).getPolygons();
}

public static Point2D coordinateToPoint2D(Coordinate coordinate) {
return new Point2D.Double(coordinate.getX(), coordinate.getY());
}

public static Coordinate point2DToCoordinate(Point2D point2D) {
return new Coordinate(point2D.getX(), point2D.getY());
}
}
3 changes: 3 additions & 0 deletions src/main/java/net/rptools/maptool/client/AppStyle.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public class AppStyle {
public static Color selectionBoxFill = Color.blue;
public static Color resizeBoxOutline = Color.red;
public static Color resizeBoxFill = Color.yellow;
public static Color wallTopologyColor = new Color(255, 182, 0, 255);
public static Color wallTopologyOutlineColor = Color.black;
public static Color selectedWallTopologyColor = new Color(255, 136, 0, 255);
public static Color topologyColor = new Color(0, 0, 255, 128);
public static Color topologyAddColor = new Color(255, 0, 0, 128);
public static Color topologyRemoveColor = new Color(255, 255, 255, 128);
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/net/rptools/maptool/client/ClientMessageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import net.rptools.maptool.model.library.addon.AddOnLibraryImporter;
import net.rptools.maptool.model.library.addon.TransferableAddOnLibrary;
import net.rptools.maptool.model.player.Player;
import net.rptools.maptool.model.topology.WallTopology;
import net.rptools.maptool.model.zones.TokensAdded;
import net.rptools.maptool.model.zones.TokensRemoved;
import net.rptools.maptool.model.zones.ZoneAdded;
Expand Down Expand Up @@ -101,7 +102,7 @@ public void handleMessage(String id, byte[] message) {
log.debug("{} got: {}", id, msgType);

switch (msgType) {
case UPDATE_TOPOLOGY_MSG -> handle(msg.getUpdateTopologyMsg());
case UPDATE_MASK_TOPOLOGY_MSG -> handle(msg.getUpdateMaskTopologyMsg());
case BOOT_PLAYER_MSG -> handle(msg.getBootPlayerMsg());
case CHANGE_ZONE_DISPLAY_NAME_MSG -> handle(msg.getChangeZoneDisplayNameMsg());
case CLEAR_ALL_DRAWINGS_MSG -> handle(msg.getClearAllDrawingsMsg());
Expand Down Expand Up @@ -169,6 +170,7 @@ public void handleMessage(String id, byte[] message) {
case UPDATE_EXPOSED_AREA_META_MSG -> handle(msg.getUpdateExposedAreaMetaMsg());
case UPDATE_TOKEN_MOVE_MSG -> handle(msg.getUpdateTokenMoveMsg());
case UPDATE_PLAYER_STATUS_MSG -> handle(msg.getUpdatePlayerStatusMsg());
case SET_WALL_TOPOLOGY_MSG -> handle(msg.getSetWallTopologyMsg());
default -> log.warn(msgType + "not handled.");
}
log.debug(id + " handled: " + msgType);
Expand Down Expand Up @@ -1015,16 +1017,16 @@ private void handle(ChangeZoneDisplayNameMsg changeZoneDisplayNameMsg) {
});
}

private void handle(UpdateTopologyMsg updateTopologyMsg) {
private void handle(UpdateMaskTopologyMsg updateMaskTopologyMsg) {
EventQueue.invokeLater(
() -> {
var zoneGUID = GUID.valueOf(updateTopologyMsg.getZoneGuid());
var area = Mapper.map(updateTopologyMsg.getArea());
var erase = updateTopologyMsg.getErase();
var topologyType = Zone.TopologyType.valueOf(updateTopologyMsg.getType().name());
var zoneGUID = GUID.valueOf(updateMaskTopologyMsg.getZoneGuid());
var area = Mapper.map(updateMaskTopologyMsg.getArea());
var erase = updateMaskTopologyMsg.getErase();
var topologyType = Zone.TopologyType.valueOf(updateMaskTopologyMsg.getType().name());

var zone = client.getCampaign().getZone(zoneGUID);
zone.updateTopology(area, erase, topologyType);
zone.updateMaskTopology(area, erase, topologyType);
});
}

Expand Down Expand Up @@ -1060,4 +1062,14 @@ private void handle(UpdatePlayerStatusMsg updatePlayerStatusMsg) {
final var eventBus = new MapToolEventBus().getMainEventBus();
eventBus.post(new PlayerStatusChanged(player));
}

private void handle(SetWallTopologyMsg setWallTopologyMsg) {
EventQueue.invokeLater(
() -> {
var zoneId = new GUID(setWallTopologyMsg.getZoneGuid());
var zone = client.getCampaign().getZone(zoneId);
var topology = WallTopology.fromDto(setWallTopologyMsg.getTopology());
zone.replaceWalls(topology);
});
}
}
25 changes: 8 additions & 17 deletions src/main/java/net/rptools/maptool/client/ScreenPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public ScreenPoint(double x, double y) {
super(x, y);
}

public static Point2D.Double convertToZone2d(ZoneRenderer renderer, double x, double y) {
double scale = renderer.getScale();
return new Point2D.Double(
(x - renderer.getViewOffsetX()) / scale, (y - renderer.getViewOffsetY()) / scale);
}

/**
* Translate the point from screen x,y to zone x,y.
*
Expand All @@ -33,23 +39,8 @@ public ScreenPoint(double x, double y) {
* @return the {@link ZonePoint} representing the screen point.
*/
public static ZonePoint convertToZone(ZoneRenderer renderer, double x, double y) {
double scale = renderer.getScale();

double zX = x;
double zY = y;

// Translate
zX -= renderer.getViewOffsetX();
zY -= renderer.getViewOffsetY();

// Scale
zX = (int) Math.floor(zX / scale);
zY = (int) Math.floor(zY / scale);

// System.out.println("s:" + scale + " x:" + x + " zx:" + zX + " c:" + (zX / scale) + " - " +
// Math.floor(zX / scale));

return new ZonePoint((int) zX, (int) zY);
var doublePrecision = convertToZone2d(renderer, x, y);
return new ZonePoint((int) Math.floor(doublePrecision.x), (int) Math.floor(doublePrecision.y));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import net.rptools.maptool.model.gamedata.proto.GameDataValueDto;
import net.rptools.maptool.model.library.addon.TransferableAddOnLibrary;
import net.rptools.maptool.model.player.Player;
import net.rptools.maptool.model.topology.WallTopology;
import net.rptools.maptool.server.Mapper;
import net.rptools.maptool.server.ServerCommand;
import net.rptools.maptool.server.ServerMessageHandler;
Expand Down Expand Up @@ -414,18 +415,28 @@ public void toggleTokenMoveWaypoint(GUID zoneGUID, GUID tokenGUID, ZonePoint cp)
makeServerCall(Message.newBuilder().setToggleTokenMoveWaypointMsg(msg).build());
}

public void replaceWalls(Zone zone, WallTopology walls) {
zone.replaceWalls(walls);
var msg =
SetWallTopologyMsg.newBuilder()
.setZoneGuid(zone.getId().toString())
.setTopology(walls.toDto());
makeServerCall(Message.newBuilder().setSetWallTopologyMsg(msg).build());
}

@Override
public void updateTopology(Zone zone, Area area, boolean erase, Zone.TopologyType topologyType) {
public void updateMaskTopology(
Zone zone, Area area, boolean erase, Zone.TopologyType topologyType) {
var msg =
UpdateTopologyMsg.newBuilder()
UpdateMaskTopologyMsg.newBuilder()
.setZoneGuid(zone.getId().toString())
.setArea(Mapper.map(area))
.setErase(erase)
.setType(TopologyTypeDto.valueOf(topologyType.name()));

// Update locally as well.
zone.updateTopology(area, erase, topologyType);
makeServerCall(Message.newBuilder().setUpdateTopologyMsg(msg).build());
zone.updateMaskTopology(area, erase, topologyType);
makeServerCall(Message.newBuilder().setUpdateMaskTopologyMsg(msg).build());
}

public void exposePCArea(GUID zoneGUID) {
Expand Down Expand Up @@ -652,16 +663,16 @@ public void toggleLightSourceOnToken(Token token, boolean toggleOn, LightSource
.build());
}

@Override
public void setTokenTopology(Token token, @Nullable Area area, Zone.TopologyType topologyType) {
public void setTokenMaskTopology(
Token token, @Nullable Area area, Zone.TopologyType topologyType) {
if (area == null) {
// Will be converted back to null on the other end.
area = new Area();
}

updateTokenProperty(
token,
Token.Update.setTopology,
Token.Update.setMaskTopology,
TokenPropertyValueDto.newBuilder().setTopologyType(topologyType.name()).build(),
TokenPropertyValueDto.newBuilder().setArea(Mapper.map(area)).build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,12 @@ public double getDistance(Token source, Token target, boolean units, String metr
? new AStarSquareEuclideanWalker(zone, wmetric)
: grid.createZoneWalker();

for (CellPoint scell : sourceCells) {
for (CellPoint tcell : targetCells) {
walker.setWaypoints(scell, tcell);
distance = Math.min(distance, walker.getDistance());
try (walker) {
for (CellPoint scell : sourceCells) {
for (CellPoint tcell : targetCells) {
walker.setWaypoints(scell, tcell);
distance = Math.min(distance, walker.getDistance());
}
}
}
if (!units) distance /= zone.getUnitsPerCell();
Expand Down Expand Up @@ -381,7 +383,6 @@ public double getDistance(
try {
WalkerMetric wmetric = WalkerMetric.valueOf(metric);
walker = new AStarSquareEuclideanWalker(zone, wmetric);

} catch (IllegalArgumentException e) {
throw new ParserException(
I18N.getText("macro.function.getDistance.invalidMetric", metric));
Expand All @@ -392,9 +393,11 @@ public double getDistance(

// Get the distances from each source to target cell and keep the minimum one
double distance = Double.MAX_VALUE;
for (CellPoint scell : sourceCells) {
walker.setWaypoints(scell, targetCell);
distance = Math.min(distance, walker.getDistance());
try (walker) {
for (CellPoint scell : sourceCells) {
walker.setWaypoints(scell, targetCell);
distance = Math.min(distance, walker.getDistance());
}
}

if (units) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,6 @@ public static List<Token> callForIndividualTokenMoveVetoes(

private String getMovement(
final Token source, boolean returnFractionOnly, boolean useTerrainModifiers) {
ZoneWalker walker = null;

WalkerMetric metric =
MapTool.isPersonalServer()
? AppPreferences.movementMetric.get()
Expand All @@ -503,12 +501,14 @@ private String getMovement(
if (zone.getGrid().getCapabilities().isPathingSupported()) {
var firstPoint = cellPath.getFirst();
List<CellPoint> cplist = new ArrayList<CellPoint>();
walker = grid.createZoneWalker();
walker.replaceLastWaypoint(new CellPoint(firstPoint.x, firstPoint.y));
for (AbstractPoint point : cellPath) {
CellPoint tokenPoint = new CellPoint(point.x, point.y);
walker.replaceLastWaypoint(tokenPoint);
cplist.add(tokenPoint);

try (ZoneWalker walker = grid.createZoneWalker()) {
walker.replaceLastWaypoint(new CellPoint(firstPoint.x, firstPoint.y));
for (AbstractPoint point : cellPath) {
CellPoint tokenPoint = new CellPoint(point.x, point.y);
walker.replaceLastWaypoint(tokenPoint);
cplist.add(tokenPoint);
}
}

double bar =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ private void childEvaluateDrawEraseTopology(String functionName, List<Object> pa
default -> null;
};
if (newArea != null) {
MapTool.serverCommand().updateTopology(renderer.getZone(), newArea, erase, topologyType);
MapTool.serverCommand()
.updateMaskTopology(renderer.getZone(), newArea, erase, topologyType);
}
}
}
Expand Down Expand Up @@ -349,7 +350,7 @@ private Object childEvaluateGetTopology(String functionName, List<Object> parame
Area topologyArea = new Area();
for (int i = 0; i < topologyArray.size(); i++) {
JsonObject topologyObject = topologyArray.get(i).getAsJsonObject();
Area tempTopologyArea = getTopology(renderer, topologyObject, topologyType, functionName);
Area tempTopologyArea = getMaskTopology(renderer, topologyObject, topologyType, functionName);
topologyArea.add(tempTopologyArea);
}

Expand Down Expand Up @@ -405,7 +406,7 @@ private JsonArray childEvaluateGetTokenTopology(
}

JsonArray allShapes = new JsonArray();
Area topologyArea = token.getTopology(topologyType);
Area topologyArea = token.getMaskTopology(topologyType);
if (topologyArea != null) {
var areaShape = getAreaShapeObject(topologyArea);
if (areaShape != null) {
Expand Down Expand Up @@ -515,7 +516,7 @@ private int childEvaluateSetTokenTopology(
}
}
// Replace with new topology
MapTool.serverCommand().setTokenTopology(token, tokenTopology, topologyType);
MapTool.serverCommand().setTokenMaskTopology(token, tokenTopology, topologyType);

return results;
}
Expand Down Expand Up @@ -594,21 +595,21 @@ private void childEvaluateTransferTopology(

Zone zone = MapTool.getFrame().getCurrentZoneRenderer().getZone();
if (topologyFromToken) {
var newMapTopology = token.getTransformedTopology(topologyType);
var newMapTopology = token.getTransformedMaskTopology(topologyType);
if (newMapTopology != null) {
MapTool.serverCommand().updateTopology(zone, newMapTopology, false, topologyType);
MapTool.serverCommand().updateMaskTopology(zone, newMapTopology, false, topologyType);
}
if (delete) {
MapTool.serverCommand().setTokenTopology(token, null, topologyType);
MapTool.serverCommand().setTokenMaskTopology(token, null, topologyType);
}
} else {
Area topology = TokenVBL.getTopology_underToken(zone, token, topologyType);

MapTool.serverCommand()
.setTokenTopology(
.setTokenMaskTopology(
token, TokenVBL.transformTopology_toToken(zone, token, topology), topologyType);
if (delete) {
MapTool.serverCommand().updateTopology(zone, topology, true, topologyType);
MapTool.serverCommand().updateMaskTopology(zone, topology, true, topologyType);
}
}
}
Expand Down Expand Up @@ -1065,7 +1066,7 @@ private void applyScale(
* @return the topology area.
* @throws ParserException If the minimum required parameters are not present in the JSON.
*/
private Area getTopology(
private Area getMaskTopology(
ZoneRenderer renderer,
JsonObject topologyObject,
Zone.TopologyType topologyType,
Expand Down Expand Up @@ -1154,7 +1155,7 @@ private Area getTopology(

// Note: when multiple modes are requested, the overlap between each topology is returned.
var zone = renderer.getZone();
var topology = zone.getTopology(topologyType);
var topology = zone.getMaskTopology(topologyType);
area.intersect(topology);

return area;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ public TopologyModeSelectionPanel() {
this.add(Box.createHorizontalStrut(5));
}

@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);

if (enabled) {
for (var button : modeButtons.values()) {
button.setEnabled(true);
}
} else {
for (var button : modeButtons.values()) {
button.setEnabled(false);
}
}
}

private void createAndAddModeButton(
Zone.TopologyType type,
final Icons icon,
Expand Down
Loading

0 comments on commit 7f317c8

Please sign in to comment.