Skip to content
This repository was archived by the owner on Sep 16, 2025. It is now read-only.

Commit 2625582

Browse files
committed
Adds storage capability
The creator now saves the state of the model (but doesn't yet load it)
1 parent 6f67ecf commit 2625582

File tree

19 files changed

+444
-49
lines changed

19 files changed

+444
-49
lines changed

VirtualGloomhavenBoard/Indigo/build.sc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ trait VgbModule extends ScalaJSModule {
5757

5858
val indigoVersion = "0.15.0-RC2"
5959
val tyrianVersion = "0.7.1"
60+
val circeVersion = "0.14.1"
6061

6162
def ivyDeps =
6263
Agg(
@@ -65,7 +66,10 @@ trait VgbModule extends ScalaJSModule {
6566
ivy"io.indigoengine::indigo-json-circe::$indigoVersion",
6667
ivy"io.indigoengine::indigo::$indigoVersion",
6768
ivy"io.indigoengine::indigo-extras::$indigoVersion",
68-
ivy"org.scala-js:scalajs-java-securerandom_sjs1_2.13:1.0.0"
69+
ivy"org.scala-js:scalajs-java-securerandom_sjs1_2.13:1.0.0",
70+
ivy"io.circe::circe-core::$circeVersion",
71+
ivy"io.circe::circe-generic::$circeVersion",
72+
ivy"io.circe::circe-parser::$circeVersion"
6973
)
7074

7175
override def esFeatures: T[ESFeatures] = T {

VirtualGloomhavenBoard/Indigo/vgb-common/src/vgb/common/BoardOverlayType.scala

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,48 @@ trait BoardOverlayType:
1010
val baseGame: BaseGame
1111
val cells: Batch[Point]
1212
val flag: Flag
13-
val name: String = s"""${baseGame.shortName}-${this}"""
14-
val assetName: AssetName = AssetName(name)
13+
val verticalAssetName: Option[AssetName]
14+
val name: String = this
15+
.toString()
16+
.toCharArray()
17+
.foldLeft("")((s, c) => if c >= '0' && c <= 'Z' then s + " " + c else s + c)
18+
.trim()
19+
val assetName: AssetName = AssetName(s"""${baseGame.shortName}-${name.toLowerCase().replace(" ", "-")}""")
1520

1621
enum DifficultTerrain(
1722
val baseGame: BaseGame,
1823
val cells: Batch[Point]
1924
) extends BoardOverlayType:
20-
val flag: Flag = Flag.DifficultTerrain
25+
val flag: Flag = Flag.DifficultTerrain
26+
val verticalAssetName = None
2127
case Rubble extends DifficultTerrain(BaseGame.Gloomhaven, Batch(Point(0, 0)))
2228

23-
enum Door(
29+
enum Corridor(
2430
val baseGame: BaseGame,
2531
val cells: Batch[Point]
32+
) extends BoardOverlayType:
33+
val flag: Flag = Flag.Corridor
34+
val verticalAssetName = None
35+
case Altar extends Corridor(BaseGame.Gloomhaven, Batch(Point(0, 0)))
36+
37+
enum Door(
38+
val baseGame: BaseGame,
39+
val cells: Batch[Point],
40+
val verticalAssetName: Option[AssetName]
2641
) extends BoardOverlayType:
2742
val flag: Flag = Flag.Door
28-
case Altar extends Door(BaseGame.Gloomhaven, Batch(Point(0, 0)))
43+
case Altar extends Door(BaseGame.Gloomhaven, Batch(Point(0, 0)), None)
2944

3045
enum Hazard(val baseGame: BaseGame, val cells: Batch[Point]) extends BoardOverlayType:
31-
val flag: Flag = Flag.Hazard
46+
val flag: Flag = Flag.Hazard
47+
val verticalAssetName = None
3248
case HotCoals extends Hazard(BaseGame.Gloomhaven, Batch(Point(0, 0)))
3349
case Thorns extends Hazard(BaseGame.Gloomhaven, Batch(Point(0, 0)))
3450

3551
enum Highlight(val baseGame: BaseGame, val colour: RGB) extends BoardOverlayType:
3652
val cells: Batch[Point] = Batch(Point(0, 0))
3753
val flag: Flag = Flag.Highlight
54+
val verticalAssetName = None
3855
case Red extends Highlight(BaseGame.Gloomhaven, RGB.Red)
3956
case Orange extends Highlight(BaseGame.Gloomhaven, RGB.Orange)
4057
case Yellow extends Highlight(BaseGame.Gloomhaven, RGB.Yellow)
@@ -43,25 +60,30 @@ enum Highlight(val baseGame: BaseGame, val colour: RGB) extends BoardOverlayType
4360
case Indigo extends Highlight(BaseGame.Gloomhaven, RGB.Indigo)
4461

4562
enum Obstacle(val baseGame: BaseGame, val cells: Batch[Point]) extends BoardOverlayType:
46-
val flag: Flag = Flag.Obstacle
63+
val flag: Flag = Flag.Obstacle
64+
val verticalAssetName = None
4765
case Boulder2 extends Obstacle(BaseGame.Gloomhaven, Batch(Point(0, 0), Point(1, 0)))
4866

4967
final case class Rift(val baseGame: BaseGame) extends BoardOverlayType {
5068
val cells: Batch[Point] = Batch(Point(0, 0))
5169
val flag: Flag = Flag.Rift
70+
val verticalAssetName = None
5271
}
5372

5473
enum StartingLocation(val baseGame: BaseGame) extends BoardOverlayType:
5574
val cells: Batch[Point] = Batch(Point(0, 0))
5675
val flag: Flag = Flag.StartingLocation
76+
val verticalAssetName = None
5777
case Gloomhaven extends StartingLocation(BaseGame.Gloomhaven)
5878

5979
enum Trap(val baseGame: BaseGame, val cells: Batch[Point]) extends BoardOverlayType:
60-
val flag: Flag = Flag.Trap
80+
val flag: Flag = Flag.Trap
81+
val verticalAssetName = None
6182
case BearTrap extends Trap(BaseGame.Gloomhaven, Batch(Point(0, 0)))
6283

6384
enum Treasure(val flag: Flag) extends BoardOverlayType:
6485
val cells: Batch[Point] = Batch(Point(0, 0))
86+
val verticalAssetName = None
6587

6688
override def toString(): String = this match
6789
case Chest(_, _) => "Chest"
@@ -73,8 +95,10 @@ enum Treasure(val flag: Flag) extends BoardOverlayType:
7395
final case class Token(val baseGame: BaseGame, val letter: Char) extends BoardOverlayType:
7496
val cells: Batch[Point] = Batch(Point(0, 0))
7597
val flag: Flag = Flag.Token
98+
val verticalAssetName = None
7699
override def toString(): String = s"""Token (${letter})"""
77100

78101
enum Wall(val baseGame: BaseGame, val cells: Batch[Point]) extends BoardOverlayType:
79-
val flag: Flag = Flag.Wall
102+
val flag: Flag = Flag.Wall
103+
val verticalAssetName = None
80104
case Rock extends Wall(BaseGame.Gloomhaven, Batch(Point(0, 0)))

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/Main.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ final case class Main(tyrianSubSystem: TyrianSubSystem[IO, GloomhavenMsg])
5050
.withAssets(
5151
AssetType.Image(AssetName("gh-a1a"), AssetPath("img/map-tiles/gloomhaven/a1a.webp")),
5252
AssetType.Image(AssetName("aesther-ashblade"), AssetPath("img/monsters/aesther-ashblade.webp")),
53-
AssetType.Image(AssetName("gh-Boulder2"), AssetPath("img/overlays/obstacle-boulder-2.webp")),
54-
AssetType.Image(AssetName("gh-Coin"), AssetPath("img/overlays/treasure-coin.webp")),
53+
AssetType.Image(AssetName("gh-boulder-2"), AssetPath("img/overlays/obstacle-boulder-2.webp")),
54+
AssetType.Image(AssetName("gh-coin"), AssetPath("img/overlays/treasure-coin.webp")),
5555
AssetType.Image(AssetName("move-icon"), AssetPath("img/move-icon.webp")),
5656
AssetType.Image(AssetName("hex-border"), AssetPath("img/hex-border.webp")),
5757
AssetType.Font(AssetName("PirateOne"), AssetPath("fonts/PirataOne-Gloomhaven.woff2"))

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/BoardOverlay.scala

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package vgb.game.models
22

33
import indigo.*
44
import vgb.common.BoardOverlayType
5+
import vgb.common.RoomType
56

67
final case class BoardOverlay(
78
id: Int,
89
overlayType: BoardOverlayType,
910
origin: Point,
10-
numRotations: Byte
11+
rotation: TileRotation,
12+
linkedRooms: Option[Batch[RoomType]]
1113
) extends Placeable {
1214
val maxPoint = overlayType.cells.foldLeft(Point.zero)((max, p) => max.max(p))
1315
val minPoint = overlayType.cells.foldLeft(maxPoint)((min, p) => min.min(p))
@@ -16,8 +18,18 @@ final case class BoardOverlay(
1618
def rotate() =
1719
val rotatedOrigin =
1820
Hexagon.evenRowRotate(Vector2.fromPoint(origin), Vector2.fromPoint(rotationPoint), 1)
21+
1922
this.copy(
2023
origin = rotatedOrigin.toPoint,
21-
numRotations = if numRotations == 5 then 0 else (numRotations + 1).toByte
24+
rotation = rotation.nextRotation(overlayType.verticalAssetName != None)
2225
)
2326
}
27+
28+
object BoardOverlay:
29+
def apply(
30+
id: Int,
31+
overlayType: BoardOverlayType,
32+
origin: Point,
33+
rotation: TileRotation
34+
): BoardOverlay =
35+
BoardOverlay(id, overlayType, origin, rotation, None)

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/GameRules.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ object GameRules:
2525
): Batch[BoardOverlay] =
2626
if (isValidOverlayPos(newPos, overlay, cellMap))
2727
overlays.filter(o => o.id != overlay.id)
28-
:+ overlay.copy(origin = newPos, id = if overlay.id == 0 then overlays.length + 1 else overlay.id)
28+
:+ overlay.copy(
29+
origin = newPos,
30+
id = if overlay.id == 0 then (overlays.foldLeft(0)((i, o) => Math.max(o.id, i))) + 1 else overlay.id
31+
)
2932
else
3033
overlays
3134

@@ -42,7 +45,7 @@ object GameRules:
4245
}
4346
)
4447

45-
val newOverlay = nextValidOverlayRotation(overlay.numRotations, overlay, newMap)
48+
val newOverlay = nextValidOverlayRotation(overlay.rotation, overlay, newMap)
4649
overlays.map(o => if o.id == overlay.id then newOverlay else o)
4750

4851
def isValidMonsterPos(newPos: Point, monster: ScenarioMonster, cellMap: Map[Point, Int]): Boolean =
@@ -69,10 +72,13 @@ object GameRules:
6972

7073
canPlaceOverlay(overlay.copy(origin = newPos), newMap)
7174

72-
def nextValidOverlayRotation(initialRotation: Byte, overlay: BoardOverlay, cellMap: Map[Point, Int]): BoardOverlay =
75+
def nextValidOverlayRotation(
76+
initialRotation: TileRotation,
77+
overlay: BoardOverlay,
78+
cellMap: Map[Point, Int]
79+
): BoardOverlay =
7380
val rotatedOverlay = overlay.rotate()
74-
if rotatedOverlay.overlayType.flag == Flag.Coin || initialRotation == rotatedOverlay.numRotations then
75-
rotatedOverlay
81+
if rotatedOverlay.overlayType.flag == Flag.Coin || initialRotation == rotatedOverlay.rotation then rotatedOverlay
7682
else if canPlaceOverlay(rotatedOverlay, cellMap) then rotatedOverlay
7783
else nextValidOverlayRotation(initialRotation, rotatedOverlay, cellMap)
7884

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/Placeable.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package vgb.game.models
33
import indigo.*
44

55
trait Placeable:
6-
val numRotations: Byte
6+
val rotation: TileRotation
77
val origin: Point
88
val minPoint: Point
99
val maxPoint: Point
@@ -17,7 +17,7 @@ trait Placeable:
1717
.evenRowRotate(
1818
Vector2.fromPoint(localPoint),
1919
Vector2.fromPoint(minPoint),
20-
numRotations
20+
rotation.toByte()
2121
)
2222
.toPoint
2323

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/Room.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import vgb.common.RoomType
66
final case class Room(
77
roomType: RoomType,
88
origin: Point,
9-
numRotations: Byte
9+
rotation: TileRotation
1010
) extends Placeable {
1111
val maxPoint = roomType.cells.foldLeft(Point.zero)((max, p) => max.max(p))
1212
val minPoint = roomType.cells.foldLeft(maxPoint)((min, p) => min.min(p))
@@ -17,6 +17,6 @@ final case class Room(
1717
Hexagon.evenRowRotate(Vector2.fromPoint(origin), Vector2.fromPoint(rotationPoint), 1)
1818
this.copy(
1919
origin = rotatedOrigin.toPoint,
20-
numRotations = if numRotations == 5 then 0 else (numRotations + 1).toByte
20+
rotation = rotation.nextRotation(false)
2121
)
2222
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package vgb.game.models
2+
3+
enum TileRotation:
4+
case Default, Horizontal, Vertical, VerticalReverse, DiagonalLeft,
5+
DiagonalRight, DiagonalLeftReverse, DiagonalRightReverse
6+
7+
def toByte(): Byte =
8+
this match {
9+
case Default => 0
10+
case DiagonalLeft => 1
11+
case Vertical => 0
12+
case DiagonalRight => 2
13+
case Horizontal => 3
14+
case DiagonalLeftReverse => 4
15+
case VerticalReverse => 3
16+
case DiagonalRightReverse => 5
17+
}
18+
19+
def nextRotation(hasVertical: Boolean) =
20+
this match {
21+
case Default => DiagonalLeft
22+
case DiagonalLeft => if hasVertical then Vertical else DiagonalRight
23+
case Vertical => DiagonalRight
24+
case DiagonalRight => Horizontal
25+
case Horizontal => DiagonalLeftReverse
26+
case DiagonalLeftReverse => if hasVertical then VerticalReverse else DiagonalRightReverse
27+
case VerticalReverse => DiagonalRightReverse
28+
case DiagonalRightReverse => Default
29+
}
30+
31+
override def toString() =
32+
this match {
33+
case Default => "default"
34+
case DiagonalLeft => "diagonal-left"
35+
case Vertical => "vertical"
36+
case DiagonalRight => "diagonal-right"
37+
case Horizontal => "horizontal"
38+
case DiagonalLeftReverse => "diagonal-left-reverse"
39+
case VerticalReverse => "vertical-reverse"
40+
case DiagonalRightReverse => "diagonal-right-reverse"
41+
}
42+
43+
object TileRotation:
44+
def fromString(dir: String) =
45+
dir match {
46+
case "diagonal-left" => DiagonalLeft
47+
case "vertical" => Vertical
48+
case "diagonal-right" => DiagonalRight
49+
case "horizontal" => Horizontal
50+
case "diagonal-left-reverse" => DiagonalLeftReverse
51+
case "vertical-reverse" => VerticalReverse
52+
case "diagonal-right-reverse" => DiagonalRightReverse
53+
case _ => Default
54+
}

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/components/BoardOverlayComponent.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package vgb.game.models.components
33
import indigo.*
44
import vgb.game.models.BoardOverlay
55
import vgb.game.models.Hexagon
6+
import vgb.game.models.TileRotation
67
import indigo.shared.scenegraph.Shape.Circle
78
import vgb.game.models.LayerDepths
89
import vgb.common.Treasure
@@ -60,10 +61,16 @@ object BoardOverlayComponent:
6061
case t: Treasure.Coin => Size(90, 90)
6162
case _ => Size(156, 90)
6263
},
63-
Material.Bitmap(boardOverlay.overlayType.assetName)
64+
Material.Bitmap(
65+
(boardOverlay.rotation, boardOverlay.overlayType.verticalAssetName) match {
66+
case (TileRotation.Vertical, Some(verticalAsset)) => verticalAsset
67+
case (TileRotation.VerticalReverse, Some(verticalAsset)) => verticalAsset
68+
case _ => boardOverlay.overlayType.assetName
69+
}
70+
)
6471
)
6572
.withRef(offset)
66-
.rotateTo(Radians.fromDegrees(60 * boardOverlay.numRotations))
73+
.rotateTo(Radians.fromDegrees(60 * boardOverlay.rotation.toByte()))
6774
Layer(
6875
boardOverlay.overlayType match {
6976
case t: Treasure.Coin =>

VirtualGloomhavenBoard/Indigo/vgb-game/src/vgb/game/models/components/RoomComponent.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object RoomComponent:
3131
Batch(
3232
Graphic(room.roomType.size, Material.Bitmap(room.roomType.assetName))
3333
.withRef(offset)
34-
.rotateTo(Radians.fromDegrees(60 * room.numRotations))
34+
.rotateTo(Radians.fromDegrees(60 * room.rotation.toByte()))
3535
.moveTo(origin)
3636
) ++
3737
(if moveable then

0 commit comments

Comments
 (0)