package game import ( "fmt" "image/color" "log/slog" "openquell/assets" "openquell/config" "openquell/gameui" "openquell/observers" "github.com/ebitenui/ebitenui" "github.com/ebitenui/ebitenui/image" "github.com/ebitenui/ebitenui/widget" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" ) type SelectScene struct { Game *Game Next SceneName Whoami SceneName UseCache bool Ui *ebitenui.UI SelectedLevel int } func NewSelectScene(game *Game) Scene { scene := &SelectScene{Whoami: Select, Game: game, Next: Select} scene.SetupUI() return scene } func (scene *SelectScene) SetLevel(level int) {} func (scene *SelectScene) GetNext() SceneName { return scene.Next } func (scene *SelectScene) ResetNext() { scene.Next = scene.Whoami } func (scene *SelectScene) SetNext(next SceneName) { scene.Next = next } func (scene *SelectScene) Clearscreen() bool { // both level_scene AND the popup must not clear to get an actual popup return true } func (scene *SelectScene) Update() error { scene.Ui.Update() if inpututil.IsKeyJustPressed(ebiten.KeyEscape) { scene.SetNext(Menu) } return nil } func (scene *SelectScene) Draw(screen *ebiten.Image) { scene.Ui.Draw(screen) } type LevelEntry struct { Id int Number int Name string } func (scene *SelectScene) SetupUI() { gameobserver := observers.GetGameObserver(scene.Game.World) rowContainer := gameui.NewRowContainer() label := widget.NewText( widget.TextOpts.Text("Select Level", *assets.FontRenderer.FontBig, config.FontColorFG), widget.TextOpts.Position(widget.TextPositionCenter, widget.TextPositionCenter), ) levels := make([]any, 0, len(scene.Game.Levels)) for id := 0; id < len(scene.Game.Levels); id++ { levels = append(levels, LevelEntry{Id: id, Number: scene.Game.Levels[id].Number, Name: scene.Game.Levels[id].Name}) } slog.Debug("levels", "levels", levels) buttonImage, err := gameui.LoadButtonImage() if err != nil { panic(err) } list := widget.NewList( // Set how wide the list should be widget.ListOpts.ContainerOpts(widget.ContainerOpts.WidgetOpts( widget.WidgetOpts.MinSize(150, 0), widget.WidgetOpts.LayoutData(widget.RowLayoutData{ Position: widget.RowLayoutPositionCenter, Stretch: true, MaxHeight: 200, }), )), // Set the entries in the list widget.ListOpts.Entries(levels), widget.ListOpts.ScrollContainerOpts( // Set the background images/color for the list widget.ScrollContainerOpts.Image(&widget.ScrollContainerImage{ Idle: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}), Disabled: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}), Mask: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}), }), ), widget.ListOpts.SliderOpts( // Set the background images/color for the background of the slider track widget.SliderOpts.Images(&widget.SliderTrackImage{ Idle: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}), Hover: image.NewNineSliceColor(color.NRGBA{100, 100, 100, 255}), }, buttonImage), widget.SliderOpts.MinHandleSize(5), // Set how wide the track should be widget.SliderOpts.TrackPadding(widget.NewInsetsSimple(2))), // Hide the horizontal slider widget.ListOpts.HideHorizontalSlider(), // Set the font for the list options widget.ListOpts.EntryFontFace(*assets.FontRenderer.FontNormal), // Set the colors for the list widget.ListOpts.EntryColor(&widget.ListEntryColor{ Selected: color.NRGBA{0, 255, 0, 255}, Unselected: color.NRGBA{254, 255, 255, 255}, SelectedBackground: color.NRGBA{R: 130, G: 130, B: 200, A: 255}, SelectedFocusedBackground: color.NRGBA{R: 130, G: 130, B: 170, A: 255}, FocusedBackground: color.NRGBA{R: 170, G: 170, B: 180, A: 255}, DisabledUnselected: color.NRGBA{100, 100, 100, 255}, DisabledSelected: color.NRGBA{100, 100, 100, 255}, DisabledSelectedBackground: color.NRGBA{100, 100, 100, 255}, }), // This required function returns the string displayed in the list widget.ListOpts.EntryLabelFunc(func(e interface{}) string { return fmt.Sprintf("%02d %s", e.(LevelEntry).Number, e.(LevelEntry).Name) }), // Padding for each entry widget.ListOpts.EntryTextPadding(widget.NewInsetsSimple(5)), // Text position for each entry widget.ListOpts.EntryTextPosition(widget.TextPositionStart, widget.TextPositionCenter), // This handler defines what function to run when a list item is selected. widget.ListOpts.EntrySelectedHandler(func(args *widget.ListEntrySelectedEventArgs) { entry := args.Entry.(LevelEntry) fmt.Println("Entry Selected: ", entry) gameobserver.CurrentLevel = entry.Id }), ) buttonContainer := widget.NewContainer( widget.ContainerOpts.Layout(widget.NewGridLayout( widget.GridLayoutOpts.Columns(2), widget.GridLayoutOpts.Padding(widget.NewInsetsSimple(30)), widget.GridLayoutOpts.Spacing(20, 10), widget.GridLayoutOpts.Stretch([]bool{true, false}, []bool{false, true}), )), ) buttonPlay := gameui.NewMenuButton("Play selected level", *assets.FontRenderer.FontNormal, func(args *widget.ButtonClickedEventArgs) { scene.SetNext(Play) }) buttonAbort := gameui.NewMenuButton("Back", *assets.FontRenderer.FontNormal, func(args *widget.ButtonClickedEventArgs) { scene.SetNext(Menu) }) buttonContainer.AddChild(buttonPlay) buttonContainer.AddChild(buttonAbort) rowContainer.AddChild(label) rowContainer.AddChild(list) rowContainer.AddChild(buttonContainer) scene.Ui = &ebitenui.UI{ Container: rowContainer.Container(), } }