237 lines
5.7 KiB
Go
237 lines
5.7 KiB
Go
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"
|
|
|
|
"github.com/atridad/LilGuy/internal/screens"
|
|
)
|
|
|
|
var (
|
|
basicFace = text.NewGoXFace(basicfont.Face7x13)
|
|
basicFaceAscent = basicFace.Metrics().HAscent
|
|
overlayImage *ebiten.Image
|
|
overlaySize struct {
|
|
width int
|
|
height int
|
|
}
|
|
)
|
|
|
|
// Menu options
|
|
|
|
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
|
|
OptionSave
|
|
OptionSettings
|
|
OptionMainMenu
|
|
OptionQuit
|
|
optionCount
|
|
)
|
|
|
|
// Screen modes
|
|
|
|
type menuScreen int
|
|
|
|
const (
|
|
screenMain menuScreen = iota
|
|
screenSettings
|
|
)
|
|
|
|
type PauseMenu struct {
|
|
selectedIndex int
|
|
currentScreen menuScreen
|
|
settingsScreen *screens.SettingsScreen
|
|
}
|
|
|
|
type FPSCapSetting interface {
|
|
String() string
|
|
Cycle()
|
|
}
|
|
|
|
func NewPauseMenu() *PauseMenu {
|
|
return &PauseMenu{
|
|
selectedIndex: 0,
|
|
currentScreen: screenMain,
|
|
settingsScreen: screens.NewSettingsScreen(),
|
|
}
|
|
}
|
|
|
|
func (m *PauseMenu) SetFPSMonitor(enabled *bool) {
|
|
m.settingsScreen.SetFPSMonitor(enabled)
|
|
}
|
|
|
|
func (m *PauseMenu) SetFPSCap(cap FPSCapSetting) {
|
|
m.settingsScreen.SetFPSCap(cap)
|
|
}
|
|
|
|
func (m *PauseMenu) SetPortalVisibility(enabled *bool) {
|
|
m.settingsScreen.SetPortalVisibility(enabled)
|
|
}
|
|
|
|
// Update logic
|
|
|
|
func (m *PauseMenu) Update() *MenuOption {
|
|
if m.currentScreen == screenSettings {
|
|
return m.updateSettings()
|
|
}
|
|
return m.updateMain()
|
|
}
|
|
|
|
func (m *PauseMenu) updateMain() *MenuOption {
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) {
|
|
m.selectedIndex--
|
|
if m.selectedIndex < 0 {
|
|
m.selectedIndex = int(optionCount) - 1
|
|
}
|
|
}
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) {
|
|
m.selectedIndex++
|
|
if m.selectedIndex >= int(optionCount) {
|
|
m.selectedIndex = 0
|
|
}
|
|
}
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) || inpututil.IsKeyJustPressed(ebiten.KeySpace) {
|
|
selected := MenuOption(m.selectedIndex)
|
|
if selected == OptionSettings {
|
|
m.currentScreen = screenSettings
|
|
m.selectedIndex = 0
|
|
return nil
|
|
}
|
|
return &selected
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *PauseMenu) updateSettings() *MenuOption {
|
|
if m.settingsScreen.Update() {
|
|
m.currentScreen = screenMain
|
|
m.selectedIndex = 0
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Rendering
|
|
|
|
func (m *PauseMenu) Draw(screen *ebiten.Image, screenWidth, screenHeight int) {
|
|
if overlay := getOverlayImage(screenWidth, screenHeight); overlay != nil {
|
|
screen.DrawImage(overlay, nil)
|
|
}
|
|
|
|
menuWidth := 400
|
|
menuHeight := 380
|
|
menuX := (screenWidth - menuWidth) / 2
|
|
menuY := (screenHeight - menuHeight) / 2
|
|
|
|
vector.FillRect(screen,
|
|
float32(menuX), float32(menuY),
|
|
float32(menuWidth), float32(menuHeight),
|
|
color.RGBA{R: 40, G: 40, B: 50, A: 255},
|
|
false,
|
|
)
|
|
|
|
vector.StrokeRect(screen,
|
|
float32(menuX), float32(menuY),
|
|
float32(menuWidth), float32(menuHeight),
|
|
2,
|
|
color.RGBA{R: 100, G: 100, B: 120, A: 255},
|
|
false,
|
|
)
|
|
|
|
if m.currentScreen == screenSettings {
|
|
m.drawSettings(screen, menuX, menuY, menuWidth, menuHeight)
|
|
} else {
|
|
m.drawMain(screen, menuX, menuY, menuWidth, menuHeight)
|
|
}
|
|
}
|
|
|
|
func (m *PauseMenu) drawMain(screen *ebiten.Image, menuX, menuY, menuWidth, menuHeight int) {
|
|
titleText := "PAUSED"
|
|
titleX := menuX + (menuWidth / 2) - (len(titleText) * 7 / 2)
|
|
titleY := menuY + 50
|
|
m.drawText(screen, titleText, color.White, titleX, titleY)
|
|
|
|
options := []string{"Resume", "Save", "Settings", "Main Menu", "Quit"}
|
|
startY := menuY + 90
|
|
|
|
for i, option := range options {
|
|
optionY := startY + (i * 45)
|
|
optionX := menuX + (menuWidth / 2) - (len(option) * 7 / 2)
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Instructions
|
|
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) drawSettings(screen *ebiten.Image, menuX, menuY, menuWidth, menuHeight int) {
|
|
vector.FillRect(screen,
|
|
float32(menuX), float32(menuY),
|
|
float32(menuWidth), float32(menuHeight),
|
|
color.RGBA{R: 40, G: 40, B: 50, A: 255},
|
|
false,
|
|
)
|
|
|
|
vector.StrokeRect(screen,
|
|
float32(menuX), float32(menuY),
|
|
float32(menuWidth), float32(menuHeight),
|
|
2,
|
|
color.RGBA{R: 100, G: 100, B: 120, A: 255},
|
|
false,
|
|
)
|
|
|
|
screenWidth := menuWidth
|
|
screenHeight := menuHeight
|
|
|
|
subScreen := ebiten.NewImage(screenWidth, screenHeight)
|
|
m.settingsScreen.Draw(subScreen, screenWidth, screenHeight, "SETTINGS")
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
op.GeoM.Translate(float64(menuX), float64(menuY))
|
|
screen.DrawImage(subScreen, op)
|
|
}
|
|
|
|
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.currentScreen = screenMain
|
|
m.settingsScreen.Reset()
|
|
}
|