diff --git a/.gitignore b/.gitignore index ba698c7..a47f791 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .vscode/launch.json + +cmd/tins2020/*rice-box.* diff --git a/animation.go b/animation.go index 4afd21b..f3b4045 100644 --- a/animation.go +++ b/animation.go @@ -49,6 +49,7 @@ func (a *Animation) Run() { } a.active = true a.start = time.Now() + a.lastUpdate = 0 } func (a *Animation) SetInterval(interval time.Duration) { diff --git a/cmd/tins2020/tins2020.go b/cmd/tins2020/tins2020.go index 48bb930..a989243 100644 --- a/cmd/tins2020/tins2020.go +++ b/cmd/tins2020/tins2020.go @@ -9,6 +9,9 @@ import ( "opslag.de/schobers/tins2020" ) +//go:generate go get -u github.com/GeertJohan/go.rice/rice +//go:generate rice embed + func main() { err := run() if err != nil { diff --git a/color.go b/color.go index fc953d1..a12f61e 100644 --- a/color.go +++ b/color.go @@ -11,6 +11,14 @@ var Transparent = sdl.Color{R: 0, G: 0, B: 0, A: 0} var TransparentWhite = sdl.Color{R: 255, G: 255, B: 255, A: 31} var White = sdl.Color{R: 255, G: 255, B: 255, A: 255} +func HexColor(s string) (sdl.Color, error) { + c, err := img.HexColor(s) + if err != nil { + return sdl.Color{}, err + } + return sdl.Color(c), nil +} + func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) } func SetDrawColor(renderer *sdl.Renderer, color sdl.Color) { diff --git a/control.go b/control.go index c4b43d5..b7cf6f4 100644 --- a/control.go +++ b/control.go @@ -22,6 +22,7 @@ func EmptyEvent(fn EventFn) EventContextFn { type ControlBase struct { Bounds Rectangle + IsDisabled bool IsMouseOver bool OnLeftMouseButtonClick EventContextFn @@ -36,7 +37,7 @@ func (b *ControlBase) Handle(ctx *Context, event sdl.Event) bool { case *sdl.MouseMotionEvent: b.IsMouseOver = b.Bounds.IsPointInside(e.X, e.Y) case *sdl.MouseButtonEvent: - if b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN { + if !b.IsDisabled && b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN { return b.Invoke(ctx, b.OnLeftMouseButtonClick) } case *sdl.WindowEvent: diff --git a/game.go b/game.go index baade03..ada276e 100644 --- a/game.go +++ b/game.go @@ -163,6 +163,11 @@ func (g *Game) Load() { func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) } func (g *Game) PlantFlower(id string, tile Point) { + if g.Terrain.HasFlower(tile) { + // TODO: notify user it tried to plant on tile with flower? + return + } + flower, ok := g.Herbarium.Find(id) if !ok { log.Println("user was able to plant a flower that doesn't exist") @@ -255,7 +260,8 @@ func (g *Game) Tool() Tool { return g.tool } func (g *Game) ToolChanged() EventHandler { return g.toolChanged } func (g *Game) UnlockNextFlower() { - g.Herbarium.UnlockNext() + price := g.Herbarium.UnlockNext() + g.Balance -= price g.selectTool(nil) } diff --git a/gamecontrols.go b/gamecontrols.go index 99f29c9..4294e0b 100644 --- a/gamecontrols.go +++ b/gamecontrols.go @@ -87,7 +87,10 @@ func (c *GameControls) Init(ctx *Context) error { c.game.SpeedChanged().RegisterItf(c.speedChanged) c.game.ToolChanged().RegisterItf(c.toolChanged) c.dialogs.DialogOpened().Register(func() { c.game.Pause() }) - c.dialogs.DialogClosed().Register(func() { c.game.Resume() }) + c.dialogs.DialogClosed().Register(func() { + c.updateFlowerControls(ctx) + c.game.Resume() + }) c.flowers.Background = MustHexColor("#356dad") c.flowers.ButtonLength = 64 @@ -117,7 +120,10 @@ func (c *GameControls) Init(ctx *Context) error { c.menu.Background = MustHexColor("#356dad") c.menu.Buttons = []Control{ - NewIconButton("control-settings", c.dialogs.ShowSettings), + NewIconButtonConfig("control-settings", c.dialogs.ShowSettings, func(b *IconButton) { + b.IsDisabled = true + b.IconDisabled = "#afafaf" + }), NewIconButton("control-save", func(*Context) { c.game.Save() }), NewIconButton("control-load", func(ctx *Context) { c.game.Load() diff --git a/herbarium.go b/herbarium.go index d04a7b5..59f8465 100644 --- a/herbarium.go +++ b/herbarium.go @@ -130,12 +130,12 @@ func (h *Herbarium) NextFlowerToUnlock() (string, FlowerDescriptor, int) { return "", FlowerDescriptor{}, 0 } -func (h *Herbarium) UnlockNext() { - // id, flower, _ := h.NextFlowerToUnlock() - // if flower == nil { - // return - // } - - // flower.Unlocked = true - // h.flowers[flower] +func (h *Herbarium) UnlockNext() int { + id, flower, price := h.NextFlowerToUnlock() + if len(id) == 0 { + return 0 + } + flower.Unlocked = true + h.flowers[id] = flower + return price } diff --git a/iconbutton.go b/iconbutton.go index 32703f6..6682ea3 100644 --- a/iconbutton.go +++ b/iconbutton.go @@ -19,8 +19,7 @@ type IconButton struct { IconActive HoverEffect IconHover HoverEffect - IsActive bool - IsDisabled bool + IsActive bool } func NewIconButton(icon string, onClick EventContextFn) *IconButton { @@ -44,6 +43,16 @@ func (b *IconButton) activeTexture(ctx *Context) *Texture { if texture != nil { return texture } + + texture = ctx.Textures.Texture(b.Icon) + if len(b.IconDisabled) == 0 { + return texture + } + color, err := HexColor(b.IconDisabled) + if err == nil { + texture.SetColor(color) + } + return texture } return ctx.Textures.Texture(b.Icon) } diff --git a/map.go b/map.go index cb63b79..3d9dcd5 100644 --- a/map.go +++ b/map.go @@ -24,6 +24,11 @@ func (m *Map) DigFlower(pos Point) string { return flower.ID } +func (m *Map) HasFlower(pos Point) bool { + _, ok := m.Flowers[pos] + return ok +} + func (m *Map) NewFlower(pos Point, id string, traits FlowerTraits) Flower { flower := Flower{ ID: id, diff --git a/research.go b/research.go index 9ed5a7a..ed0fce2 100644 --- a/research.go +++ b/research.go @@ -56,13 +56,13 @@ func (d *Digit) Render(ctx *Context) { font := ctx.Fonts.Font("title") color := White if d.highlight > 0 { - color = MustHexColor("#356DAD") + color = MustHexColor("#15569F") } font.RenderCopyAlign(ctx.Renderer, d.Value, Pt(d.Bounds.X+d.Bounds.W/2, d.Bounds.Y+int32(font.Height())), color, TextAlignmentCenter) } func (d *Digit) Blink() { - d.highlight = 10 + d.highlight = 4 } func (d *Digit) Tick() { @@ -90,33 +90,10 @@ func (r *Research) Init(ctx *Context) error { return nil } -func (r *Research) lastAvailableFlower() *FlowerDescriptor { - var desc *FlowerDescriptor - for _, id := range r.game.Herbarium.Flowers() { - flower, _ := r.game.Herbarium.Find(id) - if !flower.Unlocked { - return desc - } - desc = &flower - } - return desc - -} - -func (r *Research) firstLockedFlower() *FlowerDescriptor { - for _, id := range r.game.Herbarium.Flowers() { - flower, _ := r.game.Herbarium.Find(id) - if !flower.Unlocked { - return &flower - } - } - return nil -} - func (r *Research) Arrange(ctx *Context, bounds Rectangle) { r.Container.Arrange(ctx, bounds) r.specialists.Arrange(ctx, RectSize(r.Bounds.X, r.Bounds.Y+40, r.Bounds.W, r.Bounds.H-40)) - r.input.Arrange(ctx, RectSize(r.Bounds.X, r.Bounds.X+r.Bounds.H-24, r.Bounds.W, 24)) + r.input.Arrange(ctx, RectSize(r.Bounds.X, r.Bounds.X+r.Bounds.H-48, r.Bounds.W, 24)) r.input.Alignment = TextAlignmentCenter center := Pt(r.Bounds.X+r.Bounds.W/2, r.Bounds.Y+r.Bounds.H/2) @@ -133,25 +110,26 @@ func (r *Research) Arrange(ctx *Context, bounds Rectangle) { func (r *Research) userTyped(i int) { r.digits[i].Blink() digit := strconv.Itoa(i) - if len(r.typing) == 0 { + if len(r.typing) == 0 || digit != r.typing { r.typing = digit r.digitCount = 1 - } else if digit != r.typing { - r.typing = "" - r.digitCount = 0 } else { r.digitCount++ } - if r.digitCount == i || r.digitCount == 10 { + + if !strings.HasPrefix(r.botanist.Number, r.input.Text+r.typing) { + r.input.Text = "" + r.typing = "" + r.digitCount = 0 + } else if r.digitCount == i || r.digitCount == 10 { r.input.Text += digit r.typing = "" r.digitCount = 0 - if !strings.HasPrefix(r.input.Text, r.botanist.Number) { - r.input.Text = "" - } else if r.input.Text == r.botanist.Number { + if r.input.Text == r.botanist.Number { r.game.UnlockNextFlower() r.close() + r.input.Text = "" } } } @@ -159,47 +137,49 @@ func (r *Research) userTyped(i int) { func (r *Research) Handle(ctx *Context, event sdl.Event) bool { switch e := event.(type) { case *sdl.KeyboardEvent: - switch e.Keysym.Sym { - case sdl.K_0: - r.userTyped(0) - case sdl.K_KP_0: - r.userTyped(0) - case sdl.K_1: - r.userTyped(1) - case sdl.K_KP_1: - r.userTyped(1) - case sdl.K_2: - r.userTyped(2) - case sdl.K_KP_2: - r.userTyped(2) - case sdl.K_3: - r.userTyped(3) - case sdl.K_KP_3: - r.userTyped(3) - case sdl.K_4: - r.userTyped(4) - case sdl.K_KP_4: - r.userTyped(4) - case sdl.K_5: - r.userTyped(5) - case sdl.K_KP_5: - r.userTyped(5) - case sdl.K_6: - r.userTyped(6) - case sdl.K_KP_6: - r.userTyped(6) - case sdl.K_7: - r.userTyped(7) - case sdl.K_KP_7: - r.userTyped(7) - case sdl.K_8: - r.userTyped(8) - case sdl.K_KP_8: - r.userTyped(8) - case sdl.K_9: - r.userTyped(9) - case sdl.K_KP_9: - r.userTyped(9) + if e.Type == sdl.KEYDOWN { + switch e.Keysym.Sym { + case sdl.K_0: + r.userTyped(0) + case sdl.K_KP_0: + r.userTyped(0) + case sdl.K_1: + r.userTyped(1) + case sdl.K_KP_1: + r.userTyped(1) + case sdl.K_2: + r.userTyped(2) + case sdl.K_KP_2: + r.userTyped(2) + case sdl.K_3: + r.userTyped(3) + case sdl.K_KP_3: + r.userTyped(3) + case sdl.K_4: + r.userTyped(4) + case sdl.K_KP_4: + r.userTyped(4) + case sdl.K_5: + r.userTyped(5) + case sdl.K_KP_5: + r.userTyped(5) + case sdl.K_6: + r.userTyped(6) + case sdl.K_KP_6: + r.userTyped(6) + case sdl.K_7: + r.userTyped(7) + case sdl.K_KP_7: + r.userTyped(7) + case sdl.K_8: + r.userTyped(8) + case sdl.K_KP_8: + r.userTyped(8) + case sdl.K_9: + r.userTyped(9) + case sdl.K_KP_9: + r.userTyped(9) + } } } return false @@ -227,21 +207,21 @@ func (r *Research) onShow(ctx *Context) { defer func() { r.specialists.Text = specialists }() - available := r.lastAvailableFlower() - locked := r.firstLockedFlower() - if locked == nil { + + _, _, price := r.game.Herbarium.NextFlowerToUnlock() + if price == 0 { specialists += "Botanist (unlocks next flower; unavailable)\n" specialists += "Farmer (fertilizes land; unavailable)\n" return } - r.botanist.Cost = 2 * available.BuyPrice + r.botanist.Cost = price if r.game.Balance < r.botanist.Cost { r.botanist.Number = "**unavailable**" } else { r.botanist.Number = generateNumber() } - specialists += fmt.Sprintf("Botanist: no. %s (unlocks next flower; $ %d)\n", r.botanist.Number, r.botanist.Cost) + specialists += fmt.Sprintf("Botanist: no. %s (unlocks next flower; $ %d)\n", r.botanist.Number, r.botanist.Cost) specialists += "Farmer: no. **unavailable** (fertilizes land; $ ---)\n" } diff --git a/terrainrenderer.go b/terrainrenderer.go index ea35496..ed97417 100644 --- a/terrainrenderer.go +++ b/terrainrenderer.go @@ -34,7 +34,7 @@ func (r *terrainRenderer) Init(ctx *Context) error { func isControlKeyDown() bool { state := sdl.GetKeyboardState() - return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1 + return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1 || state[sdl.SCANCODE_LGUI] == 1 || state[sdl.SCANCODE_RGUI] == 1 } func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool { @@ -42,12 +42,13 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool { case *sdl.MouseButtonEvent: if r.project.windowInteractRect.IsPointInside(e.X, e.Y) { if e.Type == sdl.MOUSEBUTTONDOWN { - if e.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && isControlKeyDown()) { + controlKeyDown := isControlKeyDown() + if e.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && controlKeyDown) { if !r.drag.IsDragging() { r.drag.Start(Pt(e.X, e.Y)) } } - if e.Button == sdl.BUTTON_LEFT { + if e.Button == sdl.BUTTON_LEFT && !controlKeyDown { pos := r.project.screenToMapInt(e.X, e.Y) r.game.UserClickedTile(pos) }