package menu import ( "image/color" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/hajimehoshi/ebiten/v2/vector" "golang.org/x/image/font/basicfont" ) var ( basicFace = text.NewGoXFace(basicfont.Face7x13) basicFaceAscent = basicFace.Metrics().HAscent overlayImage *ebiten.Image overlaySize struct { width int height int } ) func getOverlayImage(width, height int) *ebiten.Image { if width <= 0 || height <= 0 { return nil } if overlayImage == nil || overlaySize.width != width || overlaySize.height != height { overlayImage = ebiten.NewImage(width, height) overlayImage.Fill(color.RGBA{R: 0, G: 0, B: 0, A: 180}) overlaySize.width = width overlaySize.height = height } return overlayImage } type MenuOption int const ( OptionResume MenuOption = iota OptionSettings OptionQuit optionCount ) type PauseMenu struct { selectedIndex int showWIP bool } func NewPauseMenu() *PauseMenu { return &PauseMenu{ selectedIndex: 0, showWIP: false, } } // Returns the selected option if one was chosen, nil otherwise func (m *PauseMenu) Update() *MenuOption { // Handle up/down navigation if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) { m.selectedIndex-- if m.selectedIndex < 0 { m.selectedIndex = int(optionCount) - 1 } m.showWIP = false } if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) { m.selectedIndex++ if m.selectedIndex >= int(optionCount) { m.selectedIndex = 0 } m.showWIP = false } // Handle selection if inpututil.IsKeyJustPressed(ebiten.KeyEnter) || inpututil.IsKeyJustPressed(ebiten.KeySpace) { selected := MenuOption(m.selectedIndex) if selected == OptionSettings { m.showWIP = true return nil } return &selected } return nil } func (m *PauseMenu) Draw(screen *ebiten.Image, screenWidth, screenHeight int) { // Draw semi-transparent overlay if overlay := getOverlayImage(screenWidth, screenHeight); overlay != nil { screen.DrawImage(overlay, nil) } // Menu dimensions menuWidth := 400 menuHeight := 300 menuX := (screenWidth - menuWidth) / 2 menuY := (screenHeight - menuHeight) / 2 // Draw menu background vector.DrawFilledRect(screen, float32(menuX), float32(menuY), float32(menuWidth), float32(menuHeight), color.RGBA{R: 40, G: 40, B: 50, A: 255}, false, ) // Draw menu border vector.StrokeRect(screen, float32(menuX), float32(menuY), float32(menuWidth), float32(menuHeight), 2, color.RGBA{R: 100, G: 100, B: 120, A: 255}, false, ) // Draw title titleText := "PAUSED" titleX := menuX + (menuWidth / 2) - (len(titleText) * 7 / 2) titleY := menuY + 50 m.drawText(screen, titleText, color.White, titleX, titleY) // Draw menu options options := []string{"Resume", "Settings", "Quit"} startY := menuY + 110 for i, option := range options { optionY := startY + (i * 40) optionX := menuX + (menuWidth / 2) - (len(option) * 7 / 2) // Draw selection indicator if i == m.selectedIndex { indicatorX := optionX - 20 m.drawText(screen, ">", color.RGBA{R: 255, G: 200, B: 0, A: 255}, indicatorX, optionY) m.drawText(screen, option, color.RGBA{R: 255, G: 255, B: 100, A: 255}, optionX, optionY) } else { m.drawText(screen, option, color.RGBA{R: 180, G: 180, B: 180, A: 255}, optionX, optionY) } } // Draw WIP message if settings was selected if m.showWIP { wipY := startY + 140 wipText := "Work In Progress" wipX := menuX + (menuWidth / 2) - (len(wipText) * 7 / 2) m.drawText(screen, wipText, color.RGBA{R: 255, G: 150, B: 0, A: 255}, wipX, wipY) } // Draw controls hint at bottom hintText := "Use Arrow Keys/WASD to navigate, Enter/Space to select" hintX := menuX + (menuWidth / 2) - (len(hintText) * 7 / 2) hintY := menuY + menuHeight - 30 m.drawText(screen, hintText, color.RGBA{R: 150, G: 150, B: 150, A: 255}, hintX, hintY) } func (m *PauseMenu) drawText(screen *ebiten.Image, txt string, clr color.Color, x, y int) { op := &text.DrawOptions{} op.GeoM.Translate(float64(x), float64(y)-basicFaceAscent) op.ColorScale.ScaleWithColor(clr) text.Draw(screen, txt, basicFace, op) } func (m *PauseMenu) Reset() { m.selectedIndex = 0 m.showWIP = false }