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

More code cleanup. Created UI manager.

This commit is contained in:
Tim Sarbin 2019-10-25 19:12:42 -04:00
parent c212a9875c
commit 56eb461f1f
11 changed files with 116 additions and 44 deletions

View File

@ -1,4 +1,4 @@
package OpenDiablo2
package Common
import (
"image/color"
@ -32,6 +32,7 @@ func now() int64 {
return monotonicClock
}
// ColorToColorM converts a normal color to a color matrix
func ColorToColorM(clr color.Color) ebiten.ColorM {
// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
cr, cg, cb, ca := clr.RGBA()

View File

@ -1,4 +1,4 @@
package OpenDiablo2
package Common
// Min returns the lower of two values
func Min(a, b uint32) uint32 {
@ -16,7 +16,7 @@ func Max(a, b uint32) uint32 {
return b
}
// Max returns the higher of two values
// MaxInt32 returns the higher of two values
func MaxInt32(a, b int32) int32 {
if a > b {
return a

View File

@ -1,4 +1,4 @@
package OpenDiablo2
package Common
import "github.com/essial/OpenDiablo2/Palettes"

View File

@ -1,4 +1,4 @@
package OpenDiablo2
package Common
import (
"encoding/binary"

8
Common/SpriteProvider.go Normal file
View File

@ -0,0 +1,8 @@
package Common
import "github.com/essial/OpenDiablo2/Palettes"
// SpriteProvider is an instance that can provide sprites
type SpriteProvider interface {
LoadSprite(fileName string, palette Palettes.Palette) *Sprite
}

View File

@ -11,6 +11,7 @@ import (
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
"github.com/essial/OpenDiablo2/UI"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/audio"
@ -40,22 +41,22 @@ const (
// Engine is the core OpenDiablo2 engine
type Engine struct {
Settings EngineConfig // Engine configuration settings from json file
Files map[string]string // Map that defines which files are in which MPQs
Palettes map[Palettes.Palette]Palette // Color palettes
SoundEntries map[string]SoundEntry // Sound configurations
CursorSprite *Sprite // The sprite shown for cursors
LoadingSprite *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
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
fontCache map[string]*MPQFont // The font cash
audioContext *audio.Context // The Audio context
bgmAudio *audio.Player // The audio player
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
Settings EngineConfig // Engine configuration settings from json file
Files map[string]string // Map that defines which files are in which MPQs
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
UIManager *UI.Manager // The UI manager
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
fontCache map[string]*MPQFont // The font cash
audioContext *audio.Context // The Audio context
bgmAudio *audio.Player // The audio player
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
}
// CreateEngine creates and instance of the OpenDiablo2 engine
@ -70,12 +71,12 @@ func CreateEngine() *Engine {
result.mapMpqFiles()
result.loadPalettes()
result.loadSoundEntries()
result.UIManager = UI.CreateManager(result)
audioContext, err := audio.NewContext(22050)
if err != nil {
log.Fatal(err)
}
result.audioContext = audioContext
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units)
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading)
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
@ -146,7 +147,7 @@ func (v *Engine) IsLoading() bool {
}
func (v *Engine) loadPalettes() {
v.Palettes = make(map[Palettes.Palette]Palette)
v.Palettes = make(map[Palettes.Palette]Common.Palette)
log.Println("loading palettes")
for file := range v.Files {
if strings.Index(file, "/data/global/palette/") != 0 || strings.Index(file, ".dat") != len(file)-4 {
@ -154,7 +155,7 @@ func (v *Engine) loadPalettes() {
}
nameParts := strings.Split(file, `/`)
paletteName := Palettes.Palette(nameParts[len(nameParts)-2])
palette := CreatePalette(paletteName, v.GetFile(file))
palette := Common.CreatePalette(paletteName, v.GetFile(file))
v.Palettes[paletteName] = palette
}
}
@ -173,9 +174,9 @@ func (v *Engine) loadSoundEntries() {
}
// LoadSprite loads a sprite from the game's data files
func (v *Engine) LoadSprite(fileName string, palette Palettes.Palette) *Sprite {
func (v *Engine) LoadSprite(fileName string, palette Palettes.Palette) *Common.Sprite {
data := v.GetFile(fileName)
sprite := CreateSprite(data, v.Palettes[palette])
sprite := Common.CreateSprite(data, v.Palettes[palette])
return sprite
}
@ -189,6 +190,7 @@ func (v *Engine) updateScene() {
}
v.CurrentScene = v.nextScene
v.nextScene = nil
v.UIManager.Reset()
v.CurrentScene.Load()
}
@ -212,6 +214,10 @@ func (v *Engine) Update() {
if v.CurrentScene == nil {
log.Fatal("no scene loaded")
}
if v.IsLoading() {
return
}
v.CursorButtons = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
v.CursorButtons |= CursorButtonLeft
@ -220,23 +226,22 @@ func (v *Engine) Update() {
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(Max(0, Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.LoadingProgress))))
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 {
log.Fatal("no scene loaded")
}
v.CurrentScene.Render(screen)
v.UIManager.Draw(screen)
}
v.CursorSprite.MoveTo(v.CursorX, v.CursorY)
v.CursorSprite.Draw(screen)
}
// SetNextScene tells the engine what scene to load on the next update cycle

View File

@ -1,6 +1,9 @@
package OpenDiablo2
import "github.com/essial/OpenDiablo2/Palettes"
import (
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
)
// MPQFontSize represents the size of a character in a font
type MPQFontSize struct {
@ -11,7 +14,7 @@ type MPQFontSize struct {
// MPQFont represents a font
type MPQFont struct {
Engine *Engine
FontSprite *Sprite
FontSprite *Common.Sprite
Metrics map[uint8]MPQFontSize
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"github.com/JoshVarga/blast"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Compression"
)
@ -84,7 +85,7 @@ func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32)
v.loadSingleUnit()
}
bytesToCopy := Min(uint32(len(v.CurrentData))-v.CurrentPosition, count)
bytesToCopy := Common.Min(uint32(len(v.CurrentData))-v.CurrentPosition, count)
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[v.CurrentPosition:v.CurrentPosition+bytesToCopy])
v.CurrentPosition += bytesToCopy
return bytesToCopy
@ -93,7 +94,7 @@ func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32)
func (v *MPQStream) readInternal(buffer []byte, offset, count uint32) uint32 {
v.bufferData()
localPosition := v.CurrentPosition % v.BlockSize
bytesToCopy := Min(uint32(len(v.CurrentData))-localPosition, count)
bytesToCopy := Common.Min(uint32(len(v.CurrentData))-localPosition, count)
if bytesToCopy <= 0 {
return 0
}
@ -107,7 +108,7 @@ func (v *MPQStream) bufferData() {
if requiredBlock == v.CurrentBlockIndex {
return
}
expectedLength := Min(v.BlockTableEntry.UncompressedFileSize-(requiredBlock*v.BlockSize), v.BlockSize)
expectedLength := Common.Min(v.BlockTableEntry.UncompressedFileSize-(requiredBlock*v.BlockSize), v.BlockSize)
v.CurrentData = v.loadBlock(requiredBlock, expectedLength)
v.CurrentBlockIndex = requiredBlock
}

View File

@ -3,6 +3,7 @@ package OpenDiablo2
import (
"image/color"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
@ -12,12 +13,12 @@ import (
// MainMenu represents the main menu
type MainMenu struct {
engine *Engine
trademarkBackground *Sprite
background *Sprite
diabloLogoLeft *Sprite
diabloLogoRight *Sprite
diabloLogoLeftBack *Sprite
diabloLogoRightBack *Sprite
trademarkBackground *Common.Sprite
background *Common.Sprite
diabloLogoLeft *Common.Sprite
diabloLogoRight *Common.Sprite
diabloLogoLeftBack *Common.Sprite
diabloLogoRightBack *Common.Sprite
copyrightLabel *UILabel
copyrightLabel2 *UILabel
showTrademarkScreen bool

45
UI/Manager.go Normal file
View File

@ -0,0 +1,45 @@
package UI
import (
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
"github.com/hajimehoshi/ebiten"
)
// Manager represents the UI manager
type Manager struct {
widgets []*Widget
cursorSprite *Common.Sprite
}
// CreateManager creates a new instance of a UI manager
func CreateManager(provider Common.SpriteProvider) *Manager {
result := &Manager{
widgets: make([]*Widget, 0),
cursorSprite: provider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units),
}
return result
}
// Reset resets the state of the UI manager. Typically called for new scenes
func (v *Manager) Reset() {
v.widgets = make([]*Widget, 0)
}
// AddWidget adds a widget to the UI manager
func (v *Manager) AddWidget(widget *Widget) {
v.widgets = append(v.widgets, widget)
}
// Draw renders all of the UI elements
func (v *Manager) Draw(screen *ebiten.Image) {
cx, cy := ebiten.CursorPosition()
v.cursorSprite.MoveTo(cx, cy)
v.cursorSprite.Draw(screen)
}
// Update updates all of the UI elements
func (v *Manager) Update() {
}

View File

@ -3,18 +3,24 @@ package OpenDiablo2
import (
"image/color"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/hajimehoshi/ebiten"
)
// UILabelAlignment represents a label's alignment
type UILabelAlignment uint8
const (
UILabelAlignLeft UILabelAlignment = 0
// UILabelAlignLeft represents a left-aligned label
UILabelAlignLeft UILabelAlignment = 0
// UILabelAlignCenter represents a center-aligned label
UILabelAlignCenter UILabelAlignment = 1
UILabelAlignRight UILabelAlignment = 2
// UILabelAlignRight represents a right-aligned label
UILabelAlignRight UILabelAlignment = 2
)
// UILabel represents a user interface label
type UILabel struct {
text string
X int
@ -64,11 +70,12 @@ func (v *UILabel) calculateSize() (uint32, uint32) {
for _, ch := range v.text {
metric := v.font.Metrics[uint8(ch)]
width += uint32(metric.Width)
height = Max(height, uint32(metric.Height))
height = Common.Max(height, uint32(metric.Height))
}
return width, height
}
// MoveTo moves the label to the specified location
func (v *UILabel) MoveTo(x, y int) {
v.X = x
v.Y = y
@ -94,6 +101,7 @@ func (v *UILabel) cacheImage() {
}
}
// SetText sets the label's text
func (v *UILabel) SetText(newText string) {
if v.text == newText {
return