Add RCI window

This commit is contained in:
Trevor Slocum 2022-01-23 00:13:55 -08:00
parent c16dec2e65
commit cabff986f4
7 changed files with 196 additions and 45 deletions

View File

@ -51,7 +51,7 @@ func LoadSounds(ctx *audio.Context) {
SoundSelect.SetVolume(0.6)
SoundBulldoze = LoadOGG(ctx, "sound/bulldozer/bulldozer.ogg", true)
SoundBulldoze.SetVolume(0.4)
SoundBulldoze.SetVolume(0.6)
const popVolume = 0.15
SoundPop1 = LoadWAV(ctx, "sound/pop/pop1.wav")

View File

@ -135,13 +135,14 @@ func (g *game) Update() error {
Sprite: world.DrawMap(world.StructureBulldozer),
SpriteOffsetX: 12,
SpriteOffsetY: -48,
}, {
},
nil,
{
StructureType: world.StructureRoad,
Sprite: world.DrawMap(world.StructureRoad),
SpriteOffsetX: -12,
SpriteOffsetY: -28,
},
nil,
{
StructureType: world.StructureResidentialZone,
Sprite: world.DrawMap(world.StructureResidentialLow),

View File

@ -58,7 +58,7 @@ func (s *playerMoveSystem) buildStructure(structureType int, tileX int, tileY in
}
structure, err := world.BuildStructure(world.World.HoverStructure, false, tileX, tileY)
if err == nil {
if err == nil || world.World.HoverStructure == world.StructureBulldozer {
world.World.LastBuildX, world.World.LastBuildY = tileX, tileY
if world.IsPowerPlant(world.World.HoverStructure) {
@ -89,8 +89,9 @@ func (s *playerMoveSystem) buildStructure(structureType int, tileX int, tileY in
sound.Play()
}
cost := world.StructureCosts[structureType]
world.World.Funds -= cost
if err == nil {
world.World.Funds -= cost
}
world.World.HUDUpdated = true
} else {
@ -297,11 +298,19 @@ func (s *playerMoveSystem) Update(ctx *gohan.Context) error {
asset.SoundSelect.Rewind()
asset.SoundSelect.Play()
}
} else if world.AltButtonAt(x, y) == 0 {
world.World.ShowRCIWindow = !world.World.ShowRCIWindow
world.World.HUDUpdated = true
asset.SoundSelect.Rewind()
asset.SoundSelect.Play()
}
}
return nil
}
world.HandleRCIWindowClick(x, y)
if x >= world.World.ScreenW-helpW && y >= world.World.ScreenH-helpH {
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
const (

View File

@ -1,9 +1,11 @@
package system
import (
"fmt"
"image"
"image/color"
"math"
"strconv"
"strings"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
@ -16,7 +18,7 @@ import (
const (
helpW = 480
helpH = 225
helpH = 220
)
type RenderHudSystem struct {
@ -37,7 +39,7 @@ func NewRenderHudSystem() *RenderHudSystem {
helpImg: ebiten.NewImage(helpW, helpH),
}
sidebarShade := uint8(111)
sidebarShade := uint8(108)
s.sidebarColor = color.RGBA{sidebarShade, sidebarShade, sidebarShade, 255}
return s
@ -66,6 +68,7 @@ func (s *RenderHudSystem) Draw(_ *gohan.Context, screen *ebiten.Image) error {
s.drawSidebar()
s.drawMessages()
s.drawTooltip()
s.drawRCIWindow()
s.drawHelp()
world.World.HUDUpdated = false
}
@ -261,12 +264,14 @@ func (s *RenderHudSystem) drawDemand(x, y int) {
rciButtonY := rciY + (rciSize / 2) - (rciButtonHeight / 2)
rciButtonRect := image.Rect(rciX+rciButtonPadding, rciButtonY, rciX+buttonWidth-rciButtonPadding, rciButtonY+rciButtonHeight)
s.drawButtonBackground(s.tmpImg, rciButtonRect, false) // TODO
s.drawButtonBackground(s.tmpImg, rciButtonRect, world.World.ShowRCIWindow)
// Draw label.
ebitenutil.DebugPrintAt(s.tmpImg, "R C I", rciX+rciButtonPadding+rciButtonLabelPaddingX, rciButtonY+rciButtonLabelPaddingY)
s.drawButtonBorder(s.tmpImg, rciButtonRect, false) // TODO
s.drawButtonBorder(s.tmpImg, rciButtonRect, world.World.ShowRCIWindow)
world.World.RCIButtonRect = rciButtonRect
}
func (s *RenderHudSystem) drawPower(x, y int) {
@ -468,3 +473,52 @@ func (s *RenderHudSystem) drawHelp() {
op.GeoM.Translate(float64(world.World.ScreenW)-helpW, float64(world.World.ScreenH)-helpH)
s.hudImg.DrawImage(s.helpImg, op)
}
func (s *RenderHudSystem) drawRCIWindow() {
if !world.World.ShowRCIWindow {
return
}
const paddingX = 8
const (
rciWindowW = 425
rciWindowH = 100
)
rciWindowRect := image.Rect(world.World.ScreenW/2-rciWindowW/2, world.World.ScreenH/2-rciWindowH/2, world.World.ScreenW/2+rciWindowW, world.World.ScreenH/2+rciWindowH)
s.hudImg.SubImage(rciWindowRect).(*ebiten.Image).Fill(s.sidebarColor)
percentBar := func(tax float64) string {
if tax >= 1.0 {
tax = .99
}
bar := "----------"
bar = bar[:int(tax*10)] + "%" + bar[int(tax*10)+1:]
return bar
}
label := fmt.Sprintf(`
Residential %3s%% - |%s| +
Commercial %3s%% - |%s| +
Industrial %3s%% - |%s| +
`,
strconv.Itoa(int(world.World.TaxR*100)), percentBar(world.World.TaxR),
strconv.Itoa(int(world.World.TaxC*100)), percentBar(world.World.TaxC),
strconv.Itoa(int(world.World.TaxI*100)), percentBar(world.World.TaxI))
s.tmpImg.Clear()
ebitenutil.DebugPrint(s.tmpImg, strings.TrimSpace(label))
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(3, 3)
op.GeoM.Translate(float64(rciWindowRect.Min.X)+paddingX, float64(rciWindowRect.Min.Y))
s.hudImg.DrawImage(s.tmpImg, op)
s.hudImg.SubImage(image.Rect(rciWindowRect.Min.X, rciWindowRect.Min.Y, rciWindowRect.Max.X, rciWindowRect.Min.Y+1)).(*ebiten.Image).Fill(color.Black)
s.hudImg.SubImage(image.Rect(rciWindowRect.Min.X, rciWindowRect.Max.Y-1, rciWindowRect.Max.X, rciWindowRect.Max.Y)).(*ebiten.Image).Fill(color.Black)
s.hudImg.SubImage(image.Rect(rciWindowRect.Min.X, rciWindowRect.Min.Y, rciWindowRect.Min.X+1, rciWindowRect.Max.Y)).(*ebiten.Image).Fill(color.Black)
s.hudImg.SubImage(image.Rect(rciWindowRect.Max.X-1, rciWindowRect.Min.Y, rciWindowRect.Max.X, rciWindowRect.Max.Y)).(*ebiten.Image).Fill(color.Black)
world.World.RCIWindowRect = rciWindowRect
}

View File

@ -37,15 +37,24 @@ func (s *TaxSystem) Update(_ *gohan.Context) error {
return nil
}
taxCollectionAmount := 777.77
for _, zone := range world.World.Zones {
if !zone.Powered {
if zone.Population == 0 {
continue
}
_ = zone
taxRate := world.World.TaxR
if zone.Type == world.StructureCommercialZone {
taxRate = world.World.TaxC
} else if zone.Type == world.StructureIndustrialZone {
taxRate = world.World.TaxI
}
world.World.Funds += int(taxCollectionAmount * taxRate)
}
return nil
}
func (s *TaxSystem) Draw(ctx *gohan.Context, screen *ebiten.Image) error {
func (s *TaxSystem) Draw(_ *gohan.Context, _ *ebiten.Image) error {
return gohan.ErrSystemWithoutDraw
}

View File

@ -8,55 +8,55 @@ things YOUR way. For better or worse...
Will you lead the clean energy front,
or will you put profits before people?
`, `
Moving via mouse (2/10)
Moving Via Mouse (2/10)
To move around, place your cursor at
the edge of the screen, or press and
hold your middle mouse button while
moving your cursor.
`, `
Moving via keyboard (3/10)
Moving Via Keyboard (3/10)
You can also use your keyboard to move
by pressing any of the arrow keys or
W/A/S/D. Try using your mouse and/or
keyboard to move the camera around now.
`, `
Zoning areas (4/10)
Structures are built according to the
zoning category of an area. Demand for
each category (Residential, Commercial,
Zoning Areas (4/10)
Structures are built according to how
you zone areas of land. Demand for each
category (Residential, Commercial,
Industrial) is shown in the sidebar.
`, `
Did you know? (5/10)
Did You Know? (5/10)
Powering an area requires more input
energy than the resulting electricity.
In in fact, only a third of the energy
is transmitted as usable electricity.
In fact, only a third of the energy is
transmitted as usable electricity.
`, `
Building blocks of a city (6/10)
Building Blocks of a City (6/10)
Structures require power, sewer and
transportation in order to function.
This is all facilitated via roads and
underground wiring and piping.
`, `
Did you know? (7/10)
Did You Know? (7/10)
Meat-based diets require much more
energy, land and water resources than
vegetarian and especially vegan diets.
Animal farms are a source of pollution.
`, `
Power to the people (8/10)
Power to the People (8/10)
Build a power plant, then zone a few
areas for residential development.
Connect the power plant to the newly
zoned areas with a road.
`, `
Did you know? (9/10)
Did You Know? (9/10)
It takes large amounts of water to
convert fossil fuels into electricity.
Converting solar and wind energy uses
negligible amounts of water.
`, `
Collecting taxes (10/10)
Collecting Taxes (10/10)
Each year, taxes are collected from the
residents of the city. The tax rate you
set determines how appealing your city

View File

@ -13,6 +13,8 @@ import (
"sync"
"time"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/audio"
"golang.org/x/text/language"
@ -31,7 +33,7 @@ const startingYear = 1950
const maxPopulation = 100000
const (
MonthTicks = 144 * 7
MonthTicks = 144 * 5
YearTicks = MonthTicks * 12
)
@ -63,28 +65,33 @@ var HUDButtons []*HUDButton
var CameraMinZoom = 0.1
var CameraMaxZoom = 1.0
const (
startingTaxR = 0.12
startingTaxC = 0.12
startingTaxI = 0.12
)
const startingTax = 0.12
var World = &GameWorld{
CamScale: startingZoom,
CamScaleTarget: startingZoom,
CamMoving: true,
PlayerWidth: 8,
PlayerHeight: 32,
TileImages: make(map[uint32]*ebiten.Image),
ResetGame: true,
Level: NewLevel(256),
Printer: message.NewPrinter(language.English),
Power: newPowerMap(),
PowerOuts: newPowerOuts(),
BuildDragX: -1,
BuildDragY: -1,
LastBuildX: -1,
LastBuildY: -1,
PlayerWidth: 8,
PlayerHeight: 32,
TileImages: make(map[uint32]*ebiten.Image),
ResetGame: true,
Level: NewLevel(256),
Power: newPowerMap(),
PowerOuts: newPowerOuts(),
TaxR: startingTax,
TaxC: startingTax,
TaxI: startingTax,
BuildDragX: -1,
BuildDragY: -1,
LastBuildX: -1,
LastBuildY: -1,
Printer: message.NewPrinter(language.English),
}
type Zone struct {
@ -162,6 +169,10 @@ type GameWorld struct {
HUDUpdated bool
HUDButtonRects []image.Rectangle
RCIButtonRect image.Rectangle
RCIWindowRect image.Rectangle
ShowRCIWindow bool
HelpUpdated bool
HelpPage int
HelpButtonRects []image.Rectangle
@ -608,6 +619,72 @@ func HelpButtonAt(x, y int) int {
return -1
}
func AltButtonAt(x, y int) int {
point := image.Point{x, y}
if point.In(World.RCIButtonRect) {
return 0
}
return -1
}
func HandleRCIWindowClick(x, y int) {
if !World.ShowRCIWindow {
return
}
if !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
return
}
point := image.Point{x, y}
if !point.In(World.RCIWindowRect) {
return
}
var updated bool
barRectR := image.Rect(World.RCIWindowRect.Min.X+381, World.RCIWindowRect.Min.Y, World.RCIWindowRect.Min.X+575, World.RCIWindowRect.Min.Y+50)
barRectC := image.Rect(World.RCIWindowRect.Min.X+381, World.RCIWindowRect.Min.Y+50, World.RCIWindowRect.Min.X+575, World.RCIWindowRect.Min.Y+100)
barRectI := image.Rect(World.RCIWindowRect.Min.X+381, World.RCIWindowRect.Min.Y+100, World.RCIWindowRect.Min.X+575, World.RCIWindowRect.Max.Y)
if point.In(barRectR) {
World.TaxR = float64(x-barRectR.Min.X) / float64(barRectR.Dx())
if World.TaxR >= .99 {
World.TaxR = 1.0
}
World.HUDUpdated = true
updated = true
} else if point.In(barRectC) {
World.TaxC = float64(x-barRectC.Min.X) / float64(barRectC.Dx())
if World.TaxC >= .99 {
World.TaxC = 1.0
}
World.HUDUpdated = true
updated = true
} else if point.In(barRectI) {
World.TaxI = float64(x-barRectI.Min.X) / float64(barRectI.Dx())
if World.TaxI >= .99 {
World.TaxI = 1.0
}
World.HUDUpdated = true
updated = true
}
if !updated {
return
}
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) || World.Ticks%16 == 0 {
sounds := []*audio.Player{
asset.SoundPop1,
asset.SoundPop2,
asset.SoundPop3,
asset.SoundPop4,
asset.SoundPop5,
}
sound := sounds[rand.Intn(len(sounds))]
sound.Rewind()
sound.Play()
}
}
func SetHoverStructure(structureType int) {
World.HoverStructure = structureType
World.HUDUpdated = true
@ -643,6 +720,7 @@ func Demand() (r, c, i float64) {
}
barPeak := 100.0
r, c, i = r/barPeak, c/barPeak, i/barPeak
r, c, i = r*(1-World.TaxR), c*(1-World.TaxC), i*(1-World.TaxI)
clamp := func(v float64) float64 {
if math.IsNaN(v) {
return 0