From 07a2dd7953f903afd14e21be686df03265645674 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Fri, 14 Jan 2022 16:58:44 -0800 Subject: [PATCH] Add date display --- asset/asset.go | 11 ++++----- flags_web.go | 3 +++ game/game.go | 7 ++++++ system/input_move.go | 10 ++++----- system/powerscan.go | 44 ++++++++++++++++++++++++++++++++++++ system/renderhud.go | 53 +++++++++++++++++++++++++++++++++++--------- system/tick.go | 46 ++++++++++++++++++++++++++++++++++++++ world/world.go | 35 +++++++++++++++++++++++++++++ 8 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 system/powerscan.go create mode 100644 system/tick.go diff --git a/asset/asset.go b/asset/asset.go index 9f4e609..73df675 100644 --- a/asset/asset.go +++ b/asset/asset.go @@ -46,16 +46,17 @@ func LoadSounds(ctx *audio.Context) { SoundSelect = LoadWAV(ctx, "sound/select/select.wav") SoundSelect.SetVolume(0.6) + const popVolume = 0.2 SoundPop1 = LoadWAV(ctx, "sound/pop/pop1.wav") SoundPop2 = LoadWAV(ctx, "sound/pop/pop2.wav") SoundPop3 = LoadWAV(ctx, "sound/pop/pop3.wav") SoundPop4 = LoadWAV(ctx, "sound/pop/pop4.wav") SoundPop5 = LoadWAV(ctx, "sound/pop/pop5.wav") - SoundPop1.SetVolume(0.2) - SoundPop2.SetVolume(0.2) - SoundPop3.SetVolume(0.2) - SoundPop4.SetVolume(0.2) - SoundPop5.SetVolume(0.2) + SoundPop1.SetVolume(popVolume) + SoundPop2.SetVolume(popVolume) + SoundPop3.SetVolume(popVolume) + SoundPop4.SetVolume(popVolume) + SoundPop5.SetVolume(popVolume) } func LoadImage(p string) *ebiten.Image { diff --git a/flags_web.go b/flags_web.go index 9b237c6..f43817b 100644 --- a/flags_web.go +++ b/flags_web.go @@ -11,5 +11,8 @@ import ( func parseFlags() { world.World.DisableEsc = true + // Adjust minimum zoom level due to performance decrease when targeting WASM. + world.CameraMinZoom = 0.6 + ebiten.SetFullscreen(true) } diff --git a/game/game.go b/game/game.go index 8e2ba71..0d25d91 100644 --- a/game/game.go +++ b/game/game.go @@ -296,10 +296,17 @@ func (g *game) Draw(screen *ebiten.Image) { func (g *game) addSystems() { ecs := ECS + // Simulation systems. + ecs.AddSystem(system.NewTickSystem()) + ecs.AddSystem(system.NewPowerScanSystem()) + + // Input systems. g.movementSystem = system.NewMovementSystem() ecs.AddSystem(system.NewPlayerMoveSystem(world.World.Player, g.movementSystem)) ecs.AddSystem(system.NewplayerFireSystem()) ecs.AddSystem(g.movementSystem) + + // Render systems. ecs.AddSystem(system.NewCreepSystem()) ecs.AddSystem(system.NewCameraSystem()) g.renderSystem = system.NewRenderSystem() diff --git a/system/input_move.go b/system/input_move.go index 3ac325f..a0871f0 100644 --- a/system/input_move.go +++ b/system/input_move.go @@ -109,12 +109,10 @@ func (s *playerMoveSystem) Update(ctx *gohan.Context) error { } } world.World.CamScaleTarget += scrollY * (world.World.CamScaleTarget / 7) - const minZoom = .4 - const maxZoom = 1 - if world.World.CamScaleTarget < minZoom { - world.World.CamScaleTarget = minZoom - } else if world.World.CamScaleTarget > maxZoom { - world.World.CamScaleTarget = maxZoom + if world.World.CamScaleTarget < world.CameraMinZoom { + world.World.CamScaleTarget = world.CameraMinZoom + } else if world.World.CamScaleTarget > world.CameraMaxZoom { + world.World.CamScaleTarget = world.CameraMaxZoom } // Smooth zoom transition. diff --git a/system/powerscan.go b/system/powerscan.go new file mode 100644 index 0000000..ba74822 --- /dev/null +++ b/system/powerscan.go @@ -0,0 +1,44 @@ +package system + +import ( + "code.rocketnine.space/tslocum/citylimits/component" + "code.rocketnine.space/tslocum/citylimits/world" + "code.rocketnine.space/tslocum/gohan" + "github.com/hajimehoshi/ebiten/v2" +) + +type PowerScanSystem struct { +} + +func NewPowerScanSystem() *PowerScanSystem { + s := &PowerScanSystem{} + + return s +} + +func (s *PowerScanSystem) Needs() []gohan.ComponentID { + return []gohan.ComponentID{ + component.PositionComponentID, + component.VelocityComponentID, + component.WeaponComponentID, + } +} + +func (s *PowerScanSystem) Uses() []gohan.ComponentID { + return nil +} + +func (s *PowerScanSystem) Update(_ *gohan.Context) error { + if world.World.Paused { + return nil + } + + // TODO use a consistent procedure to check each building that needs power + // as connected via road to a power plant, and power-out buildings without enough power + // "citizens report brown-outs" + return nil +} + +func (s *PowerScanSystem) Draw(ctx *gohan.Context, screen *ebiten.Image) error { + return gohan.ErrSystemWithoutDraw +} diff --git a/system/renderhud.go b/system/renderhud.go index 6882a18..2a8d98b 100644 --- a/system/renderhud.go +++ b/system/renderhud.go @@ -16,14 +16,16 @@ type RenderHudSystem struct { op *ebiten.DrawImageOptions hudImg *ebiten.Image tmpImg *ebiten.Image + tmpImg2 *ebiten.Image sidebarColor color.RGBA } func NewRenderHudSystem() *RenderHudSystem { s := &RenderHudSystem{ - op: &ebiten.DrawImageOptions{}, - hudImg: ebiten.NewImage(1, 1), - tmpImg: ebiten.NewImage(1, 1), + op: &ebiten.DrawImageOptions{}, + hudImg: ebiten.NewImage(1, 1), + tmpImg: ebiten.NewImage(1, 1), + tmpImg2: ebiten.NewImage(1, 1), } sidebarShade := uint8(111) @@ -65,9 +67,11 @@ func (s *RenderHudSystem) drawSidebar() { if bounds.Dx() != world.World.ScreenW || bounds.Dy() != world.World.ScreenH { s.hudImg = ebiten.NewImage(world.World.ScreenW, world.World.ScreenH) s.tmpImg = ebiten.NewImage(world.SidebarWidth, world.World.ScreenH) + s.tmpImg2 = ebiten.NewImage(world.SidebarWidth, world.World.ScreenH) } else { s.hudImg.Clear() s.tmpImg.Clear() + s.tmpImg2.Clear() } w := world.SidebarWidth if bounds.Dx() < w { @@ -75,15 +79,15 @@ func (s *RenderHudSystem) drawSidebar() { } // Fill background. - s.tmpImg.Fill(s.sidebarColor) + s.hudImg.SubImage(image.Rect(0, 0, world.SidebarWidth, world.World.ScreenH)).(*ebiten.Image).Fill(s.sidebarColor) // Draw buttons. - paddingSize := 1 - columns := 3 + const paddingSize = 1 + const columns = 3 - buttonWidth := world.SidebarWidth / columns - buttonHeight := buttonWidth + const buttonWidth = world.SidebarWidth / columns + const buttonHeight = buttonWidth world.World.HUDButtonRects = make([]image.Rectangle, len(world.HUDButtons)) var lastButtonY int for i, button := range world.HUDButtons { @@ -107,8 +111,10 @@ func (s *RenderHudSystem) drawSidebar() { lastButtonY = y } + s.drawDate(lastButtonY + buttonHeight + 5) + // Draw RCI indicator. - rciPadding := buttonWidth / 2 + rciPadding := buttonWidth - 14 const rciSize = 100 rciX := buttonWidth rciY := lastButtonY + buttonHeight + rciPadding @@ -198,12 +204,39 @@ func (s *RenderHudSystem) drawTooltip() { x, y := world.SidebarWidth, 0 w, h := (len(label)*6+10)*int(scale), 22*(int(scale)) r := image.Rect(x, y, x+w, y+h) + s.hudImg.SubImage(r).(*ebiten.Image).Fill(color.RGBA{0, 0, 0, 120}) s.tmpImg.Clear() ebitenutil.DebugPrint(s.tmpImg, label) - s.hudImg.SubImage(r).(*ebiten.Image).Fill(color.RGBA{0, 0, 0, 120}) op := &ebiten.DrawImageOptions{} op.GeoM.Scale(scale, scale) op.GeoM.Translate(world.SidebarWidth+(4*scale), 4) s.hudImg.DrawImage(s.tmpImg, op) } + +func (s *RenderHudSystem) drawDate(y int) { + const datePadding = 10 + month, year := world.Date() + label := month + + scale := 2.0 + x, y := datePadding, y + + s.tmpImg2.Clear() + ebitenutil.DebugPrint(s.tmpImg2, label) + op := &ebiten.DrawImageOptions{} + op.GeoM.Scale(scale, scale) + op.GeoM.Translate(float64(x), float64(y)) + s.hudImg.DrawImage(s.tmpImg2, op) + + label = year + + x = world.SidebarWidth - 1 - datePadding - (len(label) * 6 * int(scale)) + + s.tmpImg2.Clear() + ebitenutil.DebugPrint(s.tmpImg2, label) + op.GeoM.Reset() + op.GeoM.Scale(scale, scale) + op.GeoM.Translate(float64(x), float64(y)) + s.hudImg.DrawImage(s.tmpImg2, op) +} diff --git a/system/tick.go b/system/tick.go new file mode 100644 index 0000000..4f06df5 --- /dev/null +++ b/system/tick.go @@ -0,0 +1,46 @@ +package system + +import ( + "code.rocketnine.space/tslocum/citylimits/component" + "code.rocketnine.space/tslocum/citylimits/world" + "code.rocketnine.space/tslocum/gohan" + "github.com/hajimehoshi/ebiten/v2" +) + +type TickSystem struct { +} + +func NewTickSystem() *TickSystem { + s := &TickSystem{} + + return s +} + +func (s *TickSystem) Needs() []gohan.ComponentID { + return []gohan.ComponentID{ + component.PositionComponentID, + component.VelocityComponentID, + component.WeaponComponentID, + } +} + +func (s *TickSystem) Uses() []gohan.ComponentID { + return nil +} + +func (s *TickSystem) Update(_ *gohan.Context) error { + if world.World.Paused { + return nil + } + + world.World.Ticks++ + // Update date display. + if world.World.Ticks%world.MonthTicks == 0 { + world.World.HUDUpdated = true + } + return nil +} + +func (s *TickSystem) Draw(ctx *gohan.Context, screen *ebiten.Image) error { + return gohan.ErrSystemWithoutDraw +} diff --git a/world/world.go b/world/world.go index 6f5092b..8f552bc 100644 --- a/world/world.go +++ b/world/world.go @@ -7,6 +7,7 @@ import ( "log" "math/rand" "path/filepath" + "strconv" "code.rocketnine.space/tslocum/citylimits/asset" "code.rocketnine.space/tslocum/citylimits/component" @@ -16,6 +17,13 @@ import ( "github.com/lafriks/go-tiled" ) +const startingYear = 1950 + +const ( + MonthTicks = 144 * 3 + YearTicks = MonthTicks * 12 +) + const TileSize = 64 var DirtTile = uint32(9*32 + (0)) @@ -41,6 +49,9 @@ type HUDButton struct { var HUDButtons []*HUDButton +var CameraMinZoom = 0.4 +var CameraMaxZoom = 1.0 + var World = &GameWorld{ CamScale: startingZoom, CamScaleTarget: startingZoom, @@ -121,6 +132,10 @@ type GameWorld struct { HUDUpdated bool HUDButtonRects []image.Rectangle + Ticks int + + Paused bool + resetTipShown bool } @@ -502,3 +517,23 @@ var tooltips = map[int]string{ func Tooltip() string { return tooltips[World.HoverStructure] } + +var monthNames = []string{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +} + +func Date() (month string, year string) { + y, m := World.Ticks/YearTicks, (World.Ticks%YearTicks)/MonthTicks + return monthNames[m], strconv.Itoa(startingYear + y) +}