Code cleanup. Added basic UI interface types.

This commit is contained in:
Tim Sarbin 2019-10-25 18:40:27 -04:00
parent ee317dafef
commit d5b37a74a3
10 changed files with 205 additions and 98 deletions

13
Common/Drawable.go Normal file
View File

@ -0,0 +1,13 @@
package Common
import "github.com/hajimehoshi/ebiten"
// Drawable represents an instance that can be drawn
type Drawable interface {
Draw(target *ebiten.Image)
GetSize() (uint32, uint32)
MoveTo(x, y int)
GetLocation() (int, int)
GetVisible() bool
SetVisible(bool)
}

View File

@ -9,6 +9,7 @@ import (
"sync"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
"github.com/hajimehoshi/ebiten"
@ -27,31 +28,34 @@ type EngineConfig struct {
MpqLoadOrder []string
}
// Engine is the core OpenDiablo2 engine
// CursorButton represents a mouse button
type CursorButton uint8
const (
CursorButtonLeft CursorButton = 1
// 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
Files map[string]string // Map that defines which files are in which MPQs
Palettes map[string]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]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
}
// CreateEngine creates and instance of the OpenDiablo2 engine
@ -71,8 +75,8 @@ func CreateEngine() *Engine {
log.Fatal(err)
}
result.audioContext = audioContext
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, result.Palettes["units"])
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, result.Palettes["loading"])
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)))
result.SetNextScene(CreateMainMenu(result))
@ -142,14 +146,14 @@ func (v *Engine) IsLoading() bool {
}
func (v *Engine) loadPalettes() {
v.Palettes = make(map[string]Palette)
v.Palettes = make(map[Palettes.Palette]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 {
continue
}
nameParts := strings.Split(file, `/`)
paletteName := nameParts[len(nameParts)-2]
paletteName := Palettes.Palette(nameParts[len(nameParts)-2])
palette := CreatePalette(paletteName, v.GetFile(file))
v.Palettes[paletteName] = palette
}
@ -169,9 +173,9 @@ func (v *Engine) loadSoundEntries() {
}
// LoadSprite loads a sprite from the game's data files
func (v *Engine) LoadSprite(fileName string, palette Palette) Sprite {
func (v *Engine) LoadSprite(fileName string, palette Palettes.Palette) *Sprite {
data := v.GetFile(fileName)
sprite := CreateSprite(data, palette)
sprite := CreateSprite(data, v.Palettes[palette])
return sprite
}
@ -241,13 +245,13 @@ func (v *Engine) SetNextScene(nextScene Common.SceneInterface) {
}
// GetFont creates or loads an existing font
func (v *Engine) GetFont(font, palette string) *MPQFont {
cacheItem, exists := v.fontCache[font+"_"+palette]
func (v *Engine) GetFont(font string, palette Palettes.Palette) *MPQFont {
cacheItem, exists := v.fontCache[font+"_"+string(palette)]
if exists {
return cacheItem
}
newFont := CreateMPQFont(v, font, v.Palettes[palette])
v.fontCache[font+"_"+palette] = newFont
newFont := CreateMPQFont(v, font, palette)
v.fontCache[font+"_"+string(palette)] = newFont
return newFont
}

View File

@ -1,18 +1,22 @@
package OpenDiablo2
import "github.com/essial/OpenDiablo2/Palettes"
// MPQFontSize represents the size of a character in a font
type MPQFontSize struct {
Width uint8
Height uint8
}
// MPQFont represents a font
type MPQFont struct {
Engine *Engine
FontSprite Sprite
FontSprite *Sprite
Metrics map[uint8]MPQFontSize
}
// CreateMPQFont creates an instance of a MPQ Font
func CreateMPQFont(engine *Engine, font string, palette Palette) *MPQFont {
func CreateMPQFont(engine *Engine, font string, palette Palettes.Palette) *MPQFont {
result := &MPQFont{
Engine: engine,
Metrics: make(map[uint8]MPQFontSize),

View File

@ -1,17 +1,20 @@
package OpenDiablo2
import "github.com/essial/OpenDiablo2/Palettes"
// PaletteRGB represents a color in a palette
type PaletteRGB struct {
R, G, B uint8
}
// Palette represents a palette
type Palette struct {
Name string
Name Palettes.Palette
Colors [256]PaletteRGB
}
// CreatePalette creates a palette
func CreatePalette(name string, data []byte) Palette {
func CreatePalette(name Palettes.Palette, data []byte) Palette {
result := Palette{Name: name}
for i := 0; i <= 255; i++ {

43
Palettes/Palettes.go Normal file
View File

@ -0,0 +1,43 @@
package Palettes
// Palette represents a named palette
type Palette string
const (
// Act1 palette
Act1 Palette = "act1"
// Act2 palette
Act2 Palette = "act2"
// Act3 palette
Act3 Palette = "act3"
// Act4 palette
Act4 Palette = "act4"
// Act5 palette
Act5 Palette = "act5"
// EndGame palette
EndGame Palette = "endgame"
// EndGame2 palette
EndGame2 Palette = "endgame2"
// Fechar palette
Fechar Palette = "fechar"
// Loading palette
Loading Palette = "loading"
// Menu0 palette
Menu0 Palette = "menu0"
// Menu1 palette
Menu1 Palette = "menu1"
// Menu2 palette
Menu2 Palette = "menu2"
// Menu3 palette
Menu3 Palette = "menu3"
// Menu4 palette
Menu4 Palette = "menu4"
// Sky palette
Sky Palette = "sky"
// Static palette
Static Palette = "static"
// Trademark palette
Trademark Palette = "trademark"
// Units palette
Units Palette = "units"
)

View File

@ -3,119 +3,127 @@ package OpenDiablo2
import (
"image/color"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/essial/OpenDiablo2/ResourcePaths"
"github.com/hajimehoshi/ebiten"
)
// MainMenu represents the main menu
type MainMenu struct {
Engine *Engine
TrademarkBackground Sprite
Background Sprite
DiabloLogoLeft Sprite
DiabloLogoRight Sprite
DiabloLogoLeftBack Sprite
DiabloLogoRightBack Sprite
CopyrightLabel *UILabel
CopyrightLabel2 *UILabel
ShowTrademarkScreen bool
LeftButtonHeld bool
engine *Engine
trademarkBackground *Sprite
background *Sprite
diabloLogoLeft *Sprite
diabloLogoRight *Sprite
diabloLogoLeftBack *Sprite
diabloLogoRightBack *Sprite
copyrightLabel *UILabel
copyrightLabel2 *UILabel
showTrademarkScreen bool
leftButtonHeld bool
}
// CreateMainMenu creates an instance of MainMenu
func CreateMainMenu(engine *Engine) *MainMenu {
result := &MainMenu{
Engine: engine,
ShowTrademarkScreen: true,
engine: engine,
showTrademarkScreen: true,
}
return result
}
// Load is called to load the resources for the main menu
func (v *MainMenu) Load() {
v.Engine.PlayBGM(ResourcePaths.BGMTitle)
v.engine.PlayBGM(ResourcePaths.BGMTitle)
go func() {
loadStep := 1.0 / 8.0
v.Engine.LoadingProgress = 0
v.engine.LoadingProgress = 0
{
v.CopyrightLabel = CreateUILabel(v.Engine, ResourcePaths.FontFormal12, "static")
v.CopyrightLabel.Alignment = UILabelAlignCenter
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.copyrightLabel = CreateUILabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static)
v.copyrightLabel.Alignment = UILabelAlignCenter
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 = CreateUILabel(v.Engine, ResourcePaths.FontFormal12, "static")
v.CopyrightLabel2.Alignment = UILabelAlignCenter
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.copyrightLabel2 = CreateUILabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static)
v.copyrightLabel2.Alignment = UILabelAlignCenter
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, v.Engine.Palettes["sky"])
v.Background.MoveTo(0, 0)
v.Engine.LoadingProgress += loadStep
v.background = v.engine.LoadSprite(ResourcePaths.GameSelectScreen, Palettes.Sky)
v.background.MoveTo(0, 0)
v.engine.LoadingProgress += loadStep
}
{
v.TrademarkBackground = v.Engine.LoadSprite(ResourcePaths.TrademarkScreen, v.Engine.Palettes["sky"])
v.TrademarkBackground.MoveTo(0, 0)
v.Engine.LoadingProgress += loadStep
v.trademarkBackground = v.engine.LoadSprite(ResourcePaths.TrademarkScreen, Palettes.Sky)
v.trademarkBackground.MoveTo(0, 0)
v.engine.LoadingProgress += loadStep
}
{
v.DiabloLogoLeft = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, v.Engine.Palettes["units"])
v.DiabloLogoLeft.Blend = true
v.DiabloLogoLeft.Animate = true
v.DiabloLogoLeft.MoveTo(400, 120)
v.Engine.LoadingProgress += loadStep
v.diabloLogoLeft = v.engine.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, v.Engine.Palettes["units"])
v.DiabloLogoRight.Blend = true
v.DiabloLogoRight.Animate = true
v.DiabloLogoRight.MoveTo(400, 120)
v.Engine.LoadingProgress += loadStep
v.diabloLogoRight = v.engine.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, v.Engine.Palettes["units"])
v.DiabloLogoLeftBack.MoveTo(400, 120)
v.Engine.LoadingProgress += loadStep
v.diabloLogoLeftBack = v.engine.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, Palettes.Units)
v.diabloLogoLeftBack.MoveTo(400, 120)
v.engine.LoadingProgress += loadStep
}
{
v.DiabloLogoRightBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, v.Engine.Palettes["units"])
v.DiabloLogoRightBack.MoveTo(400, 120)
v.Engine.LoadingProgress = 1.0
v.diabloLogoRightBack = v.engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, Palettes.Units)
v.diabloLogoRightBack.MoveTo(400, 120)
v.engine.LoadingProgress = 1.0
}
}()
}
// Unload unloads the data for the main menu
func (v *MainMenu) Unload() {
}
// Render renders the main menu
func (v *MainMenu) Render(screen *ebiten.Image) {
if v.ShowTrademarkScreen {
v.TrademarkBackground.DrawSegments(screen, 4, 3, 0)
if v.showTrademarkScreen {
v.trademarkBackground.DrawSegments(screen, 4, 3, 0)
} else {
v.Background.DrawSegments(screen, 4, 3, 0)
v.background.DrawSegments(screen, 4, 3, 0)
}
v.DiabloLogoLeftBack.Draw(screen)
v.DiabloLogoRightBack.Draw(screen)
v.DiabloLogoLeft.Draw(screen)
v.DiabloLogoRight.Draw(screen)
v.diabloLogoLeftBack.Draw(screen)
v.diabloLogoRightBack.Draw(screen)
v.diabloLogoLeft.Draw(screen)
v.diabloLogoRight.Draw(screen)
if v.ShowTrademarkScreen {
v.CopyrightLabel.Draw(screen)
v.CopyrightLabel2.Draw(screen)
if v.showTrademarkScreen {
v.copyrightLabel.Draw(screen)
v.copyrightLabel2.Draw(screen)
} else {
}
}
// Update runs the update logic on the main menu
func (v *MainMenu) Update() {
if v.ShowTrademarkScreen {
if v.Engine.CursorButtonPressed(CursorButtonLeft) {
v.LeftButtonHeld = true
v.ShowTrademarkScreen = false
if v.showTrademarkScreen {
if v.engine.CursorButtonPressed(CursorButtonLeft) {
v.leftButtonHeld = true
v.showTrademarkScreen = false
}
return
}

View File

@ -8,6 +8,7 @@ import (
"github.com/hajimehoshi/ebiten"
)
// Sprite represents a type of object in D2 that is comprised of one or more frames and directions
type Sprite struct {
Directions uint32
FramesPerDirection uint32
@ -18,8 +19,10 @@ type Sprite struct {
LastFrameTime time.Time
Animate bool
ColorMod color.Color
visible bool
}
// SpriteFrame represents a single frame of a sprite
type SpriteFrame struct {
Flip uint32
Width uint32
@ -33,8 +36,9 @@ type SpriteFrame struct {
Image *ebiten.Image
}
func CreateSprite(data []byte, palette Palette) Sprite {
result := Sprite{
// CreateSprite creates an instance of a sprite
func CreateSprite(data []byte, palette Palette) *Sprite {
result := &Sprite{
X: 50,
Y: 50,
Frame: 0,
@ -121,6 +125,7 @@ func CreateSprite(data []byte, palette Palette) Sprite {
return result
}
// GetSize returns the size of the sprite
func (v *Sprite) GetSize() (uint32, uint32) {
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
return frame.Width, frame.Height
@ -159,6 +164,7 @@ func (v *Sprite) Draw(target *ebiten.Image) {
target.DrawImage(frame.Image, opts)
}
// DrawSegments draws the sprite via a grid of segments
func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset int) {
v.updateAnimation()
yOffset := int32(0)
@ -191,3 +197,8 @@ func (v *Sprite) MoveTo(x, y int) {
v.X = x
v.Y = y
}
// GetLocation returns the location of the sprite
func (v *Sprite) GetLocation() (int, int) {
return v.X, v.Y
}

8
UI/Button.go Normal file
View File

@ -0,0 +1,8 @@
package UI
// Button defines an object that acts like a button
type Button interface {
Widget
isPressed() bool
setPressed(bool)
}

12
UI/Widget.go Normal file
View File

@ -0,0 +1,12 @@
package UI
import (
"github.com/essial/OpenDiablo2/Common"
)
// Widget defines an object that is a UI widget
type Widget interface {
Common.Drawable
getEnabled() bool
setEnabled(bool)
}

View File

@ -3,6 +3,7 @@ package OpenDiablo2
import (
"image/color"
"github.com/essial/OpenDiablo2/Palettes"
"github.com/hajimehoshi/ebiten"
)
@ -27,7 +28,7 @@ type UILabel struct {
}
// CreateUILabel creates a new instance of a UI label
func CreateUILabel(engine *Engine, font, palette string) *UILabel {
func CreateUILabel(engine *Engine, font string, palette Palettes.Palette) *UILabel {
result := &UILabel{
Alignment: UILabelAlignLeft,
ColorMod: nil,