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 ec525a47f5..0e818e78ce 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 @@ -26,6 +26,7 @@ import java.awt.geom.PathIterator; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.function.BiConsumer; import net.rptools.maptool.client.MapTool; @@ -163,7 +164,16 @@ private Topology_Functions() { "transferHillVBL", "transferPitVBL", "transferCoverVBL", - "transferMBL"); + "transferMBL", + "setTokenVBLImmunity", + "addTokenVBLImmunity", + "removeTokenVBLImmunity", + "getTokenVBLImmunity", + "setGlobalVBLImmunity", + "toggleGlobalVBLImmunity", + "getGlobalVBLImmunity", + "clearTokenVBLImmunity", + "clearGlobalVBLImmunity"); } public static Topology_Functions getInstance() { @@ -214,6 +224,17 @@ public Object childEvaluate( || functionName.equalsIgnoreCase("transferCoverVBL") || functionName.equalsIgnoreCase("transferMBL")) { childEvaluateTransferTopology(resolver, functionName, parameters); + } else if (functionName.equalsIgnoreCase("setTokenVBLImmunity") + || functionName.equalsIgnoreCase("addTokenVBLImmunity") + || functionName.equalsIgnoreCase("removeTokenVBLImmunity") + || functionName.equalsIgnoreCase("setGlobalVBLImmunity") + || functionName.equalsIgnoreCase("toggleGlobalVBLImmunity") + || functionName.equalsIgnoreCase("clearTokenVBLImmunity") + || functionName.equalsIgnoreCase("clearGlobalVBLImmunity")) { + childEvaluateSetTopologyImmunity(resolver, functionName, parameters); + } else if (functionName.equalsIgnoreCase("getTokenVBLImmunity") + || functionName.equalsIgnoreCase("getGlobalVBLImmunity")) { + return childEvaluateGetTopologyImmunity(resolver, functionName, parameters).toString(); } else { throw new ParserException( I18N.getText("macro.function.general.unknownFunction", functionName)); @@ -614,6 +635,117 @@ private void childEvaluateTransferTopology( } } + public void childEvaluateSetTopologyImmunity( + VariableResolver resolver, String functionName, List parameters) + throws ParserException { + + if (parameters.size() > 3) { + throw new ParserException( + I18N.getText("macro.function.general.tooManyParam", functionName, 1, parameters.size())); + } + + if (parameters.isEmpty()) { + throw new ParserException( + I18N.getText( + "macro.function.general.notEnoughParam", functionName, 1, parameters.size())); + } + + if (!MapTool.getParser().isMacroTrusted()) { + throw new ParserException(I18N.getText("macro.function.general.noPerm", functionName)); + } + + Token token = FindTokenFunctions.findToken(parameters.get(0).toString(), null); + if (token == null) { + throw new ParserException( + I18N.getText( + "macro.function.general.unknownToken", functionName, parameters.get(0).toString())); + } + + if (functionName.equalsIgnoreCase("setTokenVBLImmunity")) { + JsonArray setList = + net.rptools.maptool.util.FunctionUtil.paramAsJsonArray(functionName, parameters, 1); + HashSet newSet = new HashSet(); + for (JsonElement entry : setList) { + newSet.add(entry.getAsString()); + } + MapTool.serverCommand() + .updateTokenProperty( + token, Token.Update.setTokenVBLImmunity, newSet.toString().replaceAll("^\\[|]$", "")); + } else if (functionName.equalsIgnoreCase("addTokenVBLImmunity")) { + MapTool.serverCommand() + .updateTokenProperty( + token, Token.Update.addTokenVBLImmunity, parameters.get(1).toString()); + } else if (functionName.equalsIgnoreCase("removeTokenVBLImmunity")) { + MapTool.serverCommand() + .updateTokenProperty( + token, Token.Update.removeTokenVBLImmunity, parameters.get(1).toString()); + } else if (functionName.equalsIgnoreCase("setGlobalVBLImmunity")) { + MapTool.serverCommand() + .updateTokenProperty( + token, + Token.Update.setGlobalVBLImmunity, + parameters.get(1).toString(), + BigDecimal.ONE.equals(parameters.get(2))); + } else if (functionName.equalsIgnoreCase("toggleGlobalVBLImmunity")) { + MapTool.serverCommand() + .updateTokenProperty( + token, Token.Update.toggleGlobalVBLImmunity, parameters.get(1).toString()); + } else if (functionName.equalsIgnoreCase("clearTokenVBLImmunity")) { + MapTool.serverCommand().updateTokenProperty(token, Token.Update.clearTokenVBLImmunity); + } else if (functionName.equalsIgnoreCase("clearGlobalVBLImmunity")) { + MapTool.serverCommand().updateTokenProperty(token, Token.Update.clearGlobalVBLImmunity); + } + } + + public JsonArray childEvaluateGetTopologyImmunity( + VariableResolver resolver, String functionName, List parameters) + throws ParserException { + + if (parameters.size() > 3) { + throw new ParserException( + I18N.getText("macro.function.general.tooManyParam", functionName, 1, parameters.size())); + } + + if (parameters.isEmpty()) { + throw new ParserException( + I18N.getText( + "macro.function.general.notEnoughParam", functionName, 1, parameters.size())); + } + + if (!MapTool.getParser().isMacroTrusted()) { + throw new ParserException(I18N.getText("macro.function.general.noPerm", functionName)); + } + + Token token = FindTokenFunctions.findToken(parameters.get(0).toString(), null); + if (token == null) { + throw new ParserException( + I18N.getText( + "macro.function.general.unknownToken", functionName, parameters.get(0).toString())); + } + + JsonArray returnValue = new JsonArray(); + + if (functionName.equalsIgnoreCase("getTokenVBLImmunity")) { + token + .getTokenVBLImmunity() + .forEach( + (value) -> { + returnValue.add(value); + }); + } else if (functionName.equalsIgnoreCase("getGlobalVBLImmunity")) { + token + .getGlobalVBLImmunity() + .forEach( + (key, value) -> { + if (value) { + returnValue.add(key); + } + }); + } + + return returnValue; + } + /** * Auto generate topology using token topology optimzation options * diff --git a/src/main/java/net/rptools/maptool/client/script/javascript/api/JSAPIToken.java b/src/main/java/net/rptools/maptool/client/script/javascript/api/JSAPIToken.java index 0dffe95d9f..3036217d91 100644 --- a/src/main/java/net/rptools/maptool/client/script/javascript/api/JSAPIToken.java +++ b/src/main/java/net/rptools/maptool/client/script/javascript/api/JSAPIToken.java @@ -14,6 +14,7 @@ */ package net.rptools.maptool.client.script.javascript.api; +import com.google.gson.JsonArray; import java.math.BigDecimal; import java.util.Iterator; import java.util.List; @@ -350,4 +351,63 @@ public void setNPC() { public String getType() { return this.token.getType().name(); } + + @HostAccess.Export + public String getGlobalVBLImmunity() { + JsonArray returnValue = new JsonArray(); + token + .getGlobalVBLImmunity() + .forEach( + (key, value) -> { + if (value) { + returnValue.add(key); + } + }); + return returnValue.toString(); + } + + @HostAccess.Export + public String getTokenVBLImmunity() { + JsonArray returnValue = new JsonArray(); + token + .getTokenVBLImmunity() + .forEach( + (value) -> { + returnValue.add(value); + }); + return returnValue.toString(); + } + + @HostAccess.Export + public void setGlobalVBLImmunity(String vblSelector, boolean setTo) { + MapTool.serverCommand() + .updateTokenProperty(token, Token.Update.setGlobalVBLImmunity, vblSelector, setTo); + } + + @HostAccess.Export + public void toggleGlobalVBLImmunity(String vblSelector) { + MapTool.serverCommand() + .updateTokenProperty(token, Token.Update.toggleGlobalVBLImmunity, vblSelector); + } + + @HostAccess.Export + public void addTokenVBLImmunity(String tokenID) { + MapTool.serverCommand().updateTokenProperty(token, Token.Update.addTokenVBLImmunity, tokenID); + } + + @HostAccess.Export + public void removeTokenVBLImmunity(String tokenID) { + MapTool.serverCommand() + .updateTokenProperty(token, Token.Update.removeTokenVBLImmunity, tokenID); + } + + @HostAccess.Export + public void clearTokenVBLImmunity() { + MapTool.serverCommand().updateTokenProperty(token, Token.Update.clearTokenVBLImmunity); + } + + @HostAccess.Export + public void clearGlobalVBLImmunity() { + MapTool.serverCommand().updateTokenProperty(token, Token.Update.clearGlobalVBLImmunity); + } } diff --git a/src/main/java/net/rptools/maptool/client/tool/drawing/TopologyTool.java b/src/main/java/net/rptools/maptool/client/tool/drawing/TopologyTool.java index ae8d537188..e525b99cdf 100644 --- a/src/main/java/net/rptools/maptool/client/tool/drawing/TopologyTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/drawing/TopologyTool.java @@ -224,15 +224,15 @@ public void paintOverlay(ZoneRenderer renderer, Graphics2D g) { g2.scale(renderer.getScale(), renderer.getScale()); g2.setColor(AppStyle.tokenMblColor); - g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.MBL, null)); + g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.MBL)); g2.setColor(AppStyle.tokenTopologyColor); - g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.WALL_VBL, null)); + g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.WALL_VBL)); g2.setColor(AppStyle.tokenHillVblColor); - g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.HILL_VBL, null)); + g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.HILL_VBL)); g2.setColor(AppStyle.tokenPitVblColor); - g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.PIT_VBL, null)); + g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.PIT_VBL)); g2.setColor(AppStyle.tokenCoverVblColor); - g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.COVER_VBL, null)); + g2.fill(zone.getTokenMaskTopology(Zone.TopologyType.COVER_VBL)); g2.setColor(AppStyle.topologyTerrainColor); g2.fill(zone.getMaskTopology(Zone.TopologyType.MBL)); 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 e5ffed9a83..6bc7df18ce 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 @@ -121,6 +121,9 @@ public class EditTokenDialog extends AbeillePanel { // private final Toolbox toolbox = new Toolbox(); private HeroLabData heroLabData; + + private Set tokenVBLImmunity; + private Set availableTokens = new HashSet(); private AutoGenerateTopologySwingWorker autoGenerateTopologySwingWorker = new AutoGenerateTopologySwingWorker(false, Color.BLACK); @@ -505,6 +508,13 @@ public void bind(final Token token) { }; getVisibleCheckBox().addActionListener(tokenVisibleActionListener); + // Init VBL Immunity Tab + if (MapTool.getPlayer().isGM()) { + initVBLImmunityTab(token); + } else { + getVBLImmunityTab().setEnabled(false); + } + // Character Sheets // controller = null; // String form = @@ -925,6 +935,20 @@ public boolean commit() { token.setHeroLabData(heroLabData); + // Update VBL Immunity + token.setGlobalVBLImmunity("WALL_VBL", getWallVBLImmunityCB().isSelected()); + token.setGlobalVBLImmunity("HILL_VBL", getHillVBLImmunityCB().isSelected()); + token.setGlobalVBLImmunity("PIT_VBL", getPitVBLImmunityCB().isSelected()); + token.setGlobalVBLImmunity("COVER_VBL", getCoverVBLImmunityCB().isSelected()); + token.setGlobalVBLImmunity("PC", getPCVBLImmunityCB().isSelected()); + token.setGlobalVBLImmunity("NPC", getNPCVBLImmunityCB().isSelected()); + + if (getSelfVBLImmunityCB().isSelected()) { + tokenVBLImmunity.add(token.getId().toString()); + } + + token.setTokenVBLImmunity(tokenVBLImmunity); + // URI Access token.setAllowURIAccess(getAllowURLAccess().isEnabled() && getAllowURLAccess().isSelected()); // OTHER @@ -1117,6 +1141,54 @@ public JToggleButton getMblToggle() { return (JToggleButton) getComponent("mblToggle"); } + public JTabbedPane getVBLImmunityTab() { + return (JTabbedPane) getComponent("vblImmunityTab"); + } + + public JCheckBox getWallVBLImmunityCB() { + return (JCheckBox) getComponent("wallImmunityCB"); + } + + public JCheckBox getCoverVBLImmunityCB() { + return (JCheckBox) getComponent("coverImmunityCB"); + } + + public JCheckBox getHillVBLImmunityCB() { + return (JCheckBox) getComponent("hillImmunityCB"); + } + + public JCheckBox getPitVBLImmunityCB() { + return (JCheckBox) getComponent("pitImmunityCB"); + } + + public JCheckBox getPCVBLImmunityCB() { + return (JCheckBox) getComponent("pcImmunityCB"); + } + + public JCheckBox getNPCVBLImmunityCB() { + return (JCheckBox) getComponent("npcImmunityCB"); + } + + public JCheckBox getSelfVBLImmunityCB() { + return (JCheckBox) getComponent("selfImmunityCB"); + } + + public JList getTokenVBLImmunityList() { + return (JList) getComponent("tokenVBLImmunityList"); + } + + public JList getAvailableTokenList() { + return (JList) getComponent("availableTokenList"); + } + + public JButton getAddTokenVBLImmunityButton() { + return (JButton) getComponent("addTokenVBLImmunityButton"); + } + + public JButton getRemoveTokenVBLImmunityButton() { + return (JButton) getComponent("removeTokenVBLImmunityButton"); + } + public JButton getAutoGenerateTopologyButton() { return (JButton) getComponent("autoGenerateVblButton"); } @@ -1757,6 +1829,95 @@ public void keyPressed(KeyEvent e) { }); } + public void initVBLImmunityTab(Token thisToken) { + + ((JCheckBox) getWallVBLImmunityCB()) + .setSelected(thisToken.getGlobalVBLImmunity().get("WALL_VBL")); + ((JCheckBox) getCoverVBLImmunityCB()) + .setSelected(thisToken.getGlobalVBLImmunity().get("COVER_VBL")); + ((JCheckBox) getHillVBLImmunityCB()) + .setSelected(thisToken.getGlobalVBLImmunity().get("HILL_VBL")); + ((JCheckBox) getPitVBLImmunityCB()) + .setSelected(thisToken.getGlobalVBLImmunity().get("PIT_VBL")); + ((JCheckBox) getPCVBLImmunityCB()).setSelected(thisToken.getGlobalVBLImmunity().get("PC")); + ((JCheckBox) getNPCVBLImmunityCB()).setSelected(thisToken.getGlobalVBLImmunity().get("NPC")); + ((JCheckBox) getSelfVBLImmunityCB()) + .setSelected(thisToken.getTokenVBLImmunity().contains(thisToken.getId().toString())); + + tokenVBLImmunity = thisToken.getTokenVBLImmunity(); + if (tokenVBLImmunity.contains(thisToken.getId().toString())) { + tokenVBLImmunity.remove(thisToken.getId().toString()); + } + Object[] tokenVBLImmunitiesID = tokenVBLImmunity.toArray(); + ArrayList activeListEntries = new ArrayList<>(); + ArrayList inActiveListEntries = new ArrayList<>(); + var mapZone = MapTool.getFrame().getCurrentZoneRenderer().getZone(); + + JList tokenVBLImmunityList = new JList(new DefaultListModel()); + JList availableTokenList = new JList(new DefaultListModel()); + + List tokenList = mapZone.getAllTokens(); + for (Token v : tokenList) { + if (!tokenVBLImmunity.contains(v.getId().toString()) && v.getId() != thisToken.getId()) { + availableTokens.add(v.getId().toString()); + String newEntry = v.getName(); + if (v.getGMName() != null) { + newEntry += " (" + v.getGMName() + ")"; + } + inActiveListEntries.add(newEntry); + ((DefaultListModel) availableTokenList.getModel()).addElement(newEntry); + } + } + + for (var r = 0; r < tokenVBLImmunitiesID.length; r++) { + String v = (String) tokenVBLImmunitiesID[r]; + Token t = mapZone.getToken(GUID.valueOf(v)); + String newEntry = t.getName(); + if (MapTool.getPlayer().isGM() && t.getGMName() != null) { + newEntry += " (" + t.getGMName() + ")"; + } + activeListEntries.add(newEntry); + ((DefaultListModel) tokenVBLImmunityList.getModel()).addElement(newEntry); + } + + tokenVBLImmunityList.setName("tokenVBLImmunityList"); + availableTokenList.setName("availableTokenList"); + tokenVBLImmunityList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + availableTokenList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + tokenVBLImmunityList.setLayoutOrientation(JList.VERTICAL); + availableTokenList.setLayoutOrientation(JList.VERTICAL); + + getAddTokenVBLImmunityButton() + .addActionListener( + e -> { + int removeIndex = getAvailableTokenList().getSelectedIndex(); + Object selectedObject = getAvailableTokenList().getSelectedValue(); + if (removeIndex > -1) { + tokenVBLImmunity.add((String) availableTokens.toArray()[removeIndex]); + availableTokens.remove(availableTokens.toArray()[removeIndex]); + ((DefaultListModel) getAvailableTokenList().getModel()).remove(removeIndex); + ((DefaultListModel) getTokenVBLImmunityList().getModel()) + .addElement(selectedObject); + } + }); + + getRemoveTokenVBLImmunityButton() + .addActionListener( + e -> { + int removeIndex = getTokenVBLImmunityList().getSelectedIndex(); + Object selectedObject = getTokenVBLImmunityList().getSelectedValue(); + if (removeIndex > -1) { + availableTokens.add((String) tokenVBLImmunity.toArray()[removeIndex]); + tokenVBLImmunity.remove(tokenVBLImmunity.toArray()[removeIndex]); + ((DefaultListModel) getTokenVBLImmunityList().getModel()).remove(removeIndex); + ((DefaultListModel) getAvailableTokenList().getModel()).addElement(selectedObject); + } + }); + + replaceComponent("vblImmunityActivePanel", "tokenVBLImmunityList", tokenVBLImmunityList); + replaceComponent("vblImmunityAvailablePanel", "availableTokenList", availableTokenList); + } + private static class SpeechTableModel extends KeyValueTableModel { private static final long serialVersionUID = 1601750325218502846L; diff --git a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesDialog.form b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesDialog.form index 57752636be..0886f37b23 100644 --- a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesDialog.form +++ b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/TokenPropertiesDialog.form @@ -3,7 +3,7 @@ - + @@ -556,7 +556,7 @@ - + @@ -1356,6 +1356,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java index a9367d202c..d100bcd8f9 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java @@ -526,7 +526,19 @@ private Area getTokenVisibleArea(@Nonnull Token token) { Point p = FogUtil.calculateVisionCenter(token, zone); Area visibleArea = sight.getVisionShape(token, zone); visibleArea.transform(AffineTransform.getTranslateInstance(p.x, p.y)); - tokenVisibleArea = FogUtil.calculateVisibility(p, visibleArea, zone.prepareNodedTopologies()); + Set excludeTypes = new HashSet<>(); + token + .getGlobalVBLImmunity() + .forEach( + (key, value) -> { + if (value) { + excludeTypes.add(key); + } + }); + Set excludeTokens = token.getTokenVBLImmunity(); + tokenVisibleArea = + FogUtil.calculateVisibility( + p, visibleArea, zone.prepareNodedTopologies(excludeTokens, excludeTypes)); tokenVisibleAreaCache.put(token.getId(), tokenVisibleArea); } diff --git a/src/main/java/net/rptools/maptool/client/walker/astar/AbstractAStarWalker.java b/src/main/java/net/rptools/maptool/client/walker/astar/AbstractAStarWalker.java index 84caf95414..981783f04d 100644 --- a/src/main/java/net/rptools/maptool/client/walker/astar/AbstractAStarWalker.java +++ b/src/main/java/net/rptools/maptool/client/walker/astar/AbstractAStarWalker.java @@ -232,10 +232,13 @@ protected List calculatePath(CellPoint start, CellPoint goal) { MapTool.getServerPolicy().getVblBlocksMove() ? EnumSet.allOf(Zone.TopologyType.class) : EnumSet.of(Zone.TopologyType.MBL); + Set excludeTokens = new HashSet<>(); + if (keyToken != null) { + excludeTokens.add(keyToken.getId().toString()); + } this.preparedTopology = new MovementBlockingTopology( - zone.getWalls(), - zone.getMasks(topologyTypes, keyToken == null ? null : keyToken.getId())); + zone.getWalls(), zone.getMasks(topologyTypes, excludeTokens, new HashSet<>())); } var view = zoneRenderer.getPlayerView(); diff --git a/src/main/java/net/rptools/maptool/model/Token.java b/src/main/java/net/rptools/maptool/model/Token.java index 530610473a..a94b0d4de3 100644 --- a/src/main/java/net/rptools/maptool/model/Token.java +++ b/src/main/java/net/rptools/maptool/model/Token.java @@ -220,7 +220,14 @@ public enum Update { flipY, flipIso, setSpeechName, - removeFacing + removeFacing, + setTokenVBLImmunity, + addTokenVBLImmunity, + removeTokenVBLImmunity, + toggleGlobalVBLImmunity, + setGlobalVBLImmunity, + clearTokenVBLImmunity, + clearGlobalVBLImmunity } public static final Comparator NAME_COMPARATOR = @@ -276,6 +283,10 @@ public enum Update { // endregion + private Map globalVBLImmunity = new HashMap<>(); + + private Set tokenVBLImmunity = new HashSet<>(); + private String name = ""; private Set ownerList = new HashSet<>(); @@ -2241,6 +2252,60 @@ public void setSizeScale(double scale) { sizeScale = scale; } + public void setTokenVBLImmunity(Set immunityData) { + tokenVBLImmunity = immunityData; + } + + public void clearTokenVBLImmunity() { + tokenVBLImmunity.clear(); + } + + public void addTokenVBLImmunity(String tokenID) { + tokenVBLImmunity.add(tokenID); + } + + public void removeTokenVBLImmunity(String tokenID) { + tokenVBLImmunity.remove(tokenID); + } + + public Set getTokenVBLImmunity() { + return tokenVBLImmunity; + } + + public void setGlobalVBLImmunity(String key, Boolean value) { + globalVBLImmunity.put(key, value); + } + + public HashMap getGlobalVBLImmunity() { + if (!globalVBLImmunity.containsKey("WALL_VBL")) { + globalVBLImmunity.put("WALL_VBL", false); + } + if (!globalVBLImmunity.containsKey("HILL_VBL")) { + globalVBLImmunity.put("HILL_VBL", false); + } + if (!globalVBLImmunity.containsKey("PIT_VBL")) { + globalVBLImmunity.put("PIT_VBL", false); + } + if (!globalVBLImmunity.containsKey("COVER_VBL")) { + globalVBLImmunity.put("COVER_VBL", false); + } + if (!globalVBLImmunity.containsKey("PC")) { + globalVBLImmunity.put("PC", false); + } + if (!globalVBLImmunity.containsKey("NPC")) { + globalVBLImmunity.put("NPC", false); + } + return new HashMap(globalVBLImmunity); + } + + public void toggleGlobalVBLImmunity(String key) { + globalVBLImmunity.put(key, !globalVBLImmunity.get(key)); + } + + public void clearGlobalVBLImmunity() { + globalVBLImmunity.clear(); + } + /** * Convert the token into a hash map. This is used to ship all of the properties for the token to * other apps that do need access to the Token class. @@ -2934,6 +2999,28 @@ public void updateProperty(Zone zone, Update update, List case flipIso: setFlippedIso(!isFlippedIso()); break; + case setTokenVBLImmunity: + setTokenVBLImmunity( + new HashSet(List.of(parameters.get(0).getStringValue().split(", ")))); + break; + case addTokenVBLImmunity: + addTokenVBLImmunity(parameters.get(0).getStringValue()); + break; + case removeTokenVBLImmunity: + removeTokenVBLImmunity(parameters.get(0).getStringValue()); + break; + case setGlobalVBLImmunity: + setGlobalVBLImmunity(parameters.get(0).getStringValue(), parameters.get(1).getBoolValue()); + break; + case toggleGlobalVBLImmunity: + toggleGlobalVBLImmunity(parameters.get(0).getStringValue()); + break; + case clearTokenVBLImmunity: + clearTokenVBLImmunity(); + break; + case clearGlobalVBLImmunity: + clearGlobalVBLImmunity(); + break; } if (lightChanged) { getZoneRenderer().flushLight(); // flush lights if it changed @@ -3036,6 +3123,17 @@ public static Token fromDto(TokenDto dto) { token.notesType = dto.getNotesType(); token.gmNotesType = dto.getGmNotesType(); + dto.getTokenVblImmunityList() + .forEach( + (value) -> { + token.tokenVBLImmunity.add(value); + }); + dto.getGlobalVblImmunityMap() + .forEach( + (key, value) -> { + token.setGlobalVBLImmunity(key, value); + }); + dto.getStateMap() .forEach( (key, stateDto) -> { @@ -3195,6 +3293,10 @@ public TokenDto toDto() { if (statSheet != null) { dto.setStatSheetProperties(StatSheetProperties.toDto(statSheet)); } + + dto.putAllGlobalVblImmunity(getGlobalVBLImmunity()); + dto.addAllTokenVblImmunity(getTokenVBLImmunity()); + return dto.build(); } diff --git a/src/main/java/net/rptools/maptool/model/Zone.java b/src/main/java/net/rptools/maptool/model/Zone.java index 487195b401..9c2276599e 100644 --- a/src/main/java/net/rptools/maptool/model/Zone.java +++ b/src/main/java/net/rptools/maptool/model/Zone.java @@ -972,15 +972,20 @@ public WallTopology getWalls() { * All other tokens' topology will be included. * * @param types The type of masks to get. - * @param excluding + * @param excludeTokens set of Token IDs to exclude from mask collection + * @param excludeTypes set of mask and token types to exclude from mask collection * @return */ - public List getMasks(Set types, @Nullable GUID excluding) { + public List getMasks( + Set types, Set excludeTokens, Set excludeTypes) { var masks = new ArrayList(); for (var type : types) { - var mapArea = getMaskTopology(type); - var tokenArea = getTokenMaskTopology(type, excluding); + Area mapArea = null; + if (!excludeTypes.contains(type.toString())) { + mapArea = getMaskTopology(type); + } + var tokenArea = getTokenMaskTopology(type, excludeTokens, excludeTypes); if (mapArea != null) { tokenArea.add(mapArea); } @@ -1002,13 +1007,27 @@ public void replaceWalls(WallTopology walls) { * * @return */ - public NodedTopology prepareNodedTopologies() { - if (nodedTopology == null) { - var legacyMasks = getMasks(EnumSet.allOf(TopologyType.class), null); - nodedTopology = NodedTopology.prepare(walls, legacyMasks); + public NodedTopology prepareNodedTopologies(Set excludeTokens, Set excludeTypes) { + NodedTopology tempTopology = null; + if (nodedTopology == null || !excludeTokens.isEmpty() || !excludeTypes.isEmpty()) { + var legacyMasks = getMasks(EnumSet.allOf(TopologyType.class), excludeTokens, excludeTypes); + tempTopology = NodedTopology.prepare(walls, legacyMasks); + } + if (nodedTopology == null + && tempTopology != null + && excludeTokens.isEmpty() + && excludeTypes.isEmpty()) { + nodedTopology = tempTopology; + return nodedTopology; + } else if (tempTopology != null && (!excludeTokens.isEmpty() || !excludeTypes.isEmpty())) { + return tempTopology; + } else { + return nodedTopology; } + } - return nodedTopology; + public NodedTopology prepareNodedTopologies() { + return prepareNodedTopologies(new HashSet<>(), new HashSet<>()); } public Area getMaskTopology(TopologyType topologyType) { @@ -1061,10 +1080,12 @@ public void tokenMaskTopologyChanged(Collection types) { new MapToolEventBus().getMainEventBus().post(new MaskTopologyChanged(this)); } - public Area getTokenMaskTopology(TopologyType type, @Nullable GUID excluding) { + public Area getTokenMaskTopology( + TopologyType type, Set excludeTokens, Set excludeTypes) { var result = new Area(); for (var token : getAllTokens()) { - if (excluding != null && excluding.equals(token.getId())) { + if ((!excludeTokens.isEmpty() && excludeTokens.contains(token.getId().toString())) + || excludeTypes.contains(token.getType().toString())) { continue; } @@ -1076,6 +1097,10 @@ public Area getTokenMaskTopology(TopologyType type, @Nullable GUID excluding) { return result; } + public Area getTokenMaskTopology(TopologyType type) { + return getTokenMaskTopology(type, new HashSet<>(), new HashSet<>()); + } + /** * Fire the event TOKEN_CHANGED * diff --git a/src/main/proto/data_transfer_objects.proto b/src/main/proto/data_transfer_objects.proto index 7daec786ec..4a2d524d70 100644 --- a/src/main/proto/data_transfer_objects.proto +++ b/src/main/proto/data_transfer_objects.proto @@ -366,6 +366,8 @@ message TokenDto { string notes_type = 68; string gm_notes_type = 69; StatSheetPropertiesDto stat_sheet_properties = 70; + map global_vbl_immunity = 73; + repeated string token_vbl_immunity = 74; } message PathDto { @@ -658,6 +660,13 @@ enum TokenUpdateDto { flipIso = 55; setSpeechName = 56; removeFacing = 57; + setTokenVBLImmunity = 58; + addTokenVBLImmunity = 59; + removeTokenVBLImmunity = 60; + setGlobalVBLImmunity = 61; + toggleGlobalVBLImmunity = 62; + clearTokenVBLImmunity = 63; + clearGlobalVBLImmunity = 64; } message AssetTransferHeaderDto { diff --git a/src/main/resources/net/rptools/maptool/language/i18n.properties b/src/main/resources/net/rptools/maptool/language/i18n.properties index 5826278b48..afded20c00 100644 --- a/src/main/resources/net/rptools/maptool/language/i18n.properties +++ b/src/main/resources/net/rptools/maptool/language/i18n.properties @@ -3030,4 +3030,11 @@ advanced.roll.propertyNotNumber = Property {0} is not a number. advanced.roll.noTokenInContext = No token in context. advanced.roll.inputNotNumber = Input {0} is not a number. Preferences.label.tokens.stack.hide=Hide Token stack indicator -Preferences.label.tokens.stack.hide.tooltip=Token Layer stack inidicator will be hidden \ No newline at end of file +Preferences.label.tokens.stack.hide.tooltip=Token Layer stack inidicator will be hidden +EditTokenDialog.label.wallImmunity=Wall Immunity +EditTokenDialog.label.hillImmunity=Hill Immunity +EditTokenDialog.label.pitImmunity=Pit Immunity +EditTokenDialog.label.coverImmunity=Cover Immunity +EditTokenDialog.tab.vblImmunity=VBL Immunity +EditTokenDialog.label.currentImmunities=Current VBL Immunities +EditTokenDialog.label.availableTokens=Available Tokens \ No newline at end of file