package main import ( "image/color" "time" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" ) // Game holds the top-level state for the Ebitengine run loop. type Game struct { ship Ship walk map[[2]int]bool crew []Crew selectedCrew int lastTime time.Time } // Update advances simulation by dt seconds and handles input. func (g *Game) Update() error { if g.lastTime.IsZero() { g.lastTime = time.Now() return nil } dt := time.Since(g.lastTime).Seconds() g.lastTime = time.Now() if dt > 0.1 { dt = 0.1 } for i := range g.crew { updateCrew(&g.crew[i], dt) } if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { cx, cy := ebiten.CursorPosition() tx, ty := cx/TilePx, cy/TilePx for i := range g.crew { if g.crew[i].TileX == tx && g.crew[i].TileY == ty { g.selectedCrew = i return nil } } if g.selectedCrew >= 0 && g.walk[[2]int{tx, ty}] { from := [2]int{g.crew[g.selectedCrew].TileX, g.crew[g.selectedCrew].TileY} g.crew[g.selectedCrew].Path = bfsPath(g.walk, from, [2]int{tx, ty}) g.crew[g.selectedCrew].MoveT = 0 } } return nil } // Draw renders the whole frame. func (g *Game) Draw(screen *ebiten.Image) { screen.Fill(color.RGBA{10, 15, 25, 255}) drawShip(screen, g.ship) for i, c := range g.crew { drawCrew(screen, c, i == g.selectedCrew) } } // Layout fixes the virtual resolution; the window scales to fit. func (g *Game) Layout(_, _ int) (int, int) { return VirtualW, VirtualH } func main() { ship := NewPlayerShip() walk := walkableTiles(ship) crew := NewStartingCrew() ebiten.SetWindowSize(1280, 720) ebiten.SetWindowTitle("ftl-shape") if err := ebiten.RunGame(&Game{ship: ship, walk: walk, crew: crew, selectedCrew: -1}); err != nil { panic(err) } }