Added more functionality to character select screen. (#143)

This commit is contained in:
Tim Sarbin 2019-11-11 23:48:55 -05:00 committed by GitHub
parent f81eb5764d
commit a6a9434267
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 715 additions and 155 deletions

View File

@ -3,12 +3,15 @@ package d2enum
type Hero int
const (
HeroNone Hero = 0
HeroBarbarian Hero = 1
HeroNecromancer Hero = 2
HeroPaladin Hero = 3
HeroAssassin Hero = 4
HeroSorceress Hero = 5
HeroAmazon Hero = 6
HeroDruid Hero = 7
HeroNone Hero = 0 //
HeroBarbarian Hero = 1 // Barbarian
HeroNecromancer Hero = 2 // Necromancer
HeroPaladin Hero = 3 // Paladin
HeroAssassin Hero = 4 // Assassin
HeroSorceress Hero = 5 // Sorceress
HeroAmazon Hero = 6 // Amazon
HeroDruid Hero = 7 // Druid
)
//go:generate stringer -linecomment -type Hero
//go:generate string2enum -samepkg -linecomment -type Hero

View File

@ -0,0 +1,30 @@
// Code generated by "stringer -linecomment -type Hero"; DO NOT EDIT.
package d2enum
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[HeroNone-0]
_ = x[HeroBarbarian-1]
_ = x[HeroNecromancer-2]
_ = x[HeroPaladin-3]
_ = x[HeroAssassin-4]
_ = x[HeroSorceress-5]
_ = x[HeroAmazon-6]
_ = x[HeroDruid-7]
}
const _Hero_name = "BarbarianNecromancerPaladinAssassinSorceressAmazonDruid"
var _Hero_index = [...]uint8{0, 0, 9, 20, 27, 35, 44, 50, 55}
func (i Hero) String() string {
if i < 0 || i >= Hero(len(_Hero_index)-1) {
return "Hero(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Hero_name[_Hero_index[i]:_Hero_index[i+1]]
}

View File

@ -0,0 +1,18 @@
// Code generated by "string2enum -samepkg -linecomment -type Hero"; DO NOT EDIT.
package d2enum
import "fmt"
// HeroFromString returns the Hero enum corresponding to s.
func HeroFromString(s string) Hero {
if len(s) == 0 {
return 0
}
for i := range _Hero_index[:len(_Hero_index)-1] {
if s == _Hero_name[_Hero_index[i]:_Hero_index[i+1]] {
return Hero(i)
}
}
panic(fmt.Errorf("unable to locate Hero enum corresponding to %q", s))
}

View File

@ -0,0 +1,41 @@
package d2enum
type RegionIdType int
const (
RegionAct1Town RegionIdType = 1
RegionAct1Wilderness RegionIdType = 2
RegionAct1Cave RegionIdType = 3
RegionAct1Crypt RegionIdType = 4
RegionAct1Monestary RegionIdType = 5
RegionAct1Courtyard RegionIdType = 6
RegionAct1Barracks RegionIdType = 7
RegionAct1Jail RegionIdType = 8
RegionAct1Cathedral RegionIdType = 9
RegionAct1Catacombs RegionIdType = 10
RegionAct1Tristram RegionIdType = 11
RegionAct2Town RegionIdType = 12
RegionAct2Sewer RegionIdType = 13
RegionAct2Harem RegionIdType = 14
RegionAct2Basement RegionIdType = 15
RegionAct2Desert RegionIdType = 16
RegionAct2Tomb RegionIdType = 17
RegionAct2Lair RegionIdType = 18
RegionAct2Arcane RegionIdType = 19
RegionAct3Town RegionIdType = 20
RegionAct3Jungle RegionIdType = 21
RegionAct3Kurast RegionIdType = 22
RegionAct3Spider RegionIdType = 23
RegionAct3Dungeon RegionIdType = 24
RegionAct3Sewer RegionIdType = 25
RegionAct4Town RegionIdType = 26
RegionAct4Mesa RegionIdType = 27
RegionAct4Lava RegionIdType = 28
RegonAct5Town RegionIdType = 29
RegionAct5Siege RegionIdType = 30
RegionAct5Barricade RegionIdType = 31
RegionAct5Temple RegionIdType = 32
RegionAct5IceCaves RegionIdType = 33
RegionAct5Baal RegionIdType = 34
RegionAct5Lava RegionIdType = 35
)

View File

@ -0,0 +1,9 @@
package d2enum
type RegionLayerType int
const (
RegionLayerTypeFloors RegionLayerType = 0
RegionLayerTypeWalls RegionLayerType = 1
RegionLayerTypeShadows RegionLayerType = 2
)

View File

@ -80,6 +80,8 @@ const (
// -- Character Selection
CharacterSelectionBackground = "/data/global/ui/CharSelect/characterselectscreenEXP.dc6"
CharacterSelectionSelectBox = "/data/global/ui/CharSelect/charselectbox.dc6"
PopUpOkCancel = "/data/global/ui/FrontEnd/PopUpOKCancel.dc6"
// --- Game ---
@ -101,6 +103,7 @@ const (
Font16 = "/data/local/font/latin/font16"
Font24 = "/data/local/font/latin/font24"
Font30 = "/data/local/font/latin/font30"
Font42 = "/data/local/font/latin/font42"
FontFormal12 = "/data/local/font/latin/fontformal12"
FontFormal11 = "/data/local/font/latin/fontformal11"
FontFormal10 = "/data/local/font/latin/fontformal10"

View File

@ -55,20 +55,48 @@ func (v *StreamReader) SetPosition(newPosition uint64) {
v.position = newPosition
}
// GetUInt32 returns a uint32 word from the stream
// GetUInt32 returns a uint32 dword from the stream
func (v *StreamReader) GetUInt32() uint32 {
result := (uint32(v.data[v.position+3]) << uint(24)) + (uint32(v.data[v.position+2]) << uint(16)) + (uint32(v.data[v.position+1]) << uint(8)) + uint32(v.data[v.position])
v.position += 4
return result
}
// GetInt32 returns an int32 word from the stream
// GetInt32 returns an int32 dword from the stream
func (v *StreamReader) GetInt32() int32 {
result := (int32(v.data[v.position+3]) << uint(24)) + (int32(v.data[v.position+2]) << uint(16)) + (int32(v.data[v.position+1]) << uint(8)) + int32(v.data[v.position])
v.position += 4
return result
}
// GetUint64 returns a uint64 qword from the stream
func (v *StreamReader) GetUint64() uint64 {
result := (uint64(v.data[v.position+7]) << uint(56)) +
(uint64(v.data[v.position+6]) << uint(48)) +
(uint64(v.data[v.position+5]) << uint(40)) +
(uint64(v.data[v.position+4]) << uint(32)) +
(uint64(v.data[v.position+3]) << uint(24)) +
(uint64(v.data[v.position+2]) << uint(16)) +
(uint64(v.data[v.position+1]) << uint(8)) +
uint64(v.data[v.position])
v.position += 8
return result
}
// GetInt64 returns a uint64 qword from the stream
func (v *StreamReader) GetInt64() int64 {
result := (uint64(v.data[v.position+7]) << uint(56)) +
(uint64(v.data[v.position+6]) << uint(48)) +
(uint64(v.data[v.position+5]) << uint(40)) +
(uint64(v.data[v.position+4]) << uint(32)) +
(uint64(v.data[v.position+3]) << uint(24)) +
(uint64(v.data[v.position+2]) << uint(16)) +
(uint64(v.data[v.position+1]) << uint(8)) +
uint64(v.data[v.position])
v.position += 8
return int64(result)
}
// ReadByte implements io.ByteReader
func (v *StreamReader) ReadByte() (byte, error) {
return v.GetByte(), nil

View File

@ -18,26 +18,51 @@ func (v *StreamWriter) PushByte(val byte) {
v.data = append(v.data, val)
}
// PushWord writes an uint16 word to the stream
func (v *StreamWriter) PushWord(val uint16) {
// PushUint16 writes an uint16 word to the stream
func (v *StreamWriter) PushUint16(val uint16) {
v.data = append(v.data, byte(val&0xFF))
v.data = append(v.data, byte((val>>8)&0xFF))
}
// PushSWord writes a int16 word to the stream
func (v *StreamWriter) PushSWord(val int16) {
// PushInt16 writes a int16 word to the stream
func (v *StreamWriter) PushInt16(val int16) {
v.data = append(v.data, byte(val&0xFF))
v.data = append(v.data, byte((val>>8)&0xFF))
}
// PushDword writes a uint32 dword to the stream
func (v *StreamWriter) PushDword(val uint32) {
// PushUint32 writes a uint32 dword to the stream
func (v *StreamWriter) PushUint32(val uint32) {
v.data = append(v.data, byte(val&0xFF))
v.data = append(v.data, byte((val>>8)&0xFF))
v.data = append(v.data, byte((val>>16)&0xFF))
v.data = append(v.data, byte((val>>24)&0xFF))
}
// PushUint64 writes a uint64 qword to the stream
func (v *StreamWriter) PushUint64(val uint64) {
v.data = append(v.data, byte(val&0xFF))
v.data = append(v.data, byte((val>>8)&0xFF))
v.data = append(v.data, byte((val>>16)&0xFF))
v.data = append(v.data, byte((val>>24)&0xFF))
v.data = append(v.data, byte((val>>32)&0xFF))
v.data = append(v.data, byte((val>>40)&0xFF))
v.data = append(v.data, byte((val>>48)&0xFF))
v.data = append(v.data, byte((val>>56)&0xFF))
}
// PushInt64 writes a uint64 qword to the stream
func (v *StreamWriter) PushInt64(val int64) {
result := uint64(val)
v.data = append(v.data, byte(result&0xFF))
v.data = append(v.data, byte((result>>8)&0xFF))
v.data = append(v.data, byte((result>>16)&0xFF))
v.data = append(v.data, byte((result>>24)&0xFF))
v.data = append(v.data, byte((result>>32)&0xFF))
v.data = append(v.data, byte((result>>40)&0xFF))
v.data = append(v.data, byte((result>>48)&0xFF))
v.data = append(v.data, byte((result>>56)&0xFF))
}
// GetBytes returns the the byte slice of the underlying data
func (v *StreamWriter) GetBytes() []byte {
return v.data

View File

@ -21,8 +21,8 @@ func TestStreamWriterByte(t *testing.T) {
func TestStreamWriterWord(t *testing.T) {
sr := CreateStreamWriter()
data := []byte{0x12, 0x34, 0x56, 0x78}
sr.PushWord(0x3412)
sr.PushWord(0x7856)
sr.PushUint16(0x3412)
sr.PushUint16(0x7856)
output := sr.GetBytes()
for i, d := range data {
if output[i] != d {
@ -34,7 +34,7 @@ func TestStreamWriterWord(t *testing.T) {
func TestStreamWriterDword(t *testing.T) {
sr := CreateStreamWriter()
data := []byte{0x12, 0x34, 0x56, 0x78}
sr.PushDword(0x78563412)
sr.PushUint32(0x78563412)
output := sr.GetBytes()
for i, d := range data {
if output[i] != d {

View File

@ -1,11 +1,18 @@
package d2scene
import (
"image/color"
"os"
"strings"
"github.com/hajimehoshi/ebiten/ebitenutil"
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
@ -14,16 +21,31 @@ import (
)
type CharacterSelect struct {
uiManager *d2ui.Manager
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
background d2render.Sprite
newCharButton d2ui.Button
convertCharButton d2ui.Button
deleteCharButton d2ui.Button
exitButton d2ui.Button
okButton d2ui.Button
uiManager *d2ui.Manager
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
background d2render.Sprite
newCharButton d2ui.Button
convertCharButton d2ui.Button
deleteCharButton d2ui.Button
exitButton d2ui.Button
okButton d2ui.Button
deleteCharCancelButton d2ui.Button
deleteCharOkButton d2ui.Button
selectionBox d2render.Sprite
okCancelBox d2render.Sprite
d2HeroTitle d2ui.Label
deleteCharConfirmLabel d2ui.Label
characterNameLabel [8]d2ui.Label
characterStatsLabel [8]d2ui.Label
characterExpLabel [8]d2ui.Label
characterImage [8]*d2core.NPC
gameStates []*d2core.GameState
scrollOffset int
selectedCharacter int
mouseButtonPressed bool
showDeleteConfirmation bool
}
func CreateCharacterSelect(
@ -33,10 +55,11 @@ func CreateCharacterSelect(
soundManager *d2audio.Manager,
) *CharacterSelect {
result := &CharacterSelect{
uiManager: uiManager,
sceneProvider: sceneProvider,
fileProvider: fileProvider,
soundManager: soundManager,
selectedCharacter: -1,
uiManager: uiManager,
sceneProvider: sceneProvider,
fileProvider: fileProvider,
soundManager: soundManager,
}
return result
}
@ -62,8 +85,8 @@ func (v *CharacterSelect) Load() []func() {
},
func() {
v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15)))
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
v.deleteCharButton.MoveTo(433, 468)
v.deleteCharButton.SetEnabled(false)
v.uiManager.AddWidget(&v.deleteCharButton)
},
func() {
@ -72,12 +95,81 @@ func (v *CharacterSelect) Load() []func() {
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(&v.exitButton)
},
func() {
v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4231"))
v.deleteCharCancelButton.MoveTo(282, 308)
v.deleteCharCancelButton.SetVisible(false)
v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() })
v.uiManager.AddWidget(&v.deleteCharCancelButton)
},
func() {
v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4227"))
v.deleteCharOkButton.MoveTo(422, 308)
v.deleteCharOkButton.SetVisible(false)
v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() })
v.uiManager.AddWidget(&v.deleteCharOkButton)
},
func() {
v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971"))
v.okButton.MoveTo(625, 537)
v.okButton.SetEnabled(false)
v.uiManager.AddWidget(&v.okButton)
},
func() {
v.d2HeroTitle = d2ui.CreateLabel(v.fileProvider, d2resource.Font42, d2enum.Units)
v.d2HeroTitle.MoveTo(320, 23)
v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter
},
func() {
v.deleteCharConfirmLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
lines := dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#1878"), 29)
v.deleteCharConfirmLabel.SetText(strings.Join(lines, "\n"))
v.deleteCharConfirmLabel.Alignment = d2ui.LabelAlignCenter
v.deleteCharConfirmLabel.MoveTo(400, 185)
},
func() {
v.selectionBox = d2render.CreateSprite(v.fileProvider.LoadFile(d2resource.CharacterSelectionSelectBox), d2datadict.Palettes[d2enum.Sky])
v.selectionBox.MoveTo(37, 86)
},
func() {
v.okCancelBox = d2render.CreateSprite(v.fileProvider.LoadFile(d2resource.PopUpOkCancel), d2datadict.Palettes[d2enum.Fechar])
v.okCancelBox.MoveTo(270, 175)
},
func() {
for i := 0; i < 8; i++ {
xOffset := 115
if i&1 > 0 {
xOffset = 385
}
v.characterNameLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
v.characterNameLabel[i].Color = color.RGBA{188, 168, 140, 255}
v.characterNameLabel[i].MoveTo(xOffset, 100+((i/2)*95))
v.characterStatsLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
v.characterStatsLabel[i].MoveTo(xOffset, 115+((i/2)*95))
v.characterExpLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Static)
v.characterExpLabel[i].Color = color.RGBA{24, 255, 0, 255}
v.characterExpLabel[i].MoveTo(xOffset, 130+((i/2)*95))
}
v.refreshGameStates()
},
}
}
func (v *CharacterSelect) updateCharacterBoxes() {
expText := d2common.TranslateString("expansionchar2x")
for i := 0; i < 8; i++ {
idx := i + (v.scrollOffset * 2)
if idx >= len(v.gameStates) {
v.characterNameLabel[i].SetText("")
v.characterStatsLabel[i].SetText("")
v.characterExpLabel[i].SetText("")
v.characterImage[i] = nil
continue
}
v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName)
v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String())
v.characterExpLabel[i].SetText(expText)
// TODO: Generate or load the object from the actual player data...
v.characterImage[i] = d2core.CreateNPC(0, 0, d2datadict.HeroObjects[v.gameStates[idx].HeroType], v.fileProvider, 5)
}
}
@ -96,7 +188,100 @@ func (v *CharacterSelect) Unload() {
func (v *CharacterSelect) Render(screen *ebiten.Image) {
v.background.DrawSegments(screen, 4, 3, 0)
v.d2HeroTitle.Draw(screen)
if v.selectedCharacter > -1 {
v.selectionBox.DrawSegments(screen, 2, 1, 0)
}
for i := 0; i < 8; i++ {
idx := i + (v.scrollOffset * 2)
if idx >= len(v.gameStates) {
continue
}
v.characterNameLabel[i].Draw(screen)
v.characterStatsLabel[i].Draw(screen)
v.characterExpLabel[i].Draw(screen)
v.characterImage[i].Render(screen, v.characterNameLabel[i].X-40, v.characterNameLabel[i].Y+30)
}
if v.showDeleteConfirmation {
ebitenutil.DrawRect(screen, 0.0, 0.0, 800.0, 600.0, color.RGBA{0, 0, 0, 128})
v.okCancelBox.DrawSegments(screen, 2, 1, 0)
v.deleteCharConfirmLabel.Draw(screen)
}
}
func (v *CharacterSelect) moveSelectionBox() {
bw := 272
bh := 92
selectedIndex := v.selectedCharacter - (v.scrollOffset * 2)
v.selectionBox.MoveTo(37+((selectedIndex&1)*int(bw)), 86+(int(bh)*(selectedIndex/2)))
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
}
func (v *CharacterSelect) Update(tickTime float64) {
if !v.showDeleteConfirmation {
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
if !v.mouseButtonPressed {
v.mouseButtonPressed = true
mx, my := ebiten.CursorPosition()
bw := 272
bh := 92
localMouseX := mx - 37
localMouseY := my - 86
if localMouseX > 0 && localMouseX < int(bw*2) && localMouseY >= 0 && localMouseY < int(bh*4) {
adjustY := localMouseY / int(bh)
selectedIndex := adjustY * 2
if localMouseX > int(bw) {
selectedIndex += 1
}
if (v.scrollOffset*2)+selectedIndex < len(v.gameStates) {
v.selectedCharacter = (v.scrollOffset * 2) + selectedIndex
v.moveSelectionBox()
}
}
}
} else {
v.mouseButtonPressed = false
}
}
}
func (v *CharacterSelect) onDeleteCharButtonClicked() {
v.toggleDeleteCharacterDialog(true)
}
func (v *CharacterSelect) onDeleteCharacterConfirmClicked() {
_ = os.Remove(v.gameStates[v.selectedCharacter].FilePath)
v.selectedCharacter = -1
v.refreshGameStates()
v.toggleDeleteCharacterDialog(false)
}
func (v *CharacterSelect) onDeleteCharacterCancelClicked() {
v.toggleDeleteCharacterDialog(false)
}
func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) {
v.showDeleteConfirmation = showDialog
v.okButton.SetEnabled(!showDialog)
v.deleteCharButton.SetEnabled(!showDialog)
v.exitButton.SetEnabled(!showDialog)
v.newCharButton.SetEnabled(!showDialog)
v.deleteCharOkButton.SetVisible(showDialog)
v.deleteCharCancelButton.SetVisible(showDialog)
}
func (v *CharacterSelect) refreshGameStates() {
v.gameStates = d2core.GetAllGameStates()
v.updateCharacterBoxes()
if len(v.gameStates) > 0 {
v.selectedCharacter = 0
v.d2HeroTitle.SetText(v.gameStates[0].HeroName)
} else {
v.selectedCharacter = -1
}
v.deleteCharButton.SetEnabled(v.selectedCharacter > -1)
v.okButton.SetEnabled(v.selectedCharacter > -1)
v.moveSelectionBox()
}

54
d2core/d2scene/game.go Normal file
View File

@ -0,0 +1,54 @@
package d2scene
import (
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
"github.com/hajimehoshi/ebiten"
)
type Game struct {
gameState *d2core.GameState
uiManager *d2ui.Manager
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
}
func CreateGame(
fileProvider d2interface.FileProvider,
sceneProvider d2interface.SceneProvider,
uiManager *d2ui.Manager,
soundManager *d2audio.Manager,
gameState *d2core.GameState,
) *Game {
result := &Game{
gameState: gameState,
fileProvider: fileProvider,
uiManager: uiManager,
soundManager: soundManager,
sceneProvider: sceneProvider,
}
return result
}
func (v *Game) Load() []func() {
return []func(){
func() {
},
}
}
func (v *Game) Unload() {
}
func (v Game) Render(screen *ebiten.Image) {
}
func (v *Game) Update(tickTime float64) {
}

View File

@ -8,6 +8,8 @@ import (
"os/exec"
"runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
@ -202,6 +204,10 @@ func openbrowser(url string) {
func (v *MainMenu) onSinglePlayerClicked() {
// Go here only if existing characters are available to select
if d2core.HasGameStates() {
v.sceneProvider.SetNextScene(CreateCharacterSelect(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager))
return
}
v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager))
}

View File

@ -37,7 +37,7 @@ func CreateMapEngineTest(
soundManager: soundManager,
sceneProvider: sceneProvider,
}
result.gameState = d2core.CreateGameState()
result.gameState = d2core.CreateTestGameState()
return result
}

View File

@ -4,6 +4,8 @@ import (
"image"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
@ -420,6 +422,10 @@ func (v *SelectHeroClass) Load() []func() {
}
func (v *SelectHeroClass) Unload() {
for i := range v.heroRenderInfo {
v.heroRenderInfo[i].SelectSfx.Stop()
v.heroRenderInfo[i].DeselectSfx.Stop()
}
v.heroRenderInfo = nil
}
@ -428,7 +434,8 @@ func (v SelectHeroClass) onExitButtonClicked() {
}
func (v SelectHeroClass) onOkButtonClicked() {
// TODO: Start the game
gameState := d2core.CreateGameState(v.heroNameTextbox.GetText(), v.selectedHero, v.hardcoreCheckbox.GetCheckState())
v.sceneProvider.SetNextScene(CreateGame(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, gameState))
}
func (v *SelectHeroClass) Render(screen *ebiten.Image) {

View File

@ -226,7 +226,7 @@ func (v Engine) Draw(screen *ebiten.Image) {
v.CurrentScene.Render(screen)
v.UIManager.Draw(screen)
}
if v.showFPS == true {
if v.showFPS {
ebitenutil.DebugPrintAt(screen, "vsync:"+strconv.FormatBool(ebiten.IsVsyncEnabled())+"\nFPS:"+strconv.Itoa(int(ebiten.CurrentFPS())), 5, 565)
var m runtime.MemStats
runtime.ReadMemStats(&m)

View File

@ -1,15 +1,68 @@
package d2core
import (
"io/ioutil"
"log"
"os"
"path"
"runtime"
"strconv"
"strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
/*
File Spec
--------------------------------------------
UINT32 GameState Version
INT64 Game Seed
BYTE Hero Type
BYTE Act
BYTE Hero Name Length
BYTE[] Hero Name
--------------------------------------------
*/
type GameState struct {
Seed int64
Seed int64
HeroName string
HeroType d2enum.Hero
Act int
FilePath string
}
func CreateGameState() *GameState {
const GameStateVersion = uint32(1) // Update this when you make breaking changes
func HasGameStates() bool {
files, _ := ioutil.ReadDir(getGameBaseSavePath())
return len(files) > 0
}
func GetAllGameStates() []*GameState {
// TODO: Make this not crash tf out on bad files
basePath := getGameBaseSavePath()
files, _ := ioutil.ReadDir(basePath)
result := make([]*GameState, 0)
for _, file := range files {
fileName := file.Name()
if file.IsDir() || len(fileName) < 5 || strings.ToLower(fileName[len(fileName)-4:]) != ".od2" {
continue
}
gameState := LoadGameState(path.Join(basePath, file.Name()))
if gameState == nil {
continue
}
result = append(result, gameState)
}
return result
}
// CreateTestGameState is used for the map engine previewer
func CreateTestGameState() *GameState {
result := &GameState{
Seed: time.Now().UnixNano(),
}
@ -17,6 +70,95 @@ func CreateGameState() *GameState {
}
func LoadGameState(path string) *GameState {
log.Fatal("LoadGameState not implemented.")
return nil
result := &GameState{
FilePath: path,
}
f, err := os.Open(path)
if err != nil {
log.Panicf(err.Error())
}
bytes, err := ioutil.ReadAll(f)
if err != nil {
log.Panicf(err.Error())
}
sr := d2common.CreateStreamReader(bytes)
if sr.GetUInt32() > GameStateVersion {
// Unknown game version
return nil
}
result.Seed = sr.GetInt64()
result.HeroType = d2enum.Hero(sr.GetByte())
result.Act = int(sr.GetByte())
heroNameLen := sr.GetByte()
heroName, _ := sr.ReadBytes(int(heroNameLen))
result.HeroName = string(heroName)
return result
}
func CreateGameState(heroName string, hero d2enum.Hero, hardcore bool) *GameState {
result := &GameState{
HeroName: heroName,
HeroType: hero,
Act: 1,
Seed: time.Now().UnixNano(),
FilePath: "",
}
result.Save()
return result
}
func getGameBaseSavePath() string {
if runtime.GOOS == "windows" {
appDataPath := os.Getenv("APPDATA")
basePath := path.Join(appDataPath, "OpenDiablo2", "Saves")
if err := os.MkdirAll(basePath, os.ModeDir); err != nil {
log.Panicf(err.Error())
}
return basePath
}
homeDir, err := os.UserHomeDir()
if err != nil {
log.Panicf(err.Error())
}
basePath := path.Join(homeDir, ".OpenDiablo2", "Saves")
if err := os.MkdirAll(basePath, os.ModeDir); err != nil {
log.Panicf(err.Error())
}
// TODO: Is mac supposed to have a super special place for the save games?
return basePath
}
func getFirstFreefileName() string {
i := 0
basePath := getGameBaseSavePath()
for {
filePath := path.Join(basePath, strconv.Itoa(i)+".od2")
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return filePath
}
i++
}
}
func (v *GameState) Save() {
if v.FilePath == "" {
v.FilePath = getFirstFreefileName()
}
f, err := os.Create(v.FilePath)
if err != nil {
log.Panicf(err.Error())
}
defer f.Close()
sr := d2common.CreateStreamWriter()
sr.PushUint32(GameStateVersion)
sr.PushInt64(v.Seed)
sr.PushByte(byte(v.HeroType))
sr.PushByte(byte(v.Act))
sr.PushByte(byte(len(v.HeroName)))
for _, ch := range v.HeroName {
sr.PushByte(byte(ch))
}
if _, err := f.Write(sr.GetBytes()); err != nil {
log.Panicf(err.Error())
}
}

View File

@ -4,7 +4,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/hajimehoshi/ebiten"
)
@ -14,15 +14,18 @@ type NPC struct {
Paths []d2common.Path
}
func CreateNPC(object d2data.Object, fileProvider d2interface.FileProvider) *NPC {
func CreateNPC(x, y int32, object *d2datadict.ObjectLookupRecord, fileProvider d2interface.FileProvider, direction int) *NPC {
result := &NPC{
AnimatedEntity: d2render.CreateAnimatedEntity(object, fileProvider, d2enum.Units),
Paths: object.Paths,
AnimatedEntity: d2render.CreateAnimatedEntity(x, y, object, fileProvider, d2enum.Units),
}
result.AnimatedEntity.SetMode(object.Lookup.Mode, object.Lookup.Class, 1, fileProvider)
result.AnimatedEntity.SetMode(object.Mode, object.Class, direction, fileProvider)
return result
}
func (v *NPC) SetPaths(paths []d2common.Path) {
v.Paths = paths
}
func (v *NPC) Render(target *ebiten.Image, offsetX, offsetY int) {
v.AnimatedEntity.Render(target, offsetX, offsetY)
}

View File

@ -39,7 +39,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
for i := 0; i < channelCount; i++ {
temp := input.GetInt16()
Array2[i] = int(temp)
output.PushSWord(temp)
output.PushInt16(temp)
}
channel := channelCount - 1
@ -56,7 +56,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
if Array1[channel] != 0 {
Array1[channel]--
}
output.PushSWord(int16(Array2[channel]))
output.PushInt16(int16(Array2[channel]))
break
case 1:
Array1[channel] += 8
@ -115,7 +115,7 @@ func WavDecompress(data []byte, channelCount int) []byte {
}
}
Array2[channel] = temp3
output.PushSWord(int16(temp3))
output.PushInt16(int16(temp3))
Array1[channel] += sLookup2[value&0x1f]
if Array1[channel] < 0 {

View File

@ -0,0 +1,43 @@
package d2datadict
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
var HeroObjects = map[d2enum.Hero]*ObjectLookupRecord{
d2enum.HeroBarbarian: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "BA", Class: "HTH", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroNecromancer: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "NE", Class: "HTH", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroPaladin: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "PA", Class: "1HS", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroAssassin: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "AI", Class: "HTH", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroSorceress: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "SO", Class: "HTH", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroAmazon: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "AM", Class: "1HT", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
d2enum.HeroDruid: &ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars",
Token: "DZ", Class: "HTH", HD: "LIT", TR: "LIT", LG: "LIT", RA: "LIT", LA: "LIT", RH: "", LH: "",
},
}

View File

@ -48,20 +48,20 @@ type AnimatedEntity struct {
currentFrame int
frames map[string][]*ebiten.Image
frameLocations map[string][]d2common.Rectangle
object d2data.Object
object *d2datadict.ObjectLookupRecord
}
// CreateAnimatedEntity creates an instance of AnimatedEntity
func CreateAnimatedEntity(object d2data.Object, fileProvider d2interface.FileProvider, palette d2enum.PaletteType) AnimatedEntity {
func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fileProvider d2interface.FileProvider, palette d2enum.PaletteType) AnimatedEntity {
result := AnimatedEntity{
base: object.Lookup.Base,
token: object.Lookup.Token,
base: object.Base,
token: object.Token,
object: object,
palette: palette,
}
result.dccLayers = make(map[string]d2dcc.DCC)
result.LocationX = float64(object.X) / 5
result.LocationY = float64(object.Y) / 5
result.LocationX = float64(x) / 5
result.LocationY = float64(y) / 5
return result
}
@ -96,37 +96,37 @@ func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FilePr
layerName := "tr"
switch strings.ToUpper(layer) {
case "HD": // Head
layerName = v.object.Lookup.HD
layerName = v.object.HD
case "TR": // Torso
layerName = v.object.Lookup.TR
layerName = v.object.TR
case "LG": // Legs
layerName = v.object.Lookup.LG
layerName = v.object.LG
case "RA": // RightArm
layerName = v.object.Lookup.RA
layerName = v.object.RA
case "LA": // LeftArm
layerName = v.object.Lookup.LA
layerName = v.object.LA
case "RH": // RightHand
layerName = v.object.Lookup.RH
layerName = v.object.RH
case "LH": // LeftHand
layerName = v.object.Lookup.LH
layerName = v.object.LH
case "SH": // Shield
layerName = v.object.Lookup.SH
layerName = v.object.SH
case "S1": // Special1
layerName = v.object.Lookup.S1
layerName = v.object.S1
case "S2": // Special2
layerName = v.object.Lookup.S2
layerName = v.object.S2
case "S3": // Special3
layerName = v.object.Lookup.S3
layerName = v.object.S3
case "S4": // Special4
layerName = v.object.Lookup.S4
layerName = v.object.S4
case "S5": // Special5
layerName = v.object.Lookup.S5
layerName = v.object.S5
case "S6": // Special6
layerName = v.object.Lookup.S6
layerName = v.object.S6
case "S7": // Special7
layerName = v.object.Lookup.S7
layerName = v.object.S7
case "S8": // Special8
layerName = v.object.Lookup.S8
layerName = v.object.S8
}
if len(layerName) == 0 {
return d2dcc.DCC{}

View File

@ -5,6 +5,8 @@ import (
"math/rand"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -40,7 +42,7 @@ func CreateMapEngine(gameState *d2core.GameState, soundManager *d2audio.Manager,
return result
}
func (v *Engine) GenerateMap(regionType RegionIdType, levelPreset int) {
func (v *Engine) GenerateMap(regionType d2enum.RegionIdType, levelPreset int) {
randomSource := rand.NewSource(v.gameState.Seed)
region := LoadRegion(randomSource, regionType, levelPreset, v.fileProvider)
v.regions = append(v.regions, EngineRegion{
@ -52,19 +54,19 @@ func (v *Engine) GenerateMap(regionType RegionIdType, levelPreset int) {
func (v *Engine) GenerateAct1Overworld() {
v.soundManager.PlayBGM("/data/global/music/Act1/town1.wav") // TODO: Temp stuff here
randomSource := rand.NewSource(v.gameState.Seed)
region := LoadRegion(randomSource, RegionAct1Town, 1, v.fileProvider)
region := LoadRegion(randomSource, d2enum.RegionAct1Town, 1, v.fileProvider)
v.regions = append(v.regions, EngineRegion{
Rect: d2common.Rectangle{0, 0, int(region.TileWidth), int(region.TileHeight)},
Region: region,
})
if strings.Contains(region.RegionPath, "E1") {
region2 := LoadRegion(randomSource, RegionAct1Town, 2, v.fileProvider)
region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 2, v.fileProvider)
v.regions = append(v.regions, EngineRegion{
Rect: d2common.Rectangle{int(region.TileWidth - 1), 0, int(region2.TileWidth), int(region2.TileHeight)},
Region: region2,
})
} else if strings.Contains(region.RegionPath, "S1") {
region2 := LoadRegion(randomSource, RegionAct1Town, 3, v.fileProvider)
region2 := LoadRegion(randomSource, d2enum.RegionAct1Town, 3, v.fileProvider)
v.regions = append(v.regions, EngineRegion{
Rect: d2common.Rectangle{0, int(region.TileHeight - 1), int(region2.TileWidth), int(region2.TileHeight)},
Region: region2,
@ -116,19 +118,19 @@ func (v *Engine) RenderTile(region *Region, offX, offY, x, y int, target *ebiten
if tile.Floors[i].Hidden || tile.Floors[i].Prop1 == 0 {
continue
}
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeFloors, i, target)
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeFloors, i, target)
}
for i := range tile.Shadows {
if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 {
continue
}
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeShadows, i, target)
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeShadows, i, target)
}
for i := range tile.Walls {
if tile.Walls[i].Hidden || tile.Walls[i].Orientation == 15 || tile.Walls[i].Orientation == 10 || tile.Walls[i].Orientation == 11 || tile.Walls[i].Orientation == 0 {
continue
}
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target)
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeWalls, i, target)
}
for _, obj := range region.AnimationEntities {
if int(math.Floor(obj.LocationX)) == x && int(math.Floor(obj.LocationY)) == y {
@ -144,6 +146,6 @@ func (v *Engine) RenderTile(region *Region, offX, offY, x, y int, target *ebiten
if tile.Walls[i].Hidden || tile.Walls[i].Orientation != 15 {
continue
}
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target)
region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, d2enum.RegionLayerTypeWalls, i, target)
}
}

View File

@ -29,12 +29,6 @@ import (
"github.com/hajimehoshi/ebiten"
)
type TileCacheRecord struct {
Image *ebiten.Image
XOffset int
YOffset int
}
type Region struct {
RegionPath string
LevelType d2datadict.LevelTypeRecord
@ -53,55 +47,7 @@ type Region struct {
StartY float64
}
type RegionLayerType int
const (
RegionLayerTypeFloors RegionLayerType = 0
RegionLayerTypeWalls RegionLayerType = 1
RegionLayerTypeShadows RegionLayerType = 2
)
type RegionIdType int
const (
RegionAct1Town RegionIdType = 1
RegionAct1Wilderness RegionIdType = 2
RegionAct1Cave RegionIdType = 3
RegionAct1Crypt RegionIdType = 4
RegionAct1Monestary RegionIdType = 5
RegionAct1Courtyard RegionIdType = 6
RegionAct1Barracks RegionIdType = 7
RegionAct1Jail RegionIdType = 8
RegionAct1Cathedral RegionIdType = 9
RegionAct1Catacombs RegionIdType = 10
RegionAct1Tristram RegionIdType = 11
RegionAct2Town RegionIdType = 12
RegionAct2Sewer RegionIdType = 13
RegionAct2Harem RegionIdType = 14
RegionAct2Basement RegionIdType = 15
RegionAct2Desert RegionIdType = 16
RegionAct2Tomb RegionIdType = 17
RegionAct2Lair RegionIdType = 18
RegionAct2Arcane RegionIdType = 19
RegionAct3Town RegionIdType = 20
RegionAct3Jungle RegionIdType = 21
RegionAct3Kurast RegionIdType = 22
RegionAct3Spider RegionIdType = 23
RegionAct3Dungeon RegionIdType = 24
RegionAct3Sewer RegionIdType = 25
RegionAct4Town RegionIdType = 26
RegionAct4Mesa RegionIdType = 27
RegionAct4Lava RegionIdType = 28
RegonAct5Town RegionIdType = 29
RegionAct5Siege RegionIdType = 30
RegionAct5Barricade RegionIdType = 31
RegionAct5Temple RegionIdType = 32
RegionAct5IceCaves RegionIdType = 33
RegionAct5Baal RegionIdType = 34
RegionAct5Lava RegionIdType = 35
)
func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileProvider d2interface.FileProvider) *Region {
func LoadRegion(seed rand.Source, levelType d2enum.RegionIdType, levelPreset int, fileProvider d2interface.FileProvider) *Region {
result := &Region{
LevelType: d2datadict.LevelTypes[levelType],
levelPreset: d2datadict.LevelPresets[levelPreset],
@ -111,7 +57,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
WallCache: make(map[uint32]*TileCacheRecord),
}
result.Palette = d2datadict.Palettes[d2enum.PaletteType("act"+strconv.Itoa(int(result.LevelType.Act)))]
//\bm := result.levelPreset.Dt1Mask
//bm := result.levelPreset.Dt1Mask
for _, levelTypeDt1 := range result.LevelType.Files {
/*
if bm&1 == 0 {
@ -158,13 +104,14 @@ func (v *Region) loadObjects(fileProvider d2interface.FileProvider) {
if object.Lookup.Base == "" || object.Lookup.Token == "" || object.Lookup.TR == "" {
return
}
npc := d2core.CreateNPC(object, fileProvider)
npc := d2core.CreateNPC(object.X, object.Y, object.Lookup, fileProvider, 1)
npc.SetPaths(object.Paths)
v.NPCs = append(v.NPCs, npc)
case d2datadict.ObjectTypeItem:
if object.ObjectInfo == nil || !object.ObjectInfo.Draw || object.Lookup.Base == "" || object.Lookup.Token == "" {
return
}
entity := d2render.CreateAnimatedEntity(object, fileProvider, d2enum.Units)
entity := d2render.CreateAnimatedEntity(object.X, object.Y, object.Lookup, fileProvider, d2enum.Units)
entity.SetMode(object.Lookup.Mode, object.Lookup.Class, 0, fileProvider)
v.AnimationEntities = append(v.AnimationEntities, entity)
}
@ -173,14 +120,14 @@ func (v *Region) loadObjects(fileProvider d2interface.FileProvider) {
wg.Wait()
}
func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType RegionLayerType, layerIndex int, target *ebiten.Image) {
func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType d2enum.RegionLayerType, layerIndex int, target *ebiten.Image) {
offsetX -= 80
switch layerType {
case RegionLayerTypeFloors:
case d2enum.RegionLayerTypeFloors:
v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target)
case RegionLayerTypeWalls:
case d2enum.RegionLayerTypeWalls:
v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target)
case RegionLayerTypeShadows:
case d2enum.RegionLayerTypeShadows:
v.renderShadow(v.DS1.Tiles[tileY][tileX].Shadows[layerIndex], offsetX, offsetY, target)
}
}
@ -394,7 +341,10 @@ func (v *Region) generateWallCache(tile d2ds1.WallRecord) *TileCacheRecord {
yAdjust = int(tileMinY) + 80
}
image.ReplacePixels(pixels)
// TODO: This may also need to be an atlas, but could get pretty large...
if err := image.ReplacePixels(pixels); err != nil {
log.Panicf(err.Error())
}
return &TileCacheRecord{
image,
0,

View File

@ -0,0 +1,9 @@
package d2mapengine
import "github.com/hajimehoshi/ebiten"
type TileCacheRecord struct {
Image *ebiten.Image
XOffset int
YOffset int
}

View File

@ -22,12 +22,13 @@ import (
type ButtonType int
const (
ButtonTypeWide ButtonType = 1
ButtonTypeMedium ButtonType = 2
ButtonTypeNarrow ButtonType = 3
ButtonTypeCancel ButtonType = 4
ButtonTypeTall ButtonType = 5
ButtonTypeShort ButtonType = 6
ButtonTypeWide ButtonType = 1
ButtonTypeMedium ButtonType = 2
ButtonTypeNarrow ButtonType = 3
ButtonTypeCancel ButtonType = 4
ButtonTypeTall ButtonType = 5
ButtonTypeShort ButtonType = 6
ButtonTypeOkCancel ButtonType = 7
// Game UI
@ -63,10 +64,11 @@ type ButtonLayout struct {
// ButtonLayouts define the type of buttons you can have
var ButtonLayouts = map[ButtonType]ButtonLayout{
ButtonTypeWide: {2, 1, d2resource.WideButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontExocet10, nil, true, 1},
ButtonTypeShort: {1, 1, d2resource.ShortButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontRediculous, nil, true, -1},
ButtonTypeMedium: {1, 1, d2resource.MediumButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 0},
ButtonTypeTall: {1, 1, d2resource.TallButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 5},
ButtonTypeWide: {2, 1, d2resource.WideButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontExocet10, nil, true, 1},
ButtonTypeShort: {1, 1, d2resource.ShortButtonBlank, d2enum.Units, false, 0, -1, d2resource.FontRediculous, nil, true, -1},
ButtonTypeMedium: {1, 1, d2resource.MediumButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 0},
ButtonTypeTall: {1, 1, d2resource.TallButtonBlank, d2enum.Units, false, 0, 0, d2resource.FontExocet10, nil, true, 5},
ButtonTypeOkCancel: {1, 1, d2resource.CancelButton, d2enum.Units, false, 0, -1, d2resource.FontRediculous, nil, true, 0},
/*
{eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = PaletteDefs.Units } },
{eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = PaletteDefs.Units } },
@ -171,7 +173,7 @@ func (v *Button) OnActivated(callback func()) {
}
// Activate calls the on activated callback handler, if any
func (v Button) Activate() {
func (v *Button) Activate() {
if v.onClick == nil {
return
}

View File

@ -17,7 +17,7 @@ func TestMapGenerationPerformance(t *testing.T) {
d2common.ConfigBasePath = "../"
engine := d2core.CreateEngine()
gameState := d2core.CreateGameState()
gameState := d2core.CreateTestGameState()
mapEngine := _map.CreateMapEngine(gameState, engine.SoundManager, engine)
mapEngine.GenerateAct1Overworld()
surface, _ := ebiten.NewImage(800, 600, ebiten.FilterNearest)