mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-04 15:46:51 -05:00
One last restructure for now
This commit is contained in:
parent
56eb461f1f
commit
6562816710
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
__debug_bin
|
||||
**/*__debug_bin
|
||||
.vscode/*.*
|
||||
**/Client.exe
|
||||
**/Client
|
||||
|
BIN
Client.exe
BIN
Client.exe
Binary file not shown.
9
Common/FileProvider.go
Normal file
9
Common/FileProvider.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
34
Engine.go
34
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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package OpenDiablo2
|
||||
package MPQ
|
||||
|
||||
// CryptoBuffer contains the crypto bytes for filename hashing
|
||||
var CryptoBuffer [0x500]uint32
|
@ -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) {
|
@ -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
|
41
MPQFont.go
41
MPQFont.go
@ -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
|
||||
}
|
@ -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)
|
||||
|
52
UI/Font.go
Normal file
52
UI/Font.go
Normal file
@ -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
|
||||
}
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user