diff --git a/cmd/imadapt/color.go b/cmd/imadapt/color.go new file mode 100644 index 0000000..8eb2a7e --- /dev/null +++ b/cmd/imadapt/color.go @@ -0,0 +1,34 @@ +package main + +import ( + "image" + "image/color" + + "opslag.de/schobers/tins2020/img" +) + +func colorImage(path string, col color.Color) error { + src, err := img.DecodeImage(path) + if err != nil { + return err + } + + colNRGBA := color.NRGBAModel.Convert(col).(color.NRGBA) + bounds := src.Bounds() + dst := image.NewRGBA(bounds) + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + c := src.At(x, y) + srcCol := color.NRGBAModel.Convert(c).(color.NRGBA) + if srcCol.A > 0 { + dstCol := colNRGBA + dstCol.A = srcCol.A + dst.Set(x, y, dstCol) + } else { + dst.Set(x, y, c) + } + } + } + return img.EncodePNG(path, dst) + return nil +} diff --git a/cmd/imadapt/crop.go b/cmd/imadapt/crop.go index 56f6d64..1651f7c 100644 --- a/cmd/imadapt/crop.go +++ b/cmd/imadapt/crop.go @@ -5,10 +5,11 @@ import ( "image/draw" "github.com/nfnt/resize" + "opslag.de/schobers/tins2020/img" ) func crop(path string, crop rect, size *point) error { - src, err := decodeImage(path) + src, err := img.DecodeImage(path) if err != nil { return err } @@ -17,9 +18,9 @@ func crop(path string, crop rect, size *point) error { dst := image.NewRGBA(dstRect) draw.Draw(dst, dstRect, src, srcRect.Min, draw.Src) if size == nil { - return encodePNG(path, dst) + return img.EncodePNG(path, dst) } resized := resize.Resize(uint(size.x), uint(size.y), dst, resize.Bilinear) - return encodePNG(path, resized) + return img.EncodePNG(path, resized) } diff --git a/cmd/imadapt/gray.go b/cmd/imadapt/gray.go index d58700e..c91d686 100644 --- a/cmd/imadapt/gray.go +++ b/cmd/imadapt/gray.go @@ -3,10 +3,12 @@ package main import ( "image" "image/color" + + "opslag.de/schobers/tins2020/img" ) func convertToGray(path string) error { - src, err := decodeImage(path) + src, err := img.DecodeImage(path) if err != nil { return err } @@ -25,5 +27,5 @@ func convertToGray(path string) error { } } } - return encodePNG(path, dst) + return img.EncodePNG(path, dst) } diff --git a/cmd/imadapt/imadapt.go b/cmd/imadapt/imadapt.go index b8c89ae..6e0b2ce 100644 --- a/cmd/imadapt/imadapt.go +++ b/cmd/imadapt/imadapt.go @@ -5,6 +5,8 @@ import ( "flag" "fmt" "log" + + "opslag.de/schobers/tins2020/img" ) func run() error { @@ -73,6 +75,17 @@ func run() error { return fmt.Errorf("couldn't crop '%s'; error: %v", path, err) } } + case "color": + flags := flag.NewFlagSet("color", flag.ContinueOnError) + var col string + flags.StringVar(&col, "color", "#ff0000", "target color") + flags.Parse(args[1:]) + for _, path := range flags.Args() { + err := colorImage(path, img.MustHexColor(col)) + if err != nil { + return fmt.Errorf("couldn't convert to grayscale of '%s'; error: %v", path, err) + } + } } return nil } diff --git a/cmd/imadapt/setalpha.go b/cmd/imadapt/setalpha.go index a0c5e93..cbc302b 100644 --- a/cmd/imadapt/setalpha.go +++ b/cmd/imadapt/setalpha.go @@ -3,10 +3,12 @@ package main import ( "image" "image/color" + + "opslag.de/schobers/tins2020/img" ) func setAlpha(path string, alpha int) error { - src, err := decodeImage(path) + src, err := img.DecodeImage(path) if err != nil { return err } @@ -21,5 +23,5 @@ func setAlpha(path string, alpha int) error { dst.Set(x, y, c) } } - return encodePNG(path, dst) + return img.EncodePNG(path, dst) } diff --git a/cmd/tins2020/res/images/fastForward_disabled.png b/cmd/tins2020/res/images/fastForward_disabled.png new file mode 100644 index 0000000..a343fc9 Binary files /dev/null and b/cmd/tins2020/res/images/fastForward_disabled.png differ diff --git a/cmd/tins2020/res/images/forward_disabled.png b/cmd/tins2020/res/images/forward_disabled.png new file mode 100644 index 0000000..29aed4e Binary files /dev/null and b/cmd/tins2020/res/images/forward_disabled.png differ diff --git a/cmd/tins2020/res/images/pause_disabled.png b/cmd/tins2020/res/images/pause_disabled.png new file mode 100644 index 0000000..c201569 Binary files /dev/null and b/cmd/tins2020/res/images/pause_disabled.png differ diff --git a/cmd/tins2020/res/textures.txt b/cmd/tins2020/res/textures.txt index a3fb192..da7e3bc 100644 --- a/cmd/tins2020/res/textures.txt +++ b/cmd/tins2020/res/textures.txt @@ -8,8 +8,11 @@ control-load: images/basket.png control-quit: images/power.png control-pause: images/pause.png +control-pause-disabled: images/pause_disabled.png control-run: images/forward.png +control-run-disabled: images/forward_disabled.png control-run-fast: images/fastForward.png +control-run-fast-disabled: images/fastForward_disabled.png tile-dirt: images/tile_dirt.png tile-grass: images/tile_grass.png diff --git a/color.go b/color.go index 2c65621..3f26260 100644 --- a/color.go +++ b/color.go @@ -1,63 +1,9 @@ package tins2020 import ( - "errors" - "regexp" + "opslag.de/schobers/tins2020/img" "github.com/veandco/go-sdl2/sdl" ) -var hexColorRE = regexp.MustCompile(`^#?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})?$`) - -func HexColor(s string) (sdl.Color, error) { - match := hexColorRE.FindStringSubmatch(s) - if match == nil { - return sdl.Color{}, errors.New("invalid color format") - } - values, err := HexToInts(match[1:]...) - if err != nil { - return sdl.Color{}, err - } - a := 255 - if len(match[4]) > 0 { - a = values[3] - } - return sdl.Color{R: uint8(values[0]), G: uint8(values[1]), B: uint8(values[2]), A: uint8(a)}, nil -} - -func MustHexColor(s string) sdl.Color { - color, err := HexColor(s) - if err != nil { - panic(err) - } - return color -} - -func HexToInt(s string) (int, error) { - var i int - for _, c := range s { - i *= 16 - if c >= '0' && c <= '9' { - i += int(c - '0') - } else if c >= 'A' && c <= 'F' { - i += int(c - 'A' + 10) - } else if c >= 'a' && c <= 'f' { - i += int(c - 'a' + 10) - } else { - return 0, errors.New("hex digit not supported") - } - } - return i, nil -} - -func HexToInts(s ...string) ([]int, error) { - ints := make([]int, len(s)) - for i, s := range s { - value, err := HexToInt(s) - if err != nil { - return nil, err - } - ints[i] = value - } - return ints, nil -} +func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) } diff --git a/game.go b/game.go index 1bd918d..dbb6f99 100644 --- a/game.go +++ b/game.go @@ -58,6 +58,7 @@ func NewGame() *Game { } terrain.AddFlower(Pt(0, 0), NewPoppyTraits()) return &Game{ + Speed: GameSpeedNormal, Balance: 100, Terrain: terrain, diff --git a/gamecontrols.go b/gamecontrols.go index f240005..83fa6f9 100644 --- a/gamecontrols.go +++ b/gamecontrols.go @@ -19,6 +19,12 @@ func NewGameControls(game *Game) *GameControls { } func (c *GameControls) updateSpeedControls() { + disable := func(b *IconButton, speed GameSpeed) { + b.IsDisabled = speed == c.game.Speed + } + disable(c.pause, GameSpeedPaused) + disable(c.run, GameSpeedNormal) + disable(c.runFast, GameSpeedFast) } func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) { @@ -40,18 +46,25 @@ func (c *GameControls) Init(ctx *Context) error { } c.top.Orientation = OrientationHorizontal - c.pause = NewIconButton("control-pause", EmptyEvent(func() { + c.pause = NewIconButtonConfig("control-pause", EmptyEvent(func() { c.game.Pause() c.updateSpeedControls() - })) - c.run = NewIconButton("control-run", EmptyEvent(func() { + }), func(b *IconButton) { + b.IconDisabled = "control-pause-disabled" + }) + c.run = NewIconButtonConfig("control-run", EmptyEvent(func() { c.game.Run() c.updateSpeedControls() - })) - c.runFast = NewIconButton("control-run-fast", EmptyEvent(func() { + }), func(b *IconButton) { + b.IconDisabled = "control-run-disabled" + }) + c.runFast = NewIconButtonConfig("control-run-fast", EmptyEvent(func() { c.game.RunFast() c.updateSpeedControls() - })) + }), func(b *IconButton) { + b.IconDisabled = "control-run-fast-disabled" + }) + c.updateSpeedControls() c.top.Buttons = []Control{c.pause, c.run, c.runFast} c.menu.Background = MustHexColor("#356dad") @@ -71,7 +84,7 @@ func (c *GameControls) Render(ctx *Context) { topBar := MustHexColor("#0000007f") ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A) ctx.Renderer.FillRect(Rect(c.menu.Bounds.Right(), 0, c.flowers.Bounds.X, 64).SDLPtr()) - ctx.Fonts.Font("balance").RenderCopyAlign(ctx.Renderer, FmtMoney(c.game.Balance), Pt(c.top.Bounds.X-8, 58), MustHexColor("#79A6D9"), TextAlignmentRight) + ctx.Fonts.Font("balance").RenderCopyAlign(ctx.Renderer, FmtMoney(c.game.Balance), Pt(c.top.Bounds.X-8, 58), MustHexColor("#4AC69A"), TextAlignmentRight) c.Container.Render(ctx) } diff --git a/img/color.go b/img/color.go new file mode 100644 index 0000000..9013da9 --- /dev/null +++ b/img/color.go @@ -0,0 +1,62 @@ +package img + +import ( + "errors" + "image/color" + "regexp" +) + +var hexColorRE = regexp.MustCompile(`^#?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})?$`) + +func HexColor(s string) (color.RGBA, error) { + match := hexColorRE.FindStringSubmatch(s) + if match == nil { + return color.RGBA{}, errors.New("invalid color format") + } + values, err := HexToInts(match[1:]...) + if err != nil { + return color.RGBA{}, err + } + a := 255 + if len(match[4]) > 0 { + a = values[3] + } + return color.RGBA{R: uint8(values[0]), G: uint8(values[1]), B: uint8(values[2]), A: uint8(a)}, nil +} + +func MustHexColor(s string) color.RGBA { + color, err := HexColor(s) + if err != nil { + panic(err) + } + return color +} + +func HexToInt(s string) (int, error) { + var i int + for _, c := range s { + i *= 16 + if c >= '0' && c <= '9' { + i += int(c - '0') + } else if c >= 'A' && c <= 'F' { + i += int(c - 'A' + 10) + } else if c >= 'a' && c <= 'f' { + i += int(c - 'a' + 10) + } else { + return 0, errors.New("hex digit not supported") + } + } + return i, nil +} + +func HexToInts(s ...string) ([]int, error) { + ints := make([]int, len(s)) + for i, s := range s { + value, err := HexToInt(s) + if err != nil { + return nil, err + } + ints[i] = value + } + return ints, nil +} diff --git a/cmd/imadapt/io.go b/img/io.go similarity index 73% rename from cmd/imadapt/io.go rename to img/io.go index 54ec97a..b2ba033 100644 --- a/cmd/imadapt/io.go +++ b/img/io.go @@ -1,4 +1,4 @@ -package main +package img import ( "image" @@ -6,7 +6,7 @@ import ( "os" ) -func encodePNG(path string, im image.Image) error { +func EncodePNG(path string, im image.Image) error { dst, err := os.Create(path) if err != nil { return err @@ -15,7 +15,7 @@ func encodePNG(path string, im image.Image) error { return png.Encode(dst, im) } -func decodeImage(path string) (image.Image, error) { +func DecodeImage(path string) (image.Image, error) { f, err := os.Open(path) if err != nil { return nil, err