Add bulldozer sound effect
parent
6dbd53e9c4
commit
55a106e84e
|
@ -33,6 +33,5 @@ Please share issues and suggestions [here](https://code.rocketnine.space/tslocum
|
|||
## Dependencies
|
||||
|
||||
- [ebiten](https://github.com/hajimehoshi/ebiten) - Game engine
|
||||
- [gohan](https://code.rocketnine.space/tslocum/gohan) - Entity Component System framework
|
||||
- [go-tiled](https://github.com/lafriks/go-tiled) - Tiled map file (.TMX) parser
|
||||
- [go-astar](https://github.com/beefsack/go-astar) - Pathfinding library
|
||||
|
|
|
@ -27,13 +27,14 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
SoundMusic *audio.Player
|
||||
SoundSelect *audio.Player
|
||||
SoundPop1 *audio.Player
|
||||
SoundPop2 *audio.Player
|
||||
SoundPop3 *audio.Player
|
||||
SoundPop4 *audio.Player
|
||||
SoundPop5 *audio.Player
|
||||
SoundMusic *audio.Player
|
||||
SoundSelect *audio.Player
|
||||
SoundBulldoze *audio.Player
|
||||
SoundPop1 *audio.Player
|
||||
SoundPop2 *audio.Player
|
||||
SoundPop3 *audio.Player
|
||||
SoundPop4 *audio.Player
|
||||
SoundPop5 *audio.Player
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -48,6 +49,9 @@ func LoadSounds(ctx *audio.Context) {
|
|||
SoundSelect = LoadWAV(ctx, "sound/select/select.wav")
|
||||
SoundSelect.SetVolume(0.6)
|
||||
|
||||
SoundBulldoze = LoadOGG(ctx, "sound/bulldozer/bulldozer.ogg", true)
|
||||
SoundBulldoze.SetVolume(0.4)
|
||||
|
||||
const popVolume = 0.15
|
||||
SoundPop1 = LoadWAV(ctx, "sound/pop/pop1.wav")
|
||||
SoundPop2 = LoadWAV(ctx, "sound/pop/pop2.wav")
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.2" orientation="isometric" renderorder="right-down" width="5" height="5" tilewidth="64" tileheight="32" infinite="0" nextlayerid="6" nextobjectid="1">
|
||||
<tileset firstgid="1" source="../image/tileset/MRMO_BRIK.tsx"/>
|
||||
<layer id="1" name="1" width="5" height="5">
|
||||
<data encoding="csv">
|
||||
519,519,519,519,519,
|
||||
519,519,519,519,519,
|
||||
519,519,519,519,519,
|
||||
519,519,519,4,4,
|
||||
519,519,519,4,527
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="2" name="2" width="5" height="5" offsetx="0" offsety="-40">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,
|
||||
0,0,0,0,0,
|
||||
0,0,0,0,0,
|
||||
0,0,0,11,11,
|
||||
0,0,0,11,6
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
|
@ -0,0 +1,6 @@
|
|||
This sound clip was made available for use by kijjaz under the
|
||||
Creative Commons 0 License.
|
||||
|
||||
Source: https://freesound.org/people/kijjaz/sounds/389584/
|
||||
|
||||
License: https://creativecommons.org/publicdomain/zero/1.0/
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
These sound clips were made available for use by soiboi under the
|
||||
This sound clip was made available for use by soiboi under the
|
||||
Creative Commons 0 License.
|
||||
|
||||
Source: https://freesound.org/people/soiboi/sounds/556823/
|
||||
|
|
28
game/game.go
28
game/game.go
|
@ -5,7 +5,6 @@ import (
|
|||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.rocketnine.space/tslocum/citylimits/entity"
|
||||
|
||||
|
@ -87,8 +86,6 @@ func (g *game) Update() error {
|
|||
if world.World.ResetGame {
|
||||
world.Reset()
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
err := world.LoadTileset()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -166,6 +163,11 @@ func (g *game) Update() error {
|
|||
SpriteOffsetX: -20,
|
||||
SpriteOffsetY: 2,
|
||||
Sprite: world.DrawMap(world.StructurePowerPlantCoal),
|
||||
}, {
|
||||
StructureType: world.StructurePowerPlantSolar,
|
||||
SpriteOffsetX: -20,
|
||||
SpriteOffsetY: 2,
|
||||
Sprite: world.DrawMap(world.StructurePowerPlantSolar),
|
||||
}, {
|
||||
StructureType: world.StructurePoliceStation,
|
||||
SpriteOffsetX: -19,
|
||||
|
@ -175,6 +177,24 @@ func (g *game) Update() error {
|
|||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
{
|
||||
StructureType: world.StructureToggleTransparentStructures,
|
||||
Sprite: transparentImg,
|
||||
|
@ -302,7 +322,7 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
drawn += g.renderSprite(float64(x), float64(y), 0, float64(i*-40), 0, 1, colorScale, alpha, false, false, sprite, screen)
|
||||
|
||||
// Draw power-outs.
|
||||
if world.World.Ticks%(144*2) < int(144.0*1.5) && world.World.PowerOuts[x][y] {
|
||||
if world.World.HavePowerOut && world.World.Ticks%(144*2) < int(144.0*1.5) && world.World.PowerOuts[x][y] {
|
||||
drawn += g.renderSprite(float64(x), float64(y), 0, -52, 0, 1, 1, 1, false, false, asset.ImgPower, screen)
|
||||
}
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
github.com/jfreymuth/oggvorbis v1.0.3 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20220114162006-9d54fb35363c // indirect
|
||||
golang.org/x/exp v0.0.0-20220121174013-7b334a16533f // indirect
|
||||
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -40,8 +40,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20220114162006-9d54fb35363c h1:aT7yTyPzmwDVPLHo6C/scvxQYjn6J7/NNTKaiCe+qxQ=
|
||||
golang.org/x/exp v0.0.0-20220114162006-9d54fb35363c/go.mod h1:M50CtfS+xv2iy/epuEazynj250ScQ0/DOjcsin9UE8k=
|
||||
golang.org/x/exp v0.0.0-20220121174013-7b334a16533f h1:u4dL7EmDaaJ+1e0HD9rawKa15yKPQZWXQ/epCOPAU+A=
|
||||
golang.org/x/exp v0.0.0-20220121174013-7b334a16533f/go.mod h1:M50CtfS+xv2iy/epuEazynj250ScQ0/DOjcsin9UE8k=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
|
5
main.go
5
main.go
|
@ -28,10 +28,6 @@ func main() {
|
|||
|
||||
parseFlags()
|
||||
|
||||
/*if world.World.Debug == 0 {
|
||||
world.SetMessage("MOVE: ARROW KEYS\nFIRE: Z KEY\nMUTE: M KEY", 144*4)
|
||||
}*/
|
||||
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc,
|
||||
syscall.SIGINT,
|
||||
|
@ -42,7 +38,6 @@ func main() {
|
|||
g.Exit()
|
||||
}()
|
||||
|
||||
// TODO
|
||||
world.StartGame()
|
||||
|
||||
err = ebiten.RunGame(g)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -50,6 +49,61 @@ func (_ *playerMoveSystem) Uses() []gohan.ComponentID {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *playerMoveSystem) buildStructure(structureType int, tileX int, tileY int, playSound bool) (*world.Structure, error) {
|
||||
structure, err := world.BuildStructure(world.World.HoverStructure, false, tileX, tileY)
|
||||
if err == nil {
|
||||
if world.IsPowerPlant(world.World.HoverStructure) {
|
||||
plant := &world.PowerPlant{
|
||||
Type: world.World.HoverStructure,
|
||||
X: tileX,
|
||||
Y: tileY,
|
||||
}
|
||||
world.World.PowerPlants = append(world.World.PowerPlants, plant)
|
||||
}
|
||||
|
||||
if world.IsZone(structureType) {
|
||||
zone := &world.Zone{
|
||||
Type: world.World.HoverStructure,
|
||||
X: tileX,
|
||||
Y: tileY,
|
||||
}
|
||||
world.World.Zones = append(world.World.Zones, zone)
|
||||
}
|
||||
|
||||
if world.World.HoverStructure != world.StructureBulldozer && playSound {
|
||||
sounds := []*audio.Player{
|
||||
asset.SoundPop2,
|
||||
asset.SoundPop3,
|
||||
}
|
||||
sound := sounds[rand.Intn(len(sounds))]
|
||||
sound.Rewind()
|
||||
sound.Play()
|
||||
}
|
||||
|
||||
cost := world.StructureCosts[structureType]
|
||||
world.World.Funds -= cost
|
||||
|
||||
world.World.HUDUpdated = true
|
||||
} else {
|
||||
dX := tileX - world.World.LastBuildX
|
||||
if dX < 0 {
|
||||
dX *= -1
|
||||
}
|
||||
dY := tileY - world.World.LastBuildY
|
||||
if dY < 0 {
|
||||
dY *= -1
|
||||
}
|
||||
if (dX > 1 || dY > 1) && err != world.ErrNothingToBulldoze {
|
||||
errMessage := err.Error()
|
||||
if len(errMessage) > 0 {
|
||||
errMessage = strings.ToUpper(errMessage[0:1]) + errMessage[1:]
|
||||
}
|
||||
world.ShowMessage(errMessage, 3)
|
||||
}
|
||||
}
|
||||
return structure, err
|
||||
}
|
||||
|
||||
func (s *playerMoveSystem) Update(ctx *gohan.Context) error {
|
||||
if ebiten.IsKeyPressed(ebiten.KeyEscape) && !world.World.DisableEsc {
|
||||
os.Exit(0)
|
||||
|
@ -230,70 +284,132 @@ func (s *playerMoveSystem) Update(ctx *gohan.Context) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if world.World.HoverStructure != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if x >= world.World.ScreenW-helpW && y >= world.World.ScreenH-helpH {
|
||||
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
||||
const (
|
||||
helpPrev = iota
|
||||
helpClose
|
||||
helpNext
|
||||
)
|
||||
|
||||
helpButton := world.HelpButtonAt(x-(world.World.ScreenW-helpW), y-(world.World.ScreenH-helpH))
|
||||
var updated bool
|
||||
switch helpButton {
|
||||
case helpPrev:
|
||||
if world.World.HelpPage > 0 {
|
||||
world.World.HelpPage--
|
||||
updated = true
|
||||
}
|
||||
case helpClose:
|
||||
world.World.HelpPage = -1
|
||||
updated = true
|
||||
case helpNext:
|
||||
if world.World.HelpPage < len(world.HelpText)-1 {
|
||||
world.World.HelpPage++
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
if updated {
|
||||
world.World.HelpUpdated = true
|
||||
world.World.HUDUpdated = true
|
||||
|
||||
asset.SoundSelect.Rewind()
|
||||
asset.SoundSelect.Play()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if world.World.HoverStructure != 0 {
|
||||
roadTiles := func(fromX, fromY, toX, toY int) [][2]int {
|
||||
var tiles [][2]int
|
||||
fx, fy := float64(fromX), float64(fromY)
|
||||
tx, ty := float64(toX), float64(toY)
|
||||
dx, dy := tx-fx, ty-fy
|
||||
for dx < -1 || dx > 1 || dy < -1 || dy > 1 {
|
||||
dx /= 2
|
||||
dy /= 2
|
||||
}
|
||||
tiles = append(tiles, [2]int{fromX, fromY})
|
||||
for fx != tx || fy != ty {
|
||||
fx, fy = fx+dx, fy+dy
|
||||
tiles = append(tiles, [2]int{int(fx), int(fy)})
|
||||
}
|
||||
return tiles
|
||||
}
|
||||
|
||||
tileX, tileY := world.ScreenToCartesian(x, y)
|
||||
if tileX >= 0 && tileY >= 0 && tileX < 256 && tileY < 256 {
|
||||
multiUseStructure := world.World.HoverStructure == world.StructureBulldozer || world.World.HoverStructure == world.StructureRoad
|
||||
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) || (multiUseStructure && ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft)) {
|
||||
|
||||
multiUseStructure := world.World.HoverStructure == world.StructureBulldozer || world.World.HoverStructure == world.StructureRoad || world.IsZone(world.World.HoverStructure)
|
||||
dragStarted := world.World.BuildDragX != -1 || world.World.BuildDragY != -1
|
||||
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) || (multiUseStructure && ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft)) || dragStarted {
|
||||
if !dragStarted {
|
||||
world.World.BuildDragX, world.World.BuildDragY = int(tileX), int(tileY)
|
||||
|
||||
if world.World.HoverStructure == world.StructureBulldozer {
|
||||
asset.SoundBulldoze.Play()
|
||||
}
|
||||
}
|
||||
|
||||
if world.World.HoverStructure == world.StructureRoad {
|
||||
tiles := roadTiles(world.World.BuildDragX, world.World.BuildDragY, int(tileX), int(tileY))
|
||||
|
||||
if dragStarted && !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
// TODO build all tiles
|
||||
world.World.Level.ClearHoverSprites()
|
||||
var cost int
|
||||
var builtRoad bool
|
||||
for _, tile := range tiles {
|
||||
_, err := s.buildStructure(world.World.HoverStructure, tile[0], tile[1], !builtRoad)
|
||||
if err == nil {
|
||||
cost += world.StructureCosts[world.World.HoverStructure]
|
||||
builtRoad = true
|
||||
}
|
||||
}
|
||||
if cost > 0 {
|
||||
world.ShowBuildCost(world.World.HoverStructure, cost)
|
||||
}
|
||||
|
||||
world.World.BuildDragX, world.World.BuildDragY = -1, -1
|
||||
dragStarted = false
|
||||
} else {
|
||||
// TODO draw hover sprites
|
||||
// TODO move below into shared func
|
||||
world.World.Level.ClearHoverSprites()
|
||||
for _, tile := range tiles {
|
||||
world.BuildStructure(world.World.HoverStructure, true, tile[0], tile[1])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else if dragStarted && !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
world.World.BuildDragX, world.World.BuildDragY = -1, -1
|
||||
asset.SoundBulldoze.Pause()
|
||||
}
|
||||
|
||||
cost := world.StructureCosts[world.World.HoverStructure]
|
||||
if world.World.Funds < cost {
|
||||
// TODO
|
||||
log.Println("NOT ENOUGH FUNDS")
|
||||
world.ShowMessage("Insufficient funds", 3)
|
||||
} else {
|
||||
world.World.Level.ClearHoverSprites()
|
||||
|
||||
// TODO draw hovers and build all roads in a line from drag start
|
||||
|
||||
s, err := world.BuildStructure(world.World.HoverStructure, false, int(tileX), int(tileY))
|
||||
structure, err := s.buildStructure(world.World.HoverStructure, int(tileX), int(tileY), true)
|
||||
if err == nil {
|
||||
tileX, tileY = float64(s.X), float64(s.Y)
|
||||
|
||||
isPowerPlant := world.World.HoverStructure == world.StructurePowerPlantCoal
|
||||
if isPowerPlant {
|
||||
plant := &world.PowerPlant{
|
||||
Type: world.World.HoverStructure,
|
||||
X: int(tileX),
|
||||
Y: int(tileY),
|
||||
}
|
||||
world.World.PowerPlants = append(world.World.PowerPlants, plant)
|
||||
}
|
||||
|
||||
isZone := world.World.HoverStructure == world.StructureResidentialZone || world.World.HoverStructure == world.StructureCommercialZone || world.World.HoverStructure == world.StructureIndustrialZone
|
||||
if isZone {
|
||||
zone := &world.Zone{
|
||||
Type: world.World.HoverStructure,
|
||||
X: int(tileX),
|
||||
Y: int(tileY),
|
||||
}
|
||||
world.World.Zones = append(world.World.Zones, zone)
|
||||
}
|
||||
|
||||
if world.World.HoverStructure != world.StructureBulldozer {
|
||||
sounds := []*audio.Player{
|
||||
asset.SoundPop2,
|
||||
asset.SoundPop3,
|
||||
}
|
||||
sound := sounds[rand.Intn(len(sounds))]
|
||||
sound.Rewind()
|
||||
sound.Play()
|
||||
}
|
||||
world.World.Funds -= cost
|
||||
|
||||
if world.World.HoverStructure == world.StructureResidentialZone {
|
||||
world.ShowMessage(world.World.Printer.Sprintf("Zoned area for residential use (-$%d)", cost), 3)
|
||||
} else if world.World.HoverStructure == world.StructureCommercialZone {
|
||||
world.ShowMessage(world.World.Printer.Sprintf("Zoned area for commercial use (-$%d)", cost), 3)
|
||||
} else if world.World.HoverStructure == world.StructureIndustrialZone {
|
||||
world.ShowMessage(world.World.Printer.Sprintf("Zoned area for industrial use (-$%d)", cost), 3)
|
||||
} else {
|
||||
world.ShowMessage(world.World.Printer.Sprintf("Built %s (-$%d)", strings.ToLower(world.StructureTooltips[world.World.HoverStructure]), cost), 3)
|
||||
}
|
||||
|
||||
world.World.HUDUpdated = true
|
||||
tileX, tileY = float64(structure.X), float64(structure.Y)
|
||||
world.ShowBuildCost(world.World.HoverStructure, cost)
|
||||
}
|
||||
|
||||
world.BuildStructure(world.World.HoverStructure, true, int(tileX), int(tileY))
|
||||
}
|
||||
} else if int(tileX) != world.World.HoverX || int(tileY) != world.World.HoverY {
|
||||
} else {
|
||||
if world.World.LastBuildX != -1 || world.World.LastBuildY != -1 {
|
||||
world.World.LastBuildX, world.World.LastBuildY = -1, -1
|
||||
}
|
||||
|
||||
world.World.Level.ClearHoverSprites()
|
||||
|
||||
world.BuildStructure(world.World.HoverStructure, true, int(tileX), int(tileY))
|
||||
|
|
|
@ -33,7 +33,10 @@ func (s *PopulateSystem) Update(_ *gohan.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const popDuration = 144 * 4
|
||||
const popDuration = 144 * 7
|
||||
if world.World.Ticks%popDuration != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Thresholds.
|
||||
const (
|
||||
|
@ -81,58 +84,56 @@ func (s *PopulateSystem) Update(_ *gohan.Context) error {
|
|||
}
|
||||
|
||||
const maxPopulation = 10
|
||||
if world.World.Ticks%popDuration == 0 {
|
||||
popR, popC, popI := world.Population()
|
||||
targetR, targetC, targetI := world.TargetPopulation()
|
||||
for _, zone := range world.World.Zones {
|
||||
var offset int
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
if popR < targetR {
|
||||
offset = 1
|
||||
} else if popR > targetR {
|
||||
offset = -1
|
||||
}
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
if popC < targetC {
|
||||
offset = 1
|
||||
} else if popC > targetC {
|
||||
offset = -1
|
||||
}
|
||||
} else { // Industrial
|
||||
if popI < targetI {
|
||||
offset = 1
|
||||
} else if popI > targetI {
|
||||
offset = -1
|
||||
}
|
||||
popR, popC, popI := world.Population()
|
||||
targetR, targetC, targetI := world.TargetPopulation()
|
||||
for _, zone := range world.World.Zones {
|
||||
var offset int
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
if popR < targetR {
|
||||
offset = 1
|
||||
} else if popR > targetR {
|
||||
offset = -1
|
||||
}
|
||||
if offset == -1 && zone.Population > 0 {
|
||||
zone.Population--
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
popR--
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
popC--
|
||||
} else { // Industrial
|
||||
popI--
|
||||
}
|
||||
} else if offset == 1 && zone.Population < maxPopulation && zone.Powered {
|
||||
zone.Population++
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
popR++
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
popC++
|
||||
} else { // Industrial
|
||||
popI++
|
||||
}
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
if popC < targetC {
|
||||
offset = 1
|
||||
} else if popC > targetC {
|
||||
offset = -1
|
||||
}
|
||||
newType := buildStructureType(zone.Type, zone.Population)
|
||||
// TODO only bulldoze when changed
|
||||
for offsetX := 0; offsetX < 2; offsetX++ {
|
||||
for offsetY := 0; offsetY < 2; offsetY++ {
|
||||
world.BuildStructure(world.StructureBulldozer, false, zone.X-offsetX, zone.Y-offsetY)
|
||||
}
|
||||
} else { // Industrial
|
||||
if popI < targetI {
|
||||
offset = 1
|
||||
} else if popI > targetI {
|
||||
offset = -1
|
||||
}
|
||||
world.BuildStructure(newType, false, zone.X, zone.Y)
|
||||
}
|
||||
if offset == -1 && zone.Population > 0 {
|
||||
zone.Population--
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
popR--
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
popC--
|
||||
} else { // Industrial
|
||||
popI--
|
||||
}
|
||||
} else if offset == 1 && zone.Population < maxPopulation && zone.Powered {
|
||||
zone.Population++
|
||||
if zone.Type == world.StructureResidentialZone {
|
||||
popR++
|
||||
} else if zone.Type == world.StructureCommercialZone {
|
||||
popC++
|
||||
} else { // Industrial
|
||||
popI++
|
||||
}
|
||||
}
|
||||
newType := buildStructureType(zone.Type, zone.Population)
|
||||
// TODO only bulldoze when changed
|
||||
for offsetX := 0; offsetX < 2; offsetX++ {
|
||||
for offsetY := 0; offsetY < 2; offsetY++ {
|
||||
world.BuildStructure(world.StructureBulldozer, false, zone.X-offsetX, zone.Y-offsetY)
|
||||
}
|
||||
}
|
||||
world.BuildStructure(newType, false, zone.X, zone.Y)
|
||||
}
|
||||
|
||||
// TODO populate and de-populate zones by target population
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/beefsack/go-astar"
|
||||
|
||||
"code.rocketnine.space/tslocum/citylimits/component"
|
||||
|
@ -58,7 +56,6 @@ func (s *PowerScanSystem) Update(_ *gohan.Context) error {
|
|||
plantSize = 5
|
||||
)
|
||||
powerSourceTiles := make([][]*world.PowerMapTile, len(world.World.PowerPlants))
|
||||
log.Println("POWER TILES")
|
||||
for i, plant := range world.World.PowerPlants {
|
||||
for y := 0; y < plantSize; y++ {
|
||||
t := world.World.Power.GetTile(plant.X+1, plant.Y-y)
|
||||
|
@ -132,23 +129,15 @@ func (s *PowerScanSystem) Update(_ *gohan.Context) error {
|
|||
}
|
||||
|
||||
for _, powerSource := range powerSourceTiles[j] {
|
||||
from := world.World.Power.GetTile(powerSource.X, powerSource.Y)
|
||||
|
||||
for _, to := range powerDestinationTiles {
|
||||
if to == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
from := world.World.Power.GetTile(powerSource.X, powerSource.Y)
|
||||
|
||||
log.Println("SEARCH", from.X, from.Y, "TO", to.X, to.Y)
|
||||
|
||||
/*for _, n := range powerSource.PathNeighbors() {
|
||||
t := n.(*world.PowerMapTile)
|
||||
log.Println("NEIGHBOR", t.X, t.Y, t.CarriesPower)
|
||||
}*/
|
||||
|
||||
p, dist, found := astar.Path(from, to)
|
||||
_, _, found := astar.Path(from, to)
|
||||
if found {
|
||||
log.Printf("Resulting path\n%+v %f", p, dist)
|
||||
powerRemaining[j] -= powerRequired
|
||||
powered = true
|
||||
break FINDPOWERPATH
|
||||
|
@ -157,11 +146,10 @@ func (s *PowerScanSystem) Update(_ *gohan.Context) error {
|
|||
}
|
||||
}
|
||||
zone.Powered = powered
|
||||
log.Println("ZONE", zone, zone.Powered)
|
||||
|
||||
if !powered {
|
||||
havePowerOut = true
|
||||
world.World.PowerOuts[zone.X][zone.Y] = true
|
||||
world.World.HavePowerOut = true
|
||||
}
|
||||
|
||||
totalPowerRequired += powerRequired
|
||||
|
|
|
@ -3,6 +3,7 @@ package system
|
|||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
|
@ -13,11 +14,17 @@ import (
|
|||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
helpW = 480
|
||||
helpH = 185
|
||||
)
|
||||
|
||||
type RenderHudSystem struct {
|
||||
op *ebiten.DrawImageOptions
|
||||
hudImg *ebiten.Image
|
||||
tmpImg *ebiten.Image
|
||||
tmpImg2 *ebiten.Image
|
||||
helpImg *ebiten.Image
|
||||
sidebarColor color.RGBA
|
||||
}
|
||||
|
||||
|
@ -27,6 +34,7 @@ func NewRenderHudSystem() *RenderHudSystem {
|
|||
hudImg: ebiten.NewImage(1, 1),
|
||||
tmpImg: ebiten.NewImage(1, 1),
|
||||
tmpImg2: ebiten.NewImage(1, 1),
|
||||
helpImg: ebiten.NewImage(helpW, helpH),
|
||||
}
|
||||
|
||||
sidebarShade := uint8(111)
|
||||
|
@ -58,12 +66,16 @@ func (s *RenderHudSystem) Draw(_ *gohan.Context, screen *ebiten.Image) error {
|
|||
s.drawSidebar()
|
||||
s.drawMessages()
|
||||
s.drawTooltip()
|
||||
s.drawHelp()
|
||||
world.World.HUDUpdated = false
|
||||
}
|
||||
screen.DrawImage(s.hudImg, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
const columns = 3
|
||||
const buttonWidth = world.SidebarWidth / columns
|
||||
|
||||
func (s *RenderHudSystem) drawSidebar() {
|
||||
bounds := s.hudImg.Bounds()
|
||||
if bounds.Dx() != world.World.ScreenW || bounds.Dy() != world.World.ScreenH {
|
||||
|
@ -75,10 +87,6 @@ func (s *RenderHudSystem) drawSidebar() {
|
|||
s.tmpImg.Clear()
|
||||
s.tmpImg2.Clear()
|
||||
}
|
||||
w := world.SidebarWidth
|
||||
if bounds.Dx() < w {
|
||||
w = bounds.Dx()
|
||||
}
|
||||
|
||||
// Fill background.
|
||||
s.hudImg.SubImage(image.Rect(0, 0, world.SidebarWidth, world.World.ScreenH)).(*ebiten.Image).Fill(s.sidebarColor)
|
||||
|
@ -86,9 +94,6 @@ func (s *RenderHudSystem) drawSidebar() {
|
|||
// Draw buttons.
|
||||
|
||||
const paddingSize = 1
|
||||
const columns = 3
|
||||
|
||||
const buttonWidth = world.SidebarWidth / columns
|
||||
const buttonHeight = buttonWidth
|
||||
world.World.HUDButtonRects = make([]image.Rectangle, len(world.HUDButtons))
|
||||
var lastButtonY int
|
||||
|
@ -115,58 +120,25 @@ func (s *RenderHudSystem) drawSidebar() {
|
|||
}
|
||||
|
||||
world.World.HUDButtonRects[i] = r
|
||||
lastButtonY = y
|
||||
}
|
||||
|
||||
s.drawDate(lastButtonY + buttonHeight + 10)
|
||||
s.drawFunds(lastButtonY + buttonHeight + 55)
|
||||
|
||||
// Draw RCI indicator.
|
||||
rciPadding := buttonWidth - 14
|
||||
const rciSize = 100
|
||||
rciX := buttonWidth
|
||||
rciY := lastButtonY + buttonHeight + 55 + rciPadding
|
||||
|
||||
const rciButtonHeight = 20
|
||||
|
||||
// Draw RCI bars.
|
||||
colorR := color.RGBA{0, 255, 0, 255}
|
||||
colorC := color.RGBA{0, 0, 255, 255}
|
||||
colorI := color.RGBA{231, 231, 72, 255}
|
||||
demandR, demandC, demandI := world.Demand()
|
||||
drawDemandBar := func(demand float64, clr color.RGBA, i int) {
|
||||
barOffsetSize := 12
|
||||
barOffset := -barOffsetSize + (i * barOffsetSize)
|
||||
barWidth := 7
|
||||
barX := rciX + buttonWidth/2 - barWidth/2 + barOffset
|
||||
barY := rciY + (rciSize / 2)
|
||||
if demand < 0 {
|
||||
barY += rciButtonHeight / 2
|
||||
} else {
|
||||
barY -= rciButtonHeight / 2
|
||||
if button != nil && button.StructureType != world.StructureToggleTransparentStructures {
|
||||
lastButtonY = y
|
||||
}
|
||||
barHeight := int((float64(rciSize) / 2) * demand)
|
||||
s.tmpImg.SubImage(image.Rect(barX, barY, barX+barWidth, barY-barHeight)).(*ebiten.Image).Fill(clr)
|
||||
}
|
||||
drawDemandBar(demandR, colorR, 0)
|
||||
drawDemandBar(demandC, colorC, 1)
|
||||
drawDemandBar(demandI, colorI, 2)
|
||||
|
||||
// Draw RCI button.
|
||||
const rciButtonPadding = 12
|
||||
const rciButtonLabelPaddingX = 6
|
||||
const rciButtonLabelPaddingY = 1
|
||||
rciButtonY := rciY + (rciSize / 2) - (rciButtonHeight / 2)
|
||||
rciButtonRect := image.Rect(rciX+rciButtonPadding, rciButtonY, rciX+buttonWidth-rciButtonPadding, rciButtonY+rciButtonHeight)
|
||||
dateY := lastButtonY + buttonHeight*2 - buttonHeight/2 - 16
|
||||
s.drawDate(dateY)
|
||||
s.drawFunds(dateY + 50)
|
||||
|
||||
s.drawButtonBackground(s.tmpImg, rciButtonRect, false) // TODO
|
||||
indicatorY := dateY + 179
|
||||
// Draw RCI indicator.
|
||||
s.drawDemand(buttonWidth/2, indicatorY)
|
||||
|
||||
// Draw RCI label.
|
||||
ebitenutil.DebugPrintAt(s.tmpImg, "R C I", rciX+rciButtonPadding+rciButtonLabelPaddingX, rciButtonY+rciButtonLabelPaddingY)
|
||||
|
||||
s.drawButtonBorder(s.tmpImg, rciButtonRect, false) // TODO
|
||||
// Draw PWR indicator.
|
||||
s.drawPower(buttonWidth/2+buttonWidth, indicatorY)
|
||||
|
||||
s.hudImg.DrawImage(s.tmpImg, nil)
|
||||
|
||||
s.hudImg.SubImage(image.Rect(world.SidebarWidth-1, 0, world.SidebarWidth, world.World.ScreenH)).(*ebiten.Image).Fill(color.Black)
|
||||
}
|
||||
|
||||
func (s *RenderHudSystem) drawButtonBackground(img *ebiten.Image, r image.Rectangle, selected bool) {
|
||||
|
@ -243,6 +215,116 @@ func maxLen(v []string) int {
|
|||
return max
|
||||
}
|
||||
|
||||
func (s *RenderHudSystem) drawDemand(x, y int) {
|
||||
const rciSize = 100
|
||||
rciX := x
|
||||
rciY := y
|
||||
|
||||
const rciButtonHeight = 20
|
||||
|
||||
colorR := color.RGBA{0, 255, 0, 255}
|
||||
colorC := color.RGBA{0, 0, 255, 255}
|
||||
colorI := color.RGBA{231, 231, 72, 255}
|
||||
demandR, demandC, demandI := world.Demand()
|
||||
drawDemandBar := func(demand float64, clr color.RGBA, i int) {
|
||||
barOffsetSize := 12
|
||||
barOffset := -barOffsetSize + (i * barOffsetSize)
|
||||
barWidth := 7
|
||||
barX := rciX + buttonWidth/2 - barWidth/2 + barOffset
|
||||
barY := rciY + (rciSize / 2)
|
||||
if demand < 0 {
|
||||
barY += rciButtonHeight / 2
|
||||
} else {
|
||||
barY -= rciButtonHeight / 2
|
||||
}
|
||||
barHeight := int((float64(rciSize) / 2) * demand)
|
||||
s.tmpImg.SubImage(image.Rect(barX, barY, barX+barWidth, barY-barHeight)).(*ebiten.Image).Fill(clr)
|
||||
}
|
||||
drawDemandBar(demandR, colorR, 0)
|
||||
drawDemandBar(demandC, colorC, 1)
|
||||
drawDemandBar(demandI, colorI, 2)
|
||||
|
||||
// Draw button.
|
||||
const rciButtonPadding = 12
|
||||
const rciButtonLabelPaddingX = 6
|
||||
const rciButtonLabelPaddingY = 1
|
||||
rciButtonY := rciY + (rciSize / 2) - (rciButtonHeight / 2)
|
||||
rciButtonRect := image.Rect(rciX+rciButtonPadding, rciButtonY, rciX+buttonWidth-rciButtonPadding, rciButtonY+rciButtonHeight)
|
||||
|
||||
s.drawButtonBackground(s.tmpImg, rciButtonRect, false) // TODO
|
||||
|
||||
// Draw label.
|
||||
ebitenutil.DebugPrintAt(s.tmpImg, "R C I", rciX+rciButtonPadding+rciButtonLabelPaddingX, rciButtonY+rciButtonLabelPaddingY)
|
||||
|
||||
s.drawButtonBorder(s.tmpImg, rciButtonRect, false) // TODO
|
||||
}
|
||||
|
||||
func (s *RenderHudSystem) drawPower(x, y int) {
|
||||
const rciSize = 100
|
||||
rciX := x
|
||||
rciY := y
|
||||
|
||||
const rciButtonHeight = 20
|
||||
|
||||
colorPowerNormal := color.RGBA{0, 255, 0, 255}
|
||||
colorPowerOut := color.RGBA{255, 0, 0, 255}
|
||||
colorPowerCapacity := color.RGBA{16, 16, 16, 255}
|
||||
drawPowerBar := func(demand float64, clr color.RGBA, i int) {
|
||||
barOffsetSize := 7
|
||||
barOffset := -barOffsetSize + (i * barOffsetSize)
|
||||
barWidth := 7
|
||||
barX := rciX + buttonWidth/2 - barWidth/2 + barOffset + 4
|
||||
barY := rciY + (rciSize / 2)
|
||||
if demand < 0 {
|
||||
barY += rciButtonHeight / 2
|
||||
} else {
|
||||
barY -= rciButtonHeight / 2
|
||||
}
|
||||
barHeight := int((float64(rciSize) / 2) * demand)
|
||||
s.tmpImg.SubImage(image.Rect(barX, barY, barX+barWidth, barY-barHeight)).(*ebiten.Image).Fill(clr)
|
||||
}
|
||||
|
||||
powerColor := colorPowerNormal
|
||||
if world.World.HavePowerOut || world.World.PowerNeeded > world.World.PowerAvailable {
|
||||
powerColor = colorPowerOut
|
||||
}
|
||||
|
||||
max := world.World.PowerNeeded
|
||||
if world.World.PowerAvailable > max {
|
||||
max = world.World.PowerAvailable
|
||||
}
|
||||
|
||||
pctUsage, pctCapacity := float64(world.World.PowerNeeded)/float64(max), float64(world.World.PowerAvailable)/float64(max)
|
||||
clamp := func(v float64) float64 {
|
||||
if math.IsNaN(v) {
|
||||
return 0
|
||||
}
|
||||
if v < -1 {
|
||||
v = -1
|
||||
} else if v > 1 {
|
||||
v = 1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
drawPowerBar(clamp(pctUsage), powerColor, 0)
|
||||
drawPowerBar(clamp(pctCapacity), colorPowerCapacity, 1)
|
||||
|
||||
// Draw button.
|
||||
const rciButtonPadding = 12
|
||||
const rciButtonLabelPaddingX = 6
|
||||
const rciButtonLabelPaddingY = 1
|
||||
rciButtonY := rciY + (rciSize / 2) - (rciButtonHeight / 2)
|
||||
rciButtonRect := image.Rect(rciX+rciButtonPadding, rciButtonY, rciX+buttonWidth-rciButtonPadding, rciButtonY+rciButtonHeight)
|
||||
|
||||
s.drawButtonBackground(s.tmpImg, rciButtonRect, false) // TODO
|
||||
|
||||
// Draw label.
|
||||
ebitenutil.DebugPrintAt(s.tmpImg, "POWER", rciX+rciButtonPadding+rciButtonLabelPaddingX, rciButtonY+rciButtonLabelPaddingY)
|
||||
|
||||
s.drawButtonBorder(s.tmpImg, rciButtonRect, false) // TODO
|
||||
}
|
||||
|
||||
func (s *RenderHudSystem) drawMessages() {
|
||||
lines := len(world.World.Messages)
|
||||
if lines == 0 {
|
||||
|
@ -264,10 +346,10 @@ func (s *RenderHudSystem) drawMessages() {
|
|||
max := len(label)
|
||||
lines = 1
|
||||
|
||||
const padding = 12
|
||||
const padding = 10
|
||||
|
||||
scale := 2.0
|
||||
w, h := (max*6+10)*int(scale), 16*(int(scale))*lines+6
|
||||
w, h := (max*6+10)*int(scale), 16*(int(scale))*lines+10
|
||||
x, y := world.World.ScreenW-w, 0
|
||||
r := image.Rect(x, y, x+w, y+h)
|
||||
s.hudImg.SubImage(r).(*ebiten.Image).Fill(color.RGBA{0, 0, 0, 120})
|
||||
|
@ -276,7 +358,7 @@ func (s *RenderHudSystem) drawMessages() {
|
|||
ebitenutil.DebugPrint(s.tmpImg, label)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(scale, scale)
|
||||
op.GeoM.Translate(float64(x)+padding, 0)
|
||||
op.GeoM.Translate(float64(x)+padding, 3)
|
||||
s.hudImg.DrawImage(s.tmpImg, op)
|
||||
}
|
||||
|
||||
|
@ -320,3 +402,59 @@ func (s *RenderHudSystem) drawFunds(y int) {
|
|||
op.GeoM.Translate(float64(x), float64(y))
|
||||
s.hudImg.DrawImage(s.tmpImg2, op)
|
||||
}
|
||||
|
||||
func (s *RenderHudSystem) drawHelp() {
|
||||
if world.World.HelpPage < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if world.World.HelpUpdated {
|
||||
s.helpImg.Fill(s.sidebarColor)
|
||||
|
||||
label := strings.TrimSpace(world.HelpText[world.World.HelpPage])
|
||||
|
||||
s.tmpImg.Clear()
|
||||
ebitenutil.DebugPrint(s.tmpImg, label)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(2, 2)
|
||||
op.GeoM.Translate(5, 0)
|
||||
s.helpImg.DrawImage(s.tmpImg, op)
|
||||
|
||||
s.helpImg.SubImage(image.Rect(0, 0, helpW, 1)).(*ebiten.Image).Fill(color.Black)
|
||||
s.helpImg.SubImage(image.Rect(0, 0, 1, helpH)).(*ebiten.Image).Fill(color.Black)
|
||||
|
||||
// Draw prev/next buttons.
|
||||
buttonSize := 32
|
||||
buttonPadding := 4
|
||||
prevRect := image.Rect(buttonPadding+2, helpH-buttonSize-buttonPadding+1, buttonSize+buttonPadding+2, helpH-buttonPadding+1)
|
||||
closeRect := image.Rect(helpW/2-buttonSize/2, helpH-buttonSize-buttonPadding+1, helpW/2+buttonSize/2, helpH-buttonPadding+1)
|
||||
nextRect := image.Rect(helpW-buttonPadding, helpH-buttonSize-buttonPadding+1, helpW-buttonSize-buttonPadding, helpH-buttonPadding+1)
|
||||
|
||||
drawButton := func(r image.Rectangle, l string) {
|
||||
s.drawButtonBackground(s.helpImg, r, false)
|
||||
ebitenutil.DebugPrintAt(s.helpImg, l, r.Min.X+buttonSize/2-4, r.Min.Y+buttonSize/2-10)
|
||||
s.drawButtonBorder(s.helpImg, r, false)
|
||||
}
|
||||
|
||||
if world.World.HelpPage > 0 {
|
||||
drawButton(prevRect, "<")
|
||||
}
|
||||
drawButton(closeRect, "X")
|
||||
if world.World.HelpPage < len(world.HelpText)-1 {
|
||||
drawButton(nextRect, ">")
|
||||
}
|
||||
|
||||
world.World.HelpButtonRects = []image.Rectangle{
|
||||
prevRect,
|
||||
closeRect,
|
||||
nextRect,
|
||||
}
|
||||
|
||||
world.World.HelpUpdated = false
|
||||
}
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(world.World.ScreenW)-helpW, float64(world.World.ScreenH)-helpH)
|
||||
s.hudImg.DrawImage(s.helpImg, op)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package world
|
||||
|
||||
// HelpText lines must be 39 characters or less.
|
||||
var HelpText = []string{`
|
||||
Welcome to City Limits! (1/3)
|
||||
Blah blah blah...1231231231231231231234
|
||||
Testing, testing... Testing, testing...
|
||||
How does this much text per page seem?
|
||||
`, `
|
||||
Yadda yadda yadda (2/3)
|
||||
Do you like the game?
|
||||
I hoped you would.
|
||||
I spent a lot of time making it.
|
||||
`, `
|
||||
Oh no... More text?!? (3/3)
|
||||
If I wanted to read, I would have
|
||||
grabbed a book, not a game! I'm outta
|
||||
here! *Door slam*... *Engine starting*
|
||||
`,
|
||||
}
|
|
@ -4,12 +4,6 @@ import (
|
|||
"github.com/beefsack/go-astar"
|
||||
)
|
||||
|
||||
const (
|
||||
powerEmptyTile = iota
|
||||
powerSourceTile
|
||||
powerDestinationTile
|
||||
)
|
||||
|
||||
type PowerMapTile struct {
|
||||
X int
|
||||
Y int
|
||||
|
@ -94,6 +88,7 @@ func ResetPowerOuts() {
|
|||
World.PowerOuts[x][y] = false
|
||||
}
|
||||
}
|
||||
World.HavePowerOut = false
|
||||
}
|
||||
|
||||
func (m PowerMap) GetTile(x, y int) *PowerMapTile {
|
||||
|
@ -105,6 +100,9 @@ func (m PowerMap) GetTile(x, y int) *PowerMapTile {
|
|||
|
||||
func (m PowerMap) SetTile(x, y int, carriesPower bool) {
|
||||
t := m[x][y]
|
||||
if t.CarriesPower == carriesPower {
|
||||
return
|
||||
}
|
||||
t.CarriesPower = carriesPower
|
||||
|
||||
World.PowerUpdated = true
|
||||
|
|
|
@ -20,6 +20,7 @@ const (
|
|||
StructureIndustrialHigh
|
||||
StructurePoliceStation
|
||||
StructurePowerPlantCoal
|
||||
StructurePowerPlantSolar
|
||||
)
|
||||
|
||||
var StructureFilePaths = map[int]string{
|
||||
|
@ -39,6 +40,7 @@ var StructureFilePaths = map[int]string{
|
|||
StructureIndustrialHigh: "map/industrial_high1.tmx",
|
||||
StructurePoliceStation: "map/policestation.tmx",
|
||||
StructurePowerPlantCoal: "map/power_coal.tmx",
|
||||
StructurePowerPlantSolar: "map/power_solar.tmx",
|
||||
}
|
||||
|
||||
type Structure struct {
|
||||
|
|
|
@ -9,7 +9,9 @@ import (
|
|||
"math/rand"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
|
@ -27,7 +29,7 @@ const startingYear = 1950
|
|||
const maxPopulation = 100000
|
||||
|
||||
const (
|
||||
MonthTicks = 144 * 3
|
||||
MonthTicks = 144 * 7
|
||||
YearTicks = MonthTicks * 12
|
||||
)
|
||||
|
||||
|
@ -39,7 +41,7 @@ const startingFunds = 10000
|
|||
|
||||
const startingZoom = 1.0
|
||||
|
||||
const SidebarWidth = 198
|
||||
const SidebarWidth = 199
|
||||
|
||||
type HUDButton struct {
|
||||
Sprite *ebiten.Image
|
||||
|
@ -50,7 +52,7 @@ type HUDButton struct {
|
|||
|
||||
var HUDButtons []*HUDButton
|
||||
|
||||
var CameraMinZoom = 0.4
|
||||
var CameraMinZoom = 0.1
|
||||
var CameraMaxZoom = 1.0
|
||||
|
||||
var World = &GameWorld{
|
||||
|
@ -65,6 +67,10 @@ var World = &GameWorld{
|
|||
Printer: message.NewPrinter(language.English),
|
||||
Power: newPowerMap(),
|
||||
PowerOuts: newPowerOuts(),
|
||||
BuildDragX: -1,
|
||||
BuildDragY: -1,
|
||||
LastBuildX: -1,
|
||||
LastBuildY: -1,
|
||||
}
|
||||
|
||||
type Zone struct {
|
||||
|
@ -142,10 +148,15 @@ type GameWorld struct {
|
|||
HUDUpdated bool
|
||||
HUDButtonRects []image.Rectangle
|
||||
|
||||
HelpUpdated bool
|
||||
HelpPage int
|
||||
HelpButtonRects []image.Rectangle
|
||||
|
||||
PowerPlants []*PowerPlant
|
||||
Zones []*Zone
|
||||
|
||||
PowerOuts [][]bool
|
||||
HavePowerOut bool
|
||||
PowerOuts [][]bool
|
||||
|
||||
Ticks int
|
||||
|
||||
|
@ -165,9 +176,17 @@ type GameWorld struct {
|
|||
PowerAvailable int
|
||||
PowerNeeded int
|
||||
|
||||
BuildDragX int
|
||||
BuildDragY int
|
||||
|
||||
LastBuildX int
|
||||
LastBuildY int
|
||||
|
||||
resetTipShown bool
|
||||
}
|
||||
|
||||
var ErrNothingToBulldoze = errors.New("nothing to bulldoze")
|
||||
|
||||
func TileToGameCoords(x, y int) (float64, float64) {
|
||||
//return float64(x) * 32, float64(g.currentMap.Height*32) - float64(y)*32 - 32
|
||||
return float64(x) * TileSize, float64(y) * TileSize
|
||||
|
@ -179,6 +198,8 @@ func Reset() {
|
|||
}
|
||||
World.Player = 0
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
World.Funds = startingFunds
|
||||
|
||||
World.ObjectGroups = nil
|
||||
|
@ -188,6 +209,10 @@ func Reset() {
|
|||
World.TriggerEntities = nil
|
||||
World.TriggerRects = nil
|
||||
World.TriggerNames = nil
|
||||
|
||||
World.CamX = float64((32 * TileSize) - rand.Intn(64*TileSize))
|
||||
World.CamY = float64((32 * TileSize) + rand.Intn(32*TileSize))
|
||||
|
||||
}
|
||||
|
||||
func LoadMap(structureType int) (*tiled.Map, error) {
|
||||
|
@ -283,6 +308,20 @@ func LoadTileset() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ShowBuildCost(structureType int, cost int) {
|
||||
if structureType == StructureBulldozer {
|
||||
ShowMessage(World.Printer.Sprintf("Bulldozed area (-$%d)", cost), 3)
|
||||
} else if structureType == StructureResidentialZone {
|
||||
ShowMessage(World.Printer.Sprintf("Zoned area for residential use (-$%d)", cost), 3)
|
||||
} else if structureType == StructureCommercialZone {
|
||||
ShowMessage(World.Printer.Sprintf("Zoned area for commercial use (-$%d)", cost), 3)
|
||||
} else if structureType == StructureIndustrialZone {
|
||||
ShowMessage(World.Printer.Sprintf("Zoned area for industrial use (-$%d)", cost), 3)
|
||||
} else {
|
||||
ShowMessage(World.Printer.Sprintf("Built %s (-$%d)", strings.ToLower(StructureTooltips[World.HoverStructure]), cost), 3)
|
||||
}
|
||||
}
|
||||
|
||||
func BuildStructure(structureType int, hover bool, placeX int, placeY int) (*Structure, error) {
|
||||
// For previewing buildings
|
||||
/*v := rand.Intn(3)
|
||||
|
@ -361,7 +400,7 @@ func BuildStructure(structureType int, hover bool, placeX int, placeY int) (*Str
|
|||
}
|
||||
}
|
||||
if !bulldozed {
|
||||
return nil, errors.New("nothing to bulldoze")
|
||||
return nil, ErrNothingToBulldoze
|
||||
}
|
||||
World.Power.SetTile(placeX, placeY, false)
|
||||
return structure, nil
|
||||
|
@ -475,7 +514,11 @@ VALIDBUILD:
|
|||
if structureType == StructureRoad {
|
||||
World.Power.SetTile(tx, ty, true)
|
||||
}
|
||||
World.PowerUpdated = true
|
||||
|
||||
isZone := structureType == StructureResidentialZone || structureType == StructureCommercialZone || structureType == StructureIndustrialZone
|
||||
if isZone || structureType == StructurePowerPlantCoal || structureType == StructureBulldozer {
|
||||
World.PowerUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle flipping
|
||||
|
@ -535,6 +578,9 @@ func StartGame() {
|
|||
if !World.MuteMusic {
|
||||
asset.SoundMusic.Play()
|
||||
}
|
||||
|
||||
// Show initial help page.
|
||||
SetHelpPage(0)
|
||||
}
|
||||
|
||||
// CartesianToIso transforms cartesian coordinates into isometric coordinates.
|
||||
|
@ -571,15 +617,25 @@ func ScreenToCartesian(x, y int) (float64, float64) {
|
|||
}
|
||||
|
||||
func HUDButtonAt(x, y int) *HUDButton {
|
||||
for i, colorRect := range World.HUDButtonRects {
|
||||
point := image.Point{x, y}
|
||||
if point.In(colorRect) {
|
||||
point := image.Point{x, y}
|
||||
for i, rect := range World.HUDButtonRects {
|
||||
if point.In(rect) {
|
||||
return HUDButtons[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HelpButtonAt(x, y int) int {
|
||||
point := image.Point{x, y}
|
||||
for i, rect := range World.HelpButtonRects {
|
||||
if point.In(rect) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func SetHoverStructure(structureType int) {
|
||||
World.HoverStructure = structureType
|
||||
World.HUDUpdated = true
|
||||
|
@ -630,6 +686,7 @@ var StructureTooltips = map[int]string{
|
|||
StructureRoad: "Road",
|
||||
StructurePoliceStation: "Police station",
|
||||
StructurePowerPlantCoal: "Coal power plant",
|
||||
StructurePowerPlantSolar: "Solar power plant",
|
||||
StructureResidentialZone: "Residential zone",
|
||||
StructureCommercialZone: "Commercial zone",
|
||||
StructureIndustrialZone: "Industrial zone",
|
||||
|
@ -640,6 +697,7 @@ var StructureCosts = map[int]int{
|
|||
StructureRoad: 25,
|
||||
StructurePoliceStation: 1000,
|
||||
StructurePowerPlantCoal: 4000,
|
||||
StructurePowerPlantSolar: 10000,
|
||||
StructureResidentialZone: 100,
|
||||
StructureCommercialZone: 200,
|
||||
StructureIndustrialZone: 100,
|
||||
|
@ -726,7 +784,8 @@ func ValidXY(x, y int) bool {
|
|||
}
|
||||
|
||||
var PowerPlantCapacities = map[int]int{
|
||||
StructurePowerPlantCoal: 60,
|
||||
StructurePowerPlantCoal: 60,
|
||||
StructurePowerPlantSolar: 40,
|
||||
}
|
||||
|
||||
var ZonePowerRequirement = map[int]int{
|
||||
|
@ -734,3 +793,16 @@ var ZonePowerRequirement = map[int]int{
|
|||
StructureCommercialZone: 1,
|
||||
StructureIndustrialZone: 1,
|
||||
}
|
||||
|
||||
func SetHelpPage(page int) {
|
||||
World.HelpPage = page
|
||||
World.HelpUpdated = true
|
||||
}
|
||||
|
||||
func IsPowerPlant(structureType int) bool {
|
||||
return structureType == StructurePowerPlantCoal || structureType == StructurePowerPlantSolar
|
||||
}
|
||||
|
||||
func IsZone(structureType int) bool {
|
||||
return structureType == StructureResidentialZone || structureType == StructureCommercialZone || structureType == StructureIndustrialZone
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue