diff --git a/boards/bdFiles/bd10 b/boards/bdFiles/bd10 index 698c66530..f40d3d256 100644 Binary files a/boards/bdFiles/bd10 and b/boards/bdFiles/bd10 differ diff --git a/boards/bdFiles/bd50 b/boards/bdFiles/bd50 index c5ccb1978..0e01661a0 100644 Binary files a/boards/bdFiles/bd50 and b/boards/bdFiles/bd50 differ diff --git a/boards/bdFiles/bd5a b/boards/bdFiles/bd5a index 42090ae91..0fa8b8ca4 100644 Binary files a/boards/bdFiles/bd5a and b/boards/bdFiles/bd5a differ diff --git a/boards/bdFiles/bdHT b/boards/bdFiles/bdHT index a59574280..7094356db 100644 Binary files a/boards/bdFiles/bdHT and b/boards/bdFiles/bdHT differ diff --git a/boards/bdFiles/bdRO b/boards/bdFiles/bdRO index 937cbee0d..58d6d99af 100644 Binary files a/boards/bdFiles/bdRO and b/boards/bdFiles/bdRO differ diff --git a/boards/src/bd10/10_SSO_6_AP112.gif b/boards/src/bd10/10_SSO_6_AP112.gif new file mode 100644 index 000000000..0c4e3e7b9 Binary files /dev/null and b/boards/src/bd10/10_SSO_6_AP112.gif differ diff --git a/boards/src/bd10/BoardMetadata.xml b/boards/src/bd10/BoardMetadata.xml index 01ec8c04a..1cb7f86e9 100644 --- a/boards/src/bd10/BoardMetadata.xml +++ b/boards/src/bd10/BoardMetadata.xml @@ -18,7 +18,7 @@ width - width of the board in hexes. others - TBD e.g. alternate hex grain, etc. --> - + - + - + diff --git a/boards/src/bd5a/SSO_AP112_5a.gif b/boards/src/bd5a/SSO_AP112_5a.gif new file mode 100644 index 000000000..253595142 Binary files /dev/null and b/boards/src/bd5a/SSO_AP112_5a.gif differ diff --git a/boards/src/bd5a/SSRControls b/boards/src/bd5a/SSRControls index 4f4487a3d..a4f62b315 100644 --- a/boards/src/bd5a/SSRControls +++ b/boards/src/bd5a/SSRControls @@ -1,7 +1,7 @@ diff --git a/boards/src/bdHT/BoardMetadata.xml b/boards/src/bdHT/BoardMetadata.xml index e5a970c94..12504133e 100644 --- a/boards/src/bdHT/BoardMetadata.xml +++ b/boards/src/bdHT/BoardMetadata.xml @@ -26,11 +26,13 @@ v1.0 Sep 2018 Hatten in Flames VASL conversion by Tim Fachko based on the original art by C Kibler v1.1 Oct 2018 fixes missing H4 walls v2.0 Jan 2026 LOS enabled by Gordon Molek + v2.1 Feb 2026 Metadata cleanup by Gordon Molek + v2.2 May 2026 Missing orchards corrected by Gordon Molek --> - - - - - - - - - - - diff --git a/boards/src/bdHT/LOSData b/boards/src/bdHT/LOSData index e8f144857..ccc4c406a 100644 Binary files a/boards/src/bdHT/LOSData and b/boards/src/bdHT/LOSData differ diff --git a/boards/src/bdHT/data b/boards/src/bdHT/data index f45c2eab5..3a57e26e1 100644 --- a/boards/src/bdHT/data +++ b/boards/src/bdHT/data @@ -8,4 +8,8 @@ Oct 2018 TR version 2.0 Jan 2026 GM -Jan 2026 LOS enabled \ No newline at end of file +2.0 Jan 2026 LOS enabled + +version 2.2 +May 2026 GM +2.2 May 2026 Missing orchards corrected \ No newline at end of file diff --git a/boards/src/bdRO/BoardMetadata.xml b/boards/src/bdRO/BoardMetadata.xml index 924935ad8..b69b403a3 100644 --- a/boards/src/bdRO/BoardMetadata.xml +++ b/boards/src/bdRO/BoardMetadata.xml @@ -24,8 +24,8 @@ DRAFT 0.0X --> - + @@ -82,7 +82,7 @@ - + @@ -138,7 +138,7 @@ - + @@ -321,7 +321,7 @@ - + @@ -499,7 +499,7 @@ - + diff --git a/dist/CounterMetadata.xml b/dist/CounterMetadata.xml index f4b875623..9c61c0a12 100644 --- a/dist/CounterMetadata.xml +++ b/dist/CounterMetadata.xml @@ -226,9 +226,24 @@ - - - + + + + + + + + + + + + + + + + + + diff --git a/dist/Whats New VASL672.pdf b/dist/Whats New VASL672.pdf deleted file mode 100644 index fe48ade14..000000000 Binary files a/dist/Whats New VASL672.pdf and /dev/null differ diff --git a/dist/Whats New VASL673.pdf b/dist/Whats New VASL673.pdf deleted file mode 100644 index dc5c7e88d..000000000 Binary files a/dist/Whats New VASL673.pdf and /dev/null differ diff --git a/dist/Whats New VASL674-beta1.pdf b/dist/Whats New VASL674-beta1.pdf new file mode 100644 index 000000000..3f5792af7 Binary files /dev/null and b/dist/Whats New VASL674-beta1.pdf differ diff --git a/dist/boardData/SharedBoardMetadata.xml b/dist/boardData/SharedBoardMetadata.xml index 858561aad..a522f55ba 100644 --- a/dist/boardData/SharedBoardMetadata.xml +++ b/dist/boardData/SharedBoardMetadata.xml @@ -1926,7 +1926,7 @@ - + diff --git a/dist/buildFile b/dist/buildFile index 70ef877a9..e1c6fd854 100644 --- a/dist/buildFile +++ b/dist/buildFile @@ -1,5 +1,5 @@ - + @@ -164,7 +164,7 @@ - + @@ -289,8 +289,8 @@ +/null/prototype;DBCommon prototype;Info\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanWave.svg,MS\/HumanWaveB.svg;+ Melee,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\ piece;K;D;MS\/_red12.svg;Human Wave/ \ \\ 1\\\ null;0;0;83;0 +/null/prototype;DBCommon prototype;Info\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanWave.svg,MS\/HumanWaveB.svg;+ Melee,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\ piece;K;D;MS\/_red12.svg;Human Wave/ \ \\ 2\\\ null;0;0;84;0 - +/null/prototype;DBCommon label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\ rotate;12;88,130;90,130;cw;ccw;;;;;;;;true\\ mark;Location\\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanDir.svg,MS\/HumanDirB.svg;,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\\ rotate;12;90,130;88,130;ccw;cw;;;;;;;;true\\\\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanWave.svg,MS\/HumanWaveB.svg;,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\\\\ piece;K;D;MS\/_red12.svg;HW Dir/ \ 0\\ \\\ 1\\\\ 0\\\\\ 1\\\\\\ null;0;0;85;0 - +/null/prototype;DBCommon label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\ rotate;12;88,130;90,130;cw;ccw;;;;;;;;true\\ mark;Location\\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanDir.svg,MS\/HumanDirB.svg;,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\\ rotate;12;90,130;88,130;ccw;cw;;;;;;;;true\\\\\ emb2;;2;;Flip;2;F;;2;;;;0;false;0;0;MS\/HumanWave.svg,MS\/HumanWaveB.svg;,;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\\\\ piece;K;D;MS\/_red12.svg;HW Dir/ \ 0\\ \\\ 2\\\\ 0\\\\\ 2\\\\\\ null;0;0;86;0 + +/null/prototype;DBCommon replace;Flip;70,130;VASSAL.build.module.PieceWindow:VASL Counters\/VASSAL.build.widget.TabWidget\/VASSAL.build.widget.TabWidget:Unit\/VASSAL.build.widget.ListWidget:Inf\/VASSAL.build.widget.PanelWidget:HW \\/ Banzai \\/ Bayonet\/VASSAL.build.widget.PieceSlot:HW Dir White;null;0;0;true;;;15649;0;false;true;;1\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ rotate;12;88,130;90,130;cw;ccw;;;;;;;;true\\\ mark;Location\\\\ emb2;;128;A;Alt Grain;128;;;128;;;;0;false;0;0;MS\/HumanDir.svg,MS\/HWDir.svg,MS\/HWDirAlt.svg;,,;true;;;;false;;1;1;true;;65,130;;;1.0;;true\\\\\ rotate;12;90,130;88,130;ccw;cw;;;;;;;;true\\\\\\ emb2;;128;A;;128;;;128;;;;0;false;0;0;MS\/HumanWave.svg;;true;;;;false;;1;1;true;;;;;1.0;;true\\\\\\\ piece;K;D;MS\/_red12.svg;HW Dir Red/ \ \\ 0\\\ \\\\ 1\\\\\ 0\\\\\\ 1\\\\\\\ null;0;0;;0 + +/null/prototype;DBCommon replace;Flip;70,130;VASSAL.build.module.PieceWindow:VASL Counters\/VASSAL.build.widget.TabWidget\/VASSAL.build.widget.TabWidget:Unit\/VASSAL.build.widget.ListWidget:Inf\/VASSAL.build.widget.PanelWidget:HW \\/ Banzai \\/ Bayonet\/VASSAL.build.widget.PieceSlot:HW Dir Red;null;0;0;true;;;15650;0;false;true;;1\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ rotate;12;88,130;90,130;cw;ccw;;;;;;;;true\\\ mark;Location\\\\ emb2;;128;A;Alt Grain;128;;;128;;;;0;false;0;0;MS\/HumanDirB.svg,MS\/HWDirB.svg,MS\/HWDirBAlt.svg;,,;true;;;;false;;1;1;true;;65,130;;;1.0;;true\\\\\ rotate;12;90,130;88,130;ccw;cw;;;;;;;;true\\\\\\ emb2;;128;A;;128;;;128;;;;0;false;0;0;MS\/HumanWaveB.svg;;true;;;;false;;1;1;true;;;;;1.0;;true\\\\\\\ piece;K;D;MS\/_red12.svg;HW Dir White/ \ \\ 0\\\ \\\\ 1\\\\\ 0\\\\\\ 1\\\\\\\ null;0;0;15649;0 +/null/prototype;DBCommon prototype;Info\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ piece;K;D;MS\/Bayonet;Bayonet/ \ \\ null;0;0;87;0 +/null/prototype;DBCommon prototype;Info\ label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\\ emb2;;2;;Flip;2;;;2;;;;0;false;0;0;MS\/BayonetChg-1(KFW).png,MS\/BayonetChg-2(KFW).png,MS\/BayonetChg-3(KFW).png;,+ info,+ info;true;;;;false;;1;1;true;;70,130;;;1.0;;true\\\ piece;K;D;;Bayonet Charge/ \ \\ 1\\\ null;0;0;3b5:7124;0 +/null/prototype;DBCommon label;76,130;Label;10;255,255,255;0,0,0;t;0;c;0;b;c;$pieceName$ ($label$);Dialog;0;0;TextLabel;;false\ emb2;;2;;;2;;;2;;;;0;false;0;0;MS\/Hexgrain.svg;HW Range;true;;;;false;;1;1;true;;;;;1.0;;true\\ piece;K;D;MS\/_red12.svg;HW Range/ \ 1\\ null;0;0;88;0 diff --git a/dist/images/MS/HWDir.svg b/dist/images/MS/HWDir.svg new file mode 100644 index 000000000..3aa760419 --- /dev/null +++ b/dist/images/MS/HWDir.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/dist/images/MS/HWDirAlt.svg b/dist/images/MS/HWDirAlt.svg new file mode 100644 index 000000000..57ac4214a --- /dev/null +++ b/dist/images/MS/HWDirAlt.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/dist/images/MS/HWDirB.svg b/dist/images/MS/HWDirB.svg new file mode 100644 index 000000000..e734492eb --- /dev/null +++ b/dist/images/MS/HWDirB.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/dist/images/MS/HWDirBAlt.svg b/dist/images/MS/HWDirBAlt.svg new file mode 100644 index 000000000..54d9c1e22 --- /dev/null +++ b/dist/images/MS/HWDirBAlt.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/dist/images/MS/Hexgrain.svg b/dist/images/MS/Hexgrain.svg index 97d0859aa..c800c565e 100644 --- a/dist/images/MS/Hexgrain.svg +++ b/dist/images/MS/Hexgrain.svg @@ -2,7 +2,7 @@ diff --git a/src/VASL/LOS/Map/Map.java b/src/VASL/LOS/Map/Map.java index f21298057..3efa1c0c8 100644 --- a/src/VASL/LOS/Map/Map.java +++ b/src/VASL/LOS/Map/Map.java @@ -617,7 +617,7 @@ else if (this.A1CenterY==0){ } } } if (boardarchive.getBoardName().contains("RO")) { - if (this.A1CenterY == 0) { //topleft is half height + if (this.A1CenterY == 0 || this.A1CenterY == 65) { //topleft is half height for (int col = 0; col < (startcol + this.width); col++) { hexGrid[col] = new Hex[this.height]; // + (col % 2)]; // add 1 if odd for (int row = 0; row < (startrow + this.height); row++) { // + (col % 2)); row++) { @@ -644,18 +644,8 @@ else if (this.A1CenterY == this.hexHeight / 2){ //topleft is full height } } } - else if (boardarchive.getBoardName().contains("HT")){ - if (this.A1CenterY== this.hexHeight / 2) { - for (int col = 0; col < (startcol + this.width); col++) { - hexGrid[col] = new Hex[this.height + (col % 2)]; // add 1 if odd - for (int row = 0; row < (startrow + this.height + (col % 2)); row++) { - if (col >= startcol && row >= startrow) { - hexGrid[col - startcol][row- startrow] = new Hex(col - startcol, row - startrow, getGEOHexName(col, row, false, false), getHexCenterPoint(col - startcol, row - startrow), hexHeight, hexWidth, this, 0, terrainList[0]); - } - } - } - } - else if (this.A1CenterY==0){ + else if (boardarchive.getBoardName().contains("HT")) { + if (this.A1CenterY == 0) { // Cropped board int evencol =0; for (int col = 0; col < this.width; col++) { evencol = col % 2 == 0 ? 1 : 0; @@ -664,6 +654,15 @@ else if (this.A1CenterY==0){ hexGrid[col][row] = new Hex(col, row, getGEOHexName(col, row, false, false), getHexCenterPoint(col, row), hexHeight, hexWidth, this, 0, terrainList[0]); } } + } else { // uncropped board + for (int col = 0; col < (startcol + this.width); col++) { + hexGrid[col] = new Hex[this.height + (col % 2)]; // add 1 if odd + for (int row = 0; row < (startrow + this.height + (col % 2)); row++) { + if (col >= startcol && row >= startrow) { + hexGrid[col - startcol][row- startrow] = new Hex(col - startcol, row - startrow, getGEOHexName(col, row, false, false), getHexCenterPoint(col - startcol, row - startrow), hexHeight, hexWidth, this, 0, terrainList[0]); + } + } + } } // reset the hex locations to map grid for (int col = 0; col < hexGrid.length; col++) { @@ -807,6 +806,32 @@ else if (this.A1CenterY==20){ hexGrid[col][row].resetHexsideLocationNames(); } } + } else if (boardarchive.getBoardName().contains("FB")) { + if (this.A1CenterY == 0) { // Cropped board + int evencol =0; + for (int col = 0; col < this.width; col++) { + evencol = col % 2 == 0 ? 1 : 0; + hexGrid[col] = new Hex[this.height + evencol]; // add 1 if even + for (int row = 0; row < this.height + evencol; row++) { + hexGrid[col][row] = new Hex(col, row, getGEOHexName(col, row, false, false), getHexCenterPoint(col, row), hexHeight, hexWidth, this, 0, terrainList[0]); + } + } + } else { // uncropped board + for (int col = 0; col < (startcol + this.width); col++) { + hexGrid[col] = new Hex[this.height + (col % 2)]; // add 1 if odd + for (int row = 0; row < (startrow + this.height + (col % 2)); row++) { + if (col >= startcol && row >= startrow) { + hexGrid[col - startcol][row- startrow] = new Hex(col - startcol, row - startrow, getGEOHexName(col, row, false, false), getHexCenterPoint(col - startcol, row - startrow), hexHeight, hexWidth, this, 0, terrainList[0]); + } + } + } + } + // reset the hex locations to map grid + for (int col = 0; col < hexGrid.length; col++) { + for (int row = 0; row < hexGrid[col].length; row++) { + hexGrid[col][row].resetHexsideLocationNames(); + } + } } else { return null; @@ -872,7 +897,7 @@ public Map (LinkedList vaslboards, double passA1centerx, double passA //have a separate loop for non-standard boards //Dinant follows standard board layout and so can be treated as geo if (b.getName().equals("RBv3") || b.getName().equals("RO") || b.getName().equals("DaE") || - b.getName().equals("SG") || b.getName().equals("VotG")) { //b.getName().equals("Dinant") || + b.getName().equals("SG") || b.getName().equals("VotG") || b.getName().equals("FB")) { //b.getName().equals("Dinant") || createtheHASLHexGrid(b); return; } @@ -913,7 +938,7 @@ public Map (LinkedList vaslboards, double passA1centerx, double passA //Dinant follows standard board layout and so can be treated as geo if (b.getName().equals("RBv3") || b.getName().equals("RO") || b.getName().equals("DaE") || b.getName().equals("SG") || b.getName().equals("HT") || b.getName().equals("VotG") || - b.getName().equals("SaPF")) { + b.getName().equals("SaPF") || b.getName().equals("FB")) { createtheHASLHexGrid(b); } // at this point the terrainGrid, elevationGrid and HexGrid are created but hold no los data @@ -948,7 +973,7 @@ public Map (BoardArchive boardarchive, double passA1centerx, double passA1center //Dinant follows standard board layout and so can be treated as geo if (boardarchive.getBoardName().equals("RBv3") || boardarchive.getBoardName().equals("RO") || boardarchive.getBoardName().equals("DaE") || boardarchive.getBoardName().equals("SG") || boardarchive.getBoardName().equals("HT") || boardarchive.getBoardName().equals("VotG") || - boardarchive.getBoardName().equals("SaPF") || boardarchive.getBoardName().equals("BRT")) { + boardarchive.getBoardName().equals("SaPF") || boardarchive.getBoardName().equals("BRT") || boardarchive.getBoardName().equals("FB")) { createtheHASLHexGrid(boardarchive); } @@ -2324,6 +2349,8 @@ else if((sourceExitHexsides[1] != NONE && sourceHex.getBaseLevelofHex() != getA * 3. targetHex is not depression terrain and adjacent hex is depression terrain and hexside is a depression hexside (base heihts are equal else first test would have been true */ public boolean enterHexsideIsCrest() { + //error handling + if (targetHex == null){return false;} if((targetEnterHexsides[0] != NONE && targetHex.getBaseLevelofHex() != getAdjacentHex(targetHex, targetEnterHexsides[0]).getBaseLevelofHex()) || (targetEnterHexsides[0] != NONE && (targetHex.isDepressionTerrain() && targetHex.getHexsideLocation(targetEnterHexsides[0]).isDepressionTerrain() && !getAdjacentHex(targetHex, targetEnterHexsides[0]).isDepressionTerrain()) || @@ -2809,6 +2836,7 @@ else if (status.currentHex.isPointOnHexside(status.currentCol, status.currentRow } } } + status.currentTerrainHgt = status.currentTerrain.getHeight(); break; } } @@ -2920,7 +2948,8 @@ else if ((status.range == status.rangeToSource && (status.LOSis60Degree || statu } return false; } - + // this handles terrain overlay counters from the draggable overlays window; mostly one hex in size + // it does NOT handle overlays added via boardPicker protected Terrain getTerrainFromOverlayImage(LOSStatus status, GamePiece overlaypiece, int hexside){ Terrain imageterrain = null; Point imagepoint = new Point(); @@ -2947,12 +2976,16 @@ protected Terrain getTerrainFromOverlayImage(LOSStatus status, GamePiece overlay else if (color.equals(Color.BLACK) && overlaypiece.getName().contains("StoneBreach")) { imageterrain = getTerrain("Breach"); } + else if (color.equals(Color.BLACK) ) { + imageterrain = getTerrain("Open Ground"); + } else { imageterrain = getOverlayTerrainfromColor(color, status); if (imageterrain.isBuilding() && (overlaypiece.getName().contains("StoneBreach")) || overlaypiece.getName().contains("Wood Breach")) { imageterrain = getTerrain("Breach"); } + } } else { // if overlay pixel is transparent use underlying map terrain diff --git a/src/VASL/LOS/VASLGameInterface.java b/src/VASL/LOS/VASLGameInterface.java index 640cd10db..d1036438e 100644 --- a/src/VASL/LOS/VASLGameInterface.java +++ b/src/VASL/LOS/VASLGameInterface.java @@ -136,11 +136,12 @@ public void updatePieces() { * @param piece the piece */ private void updatePiece(GamePiece piece) { - + int newcounter = 0; // determine what hex and location the piece is in + if (piece.getName().contains("Control") || piece.getName().contains("Blank") || piece.getName().contains("Perimeter") || + piece.getName().contains("perimeter") || piece.getName().contains("Fortified") || piece.getName().contains("Hex Grid")){return;} Point p = piece.getPosition(); p.translate(-gameMap.getEdgeBuffer().width, -gameMap.getEdgeBuffer().height); - if (p == null || !LOSMap.onMap(p.x, p.y) || LOSMap.gridToHex(p.x, p.y) == null ) {return;} // error handling - no point or point not on map or not in a hex Hex h = LOSMap.gridToHex(p.x, p.y); Location hexloc = h.getNearestLocation(p.x, p.y ); @@ -160,7 +161,7 @@ private void updatePiece(GamePiece piece) { // add the piece if (!Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_ME))) { - + newcounter++; CounterMetadata counter = counterMetadata.get(name); if (counter == null) { if (name.contains("Hedge Overlay")) { @@ -182,6 +183,11 @@ private void updatePiece(GamePiece piece) { } else if (name.contains("Wood Breach")) { name = "Wood Breach Rowhouse Overlay"; } + String sidenum = null; + if (hexside != -1) { + sidenum = " " + String.valueOf(hexside) ; + } + if (sidenum != null) {name += sidenum;} counter = counterMetadata.get(name); } if(counter != null) { @@ -768,6 +774,10 @@ private LocationCounter getLocationCounterForPiece (GamePiece piece) { Stack stack = piece.getParent(); + // this is a bug fix to allow the HexGrid to be Nudged; if more counters affected, develop a method to handle + // or add those counters to dist/CounterMetadata.xml with "ignore" tag + if (piece.getName().contains("Hex Grid")) {return null;} + // single counter? if(stack == null || stack.getPieceCount() == 1) { return locationCounterList.get(piece.getName()); diff --git a/src/VASL/build/module/ASLMap.java b/src/VASL/build/module/ASLMap.java index 024b26a27..8936998c8 100644 --- a/src/VASL/build/module/ASLMap.java +++ b/src/VASL/build/module/ASLMap.java @@ -331,12 +331,12 @@ protected void buildVASLMap() { for (Board b : boards) { final VASLBoard board = (VASLBoard) b; // if legacy, abort - if (!"NUL".equals(b.getName()) && !"NULV".equals(b.getName())) { - if (board.isLegacyBoard()) { - throw new Exception("VASL LOS disabled - Board " + board.getName() + " does not support LOS checking. VASSAL los active - safe to continue play"); - } - mapBoundary.add(b.bounds()); + if ((!"NUL".equals(b.getName())) && (!"NULV".equals(b.getName())) && board.isLegacyBoard()) { + //if (board.isLegacyBoard()) { + throw new Exception("VASL LOS disabled - Board " + board.getName() + " does not support LOS checking. VASSAL los active - safe to continue play"); } + mapBoundary.add(b.bounds()); + //mapBoundary.add(b.bounds()); vaslboards.add(board); // make sure the hex geometry of all boards is the same @@ -916,6 +916,7 @@ private double setA1CenterX(VASLBoard board, String topleftHexWidth) { public class LOSonOverlays { public VASL.LOS.Map.Map newlosdata; public BufferedImage bi; + public BufferedImage boardi; public VASLBoard board; public Rectangle ovrrec; public int currentx; //position on overlay @@ -927,6 +928,7 @@ public class LOSonOverlays { public int overXfinish; // right side of overlay public int overYfinish; // botton side of overlay public LinkedList inherentTerrainHexesToCheckList = new LinkedList<>(); + public boolean preserveelevation; protected boolean checkIfMapImageTerrainIsInherent(){ Hex hextotest = newlosdata.gridToHex(overpositionx, overpositiony); @@ -972,6 +974,7 @@ public VASL.LOS.Map.Map adjustLOSForOverlays(VASLBoard board, VASL.LOS.Map.Map l // get the image as a buffered image final Image i = o.getImage(); losonoverlays.bi = new BufferedImage(i.getWidth(null), i.getHeight(null), BufferedImage.TYPE_INT_ARGB); + losonoverlays.boardi = board.getVASLBoardArchive().getBoardImage(); final Graphics2D bgr = losonoverlays.bi.createGraphics(); bgr.drawImage(i, 0, 0, null); bgr.dispose(); @@ -1003,7 +1006,7 @@ public VASL.LOS.Map.Map adjustLOSForOverlays(VASLBoard board, VASL.LOS.Map.Map l losonoverlays.ovrYstart = (int) (boardheight - (o.bounds().y + o.bounds().getHeight()) + (board.bounds().getY() - board.getMap().getEdgeBuffer().getHeight())); } } - updateHexGridforOverlayTerrain(losonoverlays, terraintype, o.getPersistElevation(), board.isReversed()); + updateHexGridforOverlayTerrain(losonoverlays, terraintype, o.getPersistElevation(), board.isReversed(), o); } else { updateTerrainElevationGridsforOverlays(losonoverlays, terraintype, o); @@ -1040,7 +1043,7 @@ private void updateTerrainElevationGridsforOverlays(LOSonOverlays losonoverlays, //if (isInherenttype(terraintype)) { // updateTerrainElevationGridsforOverlayInherentTerrain(losonoverlays, terraintype); //} else { - boolean preserveelevation = o.getPersistElevation(); + losonoverlays.preserveelevation = o.getPersistElevation(); HashMap inhhexes = new HashMap(); HashMap bdghexes = new HashMap(); losonoverlays.overpositionx = 0; //position on map @@ -1057,30 +1060,28 @@ private void updateTerrainElevationGridsforOverlays(LOSonOverlays losonoverlays, // not a transparent pixel //Retrieving the R G B values color = getRGBColor(c); - terr = getOverlayTerrainfromColor(color, losonoverlays); + terr = getOverlayTerrainfromColor(color, losonoverlays, o); } else { // handle transparent pixel on overlay - ToDO move to method - terr = setTerrainForTransparentPixel(losonoverlays); + terr = setTerrainForTransparentPixel(losonoverlays, o); if (terr == null) { - continue; - } - - /* - // handle special case of transparent center dot - if (isCenterDot(losonoverlays)) { - color = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); - if (color == Color.BLACK) { // no other color around center dot meaning whole hex is transparent - continue; - } if (color.equals(Color.white)) { - terr = losonoverlays.newlosdata.getTerrain(losonoverlays.board.getVASLBoardArchive().getTerrainForVASLColor("L0Winter")); - } else { - terr = getOverlayTerrainfromColor(color, losonoverlays); + // handle transparency when using preserve elevation + if(losonoverlays.preserveelevation){ + BufferedImage boardImage = losonoverlays.board.getVASLBoardArchive().getBoardImage(); + c = boardImage.getRGB(losonoverlays.overpositionx, losonoverlays.overpositiony); + color = getRGBColor(c); + int terrint = losonoverlays.board.getVASLBoardArchive().getTerrainForColor(color); + // hack + if (terrint == -1) {terrint = 0;} + terr = losonoverlays.newlosdata.getTerrain(terrint); if (terr == null) { - terr = fixnullterrain(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); + terr = losonoverlays.newlosdata.getTerrain("Open Ground"); } } - - }*/ + else { + continue; + } + } } // special case for transform where image does not change @@ -1101,16 +1102,16 @@ private void updateTerrainElevationGridsforOverlays(LOSonOverlays losonoverlays, } // handle elevation update - if (!preserveelevation) { - elevint = getOverlayElevationfromColor(losonoverlays, color); + if (!losonoverlays.preserveelevation) { + elevint = getOverlayElevationfromColor(losonoverlays, color, o); } // if elevint = -99 then method above could not find a proper elevation for terrain; revert to current elevation in mapboard losdata - if (elevint == -99 || preserveelevation ) { + if (elevint == -99 || losonoverlays.preserveelevation ) { elevint = losonoverlays.newlosdata.getGridElevation(losonoverlays.overpositionx, losonoverlays.overpositiony); //ToDo add code to preserve terraincode } if (terr.isDepression()) { - elevint = preserveelevation ? losonoverlays.newlosdata.getGridElevation(losonoverlays.overpositionx, losonoverlays.overpositiony) - 1 : -1; + elevint = losonoverlays.preserveelevation ? losonoverlays.newlosdata.getGridElevation(losonoverlays.overpositionx, losonoverlays.overpositiony) - 1 : -1; // losonoverlays.newlosdata.gridToHex(losonoverlays.overpositionx, losonoverlays.overpositiony).setBaseLevelofHex(elevint); } @@ -1126,7 +1127,7 @@ private void updateTerrainElevationGridsforOverlays(LOSonOverlays losonoverlays, } // handles all the hexGrid changes for Overlays that can't be added until all Terrain/Elevation changes are done - private void updateHexGridforOverlayTerrain(LOSonOverlays losonoverlays, String terraintype, boolean preserveelevation, boolean isreversed) { + private void updateHexGridforOverlayTerrain(LOSonOverlays losonoverlays, String terraintype, boolean preserveelevation, boolean isreversed, Overlay o) { // ToDo delete this call if no longer needed // first test for inherent terrain type and send to separate method; use this method for non-inherent or mixed non-inherent/inherent overlays //if (isInherenttype(terraintype)) { @@ -1173,11 +1174,11 @@ private void updateHexGridforOverlayTerrain(LOSonOverlays losonoverlays, String terr = losonoverlays.newlosdata.getGridTerrain(losonoverlays.overpositionx, losonoverlays.overpositiony); //terr = getOverlayTerrainfromColor(color, losonoverlays); while (terr == null) { // handles cases where pixel color does not match any color from ShardBoardMetaData.xml - color = getOverlayNearestColor(losonoverlays, losonoverlays.currentx, losonoverlays.currenty); + color = getOverlayNearestColor(losonoverlays, losonoverlays.currentx, losonoverlays.currenty, o); if (color.equals(Color.white)) { terr = losonoverlays.newlosdata.getTerrain(losonoverlays.board.getVASLBoardArchive().getTerrainForVASLColor("L0Winter")); } else { - terr = getOverlayTerrainfromColor(color, losonoverlays); + terr = getOverlayTerrainfromColor(color, losonoverlays, o); if (terr == null) { terr = fixnullterrain(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); } @@ -1227,15 +1228,15 @@ private void updateHexGridforOverlayTerrain(LOSonOverlays losonoverlays, String // bit of a hack but should work - try it until we get a bug color = getRGBColor(c); if (color.equals(Color.white) || color.equals(Color.black)) { // && j<=(x+6)) { - color = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); - elevint = color.equals(Color.white) ? 0 : getOverlayElevationfromColor(losonoverlays, color); + color = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony, o); + elevint = color.equals(Color.white) ? 0 : getOverlayElevationfromColor(losonoverlays, color, o); // if elevint = -99 then method above could not find a proper elevation for terrain; revert to current elevation in mapboard losdata if (elevint == -99) { elevint = 0; //this is a hack and may not always return a useful result - watch for errors } // add depression terrain test as elevation will always be unknown for them - depression must be on overlay if (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx, losonoverlays.currenty)) { - terr = getOverlayTerrainfromColor(color, losonoverlays); + terr = getOverlayTerrainfromColor(color, losonoverlays, o); if (terr == null) { terr = fixnullterrain(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); // use OG with elevint from existing losdata; this is a hack when can't find terrain @@ -1495,14 +1496,14 @@ else if (terr != null) { } } - private Terrain getOverlayTerrainfromColor(Color color, LOSonOverlays losonoverlays) { + private Terrain getOverlayTerrainfromColor(Color color, LOSonOverlays losonoverlays, Overlay o) { Terrain terr = null; int terrint = losonoverlays.board.getVASLBoardArchive().getTerrainForColor(color); if (terrint >= 0) { return losonoverlays.newlosdata.getTerrain(terrint); } else { while (terr == null) { // handles cases where pixel color does not match any color from ShardBoardMetaData.xml - color = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); + color = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony, o); if (color == null ) { //transparent pixel terr = losonoverlays.newlosdata.getGridTerrain(losonoverlays.overpositionx, losonoverlays.overpositiony); } else if (color.equals(Color.white)) { @@ -1522,10 +1523,10 @@ private Terrain getOverlayTerrainfromColor(Color color, LOSonOverlays losonover return terr; } - private Integer getOverlayElevationfromColor(LOSonOverlays losonoverlays, Color color) { + private Integer getOverlayElevationfromColor(LOSonOverlays losonoverlays, Color color, Overlay o) { int elevint = losonoverlays.board.getVASLBoardArchive().getElevationForColor(color); if (elevint == BoardMetadata.NO_ELEVATION) { - Color newcolor = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony); + Color newcolor = getOverlayNearestColor(losonoverlays, losonoverlays.overpositionx, losonoverlays.overpositiony, o); if (newcolor == null) { //transparent pixel elevint = losonoverlays.newlosdata.getGridElevation(losonoverlays.overpositionx, losonoverlays.overpositiony); } else if (newcolor.equals(Color.white)) { @@ -1539,24 +1540,63 @@ private Integer getOverlayElevationfromColor(LOSonOverlays losonoverlays, Color return elevint; } - private Color getOverlayNearestColor(LOSonOverlays losonoverlays, int newovrx, int newovry) { + private Color getOverlayNearestColor(LOSonOverlays losonoverlays, int newovrx, int newovry, Overlay o) { int c = 0; int a = 2; Color color = Color.BLACK; //ToDo fix use of int values of c - this need to be a method to handle all the non-terrain colors on the map while (color.equals(Color.BLACK) || isOverlayBoardNumColor(color, losonoverlays) || color.equals(getRGBColor(-5261152)) || - color.equals(getRGBColor(-262915)) || color.equals(getRGBColor(-259)) || color.equals(getRGBColor(-246)) || - color.equals(getRGBColor(-16776960))) { //-5261152 = 175,184,160 - SnowHexDots2 -259 = 255, 254, 253 - some overlay center dots -246 = 255, 255, 10 - yellow Hill Num + color.equals(getRGBColor(-262915)) || color.equals(getRGBColor(-259)) || color.equals(getRGBColor(-246)) || + color.equals(getRGBColor(-16776960))) { //-5261152 = 175,184,160 - SnowHexDots2 -259 = 255, 254, 253 - some overlay center dots -246 = 255, 255, 10 - yellow Hill Num // -1677690 = 0,1,0 - manholes // point must be (a) on map (b) on overlay (c) not transparent - if (losonoverlays.newlosdata.onMap(newovrx + a, newovry + a) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + (a - 1), losonoverlays.currenty + a) && (!((losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty + a) >> 24) == 0X00)))) { - c = losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty + a); - } else if ((losonoverlays.newlosdata.onMap(newovrx + a, newovry - a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + (a - 1), losonoverlays.currenty - a) && (!((losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty - a) >> 24) == 0X00)))) { - c = losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty - a); - } else if ((losonoverlays.newlosdata.onMap(newovrx - a, newovry + a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - (a - 1), losonoverlays.currenty + a) && (!((losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty + a) >> 24) == 0X00)))) { - c = losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty + a); - } else if ((losonoverlays.newlosdata.onMap(newovrx - a, newovry - a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - (a - 1), losonoverlays.currenty - a) && (!((losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty - a) >> 24) == 0X00)))) { - c = losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty - a); + if (losonoverlays.newlosdata.onMap(newovrx + a, newovry + a) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + (a - 1), losonoverlays.currenty + a))){ + if (!((losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty + a) >> 24) == 0X00)) { + // overlay point is not transparent + c = losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty + a); + } + else if (losonoverlays.preserveelevation) { + // overlay point is transparent + // update bi pixel with OG-level color + BufferedImage boardImage = losonoverlays.board.getVASLBoardArchive().getBoardImage(); + c = o.getOGLevel(boardImage, losonoverlays.currentx + (a - 1), losonoverlays.currenty + a); + } + } else if ((losonoverlays.newlosdata.onMap(newovrx + a, newovry - a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + (a - 1), losonoverlays.currenty - a))){ + if (!((losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty - a) >> 24) == 0X00)) { + c = losonoverlays.bi.getRGB(losonoverlays.currentx + (a - 1), losonoverlays.currenty - a); + } + else if (losonoverlays.preserveelevation) { + // overlay point is transparent + // update bi pixel with OG-level color + BufferedImage boardImage = losonoverlays.board.getVASLBoardArchive().getBoardImage(); + c = o.getOGLevel(boardImage, losonoverlays.currentx + (a - 1), losonoverlays.currenty - a); + } + } else if ((losonoverlays.newlosdata.onMap(newovrx - a, newovry + a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - (a - 1), losonoverlays.currenty + a))){ + if (!((losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty + a) >> 24) == 0X00)) { + c = losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty + a); + } + else if (losonoverlays.preserveelevation) { + // overlay point is transparent + // update bi pixel with OG-level color + BufferedImage boardImage = losonoverlays.board.getVASLBoardArchive().getBoardImage(); + c = o.getOGLevel(boardImage, losonoverlays.currentx - (a - 1), losonoverlays.currenty + a); + } + } else if ((losonoverlays.newlosdata.onMap(newovrx - a, newovry - a)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - (a - 1), losonoverlays.currenty - a))){ + if (!((losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty - a) >> 24) == 0X00)) { + c = losonoverlays.bi.getRGB(losonoverlays.currentx - (a - 1), losonoverlays.currenty - a); + } + else if (losonoverlays.preserveelevation) { + // overlay point is transparent + // update bi pixel with OG-level color + BufferedImage boardImage = losonoverlays.board.getVASLBoardArchive().getBoardImage(); + c = o.getOGLevel(boardImage, losonoverlays.currentx - (a - 1), losonoverlays.currenty - a); + } + } else if ((losonoverlays.newlosdata.onMap(newovrx, newovry)) && (pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - 1, losonoverlays.currenty) && losonoverlays.preserveelevation)) { + //get color from map as overlay is transparent + // ToDo get this working + Terrain terr = losonoverlays.newlosdata.getGridTerrain(newovrx, newovry); + c = losonoverlays.boardi.getRGB(newovrx, newovry); + } else { // } @@ -1624,29 +1664,48 @@ private Boolean pixelOnTransparentOverlayBorder(LOSonOverlays losonoverlays) { return false; // not on border } - private Terrain setTerrainForTransparentPixel (LOSonOverlays losonoverlays){ + private Terrain setTerrainForTransparentPixel (LOSonOverlays losonoverlays, Overlay o){ Terrain transterrain = null; // (1) if pixel is on the overlay edge and (2) if so are pixels 2 away also transparent // in those conditions, skip actions if (!pixelOnTransparentOverlayBorder(losonoverlays)) { - int j = 0; int k = 0; int c = 0; - while ((c >> 24) == 0x00 && j <= 6) { - j += 2; - k += 2; - if (losonoverlays.newlosdata.onMap(losonoverlays.currentx + j, losonoverlays.currenty + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty + k)) { + int j = 1; int k = 1; int c = 0; int b = 0; + while ((c >> 24) == 0x00 && j <= 4) { + // need to differentiate between transparent pixels for centre dots and hexnames and those that are + // part of transparent area of overlays - use the c and b test to do so. + // if b =0 then tested both b and c; if both are same terrain then use that terrain (b & c are opposite sides of transparent pixel) + // if b = -1 then only c tested, use its terrain if exists; b not on map or overlay + j += 1; + k += 1; + b = -1; + if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx + j, losonoverlays.overpositiony + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty + k)) { c = losonoverlays.bi.getRGB(losonoverlays.currentx + j, losonoverlays.currenty + k); - } else if (losonoverlays.newlosdata.onMap(losonoverlays.currentx + j, losonoverlays.currenty - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty - k)) { + if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx - j, losonoverlays.overpositiony - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty - k)) { + b = losonoverlays.bi.getRGB(losonoverlays.currentx - j, losonoverlays.currenty - k); + } + } else if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx + j, losonoverlays.overpositiony - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty - k)) { c = losonoverlays.bi.getRGB(losonoverlays.currentx + j, losonoverlays.currenty - k); - } else if (losonoverlays.newlosdata.onMap(losonoverlays.currentx - j, losonoverlays.currenty + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty + k)) { + if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx - j, losonoverlays.overpositiony + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty + k)) { + b = losonoverlays.bi.getRGB(losonoverlays.currentx - j, losonoverlays.currenty + k); + } + } else if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx - j, losonoverlays.overpositiony + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty + k)) { c = losonoverlays.bi.getRGB(losonoverlays.currentx - j, losonoverlays.currenty + k); - } else if (losonoverlays.newlosdata.onMap(losonoverlays.currentx - j, losonoverlays.currenty - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty - k)) { + if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx + j, losonoverlays.overpositiony - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty - k)) { + b = losonoverlays.bi.getRGB(losonoverlays.currentx + j, losonoverlays.currenty - k); + } + } else if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx - j, losonoverlays.overpositiony - k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx - j, losonoverlays.currenty - k)) { c = losonoverlays.bi.getRGB(losonoverlays.currentx - j, losonoverlays.currenty - k); + if (losonoverlays.newlosdata.onMap(losonoverlays.overpositionx + j, losonoverlays.overpositiony + k) && pointIsOnOverlay(losonoverlays.bi, losonoverlays.currentx + j, losonoverlays.currenty + k)) { + b = losonoverlays.bi.getRGB(losonoverlays.currentx + j, losonoverlays.currenty + k); + } } else { break; } if ((c >> 24) != 0x00) { final Color color = getRGBColor(c); - transterrain = getOverlayTerrainfromColor(color, losonoverlays); + if (b == -1 || b == c) { + transterrain = getOverlayTerrainfromColor(color, losonoverlays, o); + } } } } @@ -1672,7 +1731,7 @@ private void addHextoOverlayInhandBldgMaps(String terraintype, Terrain terr, LO //hack - ensure that the pixel is not close to a hexside as VASL geometry can put it in an adjacent hex final Point hexcenter = losonoverlays.newlosdata.gridToHex((int) losonoverlays.overpositionx, (int) losonoverlays.overpositiony).getHexCenter(); final Double d = Math.sqrt(((Math.pow(hexcenter.x - losonoverlays.overpositionx, 2) + (Math.pow(hexcenter.y - losonoverlays.overpositiony, 2))))); - if (d < 25) { + if (d < 15) { inhhexes.put(losonoverlays.newlosdata.gridToHex((int) losonoverlays.overpositionx, (int) losonoverlays.overpositiony), terr); doNonInherentToInherentFix(terraintype, terr, losonoverlays); } @@ -2083,23 +2142,25 @@ public void drawPiecesInRegion(Graphics g, java.util.Map pieceMap = new HashMap(); for (int i = 0; i < stack.length; ++i) { // increment the count of pieces at this point - String name = stack[i].getName(); - Stack s = null; - if (stack[i] instanceof Stack) { - s = (Stack) stack[i]; - } else { - continue; - } - int x = s.getPieceCount(); - if (x == 0 || name.equals("")) - //empty stack, ignore - continue; - Point pt = stack[i].getPosition(); - Integer count = pieceMap.get(pt); - if (count == null) { - count = 0; + if (!(stack[i] == null)) { //error handling for NPE issue#2002 + String name = stack[i].getName(); + Stack s = null; + if (stack[i] instanceof Stack) { + s = (Stack) stack[i]; + } else { + continue; + } + int x = s.getPieceCount(); + if (x == 0 || name.equals("")) + //empty stack, ignore + continue; + Point pt = stack[i].getPosition(); + Integer count = pieceMap.get(pt); + if (count == null) { + count = 0; + } + pieceMap.put(pt, count + 1); } - pieceMap.put(pt, count + 1); } for (int i = 0; i < stack.length; ++i) { diff --git a/src/VASL/build/module/map/VASLThread.java b/src/VASL/build/module/map/VASLThread.java index 16b8f67c4..7ad457211 100644 --- a/src/VASL/build/module/map/VASLThread.java +++ b/src/VASL/build/module/map/VASLThread.java @@ -41,6 +41,7 @@ import VASL.LOS.Map.Map; import VASL.build.module.ASLMap; import VASL.LOS.VASLGameInterface; +import VASL.build.module.dice.DieColor; import VASL.build.module.map.boardPicker.ASLBoard; import VASSAL.build.Buildable; import VASSAL.build.GameModule; @@ -49,8 +50,7 @@ import VASSAL.build.module.map.boardPicker.Board; import VASSAL.build.module.map.boardPicker.board.HexGrid; import VASSAL.command.Command; -import VASSAL.configure.BooleanConfigurer; -import VASSAL.configure.ColorConfigurer; +import VASSAL.configure.*; import static VASSAL.build.GameModule.getGameModule; // Needed to enable LOS checking on boards with overlays @@ -99,6 +99,7 @@ public class VASLThread extends LOS_Thread implements KeyListener, GameComponent private Color hindranceColor; private Color blockedColor; private double magnification; + private String opacityValue; private void setGridSnapToVertex(boolean toVertex) { for (Board b : map.getBoards()) { @@ -293,11 +294,15 @@ public void addTo(Buildable buildable) { final ColorConfigurer hindrance = new ColorConfigurer(HINDRANCE_THREAD_COLOR, "Hindrance Thread Color", Color.red); final ColorConfigurer blocked = new ColorConfigurer(BLOCKED_THREAD_COLOR, "Blocked Thread Color", Color.blue); final BooleanConfigurer verbose = new BooleanConfigurer("verboseLOS", "Verbose LOS mode"); + final StringEnumConfigurer opacity = new StringEnumConfigurer("CounterOpacity", "Unit opacity % during LOS checks: ", new String[] { "0", "25", "33", "50", "66", "75", "100" } ); + getGameModule().getPrefs().addOption(preferenceTabName, thread); getGameModule().getPrefs().addOption(preferenceTabName, enable); getGameModule().getPrefs().addOption(preferenceTabName, hindrance); getGameModule().getPrefs().addOption(preferenceTabName, blocked); getGameModule().getPrefs().addOption(preferenceTabName, verbose); + getGameModule().getPrefs().addOption(preferenceTabName, opacity); + final ItemListener l = new ItemListener() { public void itemStateChanged(ItemEvent evt) { @@ -311,6 +316,16 @@ public void itemStateChanged(ItemEvent evt) { enableAll(blocked.getControls(), Boolean.TRUE.equals(enable.getValue())); enableAll(verbose.getControls(), Boolean.TRUE.equals(enable.getValue())); + opacity.addPropertyChangeListener(e -> { + opacityValue = (String)e.getNewValue(); + + if (opacityValue == null) { + opacityValue = "33"; + } + + this.setAttribute("hideOpacity", opacityValue); + }); + // hook for game opening/closing getGameModule().getGameState().addGameComponent(this); } diff --git a/src/VASL/build/module/map/boardArchive/AbstractMetadata.java b/src/VASL/build/module/map/boardArchive/AbstractMetadata.java index 73e6e8763..a77cfa1af 100644 --- a/src/VASL/build/module/map/boardArchive/AbstractMetadata.java +++ b/src/VASL/build/module/map/boardArchive/AbstractMetadata.java @@ -69,25 +69,25 @@ public abstract class AbstractMetadata { private static final String underlayColorNameAttribute = "name"; // Maps color names to board color object - protected LinkedHashMap boardColors = new LinkedHashMap(100); + protected final LinkedHashMap boardColors = new LinkedHashMap(100); // Maps a Color object to VASL color name - protected LinkedHashMap colorToVASLColorName = new LinkedHashMap(100); + protected final LinkedHashMap colorToVASLColorName = new LinkedHashMap(100); // Maps rule name to the rule object - protected LinkedHashMap colorSSRules = new LinkedHashMap(100); + protected final LinkedHashMap colorSSRules = new LinkedHashMap(100); // List of LOS scenario-specific rules - protected LinkedHashMap LOSSSRules = new LinkedHashMap(100); + protected final LinkedHashMap LOSSSRules = new LinkedHashMap(100); // Lists of the counter rules // protected LinkedHashMap LOSCounterRules = new LinkedHashMap(30); // Maps SSR name to the overlay rule object - protected LinkedHashMap overlaySSRules = new LinkedHashMap(); + protected final LinkedHashMap overlaySSRules = new LinkedHashMap(); // Maps SSR name to the underlay rule object - protected LinkedHashMap underlaySSRules = new LinkedHashMap(); + protected final LinkedHashMap underlaySSRules = new LinkedHashMap(); /** * Assert the element has the given name otherwise throw an exception @@ -184,8 +184,9 @@ protected void parseLOSSSRules(Element element) throws JDOMException { // make sure the type code is valid boolean validTypeCode = false; for(int x = 0; x < LOSSSRuleTypeValues.length && !validTypeCode; x++) { - if(LOSSSRuleTypeValues[x].equals(type)) { + if (LOSSSRuleTypeValues[x].equals(type)) { validTypeCode = true; + break; } } @@ -233,7 +234,7 @@ protected void parseColorSSRules(Element element) throws JDOMException { } // make sure there is at least one mapping - if (colorSSRule.getColorMaps().size() < 1) { + if (colorSSRule.getColorMaps().isEmpty()) { throw new JDOMException("colorSSRule " + name + " has no mappings"); } if (!colorSSRules.containsKey(name)) { @@ -319,52 +320,56 @@ protected void parseOverlaySSRules(Element element) throws JDOMException { for (Element e: element.getChildren()) { // overlay rules - if(e.getName().equals(overlaySSRuleElement)){ + switch (e.getName()) { + case overlaySSRuleElement: { - OverlaySSRule rule = new OverlaySSRule(e.getAttributeValue(overlaySSRNameAttribute)); - rule.addImage(new OverlaySSRuleImage( - e.getAttributeValue(overlaySSRImageAttribute), - e.getAttribute(overlaySSRXAttribute).getIntValue(), - e.getAttribute(overlaySSRYAttribute).getIntValue())); - overlaySSRules.put(rule.getName(), rule); + OverlaySSRule rule = new OverlaySSRule(e.getAttributeValue(overlaySSRNameAttribute)); + rule.addImage(new OverlaySSRuleImage( + e.getAttributeValue(overlaySSRImageAttribute), + e.getAttribute(overlaySSRXAttribute).getIntValue(), + e.getAttribute(overlaySSRYAttribute).getIntValue())); + overlaySSRules.put(rule.getName(), rule); - } + break; + } - // overlay rules sets - those having multiple images - else if(e.getName().equals(overlaySSRuleSetElement)){ + // overlay rules sets - those having multiple images + case overlaySSRuleSetElement: { - OverlaySSRule rule = new OverlaySSRule(e.getAttributeValue(overlaySSRNameAttribute)); - for (Element image: e.getChildren()) { + OverlaySSRule rule = new OverlaySSRule(e.getAttributeValue(overlaySSRNameAttribute)); + for (Element image : e.getChildren()) { - if(image.getName().equals(overlaySSRuleImageElement)){ - rule.addImage(new OverlaySSRuleImage( - image.getAttributeValue(overlaySSRImageAttribute), - image.getAttribute(overlaySSRXAttribute).getIntValue(), - image.getAttribute(overlaySSRYAttribute).getIntValue())); + if (image.getName().equals(overlaySSRuleImageElement)) { + rule.addImage(new OverlaySSRuleImage( + image.getAttributeValue(overlaySSRImageAttribute), + image.getAttribute(overlaySSRXAttribute).getIntValue(), + image.getAttribute(overlaySSRYAttribute).getIntValue())); + } } + overlaySSRules.put(rule.getName(), rule); + break; } - overlaySSRules.put(rule.getName(), rule); - } - //underlay rules - else if(e.getName().equals(underLaySSRuleElement)) { + //underlay rules + case underLaySSRuleElement: - // read the SSR underlay attributes - String name = e.getAttributeValue(underlaySSRNameAttribute); - String imageName = e.getAttributeValue(underlaySSRImageAttribute); - ArrayList colors = new ArrayList(); + // read the SSR underlay attributes + String name = e.getAttributeValue(underlaySSRNameAttribute); + String imageName = e.getAttributeValue(underlaySSRImageAttribute); + ArrayList colors = new ArrayList(); - // read all of the color names - for (Element el: e.getChildren()) { + // read all of the color names + for (Element el : e.getChildren()) { - if(el.getName().equals(underlayColorElement)) { + if (el.getName().equals(underlayColorElement)) { - colors.add(el.getAttributeValue(underlayColorNameAttribute)); - } + colors.add(el.getAttributeValue(underlayColorNameAttribute)); + } - } + } - underlaySSRules.put(name, new UnderlaySSRule(name, imageName, colors)); + underlaySSRules.put(name, new UnderlaySSRule(name, imageName, colors)); + break; } } } diff --git a/src/VASL/build/module/map/boardArchive/BoardArchive.java b/src/VASL/build/module/map/boardArchive/BoardArchive.java index 15fd95f09..84e200275 100644 --- a/src/VASL/build/module/map/boardArchive/BoardArchive.java +++ b/src/VASL/build/module/map/boardArchive/BoardArchive.java @@ -657,7 +657,7 @@ public void writeLOSData(Map map){ } // write the hex information - if(map.getMapConfiguration().contains("EqualRowCount")){ + if(this.metadata.getHexGridConfig().contains("EqualRowCount")){ for (int col = 0; col < map.getWidth(); col++) { for (int row = 0; row < map.getHeight(); row++) { // no extra hex for boards where each col has same number of rows (eg RO/DaE) outfile.writeByte( map.getHex(col, row).hasStairway() ? 1: 0); diff --git a/src/VASL/build/module/map/boardPicker/Overlay.java b/src/VASL/build/module/map/boardPicker/Overlay.java index f5e4b779e..9b97d7369 100644 --- a/src/VASL/build/module/map/boardPicker/Overlay.java +++ b/src/VASL/build/module/map/boardPicker/Overlay.java @@ -90,12 +90,13 @@ public Overlay(String ovr, ASLBoard board, File overlayDir) throws IOException, throw new BoardException("Incorrect path name for overlay. Confirm name and retry to add overlay."); } readData(); - transform(persistelevation); + try { setBounds(); } catch (BadCoords e) { throw new BoardException(e.getMessage()); } + transform(persistelevation); check(); } @@ -557,7 +558,7 @@ public void transform(boolean persistelevation) { if (!persistelevation) { return; } - // new transform to support preserve elevation + // new transform to support preserve elevation try { readMetadata(); } @@ -566,9 +567,7 @@ public void transform(boolean persistelevation) { } final Image i = getImage(); // error handling - if (i == null) { - return; - } + if (i == null) {return;} // get the image as a buffered image BufferedImage bi = new BufferedImage(i.getWidth(null), i.getHeight(null), BufferedImage.TYPE_INT_ARGB); Rectangle ovrRec = bounds(); @@ -582,14 +581,33 @@ public void transform(boolean persistelevation) { int currentheight = 0; for (currentwidth = 0; currentwidth < bi.getWidth(); currentwidth++) { for (currentheight = 0; currentheight < bi.getHeight(); currentheight++) { + c = bi.getRGB(currentwidth, currentheight); if ((c >> 24) != 0x00) { // not a transparent pixel //Retrieving the R G B values Color color = getRGBColor(c); terr = getOverlayTerrainfromColor(color); - if (terr == null || (terr.getLOSCategory() == Terrain.LOSCategories.OPEN && !(getName().contains("og")))) { - int transparentWhite = 0x00FFFFFF; - bi.setRGB(currentwidth, currentheight, transparentWhite); + if (terr != null) { + if (terr.getLOSCategory() == Terrain.LOSCategories.OPEN && !(getName().contains("og"))) { //need to allow OpenGround overlays to work + int transparentWhite = 0x00FFFFFF; + bi.setRGB(currentwidth, currentheight, transparentWhite); + // now need to check for board terrain that should become openground at level + // read the board image + BufferedImage boardImage = board.getVASLBoardArchive().getBoardImage(); + int x = (int) boundaries.getX() + currentwidth; + int y = (int) boundaries.getY() + currentheight; + int boardc = boardImage.getRGB(x, y); + Color boardcolor = getRGBColor(boardc); + Terrain boardterr = getOverlayTerrainfromColor(boardcolor); + //test if should be replaced + if (boardterr != null) { + if (!(boardterr.getLOSCategory() == Terrain.LOSCategories.OPEN)) { // + // update bi pixel with OG-level color + c = getOGLevel(boardImage, x, y); + bi.setRGB(currentwidth, currentheight, c); + } + } + } } } } @@ -597,8 +615,58 @@ public void transform(boolean persistelevation) { setImage(bi); } - /* Methods getRGBColor, getOverlayTerrainfromColor, readMetaData, getOverlayTerrain, and SetTerrain were added - * in December 2025 to support the PreserveElevation functionality when adding overlays to a board + public int getOGLevel(BufferedImage boardImage, int x, int y) { + + int transparentWhite = 0x00FFFFFF; + try { + + // Define starting point and radius + int startX = x; + int startY = y; + int radius = 3; + + // Define the bounding box limits + int startXLimit = Math.max(0, startX - radius); + int endXLimit = Math.min(boardImage.getWidth() - 1, startX + radius); + int startYLimit = Math.max(0, startY - radius); + int endYLimit = Math.min(boardImage.getHeight() - 1, startY + radius); + + // Pre-calculate squared radius for highly efficient math + double radiusSquared = radius * radius; + + // Loop through the bounding box + for (int posy = startYLimit; posy <= endYLimit; posy++) { + for (int posx = startXLimit; posx <= endXLimit; posx++) { + + // Calculate squared distance from starting point + double dx = posx - startX; + double dy = posy - startY; + double distanceSquared = (dx * dx) + (dy * dy); + + // Check if pixel is within the radius + if (distanceSquared <= radiusSquared) { + // Extract pixel color + int rgb = boardImage.getRGB(posx, posy); + Color color = new Color(rgb); // , true); + // Execute custom pixel test logic here + Terrain testterr = getOverlayTerrainfromColor(color); + if (testterr.getLOSCategory() == Terrain.LOSCategories.OPEN || getName().contains("og")){ + return rgb; + } + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + return transparentWhite; + } + return transparentWhite; + } + + /* Methods getRGBColor, getOverlayTerrainfromColor, readMetaData, getOverlayTerrain, + * SetTerrain, getElevationforColor, and were added + * in December 2025/ May 2026 to support the PreserveElevation functionality when adding overlays to a board * They are copied (with adjustments) from other classes not available to Overlay at the point at which * they are required (especially when starting a new game). * As the LOS code continues to be reworked, it may be possible to access the methods from their original @@ -667,4 +735,28 @@ public final void setTerrain(HashMap nameMap) { terrainList[nameMap.get(name).getType()] = nameMap.get(name); } } + + /** + * Gets the elevation for the given color + * @param color the board color + * @return the elevation + */ + public int getElevationForColor(Color color) { + + int elevint =0; + LinkedHashMap colorToVASLColorName = new LinkedHashMap(100); + colorToVASLColorName.putAll(sharedBoardMetadata.getColorToVASLColorName()); + String vaslcolor = colorToVASLColorName.get(color); + if (vaslcolor == null) { + return BoardMetadata.NO_ELEVATION; + } + String vaslColorElevation = sharedBoardMetadata.getBoardColors().get(vaslcolor).getElevation(); + if(vaslColorElevation.equals(BoardMetadata.UNKNOWN)) { + return BoardMetadata.NO_ELEVATION; + } + else { + elevint = Integer.parseInt(vaslColorElevation); + } + return elevint; + } } diff --git a/src/VASL/counters/ASLTranslate.java b/src/VASL/counters/ASLTranslate.java index 48390ce5f..fbb8baa30 100644 --- a/src/VASL/counters/ASLTranslate.java +++ b/src/VASL/counters/ASLTranslate.java @@ -166,7 +166,7 @@ protected void translate(Point p) { MapGrid mgrid; VASSAL.build.module.Chatter mchat; // test for situations where Key Combos won't work: b is null; Casbin and Tray extensions (?) which don't have hexgrid if (b == null) { - // do nothing + super.translate(p); return; } else { @@ -182,7 +182,7 @@ else if (mgrid == null || mgrid instanceof SquareGrid){ // need to disable one h mchat.send("Key Combos (CTRL + Arrow or Numpad) cannot move units on this map. Drag and Drop instead!"); } else { - if (b != null && ((HexGrid) mgrid).getHexSize() != ASLBoard.DEFAULT_HEX_HEIGHT) { + if (((HexGrid) mgrid).getHexSize() != ASLBoard.DEFAULT_HEX_HEIGHT) { int x = p.x; int y = p.y; super.translate(p); diff --git a/src/VASL/counters/Concealable.java b/src/VASL/counters/Concealable.java index e5c58cb4c..cd6aac186 100644 --- a/src/VASL/counters/Concealable.java +++ b/src/VASL/counters/Concealable.java @@ -84,7 +84,8 @@ public String myGetType() { } protected void drawObscuredToMe(Graphics g, int x, int y, Component obs, double zoom) { - if (obs == null) {return;} // error handling - if obs is null then loadImages(obs) will cause NPE + //if (obs == null) { + // return;} // error handling - if obs is null then loadImages(obs) will cause NPE loadImages(obs); int size = (int) (zoom * imageSize.width); @@ -100,7 +101,8 @@ protected void drawObscuredToMe(Graphics g, int x, int y, Component obs, double } protected void drawObscuredToOthers(Graphics g, int x, int y, Component obs, double zoom) { - // if (obs == null) {return;} // error handling - if obs is null then loadImages(obs) will cause NPE + //if (obs == null) { + // return;} // error handling - if obs is null then loadImages(obs) will cause NPE loadImages(obs); piece.draw(g, x, y, obs, zoom); int size = (int) (zoom * imageSize.width); @@ -314,10 +316,23 @@ private void loadImages(Component obs) { } else { //imageSize.setSize(0, 0); - concealedToMe = obs.createImage(20, 20); - java.awt.Graphics g = concealedToMe.getGraphics(); - g.drawString("?", 0, 0); - + //May 2026 use code from concealedToOthers if obs is null + if (obs != null) { + concealedToMe = obs.createImage(20, 20); + java.awt.Graphics g = concealedToMe.getGraphics(); + g.drawString("?", 0, 0); + } + else { + concealedToMe = Op.load(nation + "/" + nation + "qmarkme.svg").getImage(); + if (concealedToMe == null) { + // Using generic qmarkme.gif image and prefs-specified colors + BufferedImage rev = new BufferedImage(20, 20, BufferedImage.TYPE_4BYTE_ABGR); + final Graphics2D g = rev.createGraphics(); + g.drawString("?", 0, 0); + concealedToMe = rev; + g.dispose(); + } + } } //} //catch (Exception ex) {