mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-23 18:47:47 -05:00
Added volume settings and sfx support
This commit is contained in:
parent
ec4949bc1b
commit
227e7e8b29
@ -3,7 +3,7 @@ go:
|
||||
- 1.13.3
|
||||
before_install:
|
||||
- sudo apt-get -y install libx11-dev mesa-common-dev libglfw3-dev libgles2-mesa-dev libasound2-dev
|
||||
script: buildci.sh
|
||||
script: go get && golangci-lint run . && go build ./cmd/Client
|
||||
git:
|
||||
depth: 1
|
||||
notifications:
|
||||
|
@ -50,9 +50,9 @@ func (v *BitStream) EnsureBits(bitCount int) bool {
|
||||
if v.dataPosition >= len(v.data) {
|
||||
return false
|
||||
}
|
||||
nextvalue := v.data[v.dataPosition]
|
||||
nextValue := v.data[v.dataPosition]
|
||||
v.dataPosition++
|
||||
v.current |= int(nextvalue) << v.bitCount
|
||||
v.current |= int(nextValue) << v.bitCount
|
||||
v.bitCount += 8
|
||||
return true
|
||||
}
|
||||
|
@ -24,6 +24,14 @@ func MaxInt32(a, b int32) int32 {
|
||||
return b
|
||||
}
|
||||
|
||||
// MinInt32 returns the higher of two values
|
||||
func MinInt32(a, b int32) int32 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// BytesToInt32 converts 4 bytes to int32
|
||||
func BytesToInt32(b []byte) int32 {
|
||||
// equivalnt of return int32(binary.LittleEndian.Uint32(b))
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// StreamReader allows you to read data from a byte array in various formats
|
||||
@ -41,7 +42,7 @@ func (v *StreamReader) GetByte() byte {
|
||||
// GetWord returns a uint16 word from the stream
|
||||
func (v *StreamReader) GetWord() uint16 {
|
||||
result := uint16(v.data[v.position])
|
||||
result += (uint16(v.data[v.position+1]) << 8)
|
||||
result += uint16(v.data[v.position+1]) << 8
|
||||
v.position += 2
|
||||
return result
|
||||
}
|
||||
@ -49,7 +50,10 @@ func (v *StreamReader) GetWord() uint16 {
|
||||
// GetSWord returns a int16 word from the stream
|
||||
func (v *StreamReader) GetSWord() int16 {
|
||||
var result int16
|
||||
binary.Read(bytes.NewReader([]byte{v.data[v.position], v.data[v.position+1]}), binary.LittleEndian, &result)
|
||||
err := binary.Read(bytes.NewReader([]byte{v.data[v.position], v.data[v.position+1]}), binary.LittleEndian, &result)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
v.position += 2
|
||||
return result
|
||||
}
|
||||
@ -57,9 +61,9 @@ func (v *StreamReader) GetSWord() int16 {
|
||||
// GetDword returns a uint32 dword from the stream
|
||||
func (v *StreamReader) GetDword() uint32 {
|
||||
result := uint32(v.data[v.position])
|
||||
result += (uint32(v.data[v.position+1]) << 8)
|
||||
result += (uint32(v.data[v.position+2]) << 16)
|
||||
result += (uint32(v.data[v.position+3]) << 24)
|
||||
result += uint32(v.data[v.position+1]) << 8
|
||||
result += uint32(v.data[v.position+2]) << 16
|
||||
result += uint32(v.data[v.position+3]) << 24
|
||||
v.position += 4
|
||||
return result
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ type EngineConfig struct {
|
||||
VsyncEnabled bool
|
||||
MpqPath string
|
||||
MpqLoadOrder []string
|
||||
SfxVolume float64
|
||||
BgmVolume float64
|
||||
}
|
||||
|
||||
// Engine is the core OpenDiablo2 engine
|
||||
@ -59,7 +61,8 @@ func CreateEngine() *Engine {
|
||||
result.loadPalettes()
|
||||
result.loadSoundEntries()
|
||||
result.SoundManager = Sound.CreateManager(result)
|
||||
result.UIManager = UI.CreateManager(result)
|
||||
result.SoundManager.SetVolumes(result.Settings.BgmVolume, result.Settings.SfxVolume)
|
||||
result.UIManager = UI.CreateManager(result, *result.SoundManager)
|
||||
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading)
|
||||
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
||||
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
|
||||
@ -75,7 +78,10 @@ func (v *Engine) loadConfigurationFile() {
|
||||
}
|
||||
var config EngineConfig
|
||||
|
||||
json.Unmarshal(configJSON, &config)
|
||||
err = json.Unmarshal(configJSON, &config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
v.Settings = config
|
||||
}
|
||||
|
||||
|
61
MPQ/MPQ.go
61
MPQ/MPQ.go
@ -43,26 +43,26 @@ type HashTableEntry struct { // 16 bytes
|
||||
type FileFlag uint32
|
||||
|
||||
const (
|
||||
// MpqFileImplode - File is compressed using PKWARE Data compression library
|
||||
MpqFileImplode FileFlag = 0x00000100
|
||||
// MpqFileCompress - File is compressed using combination of compression methods
|
||||
MpqFileCompress FileFlag = 0x00000200
|
||||
// MpqFileEncrypted - The file is encrypted
|
||||
MpqFileEncrypted FileFlag = 0x00010000
|
||||
// MpqFileFixKey - The decryption key for the file is altered according to the position of the file in the archive
|
||||
MpqFileFixKey FileFlag = 0x00020000
|
||||
// MpqFilePatchFile - The file contains incremental patch for an existing file in base MPQ
|
||||
MpqFilePatchFile FileFlag = 0x00100000
|
||||
// MpqFileSingleUnit - Instead of being divided to 0x1000-bytes blocks, the file is stored as single unit
|
||||
MpqFileSingleUnit FileFlag = 0x01000000
|
||||
// FileImplode - File is compressed using PKWARE Data compression library
|
||||
FileImplode FileFlag = 0x00000100
|
||||
// FileCompress - File is compressed using combination of compression methods
|
||||
FileCompress FileFlag = 0x00000200
|
||||
// FileEncrypted - The file is encrypted
|
||||
FileEncrypted FileFlag = 0x00010000
|
||||
// FileFixKey - The decryption key for the file is altered according to the position of the file in the archive
|
||||
FileFixKey FileFlag = 0x00020000
|
||||
// FilePatchFile - The file contains incremental patch for an existing file in base MPQ
|
||||
FilePatchFile FileFlag = 0x00100000
|
||||
// FileSingleUnit - Instead of being divided to 0x1000-bytes blocks, the file is stored as single unit
|
||||
FileSingleUnit 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 FileFlag = 0x02000000
|
||||
// FileSEctorCrc - File has checksums for each sector. Ignored if file is not compressed or imploded.
|
||||
FileSEctorCrc FileFlag = 0x04000000
|
||||
// MpqFileExists - Set if file exists, reset when the file was deleted
|
||||
MpqFileExists FileFlag = 0x80000000
|
||||
// FileSectorCrc - File has checksums for each sector. Ignored if file is not compressed or imploded.
|
||||
FileSectorCrc FileFlag = 0x04000000
|
||||
// FileExists - Set if file exists, reset when the file was deleted
|
||||
FileExists FileFlag = 0x80000000
|
||||
)
|
||||
|
||||
// BlockTableEntry represents an entry in the block table
|
||||
@ -111,9 +111,15 @@ func (v *MPQ) readHeader() error {
|
||||
}
|
||||
|
||||
func (v *MPQ) loadHashTable() {
|
||||
v.File.Seek(int64(v.Data.HashTableOffset), 0)
|
||||
_, err := v.File.Seek(int64(v.Data.HashTableOffset), 0)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
hashData := make([]uint32, v.Data.HashTableEntries*4)
|
||||
binary.Read(v.File, binary.LittleEndian, &hashData)
|
||||
err = binary.Read(v.File, binary.LittleEndian, &hashData)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
decrypt(hashData, hashString("(hash table)", 3))
|
||||
for i := uint32(0); i < v.Data.HashTableEntries; i++ {
|
||||
v.HashTableEntries = append(v.HashTableEntries, HashTableEntry{
|
||||
@ -128,9 +134,15 @@ func (v *MPQ) loadHashTable() {
|
||||
}
|
||||
|
||||
func (v *MPQ) loadBlockTable() {
|
||||
v.File.Seek(int64(v.Data.BlockTableOffset), 0)
|
||||
_, err := v.File.Seek(int64(v.Data.BlockTableOffset), 0)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
blockData := make([]uint32, v.Data.BlockTableEntries*4)
|
||||
binary.Read(v.File, binary.LittleEndian, &blockData)
|
||||
err = binary.Read(v.File, binary.LittleEndian, &blockData)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
decrypt(blockData, hashString("(block table)", 3))
|
||||
for i := uint32(0); i < v.Data.BlockTableEntries; i++ {
|
||||
v.BlockTableEntries = append(v.BlockTableEntries, BlockTableEntry{
|
||||
@ -208,9 +220,12 @@ func (v MPQ) GetFileBlockData(fileName string) (BlockTableEntry, error) {
|
||||
return v.BlockTableEntries[fileEntry.BlockIndex], nil
|
||||
}
|
||||
|
||||
// Close closses the MPQ file
|
||||
// Close closes the MPQ file
|
||||
func (v *MPQ) Close() {
|
||||
v.File.Close()
|
||||
err := v.File.Close()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFile reads a file from the MPQ and returns a memory stream
|
||||
@ -239,7 +254,7 @@ func (v MPQ) ReadTextFile(fileName string) (string, error) {
|
||||
func (v *BlockTableEntry) calculateEncryptionSeed() {
|
||||
fileName := path.Base(v.FileName)
|
||||
v.EncryptionSeed = hashString(fileName, 3)
|
||||
if !v.HasFlag(MpqFileFixKey) {
|
||||
if !v.HasFlag(FileFixKey) {
|
||||
return
|
||||
}
|
||||
v.EncryptionSeed = (v.EncryptionSeed + v.FilePosition) ^ v.UncompressedFileSize
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/JoshVarga/blast"
|
||||
@ -34,12 +35,12 @@ func CreateStream(mpq MPQ, blockTableEntry BlockTableEntry, fileName string) *St
|
||||
}
|
||||
fileSegs := strings.Split(fileName, `\`)
|
||||
result.EncryptionSeed = hashString(fileSegs[len(fileSegs)-1], 3)
|
||||
if result.BlockTableEntry.HasFlag(MpqFileFixKey) {
|
||||
if result.BlockTableEntry.HasFlag(FileFixKey) {
|
||||
result.EncryptionSeed = (result.EncryptionSeed + result.BlockTableEntry.FilePosition) ^ result.BlockTableEntry.UncompressedFileSize
|
||||
}
|
||||
result.BlockSize = 0x200 << result.MPQData.Data.BlockSize
|
||||
|
||||
if (result.BlockTableEntry.HasFlag(MpqFileCompress) || result.BlockTableEntry.HasFlag(MpqFileImplode)) && !result.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||
if (result.BlockTableEntry.HasFlag(FileCompress) || result.BlockTableEntry.HasFlag(FileImplode)) && !result.BlockTableEntry.HasFlag(FileSingleUnit) {
|
||||
result.loadBlockOffsets()
|
||||
}
|
||||
return result
|
||||
@ -51,7 +52,7 @@ func (v *Stream) loadBlockOffsets() {
|
||||
v.MPQData.File.Seek(int64(v.BlockTableEntry.FilePosition), 0)
|
||||
binary.Read(v.MPQData.File, binary.LittleEndian, &v.BlockPositions)
|
||||
blockPosSize := blockPositionCount << 2
|
||||
if v.BlockTableEntry.HasFlag(MpqFileEncrypted) {
|
||||
if v.BlockTableEntry.HasFlag(FileEncrypted) {
|
||||
decrypt(v.BlockPositions, v.EncryptionSeed-1)
|
||||
if v.BlockPositions[0] != blockPosSize {
|
||||
panic("Decryption of MPQ failed!")
|
||||
@ -63,7 +64,7 @@ func (v *Stream) loadBlockOffsets() {
|
||||
}
|
||||
|
||||
func (v *Stream) Read(buffer []byte, offset, count uint32) uint32 {
|
||||
if v.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||
if v.BlockTableEntry.HasFlag(FileSingleUnit) {
|
||||
return v.readInternalSingleUnit(buffer, offset, count)
|
||||
}
|
||||
toRead := count
|
||||
@ -94,13 +95,13 @@ func (v *Stream) readInternalSingleUnit(buffer []byte, offset, count uint32) uin
|
||||
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)
|
||||
bytesToCopy := Common.MinInt32(int32(len(v.CurrentData))-int32(localPosition), int32(count))
|
||||
if bytesToCopy <= 0 {
|
||||
return 0
|
||||
}
|
||||
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[localPosition:localPosition+bytesToCopy])
|
||||
v.CurrentPosition += bytesToCopy
|
||||
return bytesToCopy
|
||||
copy(buffer[offset:offset+uint32(bytesToCopy)], v.CurrentData[localPosition:localPosition+uint32(bytesToCopy)])
|
||||
v.CurrentPosition += uint32(bytesToCopy)
|
||||
return uint32(bytesToCopy)
|
||||
}
|
||||
|
||||
func (v *Stream) bufferData() {
|
||||
@ -129,7 +130,7 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
|
||||
offset uint32
|
||||
toRead uint32
|
||||
)
|
||||
if v.BlockTableEntry.HasFlag(MpqFileCompress) || v.BlockTableEntry.HasFlag(MpqFileImplode) {
|
||||
if v.BlockTableEntry.HasFlag(FileCompress) || v.BlockTableEntry.HasFlag(FileImplode) {
|
||||
offset = v.BlockPositions[blockIndex]
|
||||
toRead = v.BlockPositions[blockIndex+1] - offset
|
||||
} else {
|
||||
@ -140,21 +141,21 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
|
||||
data := make([]byte, toRead)
|
||||
v.MPQData.File.Seek(int64(offset), 0)
|
||||
binary.Read(v.MPQData.File, binary.LittleEndian, &data)
|
||||
if v.BlockTableEntry.HasFlag(MpqFileEncrypted) && v.BlockTableEntry.UncompressedFileSize > 3 {
|
||||
if v.BlockTableEntry.HasFlag(FileEncrypted) && v.BlockTableEntry.UncompressedFileSize > 3 {
|
||||
if v.EncryptionSeed == 0 {
|
||||
panic("Unable to determine encryption key")
|
||||
}
|
||||
|
||||
decryptBytes(data, blockIndex+v.EncryptionSeed)
|
||||
}
|
||||
if v.BlockTableEntry.HasFlag(MpqFileCompress) && (toRead != expectedLength) {
|
||||
if !v.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||
if v.BlockTableEntry.HasFlag(FileCompress) && (toRead != expectedLength) {
|
||||
if !v.BlockTableEntry.HasFlag(FileSingleUnit) {
|
||||
data = decompressMulti(data, expectedLength)
|
||||
} else {
|
||||
data = pkDecompress(data)
|
||||
}
|
||||
}
|
||||
if v.BlockTableEntry.HasFlag(MpqFileImplode) && (toRead != expectedLength) {
|
||||
if v.BlockTableEntry.HasFlag(FileImplode) && (toRead != expectedLength) {
|
||||
data = pkDecompress(data)
|
||||
}
|
||||
|
||||
@ -215,23 +216,35 @@ func decompressMulti(data []byte, expectedLength uint32) []byte {
|
||||
func deflate(data []byte) []byte {
|
||||
b := bytes.NewReader(data)
|
||||
r, err := zlib.NewReader(b)
|
||||
defer r.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.ReadFrom(r)
|
||||
_, err = buffer.ReadFrom(r)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
err = r.Close()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func pkDecompress(data []byte) []byte {
|
||||
b := bytes.NewReader(data)
|
||||
r, err := blast.NewReader(b)
|
||||
defer r.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.ReadFrom(r)
|
||||
_, err = buffer.ReadFrom(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = r.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ type MainMenu struct {
|
||||
copyrightLabel *UI.Label
|
||||
copyrightLabel2 *UI.Label
|
||||
openDiabloLabel *UI.Label
|
||||
|
||||
ShowTrademarkScreen bool
|
||||
leftButtonHeld bool
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ type Manager struct {
|
||||
audioContext *audio.Context // The Audio context
|
||||
bgmAudio *audio.Player // The audio player
|
||||
lastBgm string
|
||||
sfxVolume float64
|
||||
bgmVolume float64
|
||||
}
|
||||
|
||||
// CreateManager creates a sound provider
|
||||
@ -21,7 +23,7 @@ func CreateManager(fileProvider Common.FileProvider) *Manager {
|
||||
result := &Manager{
|
||||
fileProvider: fileProvider,
|
||||
}
|
||||
audioContext, err := audio.NewContext(22050)
|
||||
audioContext, err := audio.NewContext(44100)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -37,7 +39,10 @@ func (v *Manager) PlayBGM(song string) {
|
||||
v.lastBgm = song
|
||||
go func() {
|
||||
if v.bgmAudio != nil {
|
||||
v.bgmAudio.Close()
|
||||
err := v.bgmAudio.Close()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
audioData := v.fileProvider.LoadFile(song)
|
||||
d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData))
|
||||
@ -45,13 +50,29 @@ func (v *Manager) PlayBGM(song string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := audio.NewInfiniteLoop(d, int64(len(audioData)))
|
||||
|
||||
v.bgmAudio, err = audio.NewPlayer(v.audioContext, s)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
v.bgmAudio.SetVolume(v.bgmVolume)
|
||||
// Play the infinite-length stream. This never ends.
|
||||
v.bgmAudio.Rewind()
|
||||
v.bgmAudio.Play()
|
||||
err = v.bgmAudio.Rewind()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = v.bgmAudio.Play()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (v *Manager) LoadSoundEffect(sfx string) *SoundEffect {
|
||||
result := CreateSoundEffect(sfx, v.fileProvider, v.audioContext, v.sfxVolume)
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *Manager) SetVolumes(bgmVolume, sfxVolume float64) {
|
||||
v.sfxVolume = sfxVolume
|
||||
v.bgmVolume = bgmVolume
|
||||
}
|
||||
|
38
Sound/SoundEffect.go
Normal file
38
Sound/SoundEffect.go
Normal file
@ -0,0 +1,38 @@
|
||||
package Sound
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
|
||||
"github.com/essial/OpenDiablo2/Common"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/audio"
|
||||
)
|
||||
|
||||
type SoundEffect struct {
|
||||
player *audio.Player
|
||||
}
|
||||
|
||||
func CreateSoundEffect(sfx string, fileProvider Common.FileProvider, context *audio.Context, volume float64) *SoundEffect {
|
||||
result := &SoundEffect{}
|
||||
|
||||
audioData := fileProvider.LoadFile(sfx)
|
||||
d, err := wav.Decode(context, audio.BytesReadSeekCloser(audioData))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
player, err := audio.NewPlayer(context, d)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
player.SetVolume(volume)
|
||||
result.player = player
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *SoundEffect) Play() {
|
||||
v.player.Rewind()
|
||||
v.player.Play()
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"github.com/essial/OpenDiablo2/Common"
|
||||
"github.com/essial/OpenDiablo2/Palettes"
|
||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||
"github.com/essial/OpenDiablo2/Sound"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
@ -25,14 +26,16 @@ type Manager struct {
|
||||
pressedIndex int
|
||||
CursorX int
|
||||
CursorY int
|
||||
clickSfx *Sound.SoundEffect
|
||||
}
|
||||
|
||||
// CreateManager creates a new instance of a UI manager
|
||||
func CreateManager(provider Common.FileProvider) *Manager {
|
||||
func CreateManager(fileProvider Common.FileProvider, soundManager Sound.Manager) *Manager {
|
||||
result := &Manager{
|
||||
pressedIndex: -1,
|
||||
widgets: make([]Widget, 0),
|
||||
cursorSprite: provider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units),
|
||||
cursorSprite: fileProvider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units),
|
||||
clickSfx: soundManager.LoadSoundEffect(ResourcePaths.SFXButtonClick),
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -85,6 +88,7 @@ func (v *Manager) Update() {
|
||||
if v.pressedIndex == -1 {
|
||||
found = true
|
||||
v.pressedIndex = i
|
||||
v.clickSfx.Play()
|
||||
} else if v.pressedIndex > -1 && v.pressedIndex != i {
|
||||
v.widgets[i].SetPressed(false)
|
||||
} else {
|
||||
|
@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
go get
|
||||
golangci-lint run .
|
||||
go build ./cmd/Client
|
@ -4,6 +4,8 @@
|
||||
"TicksPerSecond": 60,
|
||||
"RunInBackground": true,
|
||||
"VsyncEnabled": true,
|
||||
"SfxVolume": 1.0,
|
||||
"BgmVolume": 0.3,
|
||||
"MpqPath": "C:/Program Files (x86)/Diablo II",
|
||||
"MpqLoadOrder": [
|
||||
"d2exp.mpq",
|
||||
|
Loading…
Reference in New Issue
Block a user