mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 07:27:19 -05:00
More code cleanup. Created UI manager.
This commit is contained in:
parent
c212a9875c
commit
56eb461f1f
@ -1,4 +1,4 @@
|
|||||||
package OpenDiablo2
|
package Common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
@ -32,6 +32,7 @@ func now() int64 {
|
|||||||
return monotonicClock
|
return monotonicClock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ColorToColorM converts a normal color to a color matrix
|
||||||
func ColorToColorM(clr color.Color) ebiten.ColorM {
|
func ColorToColorM(clr color.Color) ebiten.ColorM {
|
||||||
// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
|
// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
|
||||||
cr, cg, cb, ca := clr.RGBA()
|
cr, cg, cb, ca := clr.RGBA()
|
@ -1,4 +1,4 @@
|
|||||||
package OpenDiablo2
|
package Common
|
||||||
|
|
||||||
// Min returns the lower of two values
|
// Min returns the lower of two values
|
||||||
func Min(a, b uint32) uint32 {
|
func Min(a, b uint32) uint32 {
|
||||||
@ -16,7 +16,7 @@ func Max(a, b uint32) uint32 {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Max returns the higher of two values
|
// MaxInt32 returns the higher of two values
|
||||||
func MaxInt32(a, b int32) int32 {
|
func MaxInt32(a, b int32) int32 {
|
||||||
if a > b {
|
if a > b {
|
||||||
return a
|
return a
|
@ -1,4 +1,4 @@
|
|||||||
package OpenDiablo2
|
package Common
|
||||||
|
|
||||||
import "github.com/essial/OpenDiablo2/Palettes"
|
import "github.com/essial/OpenDiablo2/Palettes"
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package OpenDiablo2
|
package Common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
8
Common/SpriteProvider.go
Normal file
8
Common/SpriteProvider.go
Normal 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
|
||||||
|
}
|
55
Engine.go
55
Engine.go
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/essial/OpenDiablo2/Common"
|
"github.com/essial/OpenDiablo2/Common"
|
||||||
"github.com/essial/OpenDiablo2/Palettes"
|
"github.com/essial/OpenDiablo2/Palettes"
|
||||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||||
|
"github.com/essial/OpenDiablo2/UI"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"github.com/hajimehoshi/ebiten/audio"
|
"github.com/hajimehoshi/ebiten/audio"
|
||||||
@ -40,22 +41,22 @@ const (
|
|||||||
|
|
||||||
// Engine is the core OpenDiablo2 engine
|
// Engine is the core OpenDiablo2 engine
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
Settings EngineConfig // Engine configuration settings from json file
|
Settings EngineConfig // Engine configuration settings from json file
|
||||||
Files map[string]string // Map that defines which files are in which MPQs
|
Files map[string]string // Map that defines which files are in which MPQs
|
||||||
Palettes map[Palettes.Palette]Palette // Color palettes
|
Palettes map[Palettes.Palette]Common.Palette // Color palettes
|
||||||
SoundEntries map[string]SoundEntry // Sound configurations
|
SoundEntries map[string]SoundEntry // Sound configurations
|
||||||
CursorSprite *Sprite // The sprite shown for cursors
|
LoadingSprite *Common.Sprite // The sprite shown when loading stuff
|
||||||
LoadingSprite *Sprite // The sprite shown when loading stuff
|
CursorX int // X position of the cursor
|
||||||
CursorX int // X position of the cursor
|
CursorY int // Y position of the cursor
|
||||||
CursorY int // Y position of the cursor
|
CursorButtons CursorButton // The buttons that are currently being pressed
|
||||||
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.
|
||||||
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
|
||||||
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
|
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
|
||||||
fontCache map[string]*MPQFont // The font cash
|
fontCache map[string]*MPQFont // The font cash
|
||||||
audioContext *audio.Context // The Audio context
|
audioContext *audio.Context // The Audio context
|
||||||
bgmAudio *audio.Player // The audio player
|
bgmAudio *audio.Player // The audio player
|
||||||
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
|
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateEngine creates and instance of the OpenDiablo2 engine
|
// CreateEngine creates and instance of the OpenDiablo2 engine
|
||||||
@ -70,12 +71,12 @@ func CreateEngine() *Engine {
|
|||||||
result.mapMpqFiles()
|
result.mapMpqFiles()
|
||||||
result.loadPalettes()
|
result.loadPalettes()
|
||||||
result.loadSoundEntries()
|
result.loadSoundEntries()
|
||||||
|
result.UIManager = UI.CreateManager(result)
|
||||||
audioContext, err := audio.NewContext(22050)
|
audioContext, err := audio.NewContext(22050)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
result.audioContext = audioContext
|
result.audioContext = audioContext
|
||||||
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units)
|
|
||||||
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading)
|
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading)
|
||||||
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
||||||
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
|
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
|
||||||
@ -146,7 +147,7 @@ func (v *Engine) IsLoading() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Engine) loadPalettes() {
|
func (v *Engine) loadPalettes() {
|
||||||
v.Palettes = make(map[Palettes.Palette]Palette)
|
v.Palettes = make(map[Palettes.Palette]Common.Palette)
|
||||||
log.Println("loading palettes")
|
log.Println("loading palettes")
|
||||||
for file := range v.Files {
|
for file := range v.Files {
|
||||||
if strings.Index(file, "/data/global/palette/") != 0 || strings.Index(file, ".dat") != len(file)-4 {
|
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, `/`)
|
nameParts := strings.Split(file, `/`)
|
||||||
paletteName := Palettes.Palette(nameParts[len(nameParts)-2])
|
paletteName := Palettes.Palette(nameParts[len(nameParts)-2])
|
||||||
palette := CreatePalette(paletteName, v.GetFile(file))
|
palette := Common.CreatePalette(paletteName, v.GetFile(file))
|
||||||
v.Palettes[paletteName] = palette
|
v.Palettes[paletteName] = palette
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,9 +174,9 @@ func (v *Engine) loadSoundEntries() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadSprite loads a sprite from the game's data files
|
// 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)
|
data := v.GetFile(fileName)
|
||||||
sprite := CreateSprite(data, v.Palettes[palette])
|
sprite := Common.CreateSprite(data, v.Palettes[palette])
|
||||||
return sprite
|
return sprite
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +190,7 @@ func (v *Engine) updateScene() {
|
|||||||
}
|
}
|
||||||
v.CurrentScene = v.nextScene
|
v.CurrentScene = v.nextScene
|
||||||
v.nextScene = nil
|
v.nextScene = nil
|
||||||
|
v.UIManager.Reset()
|
||||||
v.CurrentScene.Load()
|
v.CurrentScene.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +214,10 @@ func (v *Engine) Update() {
|
|||||||
if v.CurrentScene == nil {
|
if v.CurrentScene == nil {
|
||||||
log.Fatal("no scene loaded")
|
log.Fatal("no scene loaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.IsLoading() {
|
||||||
|
return
|
||||||
|
}
|
||||||
v.CursorButtons = 0
|
v.CursorButtons = 0
|
||||||
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||||
v.CursorButtons |= CursorButtonLeft
|
v.CursorButtons |= CursorButtonLeft
|
||||||
@ -220,23 +226,22 @@ func (v *Engine) Update() {
|
|||||||
v.CursorButtons |= CursorButtonRight
|
v.CursorButtons |= CursorButtonRight
|
||||||
}
|
}
|
||||||
v.CurrentScene.Update()
|
v.CurrentScene.Update()
|
||||||
|
v.UIManager.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw draws the game
|
// Draw draws the game
|
||||||
func (v *Engine) Draw(screen *ebiten.Image) {
|
func (v *Engine) Draw(screen *ebiten.Image) {
|
||||||
v.CursorX, v.CursorY = ebiten.CursorPosition()
|
v.CursorX, v.CursorY = ebiten.CursorPosition()
|
||||||
if v.LoadingProgress < 1.0 {
|
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)
|
v.LoadingSprite.Draw(screen)
|
||||||
} else {
|
} else {
|
||||||
if v.CurrentScene == nil {
|
if v.CurrentScene == nil {
|
||||||
log.Fatal("no scene loaded")
|
log.Fatal("no scene loaded")
|
||||||
}
|
}
|
||||||
v.CurrentScene.Render(screen)
|
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
|
// SetNextScene tells the engine what scene to load on the next update cycle
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package OpenDiablo2
|
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
|
// MPQFontSize represents the size of a character in a font
|
||||||
type MPQFontSize struct {
|
type MPQFontSize struct {
|
||||||
@ -11,7 +14,7 @@ type MPQFontSize struct {
|
|||||||
// MPQFont represents a font
|
// MPQFont represents a font
|
||||||
type MPQFont struct {
|
type MPQFont struct {
|
||||||
Engine *Engine
|
Engine *Engine
|
||||||
FontSprite *Sprite
|
FontSprite *Common.Sprite
|
||||||
Metrics map[uint8]MPQFontSize
|
Metrics map[uint8]MPQFontSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/JoshVarga/blast"
|
"github.com/JoshVarga/blast"
|
||||||
|
"github.com/essial/OpenDiablo2/Common"
|
||||||
"github.com/essial/OpenDiablo2/Compression"
|
"github.com/essial/OpenDiablo2/Compression"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32)
|
|||||||
v.loadSingleUnit()
|
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])
|
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[v.CurrentPosition:v.CurrentPosition+bytesToCopy])
|
||||||
v.CurrentPosition += bytesToCopy
|
v.CurrentPosition += bytesToCopy
|
||||||
return 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 {
|
func (v *MPQStream) readInternal(buffer []byte, offset, count uint32) uint32 {
|
||||||
v.bufferData()
|
v.bufferData()
|
||||||
localPosition := v.CurrentPosition % v.BlockSize
|
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 {
|
if bytesToCopy <= 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -107,7 +108,7 @@ func (v *MPQStream) bufferData() {
|
|||||||
if requiredBlock == v.CurrentBlockIndex {
|
if requiredBlock == v.CurrentBlockIndex {
|
||||||
return
|
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.CurrentData = v.loadBlock(requiredBlock, expectedLength)
|
||||||
v.CurrentBlockIndex = requiredBlock
|
v.CurrentBlockIndex = requiredBlock
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package OpenDiablo2
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/essial/OpenDiablo2/Common"
|
||||||
"github.com/essial/OpenDiablo2/Palettes"
|
"github.com/essial/OpenDiablo2/Palettes"
|
||||||
|
|
||||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||||
@ -12,12 +13,12 @@ import (
|
|||||||
// MainMenu represents the main menu
|
// MainMenu represents the main menu
|
||||||
type MainMenu struct {
|
type MainMenu struct {
|
||||||
engine *Engine
|
engine *Engine
|
||||||
trademarkBackground *Sprite
|
trademarkBackground *Common.Sprite
|
||||||
background *Sprite
|
background *Common.Sprite
|
||||||
diabloLogoLeft *Sprite
|
diabloLogoLeft *Common.Sprite
|
||||||
diabloLogoRight *Sprite
|
diabloLogoRight *Common.Sprite
|
||||||
diabloLogoLeftBack *Sprite
|
diabloLogoLeftBack *Common.Sprite
|
||||||
diabloLogoRightBack *Sprite
|
diabloLogoRightBack *Common.Sprite
|
||||||
copyrightLabel *UILabel
|
copyrightLabel *UILabel
|
||||||
copyrightLabel2 *UILabel
|
copyrightLabel2 *UILabel
|
||||||
showTrademarkScreen bool
|
showTrademarkScreen bool
|
||||||
|
45
UI/Manager.go
Normal file
45
UI/Manager.go
Normal 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() {
|
||||||
|
|
||||||
|
}
|
14
UILabel.go
14
UILabel.go
@ -3,18 +3,24 @@ package OpenDiablo2
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/essial/OpenDiablo2/Common"
|
||||||
"github.com/essial/OpenDiablo2/Palettes"
|
"github.com/essial/OpenDiablo2/Palettes"
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UILabelAlignment represents a label's alignment
|
||||||
type UILabelAlignment uint8
|
type UILabelAlignment uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UILabelAlignLeft UILabelAlignment = 0
|
// UILabelAlignLeft represents a left-aligned label
|
||||||
|
UILabelAlignLeft UILabelAlignment = 0
|
||||||
|
// UILabelAlignCenter represents a center-aligned label
|
||||||
UILabelAlignCenter UILabelAlignment = 1
|
UILabelAlignCenter UILabelAlignment = 1
|
||||||
UILabelAlignRight UILabelAlignment = 2
|
// UILabelAlignRight represents a right-aligned label
|
||||||
|
UILabelAlignRight UILabelAlignment = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UILabel represents a user interface label
|
||||||
type UILabel struct {
|
type UILabel struct {
|
||||||
text string
|
text string
|
||||||
X int
|
X int
|
||||||
@ -64,11 +70,12 @@ func (v *UILabel) calculateSize() (uint32, uint32) {
|
|||||||
for _, ch := range v.text {
|
for _, ch := range v.text {
|
||||||
metric := v.font.Metrics[uint8(ch)]
|
metric := v.font.Metrics[uint8(ch)]
|
||||||
width += uint32(metric.Width)
|
width += uint32(metric.Width)
|
||||||
height = Max(height, uint32(metric.Height))
|
height = Common.Max(height, uint32(metric.Height))
|
||||||
}
|
}
|
||||||
return width, height
|
return width, height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveTo moves the label to the specified location
|
||||||
func (v *UILabel) MoveTo(x, y int) {
|
func (v *UILabel) MoveTo(x, y int) {
|
||||||
v.X = x
|
v.X = x
|
||||||
v.Y = y
|
v.Y = y
|
||||||
@ -94,6 +101,7 @@ func (v *UILabel) cacheImage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetText sets the label's text
|
||||||
func (v *UILabel) SetText(newText string) {
|
func (v *UILabel) SetText(newText string) {
|
||||||
if v.text == newText {
|
if v.text == newText {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user