diff --git a/.version b/.version index 7dff5b8..f477849 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.2.1 \ No newline at end of file +0.2.2 \ No newline at end of file diff --git a/TODO.md b/TODO.md index b32d85c..43d17b0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,10 @@ ### v0.2.2: -- [ ] Fix bug with conservation of mass (water) -- [ ] Fix mouse jittering bug when going in and out of menus -- [ ] Revisit tile highlighting -- [ ] Pipeline improvements / build & release binaries / auto-tagging -- [ ] Skybox - [ ] Shadows +- [ ] Skybox +- [ ] Pipeline improvements / build & release binaries / auto-tagging +- [x] Real object highlighting instead of wireframe +- [x] Optimize chemical quantities tracker ### v0.3.0 - Plants Upgrade: diff --git a/internal/entity/entity.go b/internal/entity/entity.go index 07ff485..801e5f8 100644 --- a/internal/entity/entity.go +++ b/internal/entity/entity.go @@ -1,6 +1,7 @@ package entity import ( + "cbeimers113/strands/internal/graphics" "fmt" "strconv" @@ -42,6 +43,12 @@ func RemoveEntity(entity Entity, entities map[int]Entity, scene *core.Node) { // Highlight or unhighlight an entity func Highlight(entity Entity, highlight bool) { if mat := entity.Material(); mat != nil { - mat.SetWireframe(highlight) + tex := graphics.Textures[graphics.TexHighlight] + + if highlight && !mat.HasTexture(tex) { + mat.AddTexture(tex) + } else if !highlight && mat.HasTexture(tex) { + mat.RemoveTexture(tex) + } } } diff --git a/internal/entity/tile.go b/internal/entity/tile.go index 2cd2184..adb38be 100644 --- a/internal/entity/tile.go +++ b/internal/entity/tile.go @@ -123,11 +123,6 @@ func (t *Tile) doWaterSpread() { } } - // Shuffle the lower neighbours to give some randomness to flow direction - t.Rand.Shuffle(len(lowerNeighbours), func(i, j int) { - lowerNeighbours[i], lowerNeighbours[j] = lowerNeighbours[j], lowerNeighbours[i] - }) - // Distribute water to lower neighbours for len(lowerNeighbours) > 0 { var neighbour *Tile @@ -144,12 +139,9 @@ func (t *Tile) doWaterSpread() { break } + // Add d/n litres of water to the neighbour, where d is the elevation difference and n is the number of lower neighbours delta := chem.CubicMetresToLitres(t.getElevation().Value-neighbour.getElevation().Value) / float32(len(lowerNeighbours)) - - if δ := neighbour.AddWater(delta - t.AddWater(-delta)); δ != 0 { - // TODO: This shouldn't ever be 0, but do something if it is - println(δ) - } + neighbour.AddWater(delta - t.AddWater(-delta)) // Remove neighbour from lowerNeighbours for i, nb := range lowerNeighbours { diff --git a/internal/game/game.go b/internal/game/game.go index 224d538..623b3fc 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -202,7 +202,7 @@ func (g *Game) Start() { g.gui.Refresh() // Spin camera in main menu and any sub-views in the main menu - if g.State.InMainMenu() { + if g.State.InSpinMenu() { if !g.camSpin { p := g.Cam.Position() g.camSpin = true diff --git a/internal/graphics/texture.go b/internal/graphics/texture.go index 46925c1..0ca7b2e 100644 --- a/internal/graphics/texture.go +++ b/internal/graphics/texture.go @@ -17,13 +17,14 @@ const ( TexCursor = "cursor" // World - TexDirt = "dirt" - TexGrass = "grass" - TexSand = "sand" - TexSeed = "seed" - TexStalk = "stalk" - TexStone = "stone" - TexWater = "water" + TexHighlight = "highlight" + TexDirt = "dirt" + TexGrass = "grass" + TexSand = "sand" + TexSeed = "seed" + TexStalk = "stalk" + TexStone = "stone" + TexWater = "water" ) var ( @@ -32,6 +33,8 @@ var ( //go:embed textures/gui/cursor.png bytesCursor []byte + //go:embed textures/world/highlight.png + bytesHighlight []byte //go:embed textures/world/dirt.png bytesDirt []byte //go:embed textures/world/grass.png @@ -60,6 +63,7 @@ func LoadTextures() { {id: TexMenuLogo, data: bytesMenuLogo}, {id: TexCursor, data: bytesCursor}, + {id: TexHighlight, data: bytesHighlight}, {id: TexDirt, data: bytesDirt}, {id: TexGrass, data: bytesGrass}, {id: TexSand, data: bytesSand}, diff --git a/internal/graphics/textures/world/highlight.png b/internal/graphics/textures/world/highlight.png new file mode 100644 index 0000000..460242d Binary files /dev/null and b/internal/graphics/textures/world/highlight.png differ diff --git a/internal/gui/config_menu.go b/internal/gui/config_menu.go index 12b6ed3..be1ab17 100644 --- a/internal/gui/config_menu.go +++ b/internal/gui/config_menu.go @@ -171,7 +171,7 @@ func (g *Gui) registerConfigMenu() { }) g.Scene.Add(g.exitButton) - g.State.SetInMainMenu(true) + g.State.SetInSpinMenu(true) }, close: func() { diff --git a/internal/gui/main_menu.go b/internal/gui/main_menu.go index 5f9f82b..8ecd6db 100644 --- a/internal/gui/main_menu.go +++ b/internal/gui/main_menu.go @@ -159,7 +159,7 @@ func (g *Gui) registerMainMenu() { }) g.Scene.Add(g.exitButton) - g.State.SetInMainMenu(true) + g.State.SetInSpinMenu(true) }, close: func() { @@ -177,7 +177,7 @@ func (g *Gui) registerMainMenu() { g.Scene.Remove(g.exitButton) g.Scene.Remove(g.popup) - g.State.SetInMainMenu(false) + g.State.SetInSpinMenu(false) }, refresh: func() { diff --git a/internal/io/input_manager/input_manager.go b/internal/io/input_manager/input_manager.go index 3175212..c514f44 100644 --- a/internal/io/input_manager/input_manager.go +++ b/internal/io/input_manager/input_manager.go @@ -149,6 +149,7 @@ func (i *InputManager) MouseDown(evname string, ev interface{}) { switch me.Button { case window.MouseButton1: tile.AddWater(chem.CubicMetresToLitres(1)) + i.State.Quantities[chem.Water].Value += 1 case window.MouseButton2: gui.Open(gui.TileContextMenu, false) } diff --git a/internal/state/state.go b/internal/state/state.go index 513139f..0cb5cd3 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -15,18 +15,18 @@ import ( type State struct { tps int // Record the number of world ticks per second inMenu bool // Whether the player is in a menu and everything in the simulation is frozen, including the player - inMainMenu bool // Whether the main menu is open + inSpinMenu bool // Whether we're in a menu where we want the camera to spin paused bool // Whether the simulation physics are paused, but the player can still interact with the simulation moving bool // Whether the player is moving fastMove bool // Whether the player is using fast movement showChems bool // Whether to show the levels of each chemical in the simulation - Seed int64 // The world's random seed value - Rand *rand.Rand // The simulation's psuedo random number generator - Clock *Clock // Keep track of in-game time - LookingAt entity.Entity // What the camera/player is looking at - Entities map[int]entity.Entity // List of entities in the game world - Quantities map[chem.ElementType]chem.Quantity // Map of quantities for tracking various substances in the simulation + Seed int64 // The world's random seed value + Rand *rand.Rand // The simulation's psuedo random number generator + Clock *Clock // Keep track of in-game time + LookingAt entity.Entity // What the camera/player is looking at + Entities map[int]entity.Entity // List of entities in the game world + Quantities map[chem.ElementType]*chem.Quantity // Map of quantities for tracking various substances in the simulation } func New(cfg *config.Config, seed int64) *State { @@ -37,7 +37,7 @@ func New(cfg *config.Config, seed int64) *State { Rand: rand.New(rand.NewSource(seed)), Clock: NewClock(cfg, 9, 00, true), Entities: make(map[int]entity.Entity), - Quantities: make(map[string]chem.Quantity), + Quantities: make(map[string]*chem.Quantity), } } @@ -53,10 +53,10 @@ func (s *State) SetInMenu(inMenu bool) { s.inMenu = inMenu } -// Set the inMainMenu state -func (s *State) SetInMainMenu(inMainMenu bool) { - s.inMainMenu = inMainMenu - s.SetInMenu(inMainMenu) +// Set the inSpinMenu state +func (s *State) SetInSpinMenu(inSpinMenu bool) { + s.inSpinMenu = inSpinMenu + s.SetInMenu(inSpinMenu) } // Set the paused state @@ -91,9 +91,9 @@ func (s State) InMenu() bool { return s.inMenu } -// Get the inMainMenu state -func (s State) InMainMenu() bool { - return s.inMainMenu +// Get the inSpinMenu state +func (s State) InSpinMenu() bool { + return s.inSpinMenu } // Get the paused state diff --git a/internal/world/world.go b/internal/world/world.go index 1aad697..3800d46 100644 --- a/internal/world/world.go +++ b/internal/world/world.go @@ -67,11 +67,13 @@ func Load(ctx *context.Context, tiles []*entity.Tile, cells []*state.Cell) *Worl w.Scene.Add(w.light) w.Scene.Add(w.sun) - + width := w.Cfg.Simulation.Width depth := w.Cfg.Simulation.Depth + w.State.Quantities[chem.Water] = &chem.Quantity{Units: chem.Litre} w.tilemap = make([][]*entity.Tile, width) + for x := 0; x < width; x++ { w.tilemap[x] = make([]*entity.Tile, depth) @@ -80,10 +82,12 @@ func Load(ctx *context.Context, tiles []*entity.Tile, cells []*state.Cell) *Worl tile.Rand = w.State.Rand w.tilemap[x][z] = tile tile.Refresh(w.State.Entities, w.Scene) + + w.State.Quantities[chem.Water].Value += tile.WaterLevel.Value } } - w.AssignTileNeighbourhoods() + w.assignTileNeighbourhoods() return w } @@ -91,7 +95,7 @@ func Load(ctx *context.Context, tiles []*entity.Tile, cells []*state.Cell) *Worl func (w *World) createMap() { heightmap, min, max := w.makeHeightmap() w.makeTilemap(heightmap, min, max) - w.AssignTileNeighbourhoods() + w.assignTileNeighbourhoods() } // Check if a given coordinate is within the tilemap boundaries @@ -132,7 +136,9 @@ func (w *World) makeTilemap(heightmap [][]float32, min, max float32) { width := w.Cfg.Simulation.Width depth := w.Cfg.Simulation.Depth + w.State.Quantities[chem.Water] = &chem.Quantity{Units: chem.Litre} w.tilemap = make([][]*entity.Tile, width) + for x := 0; x < width; x++ { w.tilemap[x] = make([]*entity.Tile, depth) @@ -151,12 +157,14 @@ func (w *World) makeTilemap(heightmap [][]float32, min, max float32) { tile := entity.NewTile(x, z, height, 22.0, 10, tType, w.State.Rand) tile.Refresh(w.State.Entities, w.Scene) w.tilemap[x][z] = tile + + w.State.Quantities[chem.Water].Value += tile.WaterLevel.Value } } } // Give each tile in a tilemap a list of pointers to its neighbours -func (w *World) AssignTileNeighbourhoods() { +func (w *World) assignTileNeighbourhoods() { // Base hexmap neighbourhood offsets nbOffsets := [][]int{ {1, 0}, // Right @@ -254,21 +262,22 @@ func (w *World) updateSun() { // Update the game world, deltaTime is time since last update in ms func (w *World) Update(deltaTime float32) { - // Track quantities of various substances in the world - waterLevel := chem.Quantity{Units: chem.Litre} - if !w.State.Paused() { w.updateSun() w.atmosphere.Update(deltaTime) // Update plants and creatures for _, e := range w.State.Entities { + if _, isTile := e.(*entity.Tile); isTile { + continue + } + e.Update() entity.Highlight(e, w.State.LookingAt == e) } } - // Update the tilemap independant of paused state so that things like tile highlighting will still work when physics paused + // Update the tilemap for x := 0; x < w.Cfg.Simulation.Width; x++ { for z := 0; z < w.Cfg.Simulation.Depth; z++ { tile := w.tilemap[x][z] @@ -277,16 +286,6 @@ func (w *World) Update(deltaTime float32) { entity.Highlight(tile, w.State.LookingAt == tile) } } - - // Update water level count after updating tiles and atmosphere - for x := 0; x < w.Cfg.Simulation.Width; x++ { - for z := 0; z < w.Cfg.Simulation.Depth; z++ { - tile := w.tilemap[x][z] - waterLevel.Value += tile.WaterLevel.Value - } - } - - w.State.Quantities[chem.Water] = waterLevel } // GetAtmosphere returns a linear slice of Cells representing the atmosphere