1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-18 02:16:23 -05:00
OpenDiablo2/d2core/d2screen/d2screen.go
David Carrell 3bdbd5c358
rely on App to know how to navigate between screens using a callback interface with explicit methods (#592)
remove unused struct vars that were only stored in order to pass to the next screens

consolidate create game code to single method

export ScreenMode consts and SetScreenMode method to allow app to create the correct screens

Co-authored-by: carrelda <carrelda@git>
2020-07-14 13:11:23 -04:00

143 lines
3.8 KiB
Go

package d2screen
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
// Screen is an exported interface
type Screen interface{}
// ScreenLoadHandler is an exported interface
type ScreenLoadHandler interface {
// OnLoad performs all necessary loading to prepare a screen to be shown such as loading assets, placing and binding
// of ui elements, etc. This loading is done asynchronously. The provided channel will allow implementations to
// provide progress via Error, Progress, or Done
OnLoad(loading LoadingState)
}
// ScreenUnloadHandler is an exported interface
type ScreenUnloadHandler interface {
OnUnload() error
}
// ScreenRenderHandler is an exported interface
type ScreenRenderHandler interface {
Render(target d2interface.Surface) error
}
// ScreenAdvanceHandler is an exported interface
type ScreenAdvanceHandler interface {
Advance(elapsed float64) error
}
var singleton struct {
nextScreen Screen
loadingScreen Screen
loadingState LoadingState
currentScreen Screen
}
// SetNextScreen is about to set a given screen as next
func SetNextScreen(screen Screen) {
singleton.nextScreen = screen
}
// Advance updates the UI on every frame
func Advance(elapsed float64) error {
switch {
case singleton.loadingScreen != nil:
// this call blocks execution and could lead to deadlock if a screen implements OnLoad incorreclty
load, ok := <-singleton.loadingState.updates
if !ok {
log.Println("loadingState chan should not be closed while in a loading screen")
}
if load.err != nil {
log.Printf("PROBLEM LOADING THE SCREEN: %v", load.err)
return load.err
}
d2gui.ShowLoadScreen(load.progress)
if load.done {
singleton.currentScreen = singleton.loadingScreen
singleton.loadingScreen = nil
d2gui.ShowCursor()
d2gui.HideLoadScreen()
}
case singleton.nextScreen != nil:
if handler, ok := singleton.currentScreen.(ScreenUnloadHandler); ok {
if err := handler.OnUnload(); err != nil {
return err
}
}
d2ui.Reset()
d2gui.SetLayout(nil)
if handler, ok := singleton.nextScreen.(ScreenLoadHandler); ok {
d2gui.ShowLoadScreen(0)
d2gui.HideCursor()
singleton.loadingState = LoadingState{updates: make(chan loadingUpdate)}
go func() {
handler.OnLoad(singleton.loadingState)
singleton.loadingState.Done()
}()
singleton.currentScreen = nil
singleton.loadingScreen = singleton.nextScreen
} else {
singleton.currentScreen = singleton.nextScreen
singleton.loadingScreen = nil
}
singleton.nextScreen = nil
case singleton.currentScreen != nil:
if handler, ok := singleton.currentScreen.(ScreenAdvanceHandler); ok {
if err := handler.Advance(elapsed); err != nil {
return err
}
}
}
return nil
}
// Render renders the UI by a given surface
func Render(surface d2interface.Surface) error {
if handler, ok := singleton.currentScreen.(ScreenRenderHandler); ok {
if err := handler.Render(surface); err != nil {
return err
}
}
return nil
}
// LoadingState represents the loading state
type LoadingState struct {
updates chan loadingUpdate
}
type loadingUpdate struct {
progress float64
err error
done bool
}
// Error provides a way for callers to report an error during loading.
func (l *LoadingState) Error(err error) {
l.updates <- loadingUpdate{err: err}
}
// Progress provides a way for callers to report the ratio between `0` and `1` of the progress made loading a screen.
func (l *LoadingState) Progress(ratio float64) {
l.updates <- loadingUpdate{progress: ratio}
}
// Done provides a way for callers to report that screen loading has been completed.
func (l *LoadingState) Done() {
l.updates <- loadingUpdate{progress: 1.0}
l.updates <- loadingUpdate{done: true}
}