1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-04 15:46:51 -05:00
OpenDiablo2/d2core/d2systems/scene_loading_screen.go
2020-12-07 12:44:11 -08:00

260 lines
6.1 KiB
Go

package d2systems
import (
"image/color"
"math"
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
sceneKeyLoading = "Loading"
)
// static check that LoadingScene implements the scene interface
var _ d2interface.Scene = &LoadingScene{}
// NewLoadingScene creates a new main menu scene. This is the first screen that the user
// will see when launching the game.
func NewLoadingScene() *LoadingScene {
scene := &LoadingScene{
BaseScene: NewBaseScene(sceneKeyLoading),
}
return scene
}
// LoadingScene represents the game's loading screen, where loading progress is displayed
type LoadingScene struct {
*BaseScene
loadingSprite akara.EID
loadStages struct {
stage1 *akara.Subscription // has path, no type
stage2 *akara.Subscription // has type, no handle
stage3 *akara.Subscription // has handle, no asset
stage4 *akara.Subscription // is loaded
}
progress float64
booted bool
}
// Init the loading scene
func (s *LoadingScene) Init(world *akara.World) {
s.World = world
s.Info("initializing ...")
s.backgroundColor = color.Black
s.setupSubscriptions()
}
func (s *LoadingScene) setupSubscriptions() {
s.Info("setting up component subscriptions")
stage1 := s.NewComponentFilter().
Require(
&d2components.FilePath{},
).
Forbid( // but we forbid files that are already loaded
&d2components.FileType{},
&d2components.FileHandle{},
&d2components.FileSource{},
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
stage2 := s.NewComponentFilter().
Require(
&d2components.FilePath{},
&d2components.FileType{},
).
Forbid( // but we forbid files that are already loaded
&d2components.FileHandle{},
&d2components.FileSource{},
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
stage3 := s.NewComponentFilter().
Require(
&d2components.FilePath{},
&d2components.FileType{},
&d2components.FileHandle{},
).
Forbid( // but we forbid files that are already loaded
&d2components.FileSource{},
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
// we want to know about loaded files, too
stage4 := s.NewComponentFilter().
RequireOne(
&d2components.FileHandle{},
&d2components.FileSource{},
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
s.loadStages.stage1 = s.World.AddSubscription(stage1) // has path, no type
s.loadStages.stage2 = s.World.AddSubscription(stage2) // has type, no handle
s.loadStages.stage3 = s.World.AddSubscription(stage3) // has handle, no asset
s.loadStages.stage4 = s.World.AddSubscription(stage4) // is loaded
}
func (s *LoadingScene) boot() {
if !s.BaseScene.booted {
s.BaseScene.boot()
return
}
s.createLoadingScreen()
s.booted = true
}
func (s *LoadingScene) createLoadingScreen() {
s.Info("creating loading screen")
s.loadingSprite = s.Add.Sprite(0, 0, d2resource.LoadingScreen, d2resource.PaletteLoading)
}
// Update the loading scene
func (s *LoadingScene) Update() {
for _, id := range s.Viewports {
s.AddPriority(id).Priority = scenePriorityLoading
}
if s.Paused() {
return
}
if !s.booted {
s.boot()
}
s.updateLoadProgress()
s.updateViewportAlpha()
s.updateLoadingSpritePosition()
s.updateLoadingSpriteFrame()
s.BaseScene.Update()
}
func (s *LoadingScene) updateLoadProgress() {
untyped := float64(len(s.loadStages.stage1.GetEntities()))
unhandled := float64(len(s.loadStages.stage2.GetEntities()))
unparsed := float64(len(s.loadStages.stage3.GetEntities()))
loaded := float64(len(s.loadStages.stage4.GetEntities()))
s.progress = 1 - ((untyped + unhandled + unparsed) / 3 / loaded)
}
func (s *LoadingScene) updateViewportAlpha() {
if len(s.Viewports) < 1 {
return
}
alpha, found := s.GetAlpha(s.Viewports[0])
if !found {
return
}
isLoading := len(s.loadStages.stage1.GetEntities()) > 0 ||
len(s.loadStages.stage2.GetEntities()) > 0 ||
len(s.loadStages.stage3.GetEntities()) > 0
if isLoading {
alpha.Alpha = math.Min(alpha.Alpha+0.125, 1)
} else {
alpha.Alpha = math.Max(alpha.Alpha-0.125, 0)
}
}
func (s *LoadingScene) updateLoadingSpritePosition() {
if len(s.Viewports) < 1 {
return
}
viewport, found := s.GetViewport(s.Viewports[0])
if !found {
return
}
sprite, found := s.GetSprite(s.loadingSprite)
if !found {
return
}
position, found := s.GetPosition(s.loadingSprite)
if !found {
return
}
centerX, centerY := viewport.Width/2, viewport.Height/2
frameW, frameH := sprite.GetCurrentFrameSize()
// we add the frameH in the Y because sprites are supposed to be drawn from bottom to top
position.X, position.Y = float64(centerX-(frameW/2)), float64(centerY+(frameH/2))
}
func (s *LoadingScene) updateLoadingSpriteFrame() {
sprite, found := s.GetSprite(s.loadingSprite)
if !found {
return
}
numFrames := float64(sprite.GetFrameCount())
if err := sprite.SetCurrentFrame(int(s.progress * (numFrames - 1))); err != nil {
_ = sprite.SetCurrentFrame(0)
}
}