mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-25 19:46:50 -05:00
removed singleton screen manager instance (#696)
This commit is contained in:
parent
4507b1e1c1
commit
ca7412aea6
24
d2app/app.go
24
d2app/app.go
@ -6,7 +6,6 @@ import (
|
||||
"container/ring"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/png"
|
||||
@ -26,6 +25,7 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
|
||||
@ -63,6 +63,7 @@ type App struct {
|
||||
scriptEngine *d2script.ScriptEngine
|
||||
audio d2interface.AudioProvider
|
||||
renderer d2interface.Renderer
|
||||
screen *d2screen.ScreenManager
|
||||
tAllocSamples *ring.Ring
|
||||
}
|
||||
|
||||
@ -93,6 +94,7 @@ func Create(gitBranch, gitCommit string,
|
||||
scriptEngine: scriptEngine,
|
||||
audio: audio,
|
||||
renderer: renderer,
|
||||
screen: d2screen.NewScreenManager(),
|
||||
tAllocSamples: createZeroedRing(nSamplesTAlloc),
|
||||
}
|
||||
|
||||
@ -362,7 +364,7 @@ func (a *App) renderCapture(target d2interface.Surface) error {
|
||||
}
|
||||
|
||||
func (a *App) render(target d2interface.Surface) error {
|
||||
if err := d2screen.Render(target); err != nil {
|
||||
if err := a.screen.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -392,7 +394,7 @@ func (a *App) advance(elapsed, elapsedUnscaled, current float64) error {
|
||||
|
||||
a.lastScreenAdvance = current
|
||||
|
||||
if err := d2screen.Advance(elapsedLastScreenAdvance); err != nil {
|
||||
if err := a.screen.Advance(elapsedLastScreenAdvance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -597,7 +599,7 @@ func (a *App) quitGame() {
|
||||
}
|
||||
|
||||
func (a *App) enterGuiPlayground() {
|
||||
d2screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer))
|
||||
a.screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer))
|
||||
}
|
||||
|
||||
func createZeroedRing(n int) *ring.Ring {
|
||||
@ -668,13 +670,13 @@ func (a *App) ToMainMenu() {
|
||||
buildInfo := d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit}
|
||||
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, buildInfo)
|
||||
mainMenu.SetScreenMode(d2gamescreen.ScreenModeMainMenu)
|
||||
d2screen.SetNextScreen(mainMenu)
|
||||
a.screen.SetNextScreen(mainMenu)
|
||||
}
|
||||
|
||||
// ToSelectHero forces the game to transition to the Select Hero (create character) screen
|
||||
func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, host string) {
|
||||
selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, connType, host)
|
||||
d2screen.SetNextScreen(selectHero)
|
||||
a.screen.SetNextScreen(selectHero)
|
||||
}
|
||||
|
||||
// ToCreateGame forces the game to transition to the Create Game screen
|
||||
@ -686,23 +688,23 @@ func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.Clie
|
||||
fmt.Printf("can not connect to the host: %s", host)
|
||||
}
|
||||
|
||||
d2screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal))
|
||||
a.screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal))
|
||||
}
|
||||
|
||||
// ToCharacterSelect forces the game to transition to the Character Select (load character) screen
|
||||
func (a *App) ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) {
|
||||
characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio, connType, connHost)
|
||||
d2screen.SetNextScreen(characterSelect)
|
||||
a.screen.SetNextScreen(characterSelect)
|
||||
}
|
||||
|
||||
// ToMapEngineTest forces the game to transition to the map engine test screen
|
||||
func (a *App) ToMapEngineTest(region, level int) {
|
||||
met := d2gamescreen.CreateMapEngineTest(region, level, a.terminal, a.renderer, a.inputManager,
|
||||
a.audio)
|
||||
d2screen.SetNextScreen(met)
|
||||
a.audio, a.screen)
|
||||
a.screen.SetNextScreen(met)
|
||||
}
|
||||
|
||||
// ToCredits forces the game to transition to the credits screen
|
||||
func (a *App) ToCredits() {
|
||||
d2screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
|
||||
a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package d2screen
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
|
||||
// NewScreenManager creates a screen manager
|
||||
func NewScreenManager() *ScreenManager {
|
||||
return &ScreenManager{}
|
||||
}
|
||||
|
||||
// Screen is an exported interface
|
||||
type Screen interface{}
|
||||
|
||||
@ -34,119 +34,3 @@ type ScreenRenderHandler 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
|
||||
}
|
||||
|
||||
const progressCompleted = 1.0
|
||||
|
||||
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: progressCompleted}
|
||||
l.updates <- loadingUpdate{done: true}
|
||||
}
|
||||
|
6
d2core/d2screen/loading_state.go
Normal file
6
d2core/d2screen/loading_state.go
Normal file
@ -0,0 +1,6 @@
|
||||
package d2screen
|
||||
|
||||
// LoadingState represents the loading state
|
||||
type LoadingState struct {
|
||||
updates chan loadingUpdate
|
||||
}
|
25
d2core/d2screen/loading_update.go
Normal file
25
d2core/d2screen/loading_update.go
Normal file
@ -0,0 +1,25 @@
|
||||
package d2screen
|
||||
|
||||
const progressCompleted = 1.0
|
||||
|
||||
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: progressCompleted}
|
||||
l.updates <- loadingUpdate{done: true}
|
||||
}
|
97
d2core/d2screen/screen_manager.go
Normal file
97
d2core/d2screen/screen_manager.go
Normal file
@ -0,0 +1,97 @@
|
||||
package d2screen
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
|
||||
// ScreenManager manages game screens (main menu, credits, character select, game, etc)
|
||||
type ScreenManager struct {
|
||||
nextScreen Screen
|
||||
loadingScreen Screen
|
||||
loadingState LoadingState
|
||||
currentScreen Screen
|
||||
}
|
||||
|
||||
// SetNextScreen is about to set a given screen as next
|
||||
func (sm *ScreenManager) SetNextScreen(screen Screen) {
|
||||
sm.nextScreen = screen
|
||||
}
|
||||
|
||||
// Advance updates the UI on every frame
|
||||
func (sm *ScreenManager) Advance(elapsed float64) error {
|
||||
switch {
|
||||
case sm.loadingScreen != nil:
|
||||
// this call blocks execution and could lead to deadlock if a screen implements OnLoad incorreclty
|
||||
load, ok := <-sm.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 {
|
||||
sm.currentScreen = sm.loadingScreen
|
||||
sm.loadingScreen = nil
|
||||
|
||||
d2gui.ShowCursor()
|
||||
d2gui.HideLoadScreen()
|
||||
}
|
||||
case sm.nextScreen != nil:
|
||||
if handler, ok := sm.currentScreen.(ScreenUnloadHandler); ok {
|
||||
if err := handler.OnUnload(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d2ui.Reset()
|
||||
d2gui.SetLayout(nil)
|
||||
|
||||
if handler, ok := sm.nextScreen.(ScreenLoadHandler); ok {
|
||||
d2gui.ShowLoadScreen(0)
|
||||
d2gui.HideCursor()
|
||||
|
||||
sm.loadingState = LoadingState{updates: make(chan loadingUpdate)}
|
||||
|
||||
go func() {
|
||||
handler.OnLoad(sm.loadingState)
|
||||
sm.loadingState.Done()
|
||||
}()
|
||||
|
||||
sm.currentScreen = nil
|
||||
sm.loadingScreen = sm.nextScreen
|
||||
} else {
|
||||
sm.currentScreen = sm.nextScreen
|
||||
sm.loadingScreen = nil
|
||||
}
|
||||
|
||||
sm.nextScreen = nil
|
||||
case sm.currentScreen != nil:
|
||||
if handler, ok := sm.currentScreen.(ScreenAdvanceHandler); ok {
|
||||
if err := handler.Advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render renders the UI by a given surface
|
||||
func (sm *ScreenManager) Render(surface d2interface.Surface) error {
|
||||
if handler, ok := sm.currentScreen.(ScreenRenderHandler); ok {
|
||||
if err := handler.Render(surface); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -94,6 +94,7 @@ type MapEngineTest struct {
|
||||
renderer d2interface.Renderer
|
||||
inputManager d2interface.InputManager
|
||||
audioProvider d2interface.AudioProvider
|
||||
screen *d2screen.ScreenManager
|
||||
|
||||
lastMouseX, lastMouseY int
|
||||
selX, selY int
|
||||
@ -115,6 +116,7 @@ func CreateMapEngineTest(currentRegion,
|
||||
renderer d2interface.Renderer,
|
||||
inputManager d2interface.InputManager,
|
||||
audioProvider d2interface.AudioProvider,
|
||||
screen *d2screen.ScreenManager,
|
||||
) *MapEngineTest {
|
||||
result := &MapEngineTest{
|
||||
currentRegion: currentRegion,
|
||||
@ -126,6 +128,7 @@ func CreateMapEngineTest(currentRegion,
|
||||
renderer: renderer,
|
||||
inputManager: inputManager,
|
||||
audioProvider: audioProvider,
|
||||
screen: screen,
|
||||
}
|
||||
result.gameState = d2player.CreateTestGameState()
|
||||
|
||||
@ -428,13 +431,13 @@ func (met *MapEngineTest) OnKeyDown(event d2interface.KeyEvent) bool {
|
||||
switch event.KeyMod() {
|
||||
case d2enum.KeyModControl:
|
||||
met.fileIndex++
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
case d2enum.KeyModShift:
|
||||
met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
default:
|
||||
met.currentRegion = increment(met.currentRegion, 0, len(getRegions()))
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -444,13 +447,13 @@ func (met *MapEngineTest) OnKeyDown(event d2interface.KeyEvent) bool {
|
||||
switch event.KeyMod() {
|
||||
case d2enum.KeyModControl:
|
||||
met.fileIndex--
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
case d2enum.KeyModShift:
|
||||
met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
default:
|
||||
met.currentRegion = decrement(met.currentRegion, 0, len(getRegions()))
|
||||
d2screen.SetNextScreen(met)
|
||||
met.screen.SetNextScreen(met)
|
||||
}
|
||||
|
||||
return true
|
||||
|
Loading…
Reference in New Issue
Block a user