diff --git a/src/main/java/net/rptools/maptool/client/ClientMessageHandler.java b/src/main/java/net/rptools/maptool/client/ClientMessageHandler.java index 34d8031a18..29a1eab816 100644 --- a/src/main/java/net/rptools/maptool/client/ClientMessageHandler.java +++ b/src/main/java/net/rptools/maptool/client/ClientMessageHandler.java @@ -101,7 +101,7 @@ public void handleMessage(String id, byte[] message) { log.debug("{} got: {}", id, msgType); switch (msgType) { - case ADD_TOPOLOGY_MSG -> handle(msg.getAddTopologyMsg()); + case UPDATE_TOPOLOGY_MSG -> handle(msg.getUpdateTopologyMsg()); case BOOT_PLAYER_MSG -> handle(msg.getBootPlayerMsg()); case CHANGE_ZONE_DISPLAY_NAME_MSG -> handle(msg.getChangeZoneDisplayNameMsg()); case CLEAR_ALL_DRAWINGS_MSG -> handle(msg.getClearAllDrawingsMsg()); @@ -128,7 +128,6 @@ public void handleMessage(String id, byte[] message) { case REMOVE_LABEL_MSG -> handle(msg.getRemoveLabelMsg()); case REMOVE_TOKEN_MSG -> handle(msg.getRemoveTokenMsg()); case REMOVE_TOKENS_MSG -> handle(msg.getRemoveTokensMsg()); - case REMOVE_TOPOLOGY_MSG -> handle(msg.getRemoveTopologyMsg()); case REMOVE_ZONE_MSG -> handle(msg.getRemoveZoneMsg()); case RENAME_ZONE_MSG -> handle(msg.getRenameZoneMsg()); case RESTORE_ZONE_VIEW_MSG -> handle(msg.getRestoreZoneViewMsg()); @@ -701,20 +700,6 @@ private void handle(RemoveZoneMsg msg) { }); } - private void handle(RemoveTopologyMsg msg) { - EventQueue.invokeLater( - () -> { - var zoneGUID = GUID.valueOf(msg.getZoneGuid()); - var area = Mapper.map(msg.getArea()); - var topologyType = Zone.TopologyType.valueOf(msg.getType().name()); - - var zone = client.getCampaign().getZone(zoneGUID); - zone.removeTopology(area, topologyType); - - MapTool.getFrame().getZoneRenderer(zoneGUID).repaint(); - }); - } - private void handle(RemoveTokensMsg msg) { EventQueue.invokeLater( () -> { @@ -1018,17 +1003,16 @@ private void handle(ChangeZoneDisplayNameMsg changeZoneDisplayNameMsg) { }); } - private void handle(AddTopologyMsg addTopologyMsg) { + private void handle(UpdateTopologyMsg updateTopologyMsg) { EventQueue.invokeLater( () -> { - var zoneGUID = GUID.valueOf(addTopologyMsg.getZoneGuid()); - var area = Mapper.map(addTopologyMsg.getArea()); - var topologyType = Zone.TopologyType.valueOf(addTopologyMsg.getType().name()); + 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 zone = client.getCampaign().getZone(zoneGUID); - zone.addTopology(area, topologyType); - - MapTool.getFrame().getZoneRenderer(zoneGUID).repaint(); + zone.updateTopology(area, erase, topologyType); }); } diff --git a/src/main/java/net/rptools/maptool/client/ServerCommandClientImpl.java b/src/main/java/net/rptools/maptool/client/ServerCommandClientImpl.java index e1dbb46f86..bdcf998b22 100644 --- a/src/main/java/net/rptools/maptool/client/ServerCommandClientImpl.java +++ b/src/main/java/net/rptools/maptool/client/ServerCommandClientImpl.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.Nullable; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.functions.ExecFunction; import net.rptools.maptool.client.functions.MacroLinkFunction; @@ -392,23 +393,18 @@ public void toggleTokenMoveWaypoint(GUID zoneGUID, GUID tokenGUID, ZonePoint cp) makeServerCall(Message.newBuilder().setToggleTokenMoveWaypointMsg(msg).build()); } - public void addTopology(GUID zoneGUID, Area area, Zone.TopologyType topologyType) { - var msg = - AddTopologyMsg.newBuilder() - .setZoneGuid(zoneGUID.toString()) - .setType(TopologyTypeDto.valueOf(topologyType.name())) - .setArea(Mapper.map(area)); - - makeServerCall(Message.newBuilder().setAddTopologyMsg(msg).build()); - } - - public void removeTopology(GUID zoneGUID, Area area, Zone.TopologyType topologyType) { + @Override + public void updateTopology(Zone zone, Area area, boolean erase, Zone.TopologyType topologyType) { var msg = - RemoveTopologyMsg.newBuilder() - .setZoneGuid(zoneGUID.toString()) + UpdateTopologyMsg.newBuilder() + .setZoneGuid(zone.getId().toString()) .setArea(Mapper.map(area)) + .setErase(erase) .setType(TopologyTypeDto.valueOf(topologyType.name())); - makeServerCall(Message.newBuilder().setRemoveTopologyMsg(msg).build()); + + // Update locally as well. + zone.updateTopology(area, erase, topologyType); + makeServerCall(Message.newBuilder().setUpdateTopologyMsg(msg).build()); } public void exposePCArea(GUID zoneGUID) { @@ -623,6 +619,20 @@ public void removeData(String type, String namespace, String name) { makeServerCall(Message.newBuilder().setRemoveDataMsg(msg).build()); } + @Override + public void setTokenTopology(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, + TokenPropertyValueDto.newBuilder().setTopologyType(topologyType.name()).build(), + TokenPropertyValueDto.newBuilder().setArea(Mapper.map(area)).build()); + } + @Override public void updateTokenProperty(Token token, Token.Update update, int value) { updateTokenProperty( @@ -757,16 +767,6 @@ public void updateTokenProperty( TokenPropertyValueDto.newBuilder().setIntValue(value3).build()); } - @Override - public void updateTokenProperty( - Token token, Token.Update update, Zone.TopologyType topologyType, Area area) { - updateTokenProperty( - token, - update, - TokenPropertyValueDto.newBuilder().setTopologyType(topologyType.name()).build(), - TokenPropertyValueDto.newBuilder().setArea(Mapper.map(area)).build()); - } - @Override public void updateTokenProperty(Token token, Token.Update update, String value1, boolean value2) { updateTokenProperty( diff --git a/src/main/java/net/rptools/maptool/client/functions/Topology_Functions.java b/src/main/java/net/rptools/maptool/client/functions/Topology_Functions.java index c25326b9a0..a7e208d2f5 100644 --- a/src/main/java/net/rptools/maptool/client/functions/Topology_Functions.java +++ b/src/main/java/net/rptools/maptool/client/functions/Topology_Functions.java @@ -290,7 +290,7 @@ private void childEvaluateDrawEraseTopology(String functionName, List pa default -> null; }; if (newArea != null) { - TokenVBL.renderTopology(renderer, newArea, erase, topologyType); + MapTool.serverCommand().updateTopology(renderer.getZone(), newArea, erase, topologyType); } } } @@ -515,8 +515,7 @@ private int childEvaluateSetTokenTopology( } } // Replace with new topology - MapTool.serverCommand() - .updateTokenProperty(token, Token.Update.setTopology, topologyType, tokenTopology); + MapTool.serverCommand().setTokenTopology(token, tokenTopology, topologyType); return results; } @@ -524,7 +523,6 @@ private int childEvaluateSetTokenTopology( private void childEvaluateTransferTopology( VariableResolver resolver, String functionName, List parameters) throws ParserException { - ZoneRenderer renderer = MapTool.getFrame().getCurrentZoneRenderer(); Token token = null; Zone.TopologyType topologyType; @@ -594,18 +592,23 @@ private void childEvaluateTransferTopology( } } + Zone zone = MapTool.getFrame().getCurrentZoneRenderer().getZone(); if (topologyFromToken) { - TokenVBL.renderTopology( - renderer, token.getTransformedTopology(topologyType), false, topologyType); + var newMapTopology = token.getTransformedTopology(topologyType); + if (newMapTopology != null) { + MapTool.serverCommand().updateTopology(zone, newMapTopology, false, topologyType); + } if (delete) { - token.setTopology(topologyType, null); + MapTool.serverCommand().setTokenTopology(token, null, topologyType); } } else { - Area topology = TokenVBL.getTopology_underToken(renderer, token, topologyType); - token.setTopology( - topologyType, TokenVBL.getMapTopology_transformed(renderer, token, topologyType)); + Area topology = TokenVBL.getTopology_underToken(zone, token, topologyType); + + MapTool.serverCommand() + .setTokenTopology( + token, TokenVBL.transformTopology_toToken(zone, token, topology), topologyType); if (delete) { - TokenVBL.renderTopology(renderer, topology, true, topologyType); + MapTool.serverCommand().updateTopology(zone, topology, true, topologyType); } } } diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/CrossTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/CrossTopologyTool.java index 226c75fe81..e7a6ac75a9 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/CrossTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/CrossTopologyTool.java @@ -91,18 +91,9 @@ public void mousePressed(MouseEvent e) { GraphicsUtil.createLine(1, new Point2D.Double(x1, y1), new Point2D.Double(x2, y2)); area.add( GraphicsUtil.createLine(1, new Point2D.Double(x1, y2), new Point2D.Double(x2, y1))); + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - // TODO: send this to the server cross = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/DiamondTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/DiamondTopologyTool.java index f8c77dc832..20a46db53b 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/DiamondTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/DiamondTopologyTool.java @@ -79,18 +79,8 @@ public void mousePressed(MouseEvent e) { return; } Area area = new ShapeDrawable(diamond, false).getArea(getZone()); - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - // TODO: send this to the server - + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); diamond = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowDiamondTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowDiamondTopologyTool.java index 2b56df06c8..cdf7d9bc8e 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowDiamondTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowDiamondTopologyTool.java @@ -78,18 +78,8 @@ public void mousePressed(MouseEvent e) { return; } Area area = new Area(diamond); - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - // TODO: send this to the server - + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); diamond = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowOvalTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowOvalTopologyTool.java index 722e1cd1a3..93076a42bf 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowOvalTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowOvalTopologyTool.java @@ -95,17 +95,8 @@ public void mousePressed(MouseEvent e) { area.subtract(innerArea); } - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); oval = null; } diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowRectangleTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowRectangleTopologyTool.java index eff224ca08..4cdba8717e 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/HollowRectangleTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/HollowRectangleTopologyTool.java @@ -89,17 +89,8 @@ public void mousePressed(MouseEvent e) { new Area(new java.awt.Rectangle(x1 + 1, y1 + 1, x2 - x1 - 2, y2 - y1 - 2)); area.subtract(innerArea); } - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - // TODO: send this to the server + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); rectangle = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/OvalTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/OvalTopologyTool.java index 32b994ac70..50293f702f 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/OvalTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/OvalTopologyTool.java @@ -83,16 +83,8 @@ public void mousePressed(MouseEvent e) { oval.getEndPoint().y, 10); - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); oval = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/PolygonTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/PolygonTopologyTool.java index 463d78e6c0..c4eb1e4f45 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/PolygonTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/PolygonTopologyTool.java @@ -97,14 +97,8 @@ protected void completeDrawable(Pen pen, Drawable drawable) { } else { area = new Area(((ShapeDrawable) drawable).getShape()); } - if (pen.isEraser()) { - getZone().removeTopology(area); - MapTool.serverCommand().removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand().addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); + MapTool.serverCommand() + .updateTopology(getZone(), area, pen.isEraser(), getZone().getTopologyTypes()); } @Override diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/RectangleTopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/RectangleTopologyTool.java index 8bf5a95d39..cb9a045595 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/RectangleTopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/RectangleTopologyTool.java @@ -80,18 +80,8 @@ public void mousePressed(MouseEvent e) { int y2 = Math.max(rectangle.getStartPoint().y, rectangle.getEndPoint().y); Area area = new Area(new java.awt.Rectangle(x1, y1, x2 - x1, y2 - y1)); - if (isEraser(e)) { - getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } else { - getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(getZone().getId(), area, getZone().getTopologyTypes()); - } - renderer.repaint(); - // TODO: send this to the server - + MapTool.serverCommand() + .updateTopology(getZone(), area, isEraser(e), getZone().getTopologyTypes()); rectangle = null; } setIsEraser(isEraser(e)); diff --git a/src/main/java/net/rptools/maptool/client/ui/drawpanel/DrawPanelPopupMenu.java b/src/main/java/net/rptools/maptool/client/ui/drawpanel/DrawPanelPopupMenu.java index 671e9a5e3a..292c8fc78b 100644 --- a/src/main/java/net/rptools/maptool/client/ui/drawpanel/DrawPanelPopupMenu.java +++ b/src/main/java/net/rptools/maptool/client/ui/drawpanel/DrawPanelPopupMenu.java @@ -584,16 +584,9 @@ private void VblTool(Drawable drawable, boolean pathOnly, boolean isEraser) { area = new Area(((ShapeDrawable) drawable).getShape()); } } - if (isEraser) { - renderer.getZone().removeTopology(area); - MapTool.serverCommand() - .removeTopology(renderer.getZone().getId(), area, renderer.getZone().getTopologyTypes()); - } else { - renderer.getZone().addTopology(area); - MapTool.serverCommand() - .addTopology(renderer.getZone().getId(), area, renderer.getZone().getTopologyTypes()); - } - renderer.repaint(); + + MapTool.serverCommand() + .updateTopology(renderer.getZone(), area, isEraser, renderer.getZone().getTopologyTypes()); } private Path2D getPath(Drawable drawable) { diff --git a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java index 390a3587a3..6f367aa107 100644 --- a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java +++ b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java @@ -1343,11 +1343,12 @@ public void initTokenTopologyPanel() { for (final var type : Zone.TopologyType.values()) { final var topology = getTokenTopologyPanel().getTopology(type); if (topology != null) { - TokenVBL.renderTopology( - MapTool.getFrame().getCurrentZoneRenderer(), - getTokenTopologyPanel().getToken().getTransformedTopology(topology), - false, - type); + MapTool.serverCommand() + .updateTopology( + MapTool.getFrame().getCurrentZoneRenderer().getZone(), + getTokenTopologyPanel().getToken().getTransformedTopology(topology), + false, + type); } } @@ -1361,24 +1362,18 @@ public void initTokenTopologyPanel() { getTransferTopologyFromMap() .addActionListener( e -> { + var zone = MapTool.getFrame().getCurrentZoneRenderer().getZone(); + var token = getTokenTopologyPanel().getToken(); final boolean removeFromMap = getCopyOrMoveCheckbox().isSelected(); for (final var type : getTokenTopologyPanel().getSelectedTopologyTypes()) { - Area mapTopology = - TokenVBL.getMapTopology_transformed( - MapTool.getFrame().getCurrentZoneRenderer(), - getTokenTopologyPanel().getToken(), - type); + Area mapTopology = TokenVBL.getTopology_underToken(zone, token, type); + Area newTokenTopology = + TokenVBL.transformTopology_toToken(zone, token, mapTopology); - getTokenTopologyPanel().putCustomTopology(type, mapTopology); + getTokenTopologyPanel().putCustomTopology(type, newTokenTopology); if (removeFromMap) { - Area topologyToDelete = - TokenVBL.getTopology_underToken( - MapTool.getFrame().getCurrentZoneRenderer(), - getTokenTopologyPanel().getToken(), - type); - TokenVBL.renderTopology( - MapTool.getFrame().getCurrentZoneRenderer(), topologyToDelete, true, type); + MapTool.serverCommand().updateTopology(zone, mapTopology, true, type); } } diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/vbl/TokenVBL.java b/src/main/java/net/rptools/maptool/client/ui/zone/vbl/TokenVBL.java index b282f248c7..8395cb179d 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/vbl/TokenVBL.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/vbl/TokenVBL.java @@ -26,9 +26,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.swing.SwingUtil; -import net.rptools.maptool.client.ui.zone.renderer.ZoneRenderer; import net.rptools.maptool.language.I18N; import net.rptools.maptool.model.Token; import net.rptools.maptool.model.Zone; @@ -177,146 +175,68 @@ public static Area simplifyArea( return simplifiedArea; } - /** - * This is a convenience method to send the topology Area to be rendered to the server - * - * @param renderer Reference to the ZoneRenderer - * @param area A valid Area containing topology polygons - * @param erase Set to true to erase the topology, otherwise draw it - * @param topologyType Determines which topology (Wall VBL, Hill VBL, Pit VBL, MBL) to modify. - */ - public static void renderTopology( - ZoneRenderer renderer, Area area, boolean erase, Zone.TopologyType topologyType) { - if (erase) { - renderer.getZone().removeTopology(area, topologyType); - MapTool.serverCommand().removeTopology(renderer.getZone().getId(), area, topologyType); - } else { - renderer.getZone().addTopology(area, topologyType); - MapTool.serverCommand().addTopology(renderer.getZone().getId(), area, topologyType); - } - - MapTool.getFrame().getCurrentZoneRenderer().getZone().tokenTopologyChanged(); - renderer.repaint(); - } - - public static Area getMapTopology_transformed( - ZoneRenderer renderer, Token token, Zone.TopologyType topologyType) { - Rectangle footprintBounds = token.getBounds(renderer.getZone()); - Area newTokenTopology = new Area(footprintBounds); - Dimension imgSize = new Dimension(token.getWidth(), token.getHeight()); - SwingUtil.constrainTo(imgSize, footprintBounds.width, footprintBounds.height); - AffineTransform atArea = new AffineTransform(); - - double tx, ty, sx, sy; - - // Prepare to reverse all the current token transformations so we can store a - // raw untransformed version on the Token - if (token.isSnapToScale()) { - tx = - -newTokenTopology.getBounds().getX() - - (int) ((footprintBounds.getWidth() - imgSize.getWidth()) / 2); - ty = - -newTokenTopology.getBounds().getY() - - (int) ((footprintBounds.getHeight() - imgSize.getHeight()) / 2); - sx = 1 / (imgSize.getWidth() / token.getWidth()); - sy = 1 / (imgSize.getHeight() / token.getHeight()); - - } else { - tx = -newTokenTopology.getBounds().getX(); - ty = -newTokenTopology.getBounds().getY(); - sx = 1 / token.getScaleX(); - sy = 1 / token.getScaleY(); - } - - atArea.concatenate(AffineTransform.getScaleInstance(sx, sy)); - - Area mapArea = renderer.getZone().getTopology(topologyType); + public static Area getTopology_underToken( + Zone zone, Token token, Zone.TopologyType topologyType) { + Area topologyOnMap = zone.getTopology(topologyType); + Rectangle footprintBounds = token.getBounds(zone); - if (token.getShape() == Token.TokenShape.TOP_DOWN - && Math.toRadians(token.getFacingInDegrees()) != 0.0) { + final AffineTransform captureArea = new AffineTransform(); + final Area topologyUnderToken; + if (token.getShape() == Token.TokenShape.TOP_DOWN && token.getFacingInDegrees() != 0) { // Get the center of the token bounds - double rx = newTokenTopology.getBounds2D().getCenterX(); - double ry = newTokenTopology.getBounds2D().getCenterY(); + double rx = footprintBounds.getCenterX() - token.getAnchor().x; + double ry = footprintBounds.getCenterY() - token.getAnchor().y; // Rotate the area to match the token facing - AffineTransform captureArea = - AffineTransform.getRotateInstance(Math.toRadians(token.getFacingInDegrees()), rx, ry); - newTokenTopology = new Area(captureArea.createTransformedShape(newTokenTopology)); - - // Capture the topology via intersection - newTokenTopology.intersect(mapArea); - - // Rotate the area back to prep to store on Token - captureArea = - AffineTransform.getRotateInstance(-Math.toRadians(token.getFacingInDegrees()), rx, ry); - newTokenTopology = new Area(captureArea.createTransformedShape(newTokenTopology)); + captureArea.rotate(Math.toRadians(token.getFacingInDegrees()), rx, ry); + topologyUnderToken = new Area(captureArea.createTransformedShape(footprintBounds)); } else { - // Token will not be rotated so lets just capture the topology - newTokenTopology.intersect(mapArea); + topologyUnderToken = new Area(footprintBounds); } - // Translate the capture to zero out the x,y to store on the Token - atArea.concatenate(AffineTransform.getTranslateInstance(tx, ty)); - newTokenTopology = new Area(atArea.createTransformedShape(newTokenTopology)); + // Capture the topology via intersection + topologyUnderToken.intersect(topologyOnMap); + return topologyUnderToken; + } - // Lets account for flipped images... - atArea = new AffineTransform(); + public static Area transformTopology_toToken(Zone zone, Token token, Area topologyUnderToken) { + Rectangle footprintBounds = token.getBounds(zone); + + // Reverse all token transformations so we can store a raw untransformed version on the Token. + AffineTransform atArea = new AffineTransform(); + // Let's account for flipped images... if (token.isFlippedX()) { - atArea.concatenate(AffineTransform.getScaleInstance(-1.0, 1.0)); - atArea.concatenate(AffineTransform.getTranslateInstance(-token.getWidth(), 0)); + atArea.scale(-1, 1); + atArea.translate(-token.getWidth(), 0); } - if (token.isFlippedY()) { - atArea.concatenate(AffineTransform.getScaleInstance(1.0, -1.0)); - atArea.concatenate(AffineTransform.getTranslateInstance(0, -token.getHeight())); + atArea.scale(1, -1); + atArea.translate(0, -token.getHeight()); } - - // Do any final transformations for flipped images - newTokenTopology = new Area(atArea.createTransformedShape(newTokenTopology)); - - return newTokenTopology; - } - - public static Area getTopology_underToken( - ZoneRenderer renderer, Token token, Zone.TopologyType topologyType) { - Rectangle footprintBounds = token.getBounds(renderer.getZone()); - Area newTokenTopology = new Area(footprintBounds); - Dimension imgSize = new Dimension(token.getWidth(), token.getHeight()); - SwingUtil.constrainTo(imgSize, footprintBounds.width, footprintBounds.height); - AffineTransform atArea = new AffineTransform(); - - double sx, sy; - Area topologyOnMap = renderer.getZone().getTopology(topologyType); - + // Translate the capture to zero out the x,y to store on the Token if (token.isSnapToScale()) { - sx = 1 / (imgSize.getWidth() / token.getWidth()); - sy = 1 / (imgSize.getHeight() / token.getHeight()); - + Dimension imgSize = new Dimension(token.getWidth(), token.getHeight()); + SwingUtil.constrainTo(imgSize, footprintBounds.width, footprintBounds.height); + atArea.scale(token.getWidth() / imgSize.getWidth(), token.getHeight() / imgSize.getHeight()); + atArea.translate( + -footprintBounds.getX() - (int) ((footprintBounds.getWidth() - imgSize.getWidth()) / 2), + -footprintBounds.getY() + - (int) ((footprintBounds.getHeight() - imgSize.getHeight()) / 2)); } else { - sx = 1 / token.getScaleX(); - sy = 1 / token.getScaleY(); + atArea.scale(1 / token.getScaleX(), 1 / token.getScaleY()); + atArea.translate(-footprintBounds.getX(), -footprintBounds.getY()); } - atArea.concatenate(AffineTransform.getScaleInstance(sx, sy)); - if (token.getShape() == Token.TokenShape.TOP_DOWN - && Math.toRadians(token.getFacingInDegrees()) != 0.0) { + if (token.getShape() == Token.TokenShape.TOP_DOWN && token.getFacingInDegrees() != 0) { // Get the center of the token bounds - double rx = newTokenTopology.getBounds2D().getCenterX(); - double ry = newTokenTopology.getBounds2D().getCenterY(); + double rx = footprintBounds.getCenterX() - token.getAnchor().x; + double ry = footprintBounds.getCenterY() - token.getAnchor().y; // Rotate the area to match the token facing - AffineTransform captureArea = - AffineTransform.getRotateInstance(Math.toRadians(token.getFacingInDegrees()), rx, ry); - newTokenTopology = new Area(captureArea.createTransformedShape(newTokenTopology)); - - // Capture the topology via intersection - newTokenTopology.intersect(topologyOnMap); - } else { - // Token will not be rotated so lets just capture the topology - newTokenTopology.intersect(topologyOnMap); + atArea.rotate(-Math.toRadians(token.getFacingInDegrees()), rx, ry); } - return newTokenTopology; + return new Area(atArea.createTransformedShape(topologyUnderToken)); } /** diff --git a/src/main/java/net/rptools/maptool/client/utilities/DungeonDraftImporter.java b/src/main/java/net/rptools/maptool/client/utilities/DungeonDraftImporter.java index ea940a5ab6..6dbf648826 100644 --- a/src/main/java/net/rptools/maptool/client/utilities/DungeonDraftImporter.java +++ b/src/main/java/net/rptools/maptool/client/utilities/DungeonDraftImporter.java @@ -203,8 +203,8 @@ public void importVTT() throws IOException { WALL_VBL_STROKE.createStrokedShape( getVBLPath(v.getAsJsonArray(), pixelsPerCell))); if (finalDo_transform) vblArea.transform(at); - zone.addTopology(vblArea, Zone.TopologyType.WALL_VBL); - zone.addTopology(vblArea, Zone.TopologyType.MBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.WALL_VBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.MBL); }); } @@ -215,8 +215,8 @@ public void importVTT() throws IOException { v -> { Area vblArea = new Area(getVBLPath(v.getAsJsonArray(), pixelsPerCell)); if (finalDo_transform) vblArea.transform(at); - zone.addTopology(vblArea, Zone.TopologyType.HILL_VBL); - zone.addTopology(vblArea, Zone.TopologyType.PIT_VBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.HILL_VBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.PIT_VBL); }); } @@ -239,8 +239,8 @@ public void importVTT() throws IOException { Area vblArea = new Area(DOOR_VBL_STROKE.createStrokedShape(getVBLPath(bounds, pixelsPerCell))); if (finalDo_transform) vblArea.transform(at); - zone.addTopology(vblArea, Zone.TopologyType.WALL_VBL); - zone.addTopology(vblArea, Zone.TopologyType.MBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.WALL_VBL); + zone.updateTopology(vblArea, true, Zone.TopologyType.MBL); } }); } diff --git a/src/main/java/net/rptools/maptool/model/Token.java b/src/main/java/net/rptools/maptool/model/Token.java index 7b9b94cab5..fbb392e64d 100644 --- a/src/main/java/net/rptools/maptool/model/Token.java +++ b/src/main/java/net/rptools/maptool/model/Token.java @@ -1399,6 +1399,10 @@ public Area getTransformedTopology(Zone.TopologyType topologyType) { * @param topology the topology area to set. */ public void setTopology(Zone.TopologyType topologyType, @Nullable Area topology) { + if (topology != null && topology.isEmpty()) { + topology = null; + } + switch (topologyType) { case WALL_VBL -> vbl = topology; case HILL_VBL -> hillVbl = topology; @@ -2651,6 +2655,7 @@ public void updateProperty(Zone zone, Update update, List boolean lightChanged = false; boolean macroChanged = false; boolean panelLookChanged = false; // appearance of token in a panel changed + boolean topologyChanged = false; switch (update) { case setState: var state = parameters.get(0).getStringValue(); @@ -2817,10 +2822,7 @@ public void updateProperty(Zone zone, Update update, List { final var topologyType = Zone.TopologyType.valueOf(parameters.get(0).getTopologyType()); setTopology(topologyType, Mapper.map(parameters.get(1).getArea())); - if (!hasTopology(topologyType)) { // if topology removed - zone.tokenTopologyChanged(); // if token lost topology, TOKEN_CHANGED won't update - // topology - } + topologyChanged = true; break; } case setImageAsset: @@ -2910,6 +2912,9 @@ public void updateProperty(Zone zone, Update update, List if (panelLookChanged) { zone.tokenPanelChanged(this); } + if (topologyChanged) { + zone.tokenTopologyChanged(); + } zone.tokenChanged(this); // fire Event.TOKEN_CHANGED, which updates topology if token has VBL } diff --git a/src/main/java/net/rptools/maptool/model/Zone.java b/src/main/java/net/rptools/maptool/model/Zone.java index 3bd4ac6714..caf86964c1 100644 --- a/src/main/java/net/rptools/maptool/model/Zone.java +++ b/src/main/java/net/rptools/maptool/model/Zone.java @@ -1013,7 +1013,7 @@ public Area getTopology(TopologyType topologyType) { * @param area the area * @param topologyType the type of the topology */ - public void addTopology(Area area, TopologyType topologyType) { + public void updateTopology(Area area, boolean erase, TopologyType topologyType) { var topology = switch (topologyType) { case WALL_VBL -> this.topology; @@ -1022,43 +1022,16 @@ public void addTopology(Area area, TopologyType topologyType) { case COVER_VBL -> coverVbl; case MBL -> topologyTerrain; }; - topology.add(area); - new MapToolEventBus().getMainEventBus().post(new TopologyChanged(this)); - } - - public void addTopology(Area area) { - for (var topologyType : getTopologyTypes()) { - addTopology(area, topologyType); + if (erase) { + topology.subtract(area); + } else { + topology.add(area); } - } - - /** - * Subtract the area from the topology, and fire the event TOPOLOGY_CHANGED - * - * @param area the area - * @param topologyType the type of the topology - */ - public void removeTopology(Area area, TopologyType topologyType) { - var topology = - switch (topologyType) { - case WALL_VBL -> this.topology; - case HILL_VBL -> hillVbl; - case PIT_VBL -> pitVbl; - case COVER_VBL -> coverVbl; - case MBL -> topologyTerrain; - }; - topology.subtract(area); new MapToolEventBus().getMainEventBus().post(new TopologyChanged(this)); } - public void removeTopology(Area area) { - for (var topologyType : getTopologyTypes()) { - removeTopology(area, topologyType); - } - } - /** Fire the event TOPOLOGY_CHANGED. */ // TODO Remove this in favour of firing from token as it own its topology. public void tokenTopologyChanged() { diff --git a/src/main/java/net/rptools/maptool/server/ServerCommand.java b/src/main/java/net/rptools/maptool/server/ServerCommand.java index 7edec64833..eb647f1f46 100644 --- a/src/main/java/net/rptools/maptool/server/ServerCommand.java +++ b/src/main/java/net/rptools/maptool/server/ServerCommand.java @@ -18,6 +18,7 @@ import java.math.BigDecimal; import java.util.List; import java.util.Set; +import javax.annotation.Nullable; import net.rptools.lib.MD5Key; import net.rptools.maptool.model.*; import net.rptools.maptool.model.Zone.VisionType; @@ -41,21 +42,14 @@ public interface ServerCommand { void setFoW(GUID zoneGUID, Area area, Set selectedToks); - default void addTopology(GUID zoneGUID, Area area, Zone.TopologyTypeSet topologyTypes) { + default void updateTopology( + Zone zone, Area area, boolean erase, Zone.TopologyTypeSet topologyTypes) { for (var topologyType : topologyTypes) { - addTopology(zoneGUID, area, topologyType); + updateTopology(zone, area, erase, topologyType); } } - void addTopology(GUID zoneGUID, Area area, Zone.TopologyType topologyType); - - default void removeTopology(GUID zoneGUID, Area area, Zone.TopologyTypeSet topologyTypes) { - for (var topologyType : topologyTypes) { - removeTopology(zoneGUID, area, topologyType); - } - } - - void removeTopology(GUID zoneGUID, Area area, Zone.TopologyType topologyType); + void updateTopology(Zone zone, Area area, boolean erase, Zone.TopologyType topologyType); void enforceZoneView(GUID zoneGUID, int x, int y, double scale, int width, int height); @@ -189,6 +183,8 @@ default void removeTopology(GUID zoneGUID, Area area, Zone.TopologyTypeSet topol void removeData(String type, String namespace, String name); + void setTokenTopology(Token token, @Nullable Area area, Zone.TopologyType topologyType); + void updateTokenProperty(Token token, Token.Update update, int value); void updateTokenProperty(Token token, Token.Update update, String value1, String value2); @@ -221,9 +217,6 @@ void updateTokenProperty( void updateTokenProperty( Token token, Token.Update update, boolean value1, int value2, int value3); - void updateTokenProperty( - Token token, Token.Update update, Zone.TopologyType topologyType, Area area); - void updateTokenProperty(Token token, Token.Update update, String value1, boolean value2); void updateTokenProperty(Token token, Token.Update update, String value, BigDecimal value2); diff --git a/src/main/java/net/rptools/maptool/server/ServerMessageHandler.java b/src/main/java/net/rptools/maptool/server/ServerMessageHandler.java index df66daae4c..82e964a949 100644 --- a/src/main/java/net/rptools/maptool/server/ServerMessageHandler.java +++ b/src/main/java/net/rptools/maptool/server/ServerMessageHandler.java @@ -78,8 +78,8 @@ public void handleMessage(String id, byte[] message) { } switch (msgType) { - case ADD_TOPOLOGY_MSG -> { - handle(msg.getAddTopologyMsg()); + case UPDATE_TOPOLOGY_MSG -> { + handle(msg.getUpdateTopologyMsg()); sendToClients(id, msg); } case BRING_TOKENS_TO_FRONT_MSG -> handle(msg.getBringTokensToFrontMsg()); @@ -171,10 +171,6 @@ public void handleMessage(String id, byte[] message) { handle(msg.getRemoveTokensMsg()); sendToClients(id, msg); } - case REMOVE_TOPOLOGY_MSG -> { - handle(msg.getRemoveTopologyMsg()); - sendToClients(id, msg); - } case REMOVE_ZONE_MSG -> { handle(msg.getRemoveZoneMsg()); sendToClients(id, msg); @@ -493,17 +489,6 @@ private void handle(RemoveZoneMsg msg) { }); } - private void handle(RemoveTopologyMsg msg) { - EventQueue.invokeLater( - () -> { - var zoneGUID = GUID.valueOf(msg.getZoneGuid()); - var area = Mapper.map(msg.getArea()); - var topologyType = Zone.TopologyType.valueOf(msg.getType().name()); - Zone zone = server.getCampaign().getZone(zoneGUID); - zone.removeTopology(area, topologyType); - }); - } - private void handle(RemoveTokensMsg msg) { EventQueue.invokeLater( () -> { @@ -677,14 +662,15 @@ private void handle(BringTokensToFrontMsg bringTokensToFrontMsg) { }); } - private void handle(AddTopologyMsg addTopologyMsg) { + private void handle(UpdateTopologyMsg updateTopologyMsg) { EventQueue.invokeLater( () -> { - var zoneGUID = GUID.valueOf(addTopologyMsg.getZoneGuid()); - var area = Mapper.map(addTopologyMsg.getArea()); - var topologyType = Zone.TopologyType.valueOf(addTopologyMsg.getType().name()); + var zoneGUID = GUID.valueOf(updateTopologyMsg.getZoneGuid()); + var area = Mapper.map(updateTopologyMsg.getArea()); + var erase = updateTopologyMsg.getErase(); + var topologyType = Zone.TopologyType.valueOf(updateTopologyMsg.getType().name()); Zone zone = server.getCampaign().getZone(zoneGUID); - zone.addTopology(area, topologyType); + zone.updateTopology(area, erase, topologyType); }); } diff --git a/src/main/proto/message.proto b/src/main/proto/message.proto index 5deea9c19c..c0fa398fc2 100644 --- a/src/main/proto/message.proto +++ b/src/main/proto/message.proto @@ -15,7 +15,7 @@ import "message_types.proto"; message Message { oneof message_type { - AddTopologyMsg add_topology_msg = 1; + UpdateTopologyMsg update_topology_msg = 501; BootPlayerMsg boot_player_msg = 2; BringTokensToFrontMsg bring_tokens_to_front_msg = 3; ChangeZoneDisplayNameMsg change_zone_display_name_msg = 4; @@ -47,7 +47,6 @@ message Message { RemoveLabelMsg remove_label_msg = 30; RemoveTokenMsg remove_token_msg = 31; RemoveTokensMsg remove_tokens_msg = 32; - RemoveTopologyMsg remove_topology_msg = 33; RemoveZoneMsg remove_zone_msg = 34; RenameZoneMsg rename_zone_msg = 35; RestoreZoneViewMsg restore_zone_view_msg = 36; diff --git a/src/main/proto/message_types.proto b/src/main/proto/message_types.proto index d2e4c92345..931e785e43 100644 --- a/src/main/proto/message_types.proto +++ b/src/main/proto/message_types.proto @@ -16,10 +16,11 @@ import "data_transfer_objects.proto"; import "drawing_dto.proto"; import "gamedata.proto"; -message AddTopologyMsg { +message UpdateTopologyMsg { string zone_guid = 1; AreaDto area = 2; - TopologyTypeDto type = 3; + bool erase = 3; + TopologyTypeDto type = 4; } message BootPlayerMsg { @@ -174,12 +175,6 @@ message RemoveTokensMsg { repeated string token_guid = 2; } -message RemoveTopologyMsg { - string zone_guid = 1; - AreaDto area = 2; - TopologyTypeDto type = 3; -} - message RemoveZoneMsg { string zone_guid = 1; }