diff --git a/.gitignore b/.gitignore index d0491558..e426a28f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -__debug_bin +**/*__debug_bin .vscode/*.* +**/Client.exe +**/Client diff --git a/Client.exe b/Client.exe deleted file mode 100644 index 2d162e8e..00000000 Binary files a/Client.exe and /dev/null differ diff --git a/Common/FileProvider.go b/Common/FileProvider.go new file mode 100644 index 00000000..095eb9b0 --- /dev/null +++ b/Common/FileProvider.go @@ -0,0 +1,9 @@ +package Common + +import "github.com/essial/OpenDiablo2/Palettes" + +// FileProvider is an instance that can provide different types of files +type FileProvider interface { + LoadFile(fileName string) []byte + LoadSprite(fileName string, palette Palettes.Palette) *Sprite +} diff --git a/Common/SpriteProvider.go b/Common/SpriteProvider.go deleted file mode 100644 index 37cf15e4..00000000 --- a/Common/SpriteProvider.go +++ /dev/null @@ -1,8 +0,0 @@ -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 -} diff --git a/Engine.go b/Engine.go index e7eabaab..8486212f 100644 --- a/Engine.go +++ b/Engine.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/essial/OpenDiablo2/Common" + "github.com/essial/OpenDiablo2/MPQ" "github.com/essial/OpenDiablo2/Palettes" "github.com/essial/OpenDiablo2/ResourcePaths" "github.com/essial/OpenDiablo2/UI" @@ -53,7 +54,6 @@ type Engine struct { 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 @@ -65,7 +65,6 @@ func CreateEngine() *Engine { LoadingProgress: float64(0.0), CurrentScene: nil, nextScene: nil, - fontCache: make(map[string]*MPQFont), } result.loadConfigurationFile() result.mapMpqFiles() @@ -102,7 +101,7 @@ func (v *Engine) mapMpqFiles() { lock := sync.RWMutex{} for _, mpqFileName := range v.Settings.MpqLoadOrder { mpqPath := path.Join(v.Settings.MpqPath, mpqFileName) - mpq, err := LoadMPQ(mpqPath) + mpq, err := MPQ.Load(mpqPath) if err != nil { log.Fatal(err) } @@ -121,20 +120,20 @@ func (v *Engine) mapMpqFiles() { } } -// GetFile loads a file from the specified mpq and returns the data as a byte array -func (v *Engine) GetFile(fileName string) []byte { +// LoadFile loads a file from the specified mpq and returns the data as a byte array +func (v *Engine) LoadFile(fileName string) []byte { // TODO: May want to cache some things if performance becomes an issue mpqFile := v.Files[strings.ToLower(fileName)] - mpq, err := LoadMPQ(mpqFile) + mpq, err := MPQ.Load(mpqFile) if err != nil { log.Fatal(err) } fileName = strings.ReplaceAll(fileName, `/`, `\`)[1:] - blockTableEntry, err := mpq.getFileBlockData(fileName) + blockTableEntry, err := mpq.GetFileBlockData(fileName) if err != nil { log.Fatal(err) } - mpqStream := CreateMPQStream(mpq, blockTableEntry, fileName) + mpqStream := MPQ.CreateStream(mpq, blockTableEntry, fileName) result := make([]byte, blockTableEntry.UncompressedFileSize) mpqStream.Read(result, 0, blockTableEntry.UncompressedFileSize) @@ -155,7 +154,7 @@ func (v *Engine) loadPalettes() { } nameParts := strings.Split(file, `/`) paletteName := Palettes.Palette(nameParts[len(nameParts)-2]) - palette := Common.CreatePalette(paletteName, v.GetFile(file)) + palette := Common.CreatePalette(paletteName, v.LoadFile(file)) v.Palettes[paletteName] = palette } } @@ -163,7 +162,7 @@ func (v *Engine) loadPalettes() { func (v *Engine) loadSoundEntries() { log.Println("loading sound configurations") v.SoundEntries = make(map[string]SoundEntry) - soundData := strings.Split(string(v.GetFile(ResourcePaths.SoundSettings)), "\r\n")[1:] + soundData := strings.Split(string(v.LoadFile(ResourcePaths.SoundSettings)), "\r\n")[1:] for _, line := range soundData { if len(line) == 0 { continue @@ -175,7 +174,7 @@ func (v *Engine) loadSoundEntries() { // LoadSprite loads a sprite from the game's data files func (v *Engine) LoadSprite(fileName string, palette Palettes.Palette) *Common.Sprite { - data := v.GetFile(fileName) + data := v.LoadFile(fileName) sprite := Common.CreateSprite(data, v.Palettes[palette]) return sprite } @@ -249,24 +248,13 @@ func (v *Engine) SetNextScene(nextScene Common.SceneInterface) { v.nextScene = nextScene } -// GetFont creates or loads an existing font -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, palette) - v.fontCache[font+"_"+string(palette)] = newFont - return newFont -} - // PlayBGM plays an infinitely looping background track func (v *Engine) PlayBGM(song string) { go func() { if v.bgmAudio != nil { v.bgmAudio.Close() } - audioData := v.GetFile(song) + audioData := v.LoadFile(song) d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData)) if err != nil { log.Fatal(err) diff --git a/CryptoBuff.go b/MPQ/CryptoBuff.go similarity index 96% rename from CryptoBuff.go rename to MPQ/CryptoBuff.go index 31ac884a..3339ec2f 100644 --- a/CryptoBuff.go +++ b/MPQ/CryptoBuff.go @@ -1,4 +1,4 @@ -package OpenDiablo2 +package MPQ // CryptoBuffer contains the crypto bytes for filename hashing var CryptoBuffer [0x500]uint32 diff --git a/MPQ.go b/MPQ/MPQ.go similarity index 78% rename from MPQ.go rename to MPQ/MPQ.go index d02813d6..996f2e65 100644 --- a/MPQ.go +++ b/MPQ/MPQ.go @@ -1,4 +1,4 @@ -package OpenDiablo2 +package MPQ import ( "encoding/binary" @@ -12,13 +12,13 @@ import ( // MPQ represents an MPQ archive type MPQ struct { File *os.File - HashTableEntries []MPQHashTableEntry - BlockTableEntries []MPQBlockTableEntry - Data MPQData + HashTableEntries []HashTableEntry + BlockTableEntries []BlockTableEntry + Data Data } -// MPQData Represents a MPQ file -type MPQData struct { +// Data Represents a MPQ file +type Data struct { Magic [4]byte HeaderSize uint32 ArchiveSize uint32 @@ -30,8 +30,8 @@ type MPQData struct { BlockTableEntries uint32 } -// MPQHashTableEntry represents a hashed file entry in the MPQ file -type MPQHashTableEntry struct { // 16 bytes +// HashTableEntry represents a hashed file entry in the MPQ file +type HashTableEntry struct { // 16 bytes NamePartA uint32 NamePartB uint32 Locale uint16 @@ -39,50 +39,50 @@ type MPQHashTableEntry struct { // 16 bytes BlockIndex uint32 } -// MPQFileFlag represents flags for a file record in the MPQ archive -type MPQFileFlag uint32 +// FileFlag represents flags for a file record in the MPQ archive +type FileFlag uint32 const ( // MpqFileImplode - File is compressed using PKWARE Data compression library - MpqFileImplode MPQFileFlag = 0x00000100 + MpqFileImplode FileFlag = 0x00000100 // MpqFileCompress - File is compressed using combination of compression methods - MpqFileCompress MPQFileFlag = 0x00000200 + MpqFileCompress FileFlag = 0x00000200 // MpqFileEncrypted - The file is encrypted - MpqFileEncrypted MPQFileFlag = 0x00010000 + MpqFileEncrypted FileFlag = 0x00010000 // MpqFileFixKey - The decryption key for the file is altered according to the position of the file in the archive - MpqFileFixKey MPQFileFlag = 0x00020000 + MpqFileFixKey FileFlag = 0x00020000 // MpqFilePatchFile - The file contains incremental patch for an existing file in base MPQ - MpqFilePatchFile MPQFileFlag = 0x00100000 + MpqFilePatchFile FileFlag = 0x00100000 // MpqFileSingleUnit - Instead of being divided to 0x1000-bytes blocks, the file is stored as single unit - MpqFileSingleUnit MPQFileFlag = 0x01000000 + MpqFileSingleUnit FileFlag = 0x01000000 // FileDeleteMarker - File is a deletion marker, indicating that the file no longer exists. This is used to allow patch // archives to delete files present in lower-priority archives in the search chain. The file usually // has length of 0 or 1 byte and its name is a hash - FileDeleteMarker MPQFileFlag = 0x02000000 + FileDeleteMarker FileFlag = 0x02000000 // FileSEctorCrc - File has checksums for each sector. Ignored if file is not compressed or imploded. - FileSEctorCrc MPQFileFlag = 0x04000000 + FileSEctorCrc FileFlag = 0x04000000 // MpqFileExists - Set if file exists, reset when the file was deleted - MpqFileExists MPQFileFlag = 0x80000000 + MpqFileExists FileFlag = 0x80000000 ) -// MPQBlockTableEntry represents an entry in the block table -type MPQBlockTableEntry struct { // 16 bytes +// BlockTableEntry represents an entry in the block table +type BlockTableEntry struct { // 16 bytes FilePosition uint32 CompressedFileSize uint32 UncompressedFileSize uint32 - Flags MPQFileFlag + Flags FileFlag // Local Stuff... FileName string EncryptionSeed uint32 } // HasFlag returns true if the specified flag is present -func (v MPQBlockTableEntry) HasFlag(flag MPQFileFlag) bool { +func (v BlockTableEntry) HasFlag(flag FileFlag) bool { return (v.Flags & flag) != 0 } -// LoadMPQ loads an MPQ file and returns a MPQ structure -func LoadMPQ(fileName string) (MPQ, error) { +// Load loads an MPQ file and returns a MPQ structure +func Load(fileName string) (MPQ, error) { result := MPQ{} file, err := os.Open(fileName) if err != nil { @@ -116,7 +116,7 @@ func (v *MPQ) loadHashTable() { binary.Read(v.File, binary.LittleEndian, &hashData) decrypt(hashData, hashString("(hash table)", 3)) for i := uint32(0); i < v.Data.HashTableEntries; i++ { - v.HashTableEntries = append(v.HashTableEntries, MPQHashTableEntry{ + v.HashTableEntries = append(v.HashTableEntries, HashTableEntry{ NamePartA: hashData[i*4], NamePartB: hashData[(i*4)+1], // TODO: Verify that we're grabbing the right high/lo word for the vars below @@ -133,11 +133,11 @@ func (v *MPQ) loadBlockTable() { binary.Read(v.File, binary.LittleEndian, &blockData) decrypt(blockData, hashString("(block table)", 3)) for i := uint32(0); i < v.Data.BlockTableEntries; i++ { - v.BlockTableEntries = append(v.BlockTableEntries, MPQBlockTableEntry{ + v.BlockTableEntries = append(v.BlockTableEntries, BlockTableEntry{ FilePosition: blockData[(i * 4)], CompressedFileSize: blockData[(i*4)+1], UncompressedFileSize: blockData[(i*4)+2], - Flags: MPQFileFlag(blockData[(i*4)+3]), + Flags: FileFlag(blockData[(i*4)+3]), }) } } @@ -185,7 +185,7 @@ func hashString(key string, hashType uint32) uint32 { return seed1 } -func (v MPQ) getFileHashEntry(fileName string) (MPQHashTableEntry, error) { +func (v MPQ) getFileHashEntry(fileName string) (HashTableEntry, error) { hashA := hashString(fileName, 1) hashB := hashString(fileName, 2) @@ -196,13 +196,14 @@ func (v MPQ) getFileHashEntry(fileName string) (MPQHashTableEntry, error) { return v.HashTableEntries[idx], nil } - return MPQHashTableEntry{}, errors.New("file not found") + return HashTableEntry{}, errors.New("file not found") } -func (v MPQ) getFileBlockData(fileName string) (MPQBlockTableEntry, error) { +// GetFileBlockData gets a block table entry +func (v MPQ) GetFileBlockData(fileName string) (BlockTableEntry, error) { fileEntry, err := v.getFileHashEntry(fileName) if err != nil { - return MPQBlockTableEntry{}, err + return BlockTableEntry{}, err } return v.BlockTableEntries[fileEntry.BlockIndex], nil } @@ -214,13 +215,13 @@ func (v *MPQ) Close() { // ReadFile reads a file from the MPQ and returns a memory stream func (v MPQ) ReadFile(fileName string) ([]byte, error) { - fileBlockData, err := v.getFileBlockData(fileName) + fileBlockData, err := v.GetFileBlockData(fileName) if err != nil { return nil, err } fileBlockData.FileName = strings.ToLower(fileName) fileBlockData.calculateEncryptionSeed() - mpqStream := CreateMPQStream(v, fileBlockData, fileName) + mpqStream := CreateStream(v, fileBlockData, fileName) buffer := make([]byte, fileBlockData.UncompressedFileSize) mpqStream.Read(buffer, 0, fileBlockData.UncompressedFileSize) return buffer, nil @@ -235,7 +236,7 @@ func (v MPQ) ReadTextFile(fileName string) (string, error) { return string(data), nil } -func (v *MPQBlockTableEntry) calculateEncryptionSeed() { +func (v *BlockTableEntry) calculateEncryptionSeed() { fileName := path.Base(v.FileName) v.EncryptionSeed = hashString(fileName, 3) if !v.HasFlag(MpqFileFixKey) { diff --git a/MPQStream.go b/MPQ/MPQStream.go similarity index 90% rename from MPQStream.go rename to MPQ/MPQStream.go index cdaa5f09..52c8d9cf 100644 --- a/MPQStream.go +++ b/MPQ/MPQStream.go @@ -1,4 +1,4 @@ -package OpenDiablo2 +package MPQ import ( "bytes" @@ -12,10 +12,10 @@ import ( "github.com/essial/OpenDiablo2/Compression" ) -// MPQStream represents a stream of data in an MPQ archive -type MPQStream struct { +// Stream represents a stream of data in an MPQ archive +type Stream struct { MPQData MPQ - BlockTableEntry MPQBlockTableEntry + BlockTableEntry BlockTableEntry FileName string EncryptionSeed uint32 BlockPositions []uint32 @@ -25,9 +25,9 @@ type MPQStream struct { BlockSize uint32 } -// CreateMPQStream creates an MPQ stream -func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) *MPQStream { - result := &MPQStream{ +// CreateStream creates an MPQ stream +func CreateStream(mpq MPQ, blockTableEntry BlockTableEntry, fileName string) *Stream { + result := &Stream{ MPQData: mpq, BlockTableEntry: blockTableEntry, CurrentBlockIndex: 0xFFFFFFFF, @@ -45,7 +45,7 @@ func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName strin return result } -func (v *MPQStream) loadBlockOffsets() { +func (v *Stream) loadBlockOffsets() { blockPositionCount := ((v.BlockTableEntry.UncompressedFileSize + v.BlockSize - 1) / v.BlockSize) + 1 v.BlockPositions = make([]uint32, blockPositionCount) v.MPQData.File.Seek(int64(v.BlockTableEntry.FilePosition), 0) @@ -62,7 +62,7 @@ func (v *MPQStream) loadBlockOffsets() { } } -func (v *MPQStream) Read(buffer []byte, offset, count uint32) uint32 { +func (v *Stream) Read(buffer []byte, offset, count uint32) uint32 { if v.BlockTableEntry.HasFlag(MpqFileSingleUnit) { return v.readInternalSingleUnit(buffer, offset, count) } @@ -80,7 +80,7 @@ func (v *MPQStream) Read(buffer []byte, offset, count uint32) uint32 { return readTotal } -func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32) uint32 { +func (v *Stream) readInternalSingleUnit(buffer []byte, offset, count uint32) uint32 { if len(v.CurrentData) == 0 { v.loadSingleUnit() } @@ -91,7 +91,7 @@ func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32) return bytesToCopy } -func (v *MPQStream) readInternal(buffer []byte, offset, count uint32) uint32 { +func (v *Stream) readInternal(buffer []byte, offset, count uint32) uint32 { v.bufferData() localPosition := v.CurrentPosition % v.BlockSize bytesToCopy := Common.Min(uint32(len(v.CurrentData))-localPosition, count) @@ -103,7 +103,7 @@ func (v *MPQStream) readInternal(buffer []byte, offset, count uint32) uint32 { return bytesToCopy } -func (v *MPQStream) bufferData() { +func (v *Stream) bufferData() { requiredBlock := uint32(v.CurrentPosition / v.BlockSize) if requiredBlock == v.CurrentBlockIndex { return @@ -113,7 +113,7 @@ func (v *MPQStream) bufferData() { v.CurrentBlockIndex = requiredBlock } -func (v *MPQStream) loadSingleUnit() { +func (v *Stream) loadSingleUnit() { fileData := make([]byte, v.BlockSize) v.MPQData.File.Seek(int64(v.MPQData.Data.HeaderSize), 0) binary.Read(v.MPQData.File, binary.LittleEndian, &fileData) @@ -124,7 +124,7 @@ func (v *MPQStream) loadSingleUnit() { v.CurrentData = decompressMulti(fileData, v.BlockTableEntry.UncompressedFileSize) } -func (v *MPQStream) loadBlock(blockIndex, expectedLength uint32) []byte { +func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte { var ( offset uint32 toRead uint32 diff --git a/MPQFont.go b/MPQFont.go deleted file mode 100644 index 1172b269..00000000 --- a/MPQFont.go +++ /dev/null @@ -1,41 +0,0 @@ -package OpenDiablo2 - -import ( - "github.com/essial/OpenDiablo2/Common" - "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 *Common.Sprite - Metrics map[uint8]MPQFontSize -} - -// CreateMPQFont creates an instance of a MPQ Font -func CreateMPQFont(engine *Engine, font string, palette Palettes.Palette) *MPQFont { - result := &MPQFont{ - Engine: engine, - Metrics: make(map[uint8]MPQFontSize), - } - result.FontSprite = result.Engine.LoadSprite(font+".dc6", palette) - woo := "Woo!\x01" - fontData := result.Engine.GetFile(font + ".tbl") - if string(fontData[0:5]) != woo { - panic("No woo :(") - } - for i := 12; i < len(fontData); i += 14 { - fontSize := MPQFontSize{ - Width: fontData[i+3], - Height: fontData[i+4], - } - result.Metrics[fontData[i+8]] = fontSize - } - return result -} diff --git a/SceneMainMenu.go b/SceneMainMenu.go index 69db2876..a840784d 100644 --- a/SceneMainMenu.go +++ b/SceneMainMenu.go @@ -5,6 +5,7 @@ import ( "github.com/essial/OpenDiablo2/Common" "github.com/essial/OpenDiablo2/Palettes" + "github.com/essial/OpenDiablo2/UI" "github.com/essial/OpenDiablo2/ResourcePaths" "github.com/hajimehoshi/ebiten" @@ -19,8 +20,8 @@ type MainMenu struct { diabloLogoRight *Common.Sprite diabloLogoLeftBack *Common.Sprite diabloLogoRightBack *Common.Sprite - copyrightLabel *UILabel - copyrightLabel2 *UILabel + copyrightLabel *UI.Label + copyrightLabel2 *UI.Label showTrademarkScreen bool leftButtonHeld bool } @@ -42,16 +43,16 @@ func (v *MainMenu) Load() { loadStep := 1.0 / 8.0 v.engine.LoadingProgress = 0 { - v.copyrightLabel = CreateUILabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static) - v.copyrightLabel.Alignment = UILabelAlignCenter + v.copyrightLabel = UI.CreateLabel(v.engine, 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 = CreateUILabel(v.engine, ResourcePaths.FontFormal12, Palettes.Static) - v.copyrightLabel2.Alignment = UILabelAlignCenter + v.copyrightLabel2 = UI.CreateLabel(v.engine, 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) diff --git a/UI/Font.go b/UI/Font.go new file mode 100644 index 00000000..5625ba43 --- /dev/null +++ b/UI/Font.go @@ -0,0 +1,52 @@ +package UI + +import ( + "github.com/essial/OpenDiablo2/Common" + "github.com/essial/OpenDiablo2/Palettes" +) + +var fontCache = map[string]*Font{} + +// FontSize represents the size of a character in a font +type FontSize struct { + Width uint8 + Height uint8 +} + +// Font represents a font +type Font struct { + FontSprite *Common.Sprite + Metrics map[uint8]FontSize +} + +// GetFont creates or loads an existing font +func GetFont(font string, palette Palettes.Palette, fileProvider Common.FileProvider) *Font { + cacheItem, exists := fontCache[font+"_"+string(palette)] + if exists { + return cacheItem + } + newFont := CreateFont(font, palette, fileProvider) + fontCache[font+"_"+string(palette)] = newFont + return newFont +} + +// CreateFont creates an instance of a MPQ Font +func CreateFont(font string, palette Palettes.Palette, fileProvider Common.FileProvider) *Font { + result := &Font{ + Metrics: make(map[uint8]FontSize), + } + result.FontSprite = fileProvider.LoadSprite(font+".dc6", palette) + woo := "Woo!\x01" + fontData := fileProvider.LoadFile(font + ".tbl") + if string(fontData[0:5]) != woo { + panic("No woo :(") + } + for i := 12; i < len(fontData); i += 14 { + fontSize := FontSize{ + Width: fontData[i+3], + Height: fontData[i+4], + } + result.Metrics[fontData[i+8]] = fontSize + } + return result +} diff --git a/UI/Manager.go b/UI/Manager.go index 33bea991..b44699f6 100644 --- a/UI/Manager.go +++ b/UI/Manager.go @@ -14,7 +14,7 @@ type Manager struct { } // CreateManager creates a new instance of a UI manager -func CreateManager(provider Common.SpriteProvider) *Manager { +func CreateManager(provider Common.FileProvider) *Manager { result := &Manager{ widgets: make([]*Widget, 0), cursorSprite: provider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units), diff --git a/UILabel.go b/UI/UILabel.go similarity index 61% rename from UILabel.go rename to UI/UILabel.go index b4d15872..880b8ae0 100644 --- a/UILabel.go +++ b/UI/UILabel.go @@ -1,4 +1,4 @@ -package OpenDiablo2 +package UI import ( "image/color" @@ -8,53 +8,53 @@ import ( "github.com/hajimehoshi/ebiten" ) -// UILabelAlignment represents a label's alignment -type UILabelAlignment uint8 +// LabelAlignment represents a label's alignment +type LabelAlignment uint8 const ( - // UILabelAlignLeft represents a left-aligned label - UILabelAlignLeft UILabelAlignment = 0 - // UILabelAlignCenter represents a center-aligned label - UILabelAlignCenter UILabelAlignment = 1 - // UILabelAlignRight represents a right-aligned label - UILabelAlignRight UILabelAlignment = 2 + // LabelAlignLeft represents a left-aligned label + LabelAlignLeft LabelAlignment = 0 + // LabelAlignCenter represents a center-aligned label + LabelAlignCenter LabelAlignment = 1 + // LabelAlignRight represents a right-aligned label + LabelAlignRight LabelAlignment = 2 ) -// UILabel represents a user interface label -type UILabel struct { +// Label represents a user interface label +type Label struct { text string X int Y int Width uint32 Height uint32 - Alignment UILabelAlignment - font *MPQFont + Alignment LabelAlignment + font *Font imageData *ebiten.Image ColorMod color.Color } -// CreateUILabel creates a new instance of a UI label -func CreateUILabel(engine *Engine, font string, palette Palettes.Palette) *UILabel { - result := &UILabel{ - Alignment: UILabelAlignLeft, +// CreateLabel creates a new instance of a UI label +func CreateLabel(provider Common.FileProvider, font string, palette Palettes.Palette) *Label { + result := &Label{ + Alignment: LabelAlignLeft, ColorMod: nil, - font: engine.GetFont(font, palette), + font: GetFont(font, palette, provider), } return result } // Draw draws the label on the screen -func (v *UILabel) Draw(target *ebiten.Image) { +func (v *Label) Draw(target *ebiten.Image) { if len(v.text) == 0 { return } v.cacheImage() opts := &ebiten.DrawImageOptions{} - if v.Alignment == UILabelAlignCenter { + if v.Alignment == LabelAlignCenter { opts.GeoM.Translate(float64(v.X-int(v.Width/2)), float64(v.Y)) - } else if v.Alignment == UILabelAlignRight { + } else if v.Alignment == LabelAlignRight { opts.GeoM.Translate(float64(v.X-int(v.Width)), float64(v.Y)) } else { opts.GeoM.Translate(float64(v.X), float64(v.Y)) @@ -64,7 +64,7 @@ func (v *UILabel) Draw(target *ebiten.Image) { target.DrawImage(v.imageData, opts) } -func (v *UILabel) calculateSize() (uint32, uint32) { +func (v *Label) calculateSize() (uint32, uint32) { width := uint32(0) height := uint32(0) for _, ch := range v.text { @@ -76,12 +76,12 @@ func (v *UILabel) calculateSize() (uint32, uint32) { } // MoveTo moves the label to the specified location -func (v *UILabel) MoveTo(x, y int) { +func (v *Label) MoveTo(x, y int) { v.X = x v.Y = y } -func (v *UILabel) cacheImage() { +func (v *Label) cacheImage() { if v.imageData != nil { return } @@ -102,7 +102,7 @@ func (v *UILabel) cacheImage() { } // SetText sets the label's text -func (v *UILabel) SetText(newText string) { +func (v *Label) SetText(newText string) { if v.text == newText { return } diff --git a/cmd/Client/main.go b/cmd/Client/main.go index 4647d2a7..c8163c08 100644 --- a/cmd/Client/main.go +++ b/cmd/Client/main.go @@ -4,6 +4,7 @@ import ( "log" "github.com/essial/OpenDiablo2" + "github.com/essial/OpenDiablo2/MPQ" "github.com/hajimehoshi/ebiten" ) @@ -11,7 +12,7 @@ var d2Engine *OpenDiablo2.Engine func main() { log.Println("OpenDiablo2 - Open source Diablo 2 engine") - OpenDiablo2.InitializeCryptoBuffer() + MPQ.InitializeCryptoBuffer() d2Engine = OpenDiablo2.CreateEngine() ebiten.SetCursorVisible(false) ebiten.SetFullscreen(d2Engine.Settings.FullScreen)