1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-29 22:56:07 -04:00

More layout cleanup

This commit is contained in:
Tim Sarbin 2019-10-25 20:28:14 -04:00
parent 6562816710
commit f485f803c8
6 changed files with 177 additions and 128 deletions

View File

@ -1,13 +0,0 @@
package Common
import (
"github.com/hajimehoshi/ebiten"
)
// SceneInterface defines the function necessary for scene management
type SceneInterface interface {
Load()
Unload()
Render(screen *ebiten.Image)
Update()
}

114
Engine.go
View File

@ -4,19 +4,21 @@ import (
"encoding/json"
"io/ioutil"
"log"
"math"
"path"
"strings"
"sync"
"github.com/essial/OpenDiablo2/Sound"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/MPQ"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
"github.com/essial/OpenDiablo2/Scenes"
"github.com/essial/OpenDiablo2/UI"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/audio"
"github.com/hajimehoshi/ebiten/audio/wav"
)
// EngineConfig defines the configuration for the engine, loaded from config.json
@ -30,16 +32,6 @@ type EngineConfig struct {
MpqLoadOrder []string
}
// CursorButton represents a mouse button
type CursorButton uint8
const (
// CursorButtonLeft represents the left mouse button
CursorButtonLeft CursorButton = 1
// CursorButtonRight represents the right mouse button
CursorButtonRight CursorButton = 2
)
// Engine is the core OpenDiablo2 engine
type Engine struct {
Settings EngineConfig // Engine configuration settings from json file
@ -47,39 +39,31 @@ type Engine struct {
Palettes map[Palettes.Palette]Common.Palette // Color palettes
SoundEntries map[string]SoundEntry // Sound configurations
LoadingSprite *Common.Sprite // The sprite shown when loading stuff
CursorX int // X position of the cursor
CursorY int // Y position of the cursor
CursorButtons CursorButton // The buttons that are currently being pressed
LoadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
CurrentScene Common.SceneInterface // The current scene being rendered
loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
stepLoadingSize float64 // The size for each loading step
CurrentScene Scenes.Scene // The current scene being rendered
UIManager *UI.Manager // The UI manager
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
audioContext *audio.Context // The Audio context
bgmAudio *audio.Player // The audio player
SoundManager *Sound.Manager // The sound manager
nextScene Scenes.Scene // The next scene to be loaded at the end of the game loop
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
}
// CreateEngine creates and instance of the OpenDiablo2 engine
func CreateEngine() *Engine {
result := &Engine{
LoadingProgress: float64(0.0),
CurrentScene: nil,
nextScene: nil,
CurrentScene: nil,
nextScene: nil,
}
result.loadConfigurationFile()
result.mapMpqFiles()
result.loadPalettes()
result.loadSoundEntries()
result.SoundManager = Sound.CreateManager(result)
result.UIManager = UI.CreateManager(result)
audioContext, err := audio.NewContext(22050)
if err != nil {
log.Fatal(err)
}
result.audioContext = audioContext
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading)
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
result.SetNextScene(CreateMainMenu(result))
result.SetNextScene(Scenes.CreateMainMenu(result, result.UIManager, result.SoundManager))
return result
}
@ -142,7 +126,7 @@ func (v *Engine) LoadFile(fileName string) []byte {
// IsLoading returns true if the engine is currently in a loading state
func (v *Engine) IsLoading() bool {
return v.LoadingProgress < 1.0
return v.loadingProgress < 1.0
}
func (v *Engine) loadPalettes() {
@ -190,12 +174,16 @@ func (v *Engine) updateScene() {
v.CurrentScene = v.nextScene
v.nextScene = nil
v.UIManager.Reset()
v.CurrentScene.Load()
}
// CursorButtonPressed determines if the specified button has been pressed
func (v *Engine) CursorButtonPressed(button CursorButton) bool {
return v.CursorButtons&button > 0
thingsToLoad := v.CurrentScene.Load()
v.SetLoadingStepSize(1.0 / float64(len(thingsToLoad)))
v.ResetLoading()
go func() {
for _, f := range thingsToLoad {
f()
v.StepLoading()
}
v.FinishLoading()
}()
}
// Update updates the internal state of the engine
@ -217,22 +205,15 @@ func (v *Engine) Update() {
if v.IsLoading() {
return
}
v.CursorButtons = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
v.CursorButtons |= CursorButtonLeft
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
v.CursorButtons |= CursorButtonRight
}
v.CurrentScene.Update()
v.UIManager.Update()
}
// Draw draws the game
func (v *Engine) Draw(screen *ebiten.Image) {
v.CursorX, v.CursorY = ebiten.CursorPosition()
if v.LoadingProgress < 1.0 {
v.LoadingSprite.Frame = uint8(Common.Max(0, Common.Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.LoadingProgress))))
if v.loadingProgress < 1.0 {
v.LoadingSprite.Frame = uint8(Common.Max(0, Common.Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.loadingProgress))))
v.LoadingSprite.Draw(screen)
} else {
if v.CurrentScene == nil {
@ -244,29 +225,26 @@ func (v *Engine) Draw(screen *ebiten.Image) {
}
// SetNextScene tells the engine what scene to load on the next update cycle
func (v *Engine) SetNextScene(nextScene Common.SceneInterface) {
func (v *Engine) SetNextScene(nextScene Scenes.Scene) {
v.nextScene = nextScene
}
// PlayBGM plays an infinitely looping background track
func (v *Engine) PlayBGM(song string) {
go func() {
if v.bgmAudio != nil {
v.bgmAudio.Close()
}
audioData := v.LoadFile(song)
d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData))
if err != nil {
log.Fatal(err)
}
s := audio.NewInfiniteLoop(d, int64(len(audioData)))
v.bgmAudio, err = audio.NewPlayer(v.audioContext, s)
if err != nil {
log.Fatal(err)
}
// Play the infinite-length stream. This never ends.
v.bgmAudio.Rewind()
v.bgmAudio.Play()
}()
// SetLoadingStepSize sets the size of the loading step
func (v *Engine) SetLoadingStepSize(size float64) {
v.stepLoadingSize = size
}
// ResetLoading resets the loading progress
func (v *Engine) ResetLoading() {
v.loadingProgress = 0.0
}
// StepLoading increments the loading progress
func (v *Engine) StepLoading() {
v.loadingProgress = math.Min(1.0, v.loadingProgress+v.stepLoadingSize)
}
// FinishLoading terminates the loading phase
func (v *Engine) FinishLoading() {
v.loadingProgress = 1.0
}

13
Scenes/Scene.go Normal file
View File

@ -0,0 +1,13 @@
package Scenes
import (
"github.com/hajimehoshi/ebiten"
)
// Scene defines the function necessary for scene management
type Scene interface {
Load() []func()
Unload()
Render(screen *ebiten.Image)
Update()
}

View File

@ -1,10 +1,11 @@
package OpenDiablo2
package Scenes
import (
"image/color"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/Sound"
"github.com/essial/OpenDiablo2/UI"
"github.com/essial/OpenDiablo2/ResourcePaths"
@ -13,7 +14,9 @@ import (
// MainMenu represents the main menu
type MainMenu struct {
engine *Engine
uiManager *UI.Manager
soundManager *Sound.Manager
fileProvider Common.FileProvider
trademarkBackground *Common.Sprite
background *Common.Sprite
diabloLogoLeft *Common.Sprite
@ -27,72 +30,63 @@ type MainMenu struct {
}
// CreateMainMenu creates an instance of MainMenu
func CreateMainMenu(engine *Engine) *MainMenu {
func CreateMainMenu(fileProvider Common.FileProvider, uiManager *UI.Manager, soundManager *Sound.Manager) *MainMenu {
result := &MainMenu{
engine: engine,
fileProvider: fileProvider,
uiManager: uiManager,
soundManager: soundManager,
showTrademarkScreen: true,
}
return result
}
// Load is called to load the resources for the main menu
func (v *MainMenu) Load() {
v.engine.PlayBGM(ResourcePaths.BGMTitle)
go func() {
loadStep := 1.0 / 8.0
v.engine.LoadingProgress = 0
{
v.copyrightLabel = UI.CreateLabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static)
func (v *MainMenu) Load() []func() {
v.soundManager.PlayBGM(ResourcePaths.BGMTitle)
return []func(){
func() {
v.copyrightLabel = UI.CreateLabel(v.fileProvider, ResourcePaths.FontFormal12, Palettes.Static)
v.copyrightLabel.Alignment = UI.LabelAlignCenter
v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment")
v.copyrightLabel.ColorMod = color.RGBA{188, 168, 140, 255}
v.copyrightLabel.MoveTo(400, 500)
v.engine.LoadingProgress += loadStep
}
{
v.copyrightLabel2 = UI.CreateLabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static)
},
func() {
v.copyrightLabel2 = UI.CreateLabel(v.fileProvider, ResourcePaths.FontFormal12, Palettes.Static)
v.copyrightLabel2.Alignment = UI.LabelAlignCenter
v.copyrightLabel2.SetText("All Rights Reserved.")
v.copyrightLabel2.ColorMod = color.RGBA{188, 168, 140, 255}
v.copyrightLabel2.MoveTo(400, 525)
v.engine.LoadingProgress += loadStep
}
{
v.background = v.engine.LoadSprite(ResourcePaths.GameSelectScreen, Palettes.Sky)
},
func() {
v.background = v.fileProvider.LoadSprite(ResourcePaths.GameSelectScreen, Palettes.Sky)
v.background.MoveTo(0, 0)
v.engine.LoadingProgress += loadStep
}
{
v.trademarkBackground = v.engine.LoadSprite(ResourcePaths.TrademarkScreen, Palettes.Sky)
},
func() {
v.trademarkBackground = v.fileProvider.LoadSprite(ResourcePaths.TrademarkScreen, Palettes.Sky)
v.trademarkBackground.MoveTo(0, 0)
v.engine.LoadingProgress += loadStep
}
{
v.diabloLogoLeft = v.engine.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, Palettes.Units)
},
func() {
v.diabloLogoLeft = v.fileProvider.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, Palettes.Units)
v.diabloLogoLeft.Blend = true
v.diabloLogoLeft.Animate = true
v.diabloLogoLeft.MoveTo(400, 120)
v.engine.LoadingProgress += loadStep
}
{
v.diabloLogoRight = v.engine.LoadSprite(ResourcePaths.Diablo2LogoFireRight, Palettes.Units)
},
func() {
v.diabloLogoRight = v.fileProvider.LoadSprite(ResourcePaths.Diablo2LogoFireRight, Palettes.Units)
v.diabloLogoRight.Blend = true
v.diabloLogoRight.Animate = true
v.diabloLogoRight.MoveTo(400, 120)
v.engine.LoadingProgress += loadStep
}
{
v.diabloLogoLeftBack = v.engine.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, Palettes.Units)
},
func() {
v.diabloLogoLeftBack = v.fileProvider.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, Palettes.Units)
v.diabloLogoLeftBack.MoveTo(400, 120)
v.engine.LoadingProgress += loadStep
}
{
v.diabloLogoRightBack = v.engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, Palettes.Units)
},
func() {
v.diabloLogoRightBack = v.fileProvider.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, Palettes.Units)
v.diabloLogoRightBack.MoveTo(400, 120)
v.engine.LoadingProgress = 1.0
}
}()
},
}
}
// Unload unloads the data for the main menu
@ -123,7 +117,7 @@ func (v *MainMenu) Render(screen *ebiten.Image) {
// Update runs the update logic on the main menu
func (v *MainMenu) Update() {
if v.showTrademarkScreen {
if v.engine.CursorButtonPressed(CursorButtonLeft) {
if v.uiManager.CursorButtonPressed(UI.CursorButtonLeft) {
v.leftButtonHeld = true
v.showTrademarkScreen = false
}

52
Sound/AudioProvider.go Normal file
View File

@ -0,0 +1,52 @@
package Sound
import (
"log"
"github.com/essial/OpenDiablo2/Common"
"github.com/hajimehoshi/ebiten/audio"
"github.com/hajimehoshi/ebiten/audio/wav"
)
// Manager provides sound
type Manager struct {
fileProvider Common.FileProvider
audioContext *audio.Context // The Audio context
bgmAudio *audio.Player // The audio player
}
// CreateManager creates a sound provider
func CreateManager(fileProvider Common.FileProvider) *Manager {
result := &Manager{
fileProvider: fileProvider,
}
audioContext, err := audio.NewContext(22050)
if err != nil {
log.Fatal(err)
}
result.audioContext = audioContext
return result
}
// PlayBGM plays an infinitely looping background track
func (v *Manager) PlayBGM(song string) {
go func() {
if v.bgmAudio != nil {
v.bgmAudio.Close()
}
audioData := v.fileProvider.LoadFile(song)
d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData))
if err != nil {
log.Fatal(err)
}
s := audio.NewInfiniteLoop(d, int64(len(audioData)))
v.bgmAudio, err = audio.NewPlayer(v.audioContext, s)
if err != nil {
log.Fatal(err)
}
// Play the infinite-length stream. This never ends.
v.bgmAudio.Rewind()
v.bgmAudio.Play()
}()
}

View File

@ -7,10 +7,23 @@ import (
"github.com/hajimehoshi/ebiten"
)
// CursorButton represents a mouse button
type CursorButton uint8
const (
// CursorButtonLeft represents the left mouse button
CursorButtonLeft CursorButton = 1
// CursorButtonRight represents the right mouse button
CursorButtonRight CursorButton = 2
)
// Manager represents the UI manager
type Manager struct {
widgets []*Widget
cursorSprite *Common.Sprite
widgets []*Widget
cursorSprite *Common.Sprite
cursorButtons CursorButton
CursorX int
CursorY int
}
// CreateManager creates a new instance of a UI manager
@ -41,5 +54,17 @@ func (v *Manager) Draw(screen *ebiten.Image) {
// Update updates all of the UI elements
func (v *Manager) Update() {
v.cursorButtons = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
v.cursorButtons |= CursorButtonLeft
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
v.cursorButtons |= CursorButtonRight
}
v.CursorX, v.CursorY = ebiten.CursorPosition()
}
// CursorButtonPressed determines if the specified button has been pressed
func (v *Manager) CursorButtonPressed(button CursorButton) bool {
return v.cursorButtons&button > 0
}