From 5e958b9174c7aa2dbbc2a628907dd3031e62b6c6 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 8 Feb 2020 18:02:37 -0800 Subject: [PATCH] Scene and GUI rework (#300) --- d2common/d2fileformats/d2mpq/crypto_buff.go | 18 +- d2common/d2fileformats/d2mpq/mpq.go | 6 +- .../d2audio/ebiten/ebiten_audio_provider.go | 57 +- d2core/d2gui/d2gui.go | 76 ++ d2core/d2gui/gui_manager.go | 125 +++ d2core/d2gui/sprite.go | 104 +++ d2core/d2gui/widget_manager.go | 13 + d2core/d2scene/d2scene.go | 143 ++-- d2core/d2ui/d2ui.go | 12 +- d2game/d2game.go | 153 ---- d2game/d2gamescene/blizzard_intro.go | 31 +- d2game/d2gamescene/character_select.go | 192 +++-- d2game/d2gamescene/credits.go | 71 +- d2game/d2gamescene/game.go | 97 ++- d2game/d2gamescene/main_menu.go | 242 +++--- d2game/d2gamescene/map_engine_testing.go | 38 +- d2game/d2gamescene/select_hero_class.go | 715 +++++++++--------- main.go | 348 ++++++--- 18 files changed, 1298 insertions(+), 1143 deletions(-) create mode 100644 d2core/d2gui/d2gui.go create mode 100644 d2core/d2gui/gui_manager.go create mode 100644 d2core/d2gui/sprite.go create mode 100644 d2core/d2gui/widget_manager.go delete mode 100644 d2game/d2game.go diff --git a/d2common/d2fileformats/d2mpq/crypto_buff.go b/d2common/d2fileformats/d2mpq/crypto_buff.go index 097d957a8..b14e4c9ef 100644 --- a/d2common/d2fileformats/d2mpq/crypto_buff.go +++ b/d2common/d2fileformats/d2mpq/crypto_buff.go @@ -1,10 +1,18 @@ package d2mpq -// CryptoBuffer contains the crypto bytes for filename hashing -var CryptoBuffer [0x500]uint32 +var cryptoBuffer [0x500]uint32 +var cryptoBufferReady bool -// InitializeCryptoBuffer initializes the crypto buffer -func InitializeCryptoBuffer() { +func cryptoLookup(index uint32) uint32 { + if !cryptoBufferReady { + cryptoInitialize() + cryptoBufferReady = true + } + + return cryptoBuffer[index] +} + +func cryptoInitialize() { seed := uint32(0x00100001) for index1 := 0; index1 < 0x100; index1++ { index2 := index1 @@ -13,7 +21,7 @@ func InitializeCryptoBuffer() { temp1 := (seed & 0xFFFF) << 0x10 seed = (seed*125 + 3) % 0x2AAAAB temp2 := seed & 0xFFFF - CryptoBuffer[index2] = temp1 | temp2 + cryptoBuffer[index2] = temp1 | temp2 index2 += 0x100 } } diff --git a/d2common/d2fileformats/d2mpq/mpq.go b/d2common/d2fileformats/d2mpq/mpq.go index 0a7a98913..222795724 100644 --- a/d2common/d2fileformats/d2mpq/mpq.go +++ b/d2common/d2fileformats/d2mpq/mpq.go @@ -202,7 +202,7 @@ func decrypt(data []uint32, seed uint32) { seed2 := uint32(0xeeeeeeee) for i := 0; i < len(data); i++ { - seed2 += CryptoBuffer[0x400+(seed&0xff)] + seed2 += cryptoLookup(0x400 + (seed & 0xff)) result := data[i] result ^= seed + seed2 @@ -215,7 +215,7 @@ func decrypt(data []uint32, seed uint32) { func decryptBytes(data []byte, seed uint32) { seed2 := uint32(0xEEEEEEEE) for i := 0; i < len(data)-3; i += 4 { - seed2 += CryptoBuffer[0x400+(seed&0xFF)] + seed2 += cryptoLookup(0x400 + (seed & 0xFF)) result := binary.LittleEndian.Uint32(data[i : i+4]) result ^= seed + seed2 seed = ((^seed << 21) + 0x11111111) | (seed >> 11) @@ -235,7 +235,7 @@ func hashString(key string, hashType uint32) uint32 { /* prepare seeds. */ for _, char := range strings.ToUpper(key) { - seed1 = CryptoBuffer[(hashType*0x100)+uint32(char)] ^ (seed1 + seed2) + seed1 = cryptoLookup((hashType*0x100)+uint32(char)) ^ (seed1 + seed2) seed2 = uint32(char) + seed1 + seed2 + (seed2 << 5) + 3 } return seed1 diff --git a/d2core/d2audio/ebiten/ebiten_audio_provider.go b/d2core/d2audio/ebiten/ebiten_audio_provider.go index dc071ae79..7cc9c90c2 100644 --- a/d2core/d2audio/ebiten/ebiten_audio_provider.go +++ b/d2core/d2audio/ebiten/ebiten_audio_provider.go @@ -38,37 +38,36 @@ func (eap *AudioProvider) PlayBGM(song string) { _ = eap.bgmAudio.Pause() return } - go func() { - if eap.bgmAudio != nil { - err := eap.bgmAudio.Close() - if err != nil { - log.Panic(err) - } - } - audioData, err := d2asset.LoadFile(song) - if err != nil { - panic(err) - } - d, err := wav.Decode(eap.audioContext, audio.BytesReadSeekCloser(audioData)) - if err != nil { - log.Fatal(err) - } - s := audio.NewInfiniteLoop(d, d.Length()) - eap.bgmAudio, err = audio.NewPlayer(eap.audioContext, s) - if err != nil { - log.Fatal(err) - } - eap.bgmAudio.SetVolume(eap.bgmVolume) - // Play the infinite-length stream. This never ends. - err = eap.bgmAudio.Rewind() - if err != nil { - panic(err) - } - err = eap.bgmAudio.Play() + + if eap.bgmAudio != nil { + err := eap.bgmAudio.Close() if err != nil { - panic(err) + log.Panic(err) } - }() + } + audioData, err := d2asset.LoadFile(song) + if err != nil { + panic(err) + } + d, err := wav.Decode(eap.audioContext, audio.BytesReadSeekCloser(audioData)) + if err != nil { + log.Fatal(err) + } + s := audio.NewInfiniteLoop(d, d.Length()) + eap.bgmAudio, err = audio.NewPlayer(eap.audioContext, s) + if err != nil { + log.Fatal(err) + } + eap.bgmAudio.SetVolume(eap.bgmVolume) + // Play the infinite-length stream. This never ends. + err = eap.bgmAudio.Rewind() + if err != nil { + panic(err) + } + err = eap.bgmAudio.Play() + if err != nil { + panic(err) + } } func (eap *AudioProvider) LoadSoundEffect(sfx string) (d2audio.SoundEffect, error) { diff --git a/d2core/d2gui/d2gui.go b/d2core/d2gui/d2gui.go new file mode 100644 index 000000000..df341070a --- /dev/null +++ b/d2core/d2gui/d2gui.go @@ -0,0 +1,76 @@ +package d2gui + +import ( + "errors" + + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +var ( + ErrWasInit = errors.New("gui system is already initialized") + ErrNotInit = errors.New("gui system is not initialized") +) + +var singleton *guiManager + +func Initialize() error { + assertNotInit() + + var err error + if singleton, err = createGuiManager(); err != nil { + return err + } + + return nil +} + +func Shutdown() { + singleton = nil +} + +func Render(target d2render.Surface) error { + assertWasInit() + return singleton.render(target) +} + +func Advance(elapsed float64) error { + assertWasInit() + return singleton.advance(elapsed) +} + +func Clear() { + assertWasInit() + singleton.clear() +} + +func ShowLoadScreen(progress float64) { + assertWasInit() + singleton.showLoadScreen(progress) +} + +func HideLoadScreen() { + assertWasInit() + singleton.hideLoadScreen() +} + +func ShowCursor() { + assertWasInit() + singleton.showCursor() +} + +func HideCursor() { + assertWasInit() + singleton.hideCursor() +} + +func assertWasInit() { + if singleton == nil { + panic(ErrNotInit) + } +} + +func assertNotInit() { + if singleton != nil { + panic(ErrWasInit) + } +} diff --git a/d2core/d2gui/gui_manager.go b/d2core/d2gui/gui_manager.go new file mode 100644 index 000000000..a812d3fd8 --- /dev/null +++ b/d2core/d2gui/gui_manager.go @@ -0,0 +1,125 @@ +package d2gui + +import ( + "image/color" + "math" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type guiWidget interface { + getLayer() int + render(target d2render.Surface) error + advance(elapsed float64) error +} + +type guiManager struct { + cursorSprite *Sprite + loadSprite *Sprite + widgets []guiWidget + loading bool +} + +func createGuiManager() (*guiManager, error) { + cursorSprite, err := CreateSprite(d2resource.CursorDefault, d2resource.PaletteUnits) + if err != nil { + return nil, err + } + + loadSprite, err := CreateSprite(d2resource.LoadingScreen, d2resource.PaletteLoading) + if err != nil { + return nil, err + } + + width, height := loadSprite.getSize() + loadSprite.SetPosition(400-width/2, 300+height/2) + + manager := &guiManager{ + cursorSprite: cursorSprite, + loadSprite: loadSprite, + } + + if err := d2input.BindHandler(manager); err != nil { + return nil, err + } + + return manager, nil +} + +func (gui *guiManager) OnMouseMove(event d2input.MouseMoveEvent) bool { + gui.cursorSprite.SetPosition(event.X, event.Y) + return false +} + +func (gui *guiManager) render(target d2render.Surface) error { + if gui.loading { + target.Clear(color.Black) + if err := gui.loadSprite.render(target); err != nil { + return err + } + } else { + for _, widget := range gui.widgets { + if err := widget.render(target); err != nil { + return err + } + } + } + + if err := gui.cursorSprite.render(target); err != nil { + return err + } + + return nil +} + +func (gui *guiManager) advance(elapsed float64) error { + if gui.loading { + gui.loadSprite.Show() + if err := gui.loadSprite.advance(elapsed); err != nil { + return err + } + } else { + gui.loadSprite.Hide() + for _, widget := range gui.widgets { + if err := widget.advance(elapsed); err != nil { + return err + } + } + } + + if err := gui.loadSprite.advance(elapsed); err != nil { + return err + } + + return nil +} + +func (gui *guiManager) showLoadScreen(progress float64) { + progress = math.Min(progress, 1.0) + progress = math.Max(progress, 0.0) + + animation := gui.loadSprite.animation + frameCount := animation.GetFrameCount() + animation.SetCurrentFrame(int(float64(frameCount-1.0) * progress)) + + gui.loading = true +} + +func (gui *guiManager) hideLoadScreen() { + gui.loading = false +} + +func (gui *guiManager) showCursor() { + gui.cursorSprite.Show() +} + +func (gui *guiManager) hideCursor() { + gui.cursorSprite.Hide() +} + +func (gui *guiManager) clear() { + gui.widgets = nil + gui.hideLoadScreen() +} diff --git a/d2core/d2gui/sprite.go b/d2core/d2gui/sprite.go new file mode 100644 index 000000000..14d7c8560 --- /dev/null +++ b/d2core/d2gui/sprite.go @@ -0,0 +1,104 @@ +package d2gui + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type Sprite struct { + x int + y int + + segmentsX int + segmentsY int + frameOffset int + + visible bool + animation *d2asset.Animation +} + +func CreateSprite(imagePath, palettePath string) (*Sprite, error) { + animation, err := d2asset.LoadAnimation(imagePath, palettePath) + if err != nil { + return nil, err + } + + sprite := &Sprite{ + animation: animation, + visible: true, + } + + return sprite, nil +} + +func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) { + s.segmentsX = segmentsX + s.segmentsY = segmentsY + s.frameOffset = frameOffset +} + +func (s *Sprite) SetPosition(x, y int) { + s.x = x + s.y = y +} + +func (s *Sprite) Show() { + s.visible = true +} + +func (s *Sprite) Hide() { + s.visible = false +} + +func (s *Sprite) getPosition() (int, int) { + return s.x, s.y +} + +func (s *Sprite) getSize() (int, int) { + return s.animation.GetCurrentFrameSize() +} + +func (s *Sprite) render(target d2render.Surface) error { + if !s.visible { + return nil + } + + _, height := s.animation.GetCurrentFrameSize() + target.PushTranslation(s.x, s.y-height) + defer target.Pop() + + if s.segmentsX == 0 && s.segmentsY == 0 { + return s.animation.Render(target) + } + + var currentY int + for y := 0; y < s.segmentsY; y++ { + var currentX int + var maxHeight int + for x := 0; x < s.segmentsX; x++ { + if err := s.animation.SetCurrentFrame(x + y*s.segmentsX + s.frameOffset*s.segmentsX*s.segmentsY); err != nil { + return err + } + + target.PushTranslation(s.x+currentX, s.y+currentY) + err := s.animation.Render(target) + target.Pop() + if err != nil { + return err + } + + width, height := s.animation.GetCurrentFrameSize() + maxHeight = d2common.MaxInt(maxHeight, height) + currentX += width + } + + currentY += maxHeight + } + + return nil +} + +func (s *Sprite) advance(elapsed float64) error { + return s.animation.Advance(elapsed) +} diff --git a/d2core/d2gui/widget_manager.go b/d2core/d2gui/widget_manager.go new file mode 100644 index 000000000..13fccf6a8 --- /dev/null +++ b/d2core/d2gui/widget_manager.go @@ -0,0 +1,13 @@ +package d2gui + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type widget interface { + Render(target d2render.Surface) error + Advance(elapsed float64) error +} + +type widgetManager struct { +} diff --git a/d2core/d2scene/d2scene.go b/d2core/d2scene/d2scene.go index 57f5c21fc..bbc67d8d1 100644 --- a/d2core/d2scene/d2scene.go +++ b/d2core/d2scene/d2scene.go @@ -1,107 +1,88 @@ package d2scene import ( - "math" - "runtime" - + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) -// Scene defines the function necessary for scene management -type Scene interface { - Load() []func() - Unload() - Render(target d2render.Surface) - Advance(tickTime float64) -} - -var nextScene Scene // The next scene to be loaded at the end of the game loop -var currentScene Scene // The current scene being rendered -var loadingIndex int // Determines which load function is currently being called -var thingsToLoad []func() // The load functions for the next scene -var loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays. -var stepLoadingSize float64 // The size for each loading step +type Scene interface{} -// SetNextScene tells the engine what scene to load on the next update cycle -func SetNextScene(scene Scene) { - nextScene = scene +type SceneLoadHandler interface { + OnLoad() error } -func GetCurrentScene() Scene { - return currentScene +type SceneUnloadHandler interface { + OnUnload() error } -// updateScene handles the scene maintenance for the engine -func UpdateScene() { - if nextScene == nil { - if thingsToLoad != nil { - if loadingIndex < len(thingsToLoad) { - thingsToLoad[loadingIndex]() - loadingIndex++ - if loadingIndex < len(thingsToLoad) { - StepLoading() - } else { - FinishLoading() - thingsToLoad = nil - } - return - } - } - return - } - if currentScene != nil { - currentScene.Unload() - runtime.GC() - } - currentScene = nextScene - nextScene = nil - d2ui.Reset() - thingsToLoad = currentScene.Load() - loadingIndex = 0 - SetLoadingStepSize(1.0 / float64(len(thingsToLoad))) - ResetLoading() +type SceneRenderHandler interface { + Render(target d2render.Surface) error } -func Advance(time float64) { - if currentScene == nil { - return - } - currentScene.Advance(time) +type SceneAdvanceHandler interface { + Advance(elapsed float64) error } -func Render(surface d2render.Surface) { - if currentScene == nil { - return - } - currentScene.Render(surface) +var singleton struct { + nextScene Scene + loadingScene Scene + currentScene Scene } -// SetLoadingStepSize sets the size of the loading step -func SetLoadingStepSize(size float64) { - stepLoadingSize = size +func SetNextScene(scene Scene) { + singleton.nextScene = scene } -// ResetLoading resets the loading progress -func ResetLoading() { - loadingProgress = 0.0 -} +func Advance(elapsed float64) error { + if singleton.nextScene != nil { + if handler, ok := singleton.currentScene.(SceneUnloadHandler); ok { + if err := handler.OnUnload(); err != nil { + return err + } + } -// StepLoading increments the loading progress -func StepLoading() { - loadingProgress = math.Min(1.0, loadingProgress+stepLoadingSize) -} + d2ui.Reset() + d2gui.Clear() -// FinishLoading terminates the loading phase -func FinishLoading() { - loadingProgress = 1.0 -} + if _, ok := singleton.nextScene.(SceneLoadHandler); ok { + d2gui.ShowLoadScreen(0) + d2gui.HideCursor() + singleton.currentScene = nil + singleton.loadingScene = singleton.nextScene + } else { + singleton.currentScene = singleton.nextScene + singleton.loadingScene = nil + } -// IsLoading returns true if the engine is currently in a loading state -func IsLoading() bool { - return loadingProgress < 1.0 + singleton.nextScene = nil + } else if singleton.loadingScene != nil { + handler := singleton.loadingScene.(SceneLoadHandler) + if err := handler.OnLoad(); err != nil { + return err + } + + singleton.currentScene = singleton.loadingScene + singleton.loadingScene = nil + d2gui.ShowCursor() + d2gui.HideLoadScreen() + } else if singleton.currentScene != nil { + if handler, ok := singleton.currentScene.(SceneAdvanceHandler); ok { + if err := handler.Advance(elapsed); err != nil { + return err + } + } + } + + return nil } -func GetLoadingProgress() float64 { - return loadingProgress +func Render(surface d2render.Surface) error { + if handler, ok := singleton.currentScene.(SceneRenderHandler); ok { + if err := handler.Render(surface); err != nil { + return err + } + } + + return nil } diff --git a/d2core/d2ui/d2ui.go b/d2core/d2ui/d2ui.go index f87bcfedc..367f8067c 100644 --- a/d2core/d2ui/d2ui.go +++ b/d2core/d2ui/d2ui.go @@ -18,7 +18,6 @@ const ( ) var widgets []Widget -var cursorSprite *Sprite var cursorButtons CursorButton var pressedIndex int var CursorX int @@ -26,8 +25,7 @@ var CursorY int var clickSfx d2audio.SoundEffect var waitForLeftMouseUp bool -func Initialize(curSprite *Sprite) { - cursorSprite = curSprite +func Initialize() { pressedIndex = -1 clickSfx, _ = d2audio.LoadSoundEffect(d2resource.SFXButtonClick) waitForLeftMouseUp = false @@ -56,10 +54,6 @@ func Render(target d2render.Surface) { widget.Render(target) } } - - cx, cy := ebiten.CursorPosition() - cursorSprite.SetPosition(cx, cy) - cursorSprite.Render(target) } // Update updates all of the UI elements @@ -142,7 +136,3 @@ func CursorButtonPressed(button CursorButton) bool { func KeyPressed(key ebiten.Key) bool { return ebiten.IsKeyPressed(key) } - -func GetCursorSprite() *Sprite { - return cursorSprite -} diff --git a/d2game/d2game.go b/d2game/d2game.go deleted file mode 100644 index 8ffdd3433..000000000 --- a/d2game/d2game.go +++ /dev/null @@ -1,153 +0,0 @@ -package d2game - -import ( - "log" - "runtime" - "strconv" - - "github.com/OpenDiablo2/OpenDiablo2/d2common" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" -) - -var singleton struct { - loadingSprite *d2ui.Sprite // The sprite shown when loading stuff - lastTime float64 // Last time we updated the scene - showFPS bool - timeScale float64 -} - -func Initialize(loadingSpr *d2ui.Sprite) error { - singleton.loadingSprite = loadingSpr - singleton.timeScale = 1.0 - singleton.lastTime = d2common.Now() - - d2term.BindAction("fullscreen", "toggles fullscreen", func() { - fullscreen, err := d2render.IsFullScreen() - if err == nil { - fullscreen = !fullscreen - d2render.SetFullScreen(fullscreen) - d2term.OutputInfo("fullscreen is now: %v", fullscreen) - } else { - d2term.OutputError(err.Error()) - } - }) - d2term.BindAction("vsync", "toggles vsync", func() { - vsync, err := d2render.GetVSyncEnabled() - if err == nil { - vsync = !vsync - d2render.SetVSyncEnabled(vsync) - d2term.OutputInfo("vsync is now: %v", vsync) - } else { - d2term.OutputError(err.Error()) - } - }) - d2term.BindAction("fps", "toggle fps counter", func() { - singleton.showFPS = !singleton.showFPS - d2term.OutputInfo("fps counter is now: %v", singleton.showFPS) - }) - d2term.BindAction("timescale", "set scalar for elapsed time", func(timeScale float64) { - if timeScale <= 0 { - d2term.OutputError("invalid time scale value") - } else { - singleton.timeScale = timeScale - d2term.OutputInfo("timescale changed from %f to %f", singleton.timeScale, timeScale) - } - }) - - return nil -} - -func Run(gitBranch string) error { - if err := d2render.Run(update, 800, 600, "OpenDiablo 2 ("+gitBranch+")"); err != nil { - log.Fatal(err) - } - return nil -} - -// Advance updates the internal state of the engine -func Advance() { - d2scene.UpdateScene() - if d2scene.GetCurrentScene() == nil { - log.Fatal("no scene loaded") - } - - if d2scene.IsLoading() { - return - } - - currentTime := d2common.Now() - deltaTime := (currentTime - singleton.lastTime) * singleton.timeScale - singleton.lastTime = currentTime - - d2scene.Advance(deltaTime) - d2ui.Advance(deltaTime) - d2term.Advance(deltaTime) - d2input.Advance(deltaTime) -} - -// Draw draws the game -func render(target d2render.Surface) { - if d2scene.GetLoadingProgress() < 1.0 { - singleton.loadingSprite.SetCurrentFrame( - int(d2common.Max(0, d2common.Min( - uint32(singleton.loadingSprite.GetFrameCount()-1), - uint32(float64(singleton.loadingSprite.GetFrameCount()-1)*d2scene.GetLoadingProgress()), - ))), - ) - singleton.loadingSprite.Render(target) - } else { - if d2scene.GetCurrentScene() == nil { - log.Fatal("no scene loaded") - } - d2scene.Render(target) - d2ui.Render(target) - } - if singleton.showFPS { - target.PushTranslation(5, 565) - vsyncEnabled, _ := d2render.GetVSyncEnabled() - fps, _ := d2render.CurrentFPS() - target.DrawText("vsync:" + strconv.FormatBool(vsyncEnabled) + "\nFPS:" + strconv.Itoa(int(fps))) - target.Pop() - - cx, cy, _ := d2render.GetCursorPos() - - var m runtime.MemStats - runtime.ReadMemStats(&m) - - target.PushTranslation(680, 0) - target.DrawText("Alloc " + strconv.FormatInt(int64(m.Alloc)/1024/1024, 10)) - target.PushTranslation(0, 16) - target.DrawText("Pause " + strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10)) - target.PushTranslation(0, 16) - target.DrawText("HeapSys " + strconv.FormatInt(int64(m.HeapSys/1024/1024), 10)) - target.PushTranslation(0, 16) - target.DrawText("NumGC " + strconv.FormatInt(int64(m.NumGC), 10)) - target.PushTranslation(0, 16) - target.DrawText("Coords " + strconv.FormatInt(int64(cx), 10) + "," + strconv.FormatInt(int64(cy), 10)) - target.PopN(5) - } - - d2term.Render(target) -} - -func update(screen d2render.Surface) error { - Advance() - err, drawingSkipped := d2render.IsDrawingSkipped() - if err != nil { - return err - } - if !drawingSkipped { - _, surface := d2render.CreateSurface(screen) - render(surface) - if surface.GetDepth() > 0 { - panic("detected surface stack leak") - } - } - - return nil -} diff --git a/d2game/d2gamescene/blizzard_intro.go b/d2game/d2gamescene/blizzard_intro.go index 6e10bb009..e7788f59d 100644 --- a/d2game/d2gamescene/blizzard_intro.go +++ b/d2game/d2gamescene/blizzard_intro.go @@ -3,7 +3,6 @@ package d2gamescene import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2video" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" - "github.com/hajimehoshi/ebiten" ) type BlizzardIntro struct { @@ -11,31 +10,15 @@ type BlizzardIntro struct { } func CreateBlizzardIntro() *BlizzardIntro { - result := &BlizzardIntro{} - - return result + return &BlizzardIntro{} } -func (v *BlizzardIntro) Load() []func() { - return []func(){ - func() { - videoBytes, err := d2asset.LoadFile("/data/local/video/BlizNorth640x480.bik") - if err != nil { - panic(err) - } - v.videoDecoder = d2video.CreateBinkDecoder(videoBytes) - }, +func (v *BlizzardIntro) OnLoad() error { + videoBytes, err := d2asset.LoadFile("/data/local/video/BlizNorth640x480.bik") + if err != nil { + return err } -} - -func (v *BlizzardIntro) Unload() { - -} - -func (v *BlizzardIntro) Render(screen *ebiten.Image) { - -} - -func (v *BlizzardIntro) Update(tickTime float64) { + v.videoDecoder = d2video.CreateBinkDecoder(videoBytes) + return nil } diff --git a/d2game/d2gamescene/character_select.go b/d2game/d2gamescene/character_select.go index bfb6765e0..56c1a1574 100644 --- a/d2game/d2gamescene/character_select.go +++ b/d2game/d2gamescene/character_select.go @@ -47,109 +47,92 @@ type CharacterSelect struct { } func CreateCharacterSelect() *CharacterSelect { - result := &CharacterSelect{ - selectedCharacter: -1, - } - return result + return &CharacterSelect{selectedCharacter: -1} } -func (v *CharacterSelect) Load() []func() { +func (v *CharacterSelect) OnLoad() error { d2audio.PlayBGM(d2resource.BGMTitle) - return []func(){ - func() { - animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky) - v.background, _ = d2ui.LoadSprite(animation) - v.background.SetPosition(0, 0) - }, - func() { - v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15))) - v.newCharButton.SetPosition(33, 468) - v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() }) - d2ui.AddWidget(&v.newCharButton) - }, - func() { - v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15))) - v.convertCharButton.SetPosition(233, 468) - v.convertCharButton.SetEnabled(false) - d2ui.AddWidget(&v.convertCharButton) - }, - func() { - v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15))) - v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() }) - v.deleteCharButton.SetPosition(433, 468) - d2ui.AddWidget(&v.deleteCharButton) - }, - func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) - v.exitButton.SetPosition(33, 537) - v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) - d2ui.AddWidget(&v.exitButton) - }, - func() { - v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4231")) - v.deleteCharCancelButton.SetPosition(282, 308) - v.deleteCharCancelButton.SetVisible(false) - v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() }) - d2ui.AddWidget(&v.deleteCharCancelButton) - }, - func() { - v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4227")) - v.deleteCharOkButton.SetPosition(422, 308) - v.deleteCharOkButton.SetVisible(false) - v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() }) - d2ui.AddWidget(&v.deleteCharOkButton) - }, - func() { - v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) - v.okButton.SetPosition(625, 537) - v.okButton.OnActivated(func() { v.onOkButtonClicked() }) - d2ui.AddWidget(&v.okButton) - }, - func() { - v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) - v.d2HeroTitle.SetPosition(320, 23) - v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter - }, - func() { - v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - lines := dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#1878"), 29) - v.deleteCharConfirmLabel.SetText(strings.Join(lines, "\n")) - v.deleteCharConfirmLabel.Alignment = d2ui.LabelAlignCenter - v.deleteCharConfirmLabel.SetPosition(400, 185) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky) - v.selectionBox, _ = d2ui.LoadSprite(animation) - v.selectionBox.SetPosition(37, 86) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) - v.okCancelBox, _ = d2ui.LoadSprite(animation) - v.okCancelBox.SetPosition(270, 175) - }, - func() { - v.charScrollbar = d2ui.CreateScrollbar(586, 87, 369) - v.charScrollbar.OnActivated(func() { v.onScrollUpdate() }) - d2ui.AddWidget(&v.charScrollbar) - }, - func() { - for i := 0; i < 8; i++ { - xOffset := 115 - if i&1 > 0 { - xOffset = 385 - } - v.characterNameLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.characterNameLabel[i].Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} - v.characterNameLabel[i].SetPosition(xOffset, 100+((i/2)*95)) - v.characterStatsLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.characterStatsLabel[i].SetPosition(xOffset, 115+((i/2)*95)) - v.characterExpLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteStatic) - v.characterExpLabel[i].Color = color.RGBA{R: 24, G: 255, A: 255} - v.characterExpLabel[i].SetPosition(xOffset, 130+((i/2)*95)) - } - v.refreshGameStates() - }, + + animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky) + v.background, _ = d2ui.LoadSprite(animation) + v.background.SetPosition(0, 0) + + v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15))) + v.newCharButton.SetPosition(33, 468) + v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() }) + d2ui.AddWidget(&v.newCharButton) + + v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15))) + v.convertCharButton.SetPosition(233, 468) + v.convertCharButton.SetEnabled(false) + d2ui.AddWidget(&v.convertCharButton) + + v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15))) + v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() }) + v.deleteCharButton.SetPosition(433, 468) + d2ui.AddWidget(&v.deleteCharButton) + + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 537) + v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) + d2ui.AddWidget(&v.exitButton) + + v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4231")) + v.deleteCharCancelButton.SetPosition(282, 308) + v.deleteCharCancelButton.SetVisible(false) + v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() }) + d2ui.AddWidget(&v.deleteCharCancelButton) + + v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#4227")) + v.deleteCharOkButton.SetPosition(422, 308) + v.deleteCharOkButton.SetVisible(false) + v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() }) + d2ui.AddWidget(&v.deleteCharOkButton) + + v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) + v.okButton.SetPosition(625, 537) + v.okButton.OnActivated(func() { v.onOkButtonClicked() }) + d2ui.AddWidget(&v.okButton) + + v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) + v.d2HeroTitle.SetPosition(320, 23) + v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter + + v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + lines := dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#1878"), 29) + v.deleteCharConfirmLabel.SetText(strings.Join(lines, "\n")) + v.deleteCharConfirmLabel.Alignment = d2ui.LabelAlignCenter + v.deleteCharConfirmLabel.SetPosition(400, 185) + + animation, _ = d2asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky) + v.selectionBox, _ = d2ui.LoadSprite(animation) + v.selectionBox.SetPosition(37, 86) + + animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar) + v.okCancelBox, _ = d2ui.LoadSprite(animation) + v.okCancelBox.SetPosition(270, 175) + + v.charScrollbar = d2ui.CreateScrollbar(586, 87, 369) + v.charScrollbar.OnActivated(func() { v.onScrollUpdate() }) + d2ui.AddWidget(&v.charScrollbar) + + for i := 0; i < 8; i++ { + xOffset := 115 + if i&1 > 0 { + xOffset = 385 + } + v.characterNameLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.characterNameLabel[i].Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} + v.characterNameLabel[i].SetPosition(xOffset, 100+((i/2)*95)) + v.characterStatsLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.characterStatsLabel[i].SetPosition(xOffset, 115+((i/2)*95)) + v.characterExpLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteStatic) + v.characterExpLabel[i].Color = color.RGBA{R: 24, G: 255, A: 255} + v.characterExpLabel[i].SetPosition(xOffset, 130+((i/2)*95)) } + v.refreshGameStates() + + return nil } func (v *CharacterSelect) onScrollUpdate() { @@ -192,10 +175,7 @@ func (v *CharacterSelect) onExitButtonClicked() { d2scene.SetNextScene(mainMenu) } -func (v *CharacterSelect) Unload() { -} - -func (v *CharacterSelect) Render(screen d2render.Surface) { +func (v *CharacterSelect) Render(screen d2render.Surface) error { v.background.RenderSegmented(screen, 4, 3, 0) v.d2HeroTitle.Render(screen) actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) @@ -219,6 +199,8 @@ func (v *CharacterSelect) Render(screen d2render.Surface) { v.okCancelBox.RenderSegmented(screen, 2, 1, 0) v.deleteCharConfirmLabel.Render(screen) } + + return nil } func (v *CharacterSelect) moveSelectionBox() { @@ -233,7 +215,7 @@ func (v *CharacterSelect) moveSelectionBox() { v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName) } -func (v *CharacterSelect) Advance(tickTime float64) { +func (v *CharacterSelect) Advance(tickTime float64) error { if !v.showDeleteConfirmation { if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if !v.mouseButtonPressed { @@ -264,6 +246,8 @@ func (v *CharacterSelect) Advance(tickTime float64) { hero.AnimatedEntity.Advance(tickTime) } } + + return nil } func (v *CharacterSelect) onDeleteCharButtonClicked() { diff --git a/d2game/d2gamescene/credits.go b/d2game/d2gamescene/credits.go index edcb4d20a..2b7bd38d3 100644 --- a/d2game/d2gamescene/credits.go +++ b/d2game/d2gamescene/credits.go @@ -8,16 +8,13 @@ import ( "path" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2common" + dh "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - - "github.com/OpenDiablo2/OpenDiablo2/d2common" - - dh "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) @@ -56,7 +53,7 @@ func (v *Credits) LoadContributors() []string { file, err := os.Open(path.Join("./", "CONTRIBUTORS")) if err != nil || file == nil { log.Print("CONTRIBUTORS file is missing") - return []string{ "MISSING CONTRIBUTOR FILES!" } + return []string{"MISSING CONTRIBUTOR FILES!"} } defer file.Close() @@ -68,41 +65,31 @@ func (v *Credits) LoadContributors() []string { } // Load is called to load the resources for the credits scene -func (v *Credits) Load() []func() { - return []func(){ - func() { - animation, _ := d2asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) - v.creditsBackground, _ = d2ui.LoadSprite(animation) - v.creditsBackground.SetPosition(0, 0) - }, - func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) - v.exitButton.SetPosition(33, 543) - v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) - d2ui.AddWidget(&v.exitButton) - }, - func() { - fileData, err := d2asset.LoadFile(d2resource.CreditsText) - if err != nil { - panic(err) - } - creditData, _ := dh.Utf16BytesToString(fileData[2:]) - v.creditsText = strings.Split(creditData, "\r\n") - for i := range v.creditsText { - v.creditsText[i] = strings.Trim(v.creditsText[i], " ") - } - v.creditsText = append(v.LoadContributors(), v.creditsText...) - }, +func (v *Credits) OnLoad() error { + animation, _ := d2asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky) + v.creditsBackground, _ = d2ui.LoadSprite(animation) + v.creditsBackground.SetPosition(0, 0) + + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 543) + v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) + d2ui.AddWidget(&v.exitButton) + + fileData, err := d2asset.LoadFile(d2resource.CreditsText) + if err != nil { + return err } -} - -// Unload unloads the data for the credits scene -func (v *Credits) Unload() { - + creditData, _ := dh.Utf16BytesToString(fileData[2:]) + v.creditsText = strings.Split(creditData, "\r\n") + for i := range v.creditsText { + v.creditsText[i] = strings.Trim(v.creditsText[i], " ") + } + v.creditsText = append(v.LoadContributors(), v.creditsText...) + return nil } // Render renders the credits scene -func (v *Credits) Render(screen d2render.Surface) { +func (v *Credits) Render(screen d2render.Surface) error { v.creditsBackground.RenderSegmented(screen, 4, 3, 0) for _, label := range v.labels { if label.Available { @@ -110,12 +97,14 @@ func (v *Credits) Render(screen d2render.Surface) { } label.Label.Render(screen) } + + return nil } const secondsPerCycle = float64(0.02) // Update runs the update logic on the credits scene -func (v *Credits) Advance(tickTime float64) { +func (v *Credits) Advance(tickTime float64) error { v.cycleTime += tickTime for v.cycleTime >= secondsPerCycle { v.cycleTime -= secondsPerCycle @@ -135,6 +124,8 @@ func (v *Credits) Advance(tickTime float64) { label.Label.Y-- } } + + return nil } func (v *Credits) onExitButtonClicked() { diff --git a/d2game/d2gamescene/game.go b/d2game/d2gamescene/game.go index 3f0382e29..a0bc6c29f 100644 --- a/d2game/d2gamescene/game.go +++ b/d2game/d2gamescene/game.go @@ -12,7 +12,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" - "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" ) @@ -26,72 +25,64 @@ type Game struct { gameControls *d2player.GameControls } -func CreateGame( - gameState *d2gamestate.GameState, -) *Game { - result := &Game{ - gameState: gameState, - } - return result +func CreateGame(gameState *d2gamestate.GameState) *Game { + return &Game{gameState: gameState} } -func (v *Game) Load() []func() { - return []func(){ - func() { - animation, _ := d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky) - v.pentSpinLeft, _ = d2ui.LoadSprite(animation) - v.pentSpinLeft.PlayBackward() - v.pentSpinLeft.SetPlayLengthMs(475) - v.pentSpinLeft.SetPosition(100, 300) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky) - v.pentSpinRight, _ = d2ui.LoadSprite(animation) - v.pentSpinRight.PlayForward() - v.pentSpinRight.SetPlayLengthMs(475) - v.pentSpinRight.SetPosition(650, 300) - }, - func() { - v.testLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) - v.testLabel.Alignment = d2ui.LabelAlignCenter - v.testLabel.SetText("Soon :tm:") - v.testLabel.SetPosition(400, 250) - }, - func() { - v.mapEngine = d2map.CreateMapEngine(v.gameState) - v.mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0) - - startX, startY := v.mapEngine.GetStartPosition() - v.hero = d2map.CreateHero( - int32(startX*5)+3, - int32(startY*5)+3, - 0, - v.gameState.HeroType, - v.gameState.Equipment, - ) - v.mapEngine.AddEntity(v.hero) - }, - func() { - v.gameControls = d2player.NewGameControls(v.hero, v.mapEngine) - v.gameControls.Load() - d2input.BindHandler(v.gameControls) - }, - } +func (v *Game) OnLoad() error { + animation, _ := d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky) + v.pentSpinLeft, _ = d2ui.LoadSprite(animation) + v.pentSpinLeft.PlayBackward() + v.pentSpinLeft.SetPlayLengthMs(475) + v.pentSpinLeft.SetPosition(100, 300) + + animation, _ = d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky) + v.pentSpinRight, _ = d2ui.LoadSprite(animation) + v.pentSpinRight.PlayForward() + v.pentSpinRight.SetPlayLengthMs(475) + v.pentSpinRight.SetPosition(650, 300) + + v.testLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits) + v.testLabel.Alignment = d2ui.LabelAlignCenter + v.testLabel.SetText("Soon :tm:") + v.testLabel.SetPosition(400, 250) + + v.mapEngine = d2map.CreateMapEngine(v.gameState) + v.mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0) + + startX, startY := v.mapEngine.GetStartPosition() + v.hero = d2map.CreateHero( + int32(startX*5)+3, + int32(startY*5)+3, + 0, + v.gameState.HeroType, + v.gameState.Equipment, + ) + v.mapEngine.AddEntity(v.hero) + + v.gameControls = d2player.NewGameControls(v.hero, v.mapEngine) + v.gameControls.Load() + d2input.BindHandler(v.gameControls) + + return nil } -func (v *Game) Unload() { +func (v *Game) OnUnload() error { d2input.UnbindHandler(v.gameControls) + return nil } -func (v Game) Render(screen d2render.Surface) { +func (v *Game) Render(screen d2render.Surface) error { screen.Clear(color.Black) v.mapEngine.Render(screen) v.gameControls.Render(screen) + return nil } -func (v *Game) Advance(tickTime float64) { +func (v *Game) Advance(tickTime float64) error { v.mapEngine.Advance(tickTime) rx, ry := v.mapEngine.WorldToOrtho(v.hero.AnimatedEntity.LocationX/5, v.hero.AnimatedEntity.LocationY/5) v.mapEngine.MoveCameraTo(rx, ry) + return nil } diff --git a/d2game/d2gamescene/main_menu.go b/d2game/d2gamescene/main_menu.go index 2cd06ecf8..17ba890d2 100644 --- a/d2game/d2gamescene/main_menu.go +++ b/d2game/d2gamescene/main_menu.go @@ -8,18 +8,14 @@ import ( "os/exec" "runtime" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" + "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" - + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - - "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) @@ -49,128 +45,110 @@ type MainMenu struct { // CreateMainMenu creates an instance of MainMenu func CreateMainMenu() *MainMenu { - result := &MainMenu{ + return &MainMenu{ ShowTrademarkScreen: true, leftButtonHeld: true, } - return result } // Load is called to load the resources for the main menu -func (v *MainMenu) Load() []func() { +func (v *MainMenu) OnLoad() error { d2audio.PlayBGM(d2resource.BGMTitle) - return []func(){ - func() { - v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) - v.versionLabel.Alignment = d2ui.LabelAlignRight - v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch) - v.versionLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255} - v.versionLabel.SetPosition(795, -10) - }, - func() { - v.commitLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) - v.commitLabel.Alignment = d2ui.LabelAlignLeft - v.commitLabel.SetText(d2common.BuildInfo.Commit) - v.commitLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255} - v.commitLabel.SetPosition(2, 2) - }, - func() { - v.copyrightLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) - v.copyrightLabel.Alignment = d2ui.LabelAlignCenter - v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment") - v.copyrightLabel.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} - v.copyrightLabel.SetPosition(400, 500) - }, - func() { - v.copyrightLabel2 = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) - v.copyrightLabel2.Alignment = d2ui.LabelAlignCenter - v.copyrightLabel2.SetText(d2common.TranslateString("#1614")) - v.copyrightLabel2.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} - v.copyrightLabel2.SetPosition(400, 525) - }, - func() { - v.openDiabloLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) - v.openDiabloLabel.Alignment = d2ui.LabelAlignCenter - v.openDiabloLabel.SetText("OpenDiablo2 is neither developed by, nor endorsed by Blizzard or its parent company Activision") - v.openDiabloLabel.Color = color.RGBA{R: 255, G: 255, B: 140, A: 255} - v.openDiabloLabel.SetPosition(400, 580) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) - v.background, _ = d2ui.LoadSprite(animation) - v.background.SetPosition(0, 0) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) - v.trademarkBackground, _ = d2ui.LoadSprite(animation) - v.trademarkBackground.SetPosition(0, 0) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) - v.diabloLogoLeft, _ = d2ui.LoadSprite(animation) - v.diabloLogoLeft.SetBlend(true) - v.diabloLogoLeft.PlayForward() - v.diabloLogoLeft.SetPosition(400, 120) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) - v.diabloLogoRight, _ = d2ui.LoadSprite(animation) - v.diabloLogoRight.SetBlend(true) - v.diabloLogoRight.PlayForward() - v.diabloLogoRight.SetPosition(400, 120) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) - v.diabloLogoLeftBack, _ = d2ui.LoadSprite(animation) - v.diabloLogoLeftBack.SetPosition(400, 120) - }, - func() { - animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) - v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation) - v.diabloLogoRightBack.SetPosition(400, 120) - }, - func() { - v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625")) - v.exitDiabloButton.SetPosition(264, 535) - v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen) - v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) - d2ui.AddWidget(&v.exitDiabloButton) - }, - func() { - v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627")) - v.creditsButton.SetPosition(264, 505) - v.creditsButton.SetVisible(!v.ShowTrademarkScreen) - v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() }) - d2ui.AddWidget(&v.creditsButton) - }, - func() { - v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639")) - v.cinematicsButton.SetPosition(401, 505) - v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen) - d2ui.AddWidget(&v.cinematicsButton) - }, - func() { - v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620")) - v.singlePlayerButton.SetPosition(264, 290) - v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen) - v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) - d2ui.AddWidget(&v.singlePlayerButton) - }, - func() { - v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE") - v.githubButton.SetPosition(264, 330) - v.githubButton.SetVisible(!v.ShowTrademarkScreen) - v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) - d2ui.AddWidget(&v.githubButton) - }, - func() { - v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST") - v.mapTestButton.SetPosition(264, 450) - v.mapTestButton.SetVisible(!v.ShowTrademarkScreen) - v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) - d2ui.AddWidget(&v.mapTestButton) - }, - } + + v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) + v.versionLabel.Alignment = d2ui.LabelAlignRight + v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch) + v.versionLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255} + v.versionLabel.SetPosition(795, -10) + + v.commitLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) + v.commitLabel.Alignment = d2ui.LabelAlignLeft + v.commitLabel.SetText(d2common.BuildInfo.Commit) + v.commitLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255} + v.commitLabel.SetPosition(2, 2) + + v.copyrightLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) + v.copyrightLabel.Alignment = d2ui.LabelAlignCenter + v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment") + v.copyrightLabel.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} + v.copyrightLabel.SetPosition(400, 500) + + v.copyrightLabel2 = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) + v.copyrightLabel2.Alignment = d2ui.LabelAlignCenter + v.copyrightLabel2.SetText(d2common.TranslateString("#1614")) + v.copyrightLabel2.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} + v.copyrightLabel2.SetPosition(400, 525) + + v.openDiabloLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic) + v.openDiabloLabel.Alignment = d2ui.LabelAlignCenter + v.openDiabloLabel.SetText("OpenDiablo2 is neither developed by, nor endorsed by Blizzard or its parent company Activision") + v.openDiabloLabel.Color = color.RGBA{R: 255, G: 255, B: 140, A: 255} + v.openDiabloLabel.SetPosition(400, 580) + + animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) + v.background, _ = d2ui.LoadSprite(animation) + v.background.SetPosition(0, 0) + + animation, _ = d2asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) + v.trademarkBackground, _ = d2ui.LoadSprite(animation) + v.trademarkBackground.SetPosition(0, 0) + + animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) + v.diabloLogoLeft, _ = d2ui.LoadSprite(animation) + v.diabloLogoLeft.SetBlend(true) + v.diabloLogoLeft.PlayForward() + v.diabloLogoLeft.SetPosition(400, 120) + + animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits) + v.diabloLogoRight, _ = d2ui.LoadSprite(animation) + v.diabloLogoRight.SetBlend(true) + v.diabloLogoRight.PlayForward() + v.diabloLogoRight.SetPosition(400, 120) + + animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits) + v.diabloLogoLeftBack, _ = d2ui.LoadSprite(animation) + v.diabloLogoLeftBack.SetPosition(400, 120) + + animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) + v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation) + v.diabloLogoRightBack.SetPosition(400, 120) + + v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625")) + v.exitDiabloButton.SetPosition(264, 535) + v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen) + v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) + d2ui.AddWidget(&v.exitDiabloButton) + + v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627")) + v.creditsButton.SetPosition(264, 505) + v.creditsButton.SetVisible(!v.ShowTrademarkScreen) + v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() }) + d2ui.AddWidget(&v.creditsButton) + + v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639")) + v.cinematicsButton.SetPosition(401, 505) + v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen) + d2ui.AddWidget(&v.cinematicsButton) + + v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620")) + v.singlePlayerButton.SetPosition(264, 290) + v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen) + v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) + d2ui.AddWidget(&v.singlePlayerButton) + + v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE") + v.githubButton.SetPosition(264, 330) + v.githubButton.SetVisible(!v.ShowTrademarkScreen) + v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) + d2ui.AddWidget(&v.githubButton) + + v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST") + v.mapTestButton.SetPosition(264, 450) + v.mapTestButton.SetVisible(!v.ShowTrademarkScreen) + v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) + + d2ui.AddWidget(&v.mapTestButton) + return nil } func (v *MainMenu) onMapTestClicked() { @@ -217,13 +195,8 @@ func (v *MainMenu) onCreditsButtonClicked() { d2scene.SetNextScene(CreateCredits()) } -// Unload unloads the data for the main menu -func (v *MainMenu) Unload() { - -} - // Render renders the main menu -func (v *MainMenu) Render(screen d2render.Surface) { +func (v *MainMenu) Render(screen d2render.Surface) error { if v.ShowTrademarkScreen { v.trademarkBackground.RenderSegmented(screen, 4, 3, 0) } else { @@ -242,10 +215,12 @@ func (v *MainMenu) Render(screen d2render.Surface) { v.versionLabel.Render(screen) v.commitLabel.Render(screen) } + + return nil } // Update runs the update logic on the main menu -func (v *MainMenu) Advance(tickTime float64) { +func (v *MainMenu) Advance(tickTime float64) error { v.diabloLogoLeftBack.Advance(tickTime) v.diabloLogoRightBack.Advance(tickTime) v.diabloLogoLeft.Advance(tickTime) @@ -254,7 +229,7 @@ func (v *MainMenu) Advance(tickTime float64) { if v.ShowTrademarkScreen { if d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) { if v.leftButtonHeld { - return + return nil } d2ui.WaitForMouseRelease() v.leftButtonHeld = true @@ -268,6 +243,7 @@ func (v *MainMenu) Advance(tickTime float64) { } else { v.leftButtonHeld = false } - return } + + return nil } diff --git a/d2game/d2gamescene/map_engine_testing.go b/d2game/d2gamescene/map_engine_testing.go index dff220ccd..0493b17e6 100644 --- a/d2game/d2gamescene/map_engine_testing.go +++ b/d2game/d2gamescene/map_engine_testing.go @@ -4,20 +4,14 @@ import ( "math" "os" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" ) type RegionSpec struct { @@ -140,23 +134,22 @@ func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) { met.mapEngine.MoveCameraTo(met.mapEngine.WorldToOrtho(met.mapEngine.GetCenterPosition())) } -func (met *MapEngineTest) Load() []func() { +func (met *MapEngineTest) OnLoad() error { // TODO: Game seed comes from the game state object d2input.BindHandler(met) - d2audio.PlayBGM("") - return []func(){ - func() { - met.mapEngine = d2map.CreateMapEngine(met.gameState) - met.LoadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) - }, - } + + met.mapEngine = d2map.CreateMapEngine(met.gameState) + met.LoadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) + + return nil } -func (met *MapEngineTest) Unload() { +func (met *MapEngineTest) OnUnload() error { d2input.UnbindHandler(met) + return nil } -func (met *MapEngineTest) Render(screen d2render.Surface) { +func (met *MapEngineTest) Render(screen d2render.Surface) error { met.mapEngine.Render(screen) screenX, screenY, _ := d2render.GetCursorPos() @@ -166,7 +159,7 @@ func (met *MapEngineTest) Render(screen d2render.Surface) { curRegion := met.mapEngine.GetRegionAtTile(int(worldX), int(worldY)) if curRegion == nil { - return + return nil } tileRect := curRegion.GetTileRect() @@ -268,10 +261,13 @@ func (met *MapEngineTest) Render(screen d2render.Surface) { } screen.PopN(popN) } + + return nil } -func (met *MapEngineTest) Advance(tickTime float64) { +func (met *MapEngineTest) Advance(tickTime float64) error { met.mapEngine.Advance(tickTime) + return nil } func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool { diff --git a/d2game/d2gamescene/select_hero_class.go b/d2game/d2gamescene/select_hero_class.go index c2ffd7e71..068305873 100644 --- a/d2game/d2gamescene/select_hero_class.go +++ b/d2game/d2gamescene/select_hero_class.go @@ -4,21 +4,16 @@ import ( "image" "image/color" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common" - dh "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) @@ -76,370 +71,349 @@ func CreateSelectHeroClass() *SelectHeroClass { return result } -func (v *SelectHeroClass) Load() []func() { +func (v *SelectHeroClass) OnLoad() error { d2audio.PlayBGM(d2resource.BGMTitle) - return []func(){ - func() { - v.bgImage = loadSprite(d2resource.CharacterSelectBackground, d2resource.PaletteFechar) - v.bgImage.SetPosition(0, 0) - }, - func() { - v.headingLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) - fontWidth, _ := v.headingLabel.GetSize() - v.headingLabel.SetPosition(400-fontWidth/2, 17) - v.headingLabel.SetText("Select Hero Class") - v.headingLabel.Alignment = d2ui.LabelAlignCenter - }, - func() { - v.heroClassLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) - v.heroClassLabel.Alignment = d2ui.LabelAlignCenter - v.heroClassLabel.SetPosition(400, 65) - }, - func() { - v.heroDesc1Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.heroDesc1Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc1Label.SetPosition(400, 100) - }, - func() { - v.heroDesc2Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.heroDesc2Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc2Label.SetPosition(400, 115) - }, - func() { - v.heroDesc3Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.heroDesc3Label.Alignment = d2ui.LabelAlignCenter - v.heroDesc3Label.SetPosition(400, 130) - }, - func() { - v.campfire = loadSprite(d2resource.CharacterSelectCampfire, d2resource.PaletteFechar) - v.campfire.SetPosition(380, 335) - v.campfire.PlayForward() - v.campfire.SetBlend(true) - }, - func() { - v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) - v.exitButton.SetPosition(33, 537) - v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) - d2ui.AddWidget(&v.exitButton) - }, - func() { - v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) - v.okButton.SetPosition(630, 537) - v.okButton.OnActivated(func() { v.onOkButtonClicked() }) - v.okButton.SetVisible(false) - v.okButton.SetEnabled(false) - d2ui.AddWidget(&v.okButton) - }, - func() { - v.heroNameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.heroNameLabel.Alignment = d2ui.LabelAlignLeft - v.heroNameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} - v.heroNameLabel.SetText(d2common.TranslateString("#1694")) - v.heroNameLabel.SetPosition(321, 475) - }, - func() { - v.heroNameTextbox = d2ui.CreateTextbox() - v.heroNameTextbox.SetPosition(318, 493) - v.heroNameTextbox.SetVisible(false) - d2ui.AddWidget(&v.heroNameTextbox) - }, - func() { - v.expansionCheckbox = d2ui.CreateCheckbox(true) - v.expansionCheckbox.SetPosition(318, 526) - v.expansionCheckbox.SetVisible(false) - d2ui.AddWidget(&v.expansionCheckbox) - }, - func() { - v.expansionCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.expansionCharLabel.Alignment = d2ui.LabelAlignLeft - v.expansionCharLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} - v.expansionCharLabel.SetText(d2common.TranslateString("#803")) - v.expansionCharLabel.SetPosition(339, 526) - }, - func() { - v.hardcoreCheckbox = d2ui.CreateCheckbox(false) - v.hardcoreCheckbox.SetPosition(318, 548) - v.hardcoreCheckbox.SetVisible(false) - d2ui.AddWidget(&v.hardcoreCheckbox) - }, - func() { - v.hardcoreCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) - v.hardcoreCharLabel.Alignment = d2ui.LabelAlignLeft - v.hardcoreCharLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} - v.hardcoreCharLabel.SetText(d2common.TranslateString("#1696")) - v.hardcoreCharLabel.SetPosition(339, 548) - }, - func() { - v.heroRenderInfo[d2enum.HeroBarbarian] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectBarbarianUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectBarbarianUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectBarbarianForwardWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectBarbarianForwardWalkOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectBarbarianSelected, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelectBarbarianBackWalk, d2resource.PaletteFechar), - nil, - image.Rectangle{Min: image.Point{X: 364, Y: 201}, Max: image.Point{X: 90, Y: 170}}, - loadSoundEffect(d2resource.SFXBarbarianSelect), - loadSoundEffect(d2resource.SFXBarbarianDeselect), - } - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPosition(400, 330) - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLengthMs(1000) - v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroSorceress] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelecSorceressUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressForwardWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressForwardWalkOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressSelected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressSelectedOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressBackWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecSorceressBackWalkOverlay, d2resource.PaletteFechar), - image.Rectangle{Min: image.Point{X: 580, Y: 240}, Max: image.Point{X: 65, Y: 160}}, - loadSoundEffect(d2resource.SFXSorceressSelect), - loadSoundEffect(d2resource.SFXSorceressDeselect), - } - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLengthMs(2300) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLengthMs(2300) - v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPlayLengthMs(450) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPlayLengthMs(450) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLengthMs(1200) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPosition(626, 352) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLengthMs(1200) - v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroNecromancer] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectNecromancerUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectNecromancerUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerForwardWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerForwardWalkOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerSelected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerSelectedOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerBackWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecNecromancerBackWalkOverlay, d2resource.PaletteFechar), - image.Rectangle{Min: image.Point{X: 265, Y: 220}, Max: image.Point{X: 55, Y: 175}}, - loadSoundEffect(d2resource.SFXNecromancerSelect), - loadSoundEffect(d2resource.SFXNecromancerDeselect), - } - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPlayLengthMs(1200) - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPlayLengthMs(1200) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLengthMs(2000) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLengthMs(2000) - v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetBlend(true) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPosition(300, 335) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroPaladin] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectPaladinUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectPaladinUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecPaladinForwardWalk, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecPaladinForwardWalkOverlay, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecPaladinSelected, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelecPaladinBackWalk, d2resource.PaletteFechar), - nil, - image.Rectangle{Min: image.Point{X: 490, Y: 210}, Max: image.Point{X: 65, Y: 180}}, - loadSoundEffect(d2resource.SFXPaladinSelect), - loadSoundEffect(d2resource.SFXPaladinDeselect), - } - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLengthMs(3400) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLengthMs(3400) - v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPlayLengthMs(650) - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPosition(521, 338) - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLengthMs(1300) - v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroAmazon] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectAmazonUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectAmazonUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelecAmazonForwardWalk, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelecAmazonSelected, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelecAmazonBackWalk, d2resource.PaletteFechar), - nil, - image.Rectangle{Min: image.Point{X: 70, Y: 220}, Max: image.Point{X: 55, Y: 200}}, - loadSoundEffect(d2resource.SFXAmazonSelect), - loadSoundEffect(d2resource.SFXAmazonDeselect), - } - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPosition(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPosition(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPosition(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLengthMs(2200) - v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPosition(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPlayLengthMs(1350) - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPosition(100, 339) - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroAssassin] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectAssassinUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectAssassinUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectAssassinForwardWalk, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelectAssassinSelected, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelectAssassinBackWalk, d2resource.PaletteFechar), - nil, - image.Rectangle{Min: image.Point{X: 175, Y: 235}, Max: image.Point{X: 50, Y: 180}}, - loadSoundEffect(d2resource.SFXAssassinSelect), - loadSoundEffect(d2resource.SFXAssassinDeselect), - } - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPosition(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPosition(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPosition(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLengthMs(3800) - v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPosition(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPlayLengthMs(2500) - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPosition(231, 365) - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLoop(false) - }, - func() { - v.heroRenderInfo[d2enum.HeroDruid] = &HeroRenderInfo{ - d2enum.HeroStanceIdle, - loadSprite(d2resource.CharacterSelectDruidUnselected, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectDruidUnselectedH, d2resource.PaletteFechar), - loadSprite(d2resource.CharacterSelectDruidForwardWalk, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelectDruidSelected, d2resource.PaletteFechar), - nil, - loadSprite(d2resource.CharacterSelectDruidBackWalk, d2resource.PaletteFechar), - nil, - image.Rectangle{Min: image.Point{X: 680, Y: 220}, Max: image.Point{X: 70, Y: 195}}, - loadSoundEffect(d2resource.SFXDruidSelect), - loadSoundEffect(d2resource.SFXDruidDeselect), - } - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPosition(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPosition(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPosition(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLengthMs(4800) - v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLoop(false) - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPosition(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPosition(720, 370) - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.PlayForward() - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLengthMs(1500) - v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLoop(false) - }, + + v.bgImage = loadSprite(d2resource.CharacterSelectBackground, d2resource.PaletteFechar) + v.bgImage.SetPosition(0, 0) + + v.headingLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) + fontWidth, _ := v.headingLabel.GetSize() + v.headingLabel.SetPosition(400-fontWidth/2, 17) + v.headingLabel.SetText("Select Hero Class") + v.headingLabel.Alignment = d2ui.LabelAlignCenter + + v.heroClassLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits) + v.heroClassLabel.Alignment = d2ui.LabelAlignCenter + v.heroClassLabel.SetPosition(400, 65) + + v.heroDesc1Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.heroDesc1Label.Alignment = d2ui.LabelAlignCenter + v.heroDesc1Label.SetPosition(400, 100) + + v.heroDesc2Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.heroDesc2Label.Alignment = d2ui.LabelAlignCenter + v.heroDesc2Label.SetPosition(400, 115) + + v.heroDesc3Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.heroDesc3Label.Alignment = d2ui.LabelAlignCenter + v.heroDesc3Label.SetPosition(400, 130) + + v.campfire = loadSprite(d2resource.CharacterSelectCampfire, d2resource.PaletteFechar) + v.campfire.SetPosition(380, 335) + v.campfire.PlayForward() + v.campfire.SetBlend(true) + + v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#970")) + v.exitButton.SetPosition(33, 537) + v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) + d2ui.AddWidget(&v.exitButton) + + v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("#971")) + v.okButton.SetPosition(630, 537) + v.okButton.OnActivated(func() { v.onOkButtonClicked() }) + v.okButton.SetVisible(false) + v.okButton.SetEnabled(false) + d2ui.AddWidget(&v.okButton) + + v.heroNameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.heroNameLabel.Alignment = d2ui.LabelAlignLeft + v.heroNameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} + v.heroNameLabel.SetText(d2common.TranslateString("#1694")) + v.heroNameLabel.SetPosition(321, 475) + + v.heroNameTextbox = d2ui.CreateTextbox() + v.heroNameTextbox.SetPosition(318, 493) + v.heroNameTextbox.SetVisible(false) + d2ui.AddWidget(&v.heroNameTextbox) + + v.expansionCheckbox = d2ui.CreateCheckbox(true) + v.expansionCheckbox.SetPosition(318, 526) + v.expansionCheckbox.SetVisible(false) + d2ui.AddWidget(&v.expansionCheckbox) + + v.expansionCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.expansionCharLabel.Alignment = d2ui.LabelAlignLeft + v.expansionCharLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} + v.expansionCharLabel.SetText(d2common.TranslateString("#803")) + v.expansionCharLabel.SetPosition(339, 526) + + v.hardcoreCheckbox = d2ui.CreateCheckbox(false) + v.hardcoreCheckbox.SetPosition(318, 548) + v.hardcoreCheckbox.SetVisible(false) + d2ui.AddWidget(&v.hardcoreCheckbox) + + v.hardcoreCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits) + v.hardcoreCharLabel.Alignment = d2ui.LabelAlignLeft + v.hardcoreCharLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} + v.hardcoreCharLabel.SetText(d2common.TranslateString("#1696")) + v.hardcoreCharLabel.SetPosition(339, 548) + + v.heroRenderInfo[d2enum.HeroBarbarian] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectBarbarianUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectBarbarianUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectBarbarianForwardWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectBarbarianForwardWalkOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectBarbarianSelected, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelectBarbarianBackWalk, d2resource.PaletteFechar), + nil, + image.Rectangle{Min: image.Point{X: 364, Y: 201}, Max: image.Point{X: 90, Y: 170}}, + loadSoundEffect(d2resource.SFXBarbarianSelect), + loadSoundEffect(d2resource.SFXBarbarianDeselect), + } + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroBarbarian].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPosition(400, 330) + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLengthMs(1000) + v.heroRenderInfo[d2enum.HeroBarbarian].BackWalkSprite.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroSorceress] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelecSorceressUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressForwardWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressForwardWalkOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressSelected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressSelectedOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressBackWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecSorceressBackWalkOverlay, d2resource.PaletteFechar), + image.Rectangle{Min: image.Point{X: 580, Y: 240}, Max: image.Point{X: 65, Y: 160}}, + loadSoundEffect(d2resource.SFXSorceressSelect), + loadSoundEffect(d2resource.SFXSorceressDeselect), + } + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLengthMs(2300) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLengthMs(2300) + v.heroRenderInfo[d2enum.HeroSorceress].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSprite.SetPlayLengthMs(450) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].SelectedSpriteOverlay.SetPlayLengthMs(450) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPosition(626, 352) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroSorceress].BackWalkSpriteOverlay.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroNecromancer] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectNecromancerUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectNecromancerUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerForwardWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerForwardWalkOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerSelected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerSelectedOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerBackWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecNecromancerBackWalkOverlay, d2resource.PaletteFechar), + image.Rectangle{Min: image.Point{X: 265, Y: 220}, Max: image.Point{X: 55, Y: 175}}, + loadSoundEffect(d2resource.SFXNecromancerSelect), + loadSoundEffect(d2resource.SFXNecromancerDeselect), + } + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].IdleSelectedSprite.SetPlayLengthMs(1200) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLengthMs(2000) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLengthMs(2000) + v.heroRenderInfo[d2enum.HeroNecromancer].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].SelectedSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetBlend(true) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPosition(300, 335) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroNecromancer].BackWalkSpriteOverlay.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroPaladin] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectPaladinUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectPaladinUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecPaladinForwardWalk, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecPaladinForwardWalkOverlay, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecPaladinSelected, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelecPaladinBackWalk, d2resource.PaletteFechar), + nil, + image.Rectangle{Min: image.Point{X: 490, Y: 210}, Max: image.Point{X: 65, Y: 180}}, + loadSoundEffect(d2resource.SFXPaladinSelect), + loadSoundEffect(d2resource.SFXPaladinDeselect), } + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLengthMs(3400) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLengthMs(3400) + v.heroRenderInfo[d2enum.HeroPaladin].ForwardWalkSpriteOverlay.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].SelectedSprite.SetPlayLengthMs(650) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPosition(521, 338) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLengthMs(1300) + v.heroRenderInfo[d2enum.HeroPaladin].BackWalkSprite.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroAmazon] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectAmazonUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectAmazonUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelecAmazonForwardWalk, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelecAmazonSelected, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelecAmazonBackWalk, d2resource.PaletteFechar), + nil, + image.Rectangle{Min: image.Point{X: 70, Y: 220}, Max: image.Point{X: 55, Y: 200}}, + loadSoundEffect(d2resource.SFXAmazonSelect), + loadSoundEffect(d2resource.SFXAmazonDeselect), + } + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLengthMs(2200) + v.heroRenderInfo[d2enum.HeroAmazon].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].SelectedSprite.SetPlayLengthMs(1350) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPosition(100, 339) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroAmazon].BackWalkSprite.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroAssassin] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectAssassinUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectAssassinUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectAssassinForwardWalk, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelectAssassinSelected, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelectAssassinBackWalk, d2resource.PaletteFechar), + nil, + image.Rectangle{Min: image.Point{X: 175, Y: 235}, Max: image.Point{X: 50, Y: 180}}, + loadSoundEffect(d2resource.SFXAssassinSelect), + loadSoundEffect(d2resource.SFXAssassinDeselect), + } + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].IdleSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].IdleSelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLengthMs(3800) + v.heroRenderInfo[d2enum.HeroAssassin].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].SelectedSprite.SetPlayLengthMs(2500) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPosition(231, 365) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroAssassin].BackWalkSprite.SetPlayLoop(false) + + v.heroRenderInfo[d2enum.HeroDruid] = &HeroRenderInfo{ + d2enum.HeroStanceIdle, + loadSprite(d2resource.CharacterSelectDruidUnselected, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectDruidUnselectedH, d2resource.PaletteFechar), + loadSprite(d2resource.CharacterSelectDruidForwardWalk, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelectDruidSelected, d2resource.PaletteFechar), + nil, + loadSprite(d2resource.CharacterSelectDruidBackWalk, d2resource.PaletteFechar), + nil, + image.Rectangle{Min: image.Point{X: 680, Y: 220}, Max: image.Point{X: 70, Y: 195}}, + loadSoundEffect(d2resource.SFXDruidSelect), + loadSoundEffect(d2resource.SFXDruidDeselect), + } + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].IdleSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].IdleSelectedSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLengthMs(4800) + v.heroRenderInfo[d2enum.HeroDruid].ForwardWalkSprite.SetPlayLoop(false) + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].SelectedSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPosition(720, 370) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.PlayForward() + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLengthMs(1500) + v.heroRenderInfo[d2enum.HeroDruid].BackWalkSprite.SetPlayLoop(false) + + return nil } -func (v *SelectHeroClass) Unload() { +func (v *SelectHeroClass) OnUnload() error { for i := range v.heroRenderInfo { v.heroRenderInfo[i].SelectSfx.Stop() v.heroRenderInfo[i].DeselectSfx.Stop() } v.heroRenderInfo = nil + return nil } func (v SelectHeroClass) onExitButtonClicked() { @@ -451,7 +425,7 @@ func (v SelectHeroClass) onOkButtonClicked() { d2scene.SetNextScene(CreateGame(gameState)) } -func (v *SelectHeroClass) Render(screen d2render.Surface) { +func (v *SelectHeroClass) Render(screen d2render.Surface) error { v.bgImage.RenderSegmented(screen, 4, 3, 0) v.headingLabel.Render(screen) if v.selectedHero != d2enum.HeroNone { @@ -476,9 +450,11 @@ func (v *SelectHeroClass) Render(screen d2render.Surface) { v.expansionCharLabel.Render(screen) v.hardcoreCharLabel.Render(screen) } + + return nil } -func (v *SelectHeroClass) Advance(tickTime float64) { +func (v *SelectHeroClass) Advance(tickTime float64) error { canSelect := true v.campfire.Advance(tickTime) for _, info := range v.heroRenderInfo { @@ -499,6 +475,7 @@ func (v *SelectHeroClass) Advance(tickTime float64) { } v.heroNameTextbox.Update() v.okButton.SetEnabled(len(v.heroNameTextbox.GetText()) >= 2) + return nil } func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect bool) { diff --git a/main.go b/main.go index cf94cafa8..0d03d1bab 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,12 @@ package main import ( + "errors" + "fmt" "log" + "os" + "runtime" + "strconv" "gopkg.in/alecthomas/kingpin.v2" @@ -9,7 +14,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2gamescene" @@ -17,14 +21,13 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" ebiten2 "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio/ebiten" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" - - "github.com/OpenDiablo2/OpenDiablo2/d2game" ) // GitBranch is set by the CI build process to the name of the branch @@ -33,13 +36,19 @@ var GitBranch string // GitCommit is set by the CI build process to the commit hash var GitCommit string +var singleton struct { + lastTime float64 + showFPS bool + timeScale float64 +} + func main() { if len(GitBranch) == 0 { - d2common.SetBuildInfo("Local Build", "") - } else { - d2common.SetBuildInfo(GitBranch, GitCommit) + GitBranch = "Local Build" } + d2common.SetBuildInfo(GitBranch, GitCommit) + region := kingpin.Arg("region", "Region type id").Int() preset := kingpin.Arg("preset", "Level preset").Int() kingpin.Parse() @@ -47,7 +56,7 @@ func main() { log.SetFlags(log.Lshortfile) log.Println("OpenDiablo2 - Open source Diablo 2 engine") - if err := initializeEverything(); err != nil { + if err := initialize(); err != nil { log.Fatal(err) } @@ -57,28 +66,222 @@ func main() { d2scene.SetNextScene(d2gamescene.CreateMapEngineTest(*region, *preset)) } - if err := d2game.Run(GitBranch); err != nil { + windowTitle := fmt.Sprintf("OpenDiablo2 (%s)", GitBranch) + if err := d2render.Run(update, 800, 600, windowTitle); err != nil { log.Fatal(err) } } -func loadStrings() error { - tablePaths := []string{ - d2resource.PatchStringTable, - d2resource.ExpansionStringTable, - d2resource.StringTable, +func initialize() error { + singleton.timeScale = 1.0 + singleton.lastTime = d2common.Now() + + if err := d2config.Initialize(); err != nil { + return err } - for _, tablePath := range tablePaths { - data, err := d2asset.LoadFile(tablePath) + config, err := d2config.Get() + if err != nil { + return err + } + d2resource.LanguageCode = config.Language + + renderer, err := ebiten.CreateRenderer() + if err != nil { + return err + } + + if err := d2render.Initialize(renderer); err != nil { + return err + } + + if err := d2render.SetWindowIcon("d2logo.png"); err != nil { + return err + } + + if err := d2asset.Initialize(); err != nil { + return err + } + + if err := d2input.Initialize(); err != nil { + return err + } + + if err := d2gui.Initialize(); err != nil { + return err + } + + if err := d2term.Initialize(); err != nil { + return err + } + + d2term.BindLogger() + d2term.BindAction("fullscreen", "toggles fullscreen", func() { + fullscreen, err := d2render.IsFullScreen() + if err == nil { + fullscreen = !fullscreen + d2render.SetFullScreen(fullscreen) + d2term.OutputInfo("fullscreen is now: %v", fullscreen) + } else { + d2term.OutputError(err.Error()) + } + }) + d2term.BindAction("vsync", "toggles vsync", func() { + vsync, err := d2render.GetVSyncEnabled() + if err == nil { + vsync = !vsync + d2render.SetVSyncEnabled(vsync) + d2term.OutputInfo("vsync is now: %v", vsync) + } else { + d2term.OutputError(err.Error()) + } + }) + d2term.BindAction("fps", "toggle fps counter", func() { + singleton.showFPS = !singleton.showFPS + d2term.OutputInfo("fps counter is now: %v", singleton.showFPS) + }) + d2term.BindAction("timescale", "set scalar for elapsed time", func(timeScale float64) { + if timeScale <= 0 { + d2term.OutputError("invalid time scale value") + } else { + d2term.OutputInfo("timescale changed from %f to %f", singleton.timeScale, timeScale) + singleton.timeScale = timeScale + } + }) + d2term.BindAction("quit", "exits the game", func() { + os.Exit(0) + }) + + audioProvider, err := ebiten2.CreateAudio() + if err != nil { + return err + } + + if err := d2audio.Initialize(audioProvider); err != nil { + return err + } + + if err := d2audio.SetVolumes(config.BgmVolume, config.SfxVolume); err != nil { + return err + } + + if err := loadDataDict(); err != nil { + return err + } + + if err := loadPalettes(); err != nil { + return err + } + + if err := loadStrings(); err != nil { + return err + } + + d2ui.Initialize() + + return nil +} + +func update(target d2render.Surface) error { + currentTime := d2common.Now() + elapsedTime := (currentTime - singleton.lastTime) * singleton.timeScale + singleton.lastTime = currentTime + + if err := advance(elapsedTime); err != nil { + return err + } + + if err := render(target); err != nil { + return err + } + + if target.GetDepth() > 0 { + return errors.New("detected surface stack leak") + } + + return nil +} + +func advance(elapsed float64) error { + if err := d2scene.Advance(elapsed); err != nil { + return err + } + + d2ui.Advance(elapsed) + + if err := d2input.Advance(elapsed); err != nil { + return err + } + + if err := d2gui.Advance(elapsed); err != nil { + return err + } + + if err := d2term.Advance(elapsed); err != nil { + return err + } + + return nil +} + +func render(target d2render.Surface) error { + if err := d2scene.Render(target); err != nil { + return err + } + + d2ui.Render(target) + + if err := d2gui.Render(target); err != nil { + return err + } + + if err := d2term.Render(target); err != nil { + return err + } + + if err := renderDebug(target); err != nil { + return err + } + + return nil +} + +func renderDebug(target d2render.Surface) error { + if singleton.showFPS { + vsyncEnabled, err := d2render.GetVSyncEnabled() if err != nil { return err } - d2common.LoadDictionary(data) + fps, err := d2render.CurrentFPS() + if err != nil { + return err + } + + target.PushTranslation(5, 565) + target.DrawText("vsync:" + strconv.FormatBool(vsyncEnabled) + "\nFPS:" + strconv.Itoa(int(fps))) + target.Pop() + + cx, cy, err := d2render.GetCursorPos() + if err != nil { + return err + } + + var m runtime.MemStats + runtime.ReadMemStats(&m) + target.PushTranslation(680, 0) + target.DrawText("Alloc " + strconv.FormatInt(int64(m.Alloc)/1024/1024, 10)) + target.PushTranslation(0, 16) + target.DrawText("Pause " + strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10)) + target.PushTranslation(0, 16) + target.DrawText("HeapSys " + strconv.FormatInt(int64(m.HeapSys/1024/1024), 10)) + target.PushTranslation(0, 16) + target.DrawText("NumGC " + strconv.FormatInt(int64(m.NumGC), 10)) + target.PushTranslation(0, 16) + target.DrawText("Coords " + strconv.FormatInt(int64(cx), 10) + "," + strconv.FormatInt(int64(cy), 10)) + target.PopN(5) } - log.Printf("Loaded %d entries from the string table", d2common.GetDictionaryEntryCount()) return nil } @@ -149,110 +352,21 @@ func loadDataDict() error { return nil } -func loadLoadingSprite() (*d2ui.Sprite, error) { - animation, err := d2asset.LoadAnimation(d2resource.LoadingScreen, d2resource.PaletteLoading) - if err != nil { - return nil, err - } - - loadingSprite, err := d2ui.LoadSprite(animation) - if err != nil { - return nil, err - } - - loadingSpriteSizeX, loadingSpriteSizeY := loadingSprite.GetCurrentFrameSize() - loadingSprite.SetPosition(400-(loadingSpriteSizeX/2), 300+(loadingSpriteSizeY/2)) - return loadingSprite, nil -} - -func loadCursorSprite() (*d2ui.Sprite, error) { - animation, err := d2asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits) - if err != nil { - return nil, err - } - - cursorSprite, err := d2ui.LoadSprite(animation) - if err != nil { - return nil, err - } - - return cursorSprite, nil -} - -func initializeEverything() error { - if err := d2config.Initialize(); err != nil { - return err - } - - config, err := d2config.Get() - if err != nil { - return err - } - d2resource.LanguageCode = config.Language - - renderer, err := ebiten.CreateRenderer() - if err != nil { - return err - } - - if err := d2render.Initialize(renderer); err != nil { - return err - } - - if err := d2render.SetWindowIcon("d2logo.png"); err != nil { - return err - } - - if err := d2input.Initialize(); err != nil { - return err - } - - if err := d2term.Initialize(); err != nil { - return err - } - d2term.BindLogger() - - d2mpq.InitializeCryptoBuffer() - if err := d2asset.Initialize(); err != nil { - return err - } - - audioProvider, err := ebiten2.CreateAudio() - if err != nil { - return err - } - - if err := d2audio.Initialize(audioProvider); err != nil { - return err - } - - if err := d2audio.SetVolumes(config.BgmVolume, config.SfxVolume); err != nil { - return err - } - - if err := loadDataDict(); err != nil { - return err - } - - if err := loadPalettes(); err != nil { - return err - } - - if err := loadStrings(); err != nil { - return err +func loadStrings() error { + tablePaths := []string{ + d2resource.PatchStringTable, + d2resource.ExpansionStringTable, + d2resource.StringTable, } - cursorSprite, err := loadCursorSprite() - if err != nil { - return err - } - d2ui.Initialize(cursorSprite) + for _, tablePath := range tablePaths { + data, err := d2asset.LoadFile(tablePath) + if err != nil { + return err + } - loadingSprite, err := loadLoadingSprite() - if err != nil { - return err + d2common.LoadDictionary(data) } - d2game.Initialize(loadingSprite) return nil }