1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-26 17:15:24 +00:00

Lint fixes for the d2game/d2gamescreen (#516)

* more lint fixes for the d2core/d2term

* lint fixes for the escape_menu.go

* fixed lint issues of credits screen

* more lint fixes for the d2gamescreen

* lint fixes for the main menu of d2game/d2gamescreen package

* lint fixes for the main menu and map engine testing of d2game/d2gamescreen package

* more lint fixes for the main menu of d2game/d2gamescreen package

* lint fixes for the character select screen of d2game/d2gamescreen package
This commit is contained in:
Gürkan Kaymak 2020-07-02 20:55:43 +03:00 committed by GitHub
parent 8e2c7d1f38
commit 9c2b1dccaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 636 additions and 392 deletions

View File

@ -1,3 +1,4 @@
// Package d2term provides a in-game terminal that allows executing custom commands for debugging
package d2term package d2term
import ( import (
@ -45,6 +46,11 @@ const (
termVisHiding termVisHiding
) )
const (
maxVisAnim = 1.0
minVisAnim = 0.0
)
type termHistoryEntry struct { type termHistoryEntry struct {
text string text string
category d2interface.TermCategory category d2interface.TermCategory
@ -79,13 +85,13 @@ type terminal struct {
func (t *terminal) Advance(elapsed float64) error { func (t *terminal) Advance(elapsed float64) error {
switch t.visState { switch t.visState {
case termVisShowing: case termVisShowing:
t.visAnim = math.Min(1.0, t.visAnim+elapsed/termAnimLength) t.visAnim = math.Min(maxVisAnim, t.visAnim+elapsed/termAnimLength)
if t.visAnim == 1.0 { if t.visAnim == maxVisAnim {
t.visState = termVisShown t.visState = termVisShown
} }
case termVisHiding: case termVisHiding:
t.visAnim = math.Max(0.0, t.visAnim-elapsed/termAnimLength) t.visAnim = math.Max(minVisAnim, t.visAnim-elapsed/termAnimLength)
if t.visAnim == 0.0 { if t.visAnim == minVisAnim {
t.visState = termVisHidden t.visState = termVisHidden
} }
} }
@ -122,12 +128,8 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool {
if t.outputIndex -= t.lineCount; t.outputIndex < 0 { if t.outputIndex -= t.lineCount; t.outputIndex < 0 {
t.outputIndex = 0 t.outputIndex = 0
} }
case d2input.KeyUp: case d2input.KeyUp, d2input.KeyDown:
t.handleKeyUp(event.KeyMod) t.handleControlKey(event.Key, event.KeyMod)
case d2input.KeyDown:
if event.KeyMod == d2input.KeyModControl {
t.lineCount = d2common.MinInt(t.lineCount+1, termRowCountMax)
}
case d2input.KeyEnter: case d2input.KeyEnter:
t.processCommand() t.processCommand()
case d2input.KeyBackspace: case d2input.KeyBackspace:
@ -166,15 +168,22 @@ func (t *terminal) processCommand() {
t.command = "" t.command = ""
} }
func (t *terminal) handleKeyUp(keyMod d2input.KeyMod) { func (t *terminal) handleControlKey(eventKey d2input.Key, keyMod d2input.KeyMod) {
if keyMod == d2input.KeyModControl { switch eventKey {
t.lineCount = d2common.MaxInt(0, t.lineCount-1) case d2input.KeyUp:
} else if len(t.commandHistory) > 0 { if keyMod == d2input.KeyModControl {
t.command = t.commandHistory[t.commandIndex] t.lineCount = d2common.MaxInt(0, t.lineCount-1)
if t.commandIndex == 0 { } else if len(t.commandHistory) > 0 {
t.commandIndex = len(t.commandHistory) - 1 t.command = t.commandHistory[t.commandIndex]
} else { if t.commandIndex == 0 {
t.commandIndex-- t.commandIndex = len(t.commandHistory) - 1
} else {
t.commandIndex--
}
}
case d2input.KeyDown:
if keyMod == d2input.KeyModControl {
t.lineCount = d2common.MinInt(t.lineCount+1, termRowCountMax)
} }
} }
} }
@ -276,45 +285,9 @@ func (t *terminal) Execute(command string) error {
return errors.New("action requires different argument count") return errors.New("action requires different argument count")
} }
var paramValues []reflect.Value paramValues, err := parseActionParams(actionType, actionParams)
if err != nil {
for i := 0; i < actionType.NumIn(); i++ { return err
actionParam := actionParams[i]
switch actionType.In(i).Kind() {
case reflect.String:
paramValues = append(paramValues, reflect.ValueOf(actionParam))
case reflect.Int:
value, err := strconv.ParseInt(actionParam, 10, 64)
if err != nil {
return err
}
paramValues = append(paramValues, reflect.ValueOf(int(value)))
case reflect.Uint:
value, err := strconv.ParseUint(actionParam, 10, 64)
if err != nil {
return err
}
paramValues = append(paramValues, reflect.ValueOf(uint(value)))
case reflect.Float64:
value, err := strconv.ParseFloat(actionParam, 64)
if err != nil {
return err
}
paramValues = append(paramValues, reflect.ValueOf(value))
case reflect.Bool:
value, err := strconv.ParseBool(actionParam)
if err != nil {
return err
}
paramValues = append(paramValues, reflect.ValueOf(value))
default:
return errors.New("action has unsupported arguments")
}
} }
actionValue := reflect.ValueOf(actionEntry.action) actionValue := reflect.ValueOf(actionEntry.action)
@ -331,6 +304,51 @@ func (t *terminal) Execute(command string) error {
return nil return nil
} }
func parseActionParams(actionType reflect.Type, actionParams []string) ([]reflect.Value, error) {
var paramValues []reflect.Value
for i := 0; i < actionType.NumIn(); i++ {
actionParam := actionParams[i]
switch actionType.In(i).Kind() {
case reflect.String:
paramValues = append(paramValues, reflect.ValueOf(actionParam))
case reflect.Int:
value, err := strconv.ParseInt(actionParam, 10, 64)
if err != nil {
return nil, err
}
paramValues = append(paramValues, reflect.ValueOf(int(value)))
case reflect.Uint:
value, err := strconv.ParseUint(actionParam, 10, 64)
if err != nil {
return nil, err
}
paramValues = append(paramValues, reflect.ValueOf(uint(value)))
case reflect.Float64:
value, err := strconv.ParseFloat(actionParam, 64)
if err != nil {
return nil, err
}
paramValues = append(paramValues, reflect.ValueOf(value))
case reflect.Bool:
value, err := strconv.ParseBool(actionParam)
if err != nil {
return nil, err
}
paramValues = append(paramValues, reflect.ValueOf(value))
default:
return nil, errors.New("action has unsupported arguments")
}
}
return paramValues, nil
}
func (t *terminal) OutputRaw(text string, category d2interface.TermCategory) { func (t *terminal) OutputRaw(text string, category d2interface.TermCategory) {
var line string var line string

View File

@ -1,6 +1,7 @@
package d2gamescreen package d2gamescreen
import ( import (
"fmt"
"image/color" "image/color"
"math" "math"
"os" "os"
@ -22,6 +23,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
) )
// CharacterSelect represents the character select screen
type CharacterSelect struct { type CharacterSelect struct {
background *d2ui.Sprite background *d2ui.Sprite
newCharButton d2ui.Button newCharButton d2ui.Button
@ -49,8 +51,12 @@ type CharacterSelect struct {
terminal d2interface.Terminal terminal d2interface.Terminal
} }
func CreateCharacterSelect(audioProvider d2interface.AudioProvider, // CreateCharacterSelect creates the character select screen and returns a pointer to it
connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string, term d2interface.Terminal) *CharacterSelect { func CreateCharacterSelect(
audioProvider d2interface.AudioProvider,
connectionType d2clientconnectiontype.ClientConnectionType,
connectionHost string, term d2interface.Terminal,
) *CharacterSelect {
return &CharacterSelect{ return &CharacterSelect{
selectedCharacter: -1, selectedCharacter: -1,
connectionType: connectionType, connectionType: connectionType,
@ -60,26 +66,89 @@ func CreateCharacterSelect(audioProvider d2interface.AudioProvider,
} }
} }
// OnLoad loads the resources for the Character Select screen
func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
v.audioProvider.PlayBGM(d2resource.BGMTitle) v.audioProvider.PlayBGM(d2resource.BGMTitle)
d2input.BindHandler(v)
if err := d2input.BindHandler(v); err != nil {
fmt.Println("failed to add Character Select screen as event handler")
}
loading.Progress(0.1) loading.Progress(0.1)
animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky) animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky)
v.background, _ = d2ui.LoadSprite(animation) v.background, _ = d2ui.LoadSprite(animation)
v.background.SetPosition(0, 0) v.background.SetPosition(0, 0)
v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("CREATE NEW CHARACTER", 15))) v.createButtons(loading)
v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
v.d2HeroTitle.SetPosition(320, 23)
v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter
loading.Progress(0.3)
v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
lines := d2common.SplitIntoLinesWithMaxWidth(
"Are you sure that you want to delete this character? Take note: this will delete all versions of this Character.",
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)
loading.Progress(0.5)
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()
}
func (v *CharacterSelect) createButtons(loading d2screen.LoadingState) {
v.newCharButton = d2ui.CreateButton(
d2ui.ButtonTypeTall,
d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("CREATE NEW CHARACTER", 15)),
)
v.newCharButton.SetPosition(33, 468) v.newCharButton.SetPosition(33, 468)
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() }) v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
d2ui.AddWidget(&v.newCharButton) d2ui.AddWidget(&v.newCharButton)
v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("CONVERT TO EXPANSION", 15))) v.convertCharButton = d2ui.CreateButton(
d2ui.ButtonTypeTall,
d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("CONVERT TO EXPANSION", 15)),
)
v.convertCharButton.SetPosition(233, 468) v.convertCharButton.SetPosition(233, 468)
v.convertCharButton.SetEnabled(false) v.convertCharButton.SetEnabled(false)
d2ui.AddWidget(&v.convertCharButton) d2ui.AddWidget(&v.convertCharButton)
v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("DELETE CHARACTER", 15))) v.deleteCharButton = d2ui.CreateButton(
d2ui.ButtonTypeTall,
d2common.CombineStrings(d2common.SplitIntoLinesWithMaxWidth("DELETE CHARACTER", 15)),
)
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() }) v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
v.deleteCharButton.SetPosition(433, 468) v.deleteCharButton.SetPosition(433, 468)
d2ui.AddWidget(&v.deleteCharButton) d2ui.AddWidget(&v.deleteCharButton)
@ -106,46 +175,6 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
v.okButton.SetPosition(625, 537) v.okButton.SetPosition(625, 537)
v.okButton.OnActivated(func() { v.onOkButtonClicked() }) v.okButton.OnActivated(func() { v.onOkButtonClicked() })
d2ui.AddWidget(&v.okButton) d2ui.AddWidget(&v.okButton)
v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
v.d2HeroTitle.SetPosition(320, 23)
v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter
loading.Progress(0.3)
v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
lines := d2common.SplitIntoLinesWithMaxWidth("Are you sure that you want to delete this character? Take note: this will delete all versions of this Character.", 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)
loading.Progress(0.5)
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()
} }
func (v *CharacterSelect) onScrollUpdate() { func (v *CharacterSelect) onScrollUpdate() {
@ -155,6 +184,7 @@ func (v *CharacterSelect) onScrollUpdate() {
func (v *CharacterSelect) updateCharacterBoxes() { func (v *CharacterSelect) updateCharacterBoxes() {
expText := "EXPANSION CHARACTER" expText := "EXPANSION CHARACTER"
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
idx := i + (v.charScrollbar.GetCurrentOffset() * 2) idx := i + (v.charScrollbar.GetCurrentOffset() * 2)
if idx >= len(v.gameStates) { if idx >= len(v.gameStates) {
@ -162,8 +192,10 @@ func (v *CharacterSelect) updateCharacterBoxes() {
v.characterStatsLabel[i].SetText("") v.characterStatsLabel[i].SetText("")
v.characterExpLabel[i].SetText("") v.characterExpLabel[i].SetText("")
v.characterImage[i] = nil v.characterImage[i] = nil
continue continue
} }
v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName) v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName)
v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String()) v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String())
v.characterExpLabel[i].SetText(expText) v.characterExpLabel[i].SetText(expText)
@ -182,22 +214,31 @@ func (v *CharacterSelect) onNewCharButtonClicked() {
func (v *CharacterSelect) onExitButtonClicked() { func (v *CharacterSelect) onExitButtonClicked() {
mainMenu := CreateMainMenu(v.audioProvider, v.terminal) mainMenu := CreateMainMenu(v.audioProvider, v.terminal)
mainMenu.SetScreenMode(ScreenModeMainMenu) mainMenu.setScreenMode(screenModeMainMenu)
d2screen.SetNextScreen(mainMenu) d2screen.SetNextScreen(mainMenu)
} }
// Render renders the Character Select screen
func (v *CharacterSelect) Render(screen d2interface.Surface) error { func (v *CharacterSelect) Render(screen d2interface.Surface) error {
v.background.RenderSegmented(screen, 4, 3, 0) if err := v.background.RenderSegmented(screen, 4, 3, 0); err != nil {
return err
}
v.d2HeroTitle.Render(screen) v.d2HeroTitle.Render(screen)
actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
if v.selectedCharacter > -1 && actualSelectionIndex >= 0 && actualSelectionIndex < 8 { if v.selectedCharacter > -1 && actualSelectionIndex >= 0 && actualSelectionIndex < 8 {
v.selectionBox.RenderSegmented(screen, 2, 1, 0) if err := v.selectionBox.RenderSegmented(screen, 2, 1, 0); err != nil {
return err
}
} }
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
idx := i + (v.charScrollbar.GetCurrentOffset() * 2) idx := i + (v.charScrollbar.GetCurrentOffset() * 2)
if idx >= len(v.gameStates) { if idx >= len(v.gameStates) {
continue continue
} }
v.characterNameLabel[i].Render(screen) v.characterNameLabel[i].Render(screen)
v.characterStatsLabel[i].Render(screen) v.characterStatsLabel[i].Render(screen)
v.characterExpLabel[i].Render(screen) v.characterExpLabel[i].Render(screen)
@ -205,9 +246,14 @@ func (v *CharacterSelect) Render(screen d2interface.Surface) error {
v.characterImage[i].Render(screen) v.characterImage[i].Render(screen)
screen.Pop() screen.Pop()
} }
if v.showDeleteConfirmation { if v.showDeleteConfirmation {
screen.DrawRect(800, 600, color.RGBA{A: 128}) screen.DrawRect(800, 600, color.RGBA{A: 128})
v.okCancelBox.RenderSegmented(screen, 2, 1, 0)
if err := v.okCancelBox.RenderSegmented(screen, 2, 1, 0); err != nil {
return err
}
v.deleteCharConfirmLabel.Render(screen) v.deleteCharConfirmLabel.Render(screen)
} }
@ -219,6 +265,7 @@ func (v *CharacterSelect) moveSelectionBox() {
v.d2HeroTitle.SetText("") v.d2HeroTitle.SetText("")
return return
} }
bw := 272 bw := 272
bh := 92 bh := 92
selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
@ -226,6 +273,7 @@ func (v *CharacterSelect) moveSelectionBox() {
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName) v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
} }
// OnMouseButtonDown is called when a mouse button is clicked
func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool { func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool {
if !v.showDeleteConfirmation { if !v.showDeleteConfirmation {
if event.Button == d2input.MouseButtonLeft { if event.Button == d2input.MouseButtonLeft {
@ -234,23 +282,29 @@ func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool {
bh := 92 bh := 92
localMouseX := mx - 37 localMouseX := mx - 37
localMouseY := my - 86 localMouseY := my - 86
if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 { if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 {
adjustY := localMouseY / bh adjustY := localMouseY / bh
selectedIndex := adjustY * 2 selectedIndex := adjustY * 2
if localMouseX > bw { if localMouseX > bw {
selectedIndex += 1 selectedIndex++
} }
if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) { if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) {
v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex
v.moveSelectionBox() v.moveSelectionBox()
} }
} }
return true return true
} }
} }
return false return false
} }
// Advance runs the update logic on the Character Select screen
func (v *CharacterSelect) Advance(tickTime float64) error { func (v *CharacterSelect) Advance(tickTime float64) error {
for _, hero := range v.characterImage { for _, hero := range v.characterImage {
if hero != nil { if hero != nil {
@ -291,6 +345,7 @@ func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) {
func (v *CharacterSelect) refreshGameStates() { func (v *CharacterSelect) refreshGameStates() {
v.gameStates = d2player.GetAllPlayerStates() v.gameStates = d2player.GetAllPlayerStates()
v.updateCharacterBoxes() v.updateCharacterBoxes()
if len(v.gameStates) > 0 { if len(v.gameStates) > 0 {
v.selectedCharacter = 0 v.selectedCharacter = 0
v.d2HeroTitle.SetText(v.gameStates[0].HeroName) v.d2HeroTitle.SetText(v.gameStates[0].HeroName)
@ -299,17 +354,21 @@ func (v *CharacterSelect) refreshGameStates() {
v.selectedCharacter = -1 v.selectedCharacter = -1
v.charScrollbar.SetMaxOffset(0) v.charScrollbar.SetMaxOffset(0)
} }
v.moveSelectionBox()
v.moveSelectionBox()
} }
func (v *CharacterSelect) onOkButtonClicked() { func (v *CharacterSelect) onOkButtonClicked() {
gameClient, _ := d2client.Create(v.connectionType) gameClient, _ := d2client.Create(v.connectionType)
switch v.connectionType {
case d2clientconnectiontype.LANClient: host := ""
gameClient.Open(v.connectionHost, v.gameStates[v.selectedCharacter].FilePath) if v.connectionType == d2clientconnectiontype.LANClient {
default: host = v.connectionHost
gameClient.Open("", v.gameStates[v.selectedCharacter].FilePath) }
if err := gameClient.Open(host, v.gameStates[v.selectedCharacter].FilePath); err != nil {
// TODO an error screen should be shown in this case
fmt.Printf("can not connect to the host: %s", host)
} }
d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient, v.terminal)) d2screen.SetNextScreen(CreateGame(v.audioProvider, gameClient, v.terminal))

View File

@ -2,6 +2,7 @@ package d2gamescreen
import ( import (
"bufio" "bufio"
"fmt"
"image/color" "image/color"
"log" "log"
"os" "os"
@ -46,24 +47,32 @@ func CreateCredits(audioProvider d2interface.AudioProvider) *Credits {
cyclesTillNextLine: 0, cyclesTillNextLine: 0,
audioProvider: audioProvider, audioProvider: audioProvider,
} }
return result return result
} }
// Load is called to load the contributors data from file // LoadContributors loads the contributors data from file
// TODO: use markdown for file and convert it to the suitable format // TODO: use markdown for file and convert it to the suitable format
func (v *Credits) LoadContributors() []string { func (v *Credits) LoadContributors() []string {
var contributors []string
file, err := os.Open(path.Join("./", "CONTRIBUTORS")) file, err := os.Open(path.Join("./", "CONTRIBUTORS"))
if err != nil || file == nil { if err != nil || file == nil {
log.Print("CONTRIBUTORS file is missing") log.Print("CONTRIBUTORS file is missing")
return []string{"MISSING CONTRIBUTOR FILES!"} return []string{"MISSING CONTRIBUTOR FILES!"}
} }
defer file.Close()
defer func() {
if err = file.Close(); err != nil {
fmt.Printf("an error occurred while closing file: %s, err: %q\n", file.Name(), err)
}
}()
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
var contributors []string
for scanner.Scan() { for scanner.Scan() {
contributors = append(contributors, strings.Trim(scanner.Text(), " ")) contributors = append(contributors, strings.Trim(scanner.Text(), " "))
} }
return contributors return contributors
} }
@ -85,13 +94,16 @@ func (v *Credits) OnLoad(loading d2screen.LoadingState) {
loading.Error(err) loading.Error(err)
return return
} }
loading.Progress(0.6) loading.Progress(0.6)
creditData, _ := d2common.Utf16BytesToString(fileData[2:]) creditData, _ := d2common.Utf16BytesToString(fileData[2:])
v.creditsText = strings.Split(creditData, "\r\n") v.creditsText = strings.Split(creditData, "\r\n")
for i := range v.creditsText { for i := range v.creditsText {
v.creditsText[i] = strings.Trim(v.creditsText[i], " ") v.creditsText[i] = strings.Trim(v.creditsText[i], " ")
} }
loading.Progress(0.8) loading.Progress(0.8)
v.creditsText = append(v.LoadContributors(), v.creditsText...) v.creditsText = append(v.LoadContributors(), v.creditsText...)
@ -99,11 +111,16 @@ func (v *Credits) OnLoad(loading d2screen.LoadingState) {
// Render renders the credits screen // Render renders the credits screen
func (v *Credits) Render(screen d2interface.Surface) error { func (v *Credits) Render(screen d2interface.Surface) error {
v.creditsBackground.RenderSegmented(screen, 4, 3, 0) err := v.creditsBackground.RenderSegmented(screen, 4, 3, 0)
if err != nil {
return err
}
for _, label := range v.labels { for _, label := range v.labels {
if label.Available { if label.Available {
continue continue
} }
label.Label.Render(screen) label.Label.Render(screen)
} }
@ -112,12 +129,13 @@ func (v *Credits) Render(screen d2interface.Surface) error {
const secondsPerCycle = float64(0.02) const secondsPerCycle = float64(0.02)
// Update runs the update logic on the credits screen // Advance runs the update logic on the credits screen
func (v *Credits) Advance(tickTime float64) error { func (v *Credits) Advance(tickTime float64) error {
v.cycleTime += tickTime v.cycleTime += tickTime
for v.cycleTime >= secondsPerCycle { for v.cycleTime >= secondsPerCycle {
v.cycleTime -= secondsPerCycle v.cycleTime -= secondsPerCycle
v.cyclesTillNextLine-- v.cyclesTillNextLine--
if !v.doneWithCredits && v.cyclesTillNextLine <= 0 { if !v.doneWithCredits && v.cyclesTillNextLine <= 0 {
v.addNextItem() v.addNextItem()
} }
@ -126,6 +144,7 @@ func (v *Credits) Advance(tickTime float64) error {
if label.Available { if label.Available {
continue continue
} }
if label.Label.Y-1 < -15 { if label.Label.Y-1 < -15 {
label.Available = true label.Available = true
continue continue
@ -139,7 +158,7 @@ func (v *Credits) Advance(tickTime float64) error {
func (v *Credits) onExitButtonClicked() { func (v *Credits) onExitButtonClicked() {
mainMenu := CreateMainMenu(v.audioProvider, v.terminal) mainMenu := CreateMainMenu(v.audioProvider, v.terminal)
mainMenu.SetScreenMode(ScreenModeMainMenu) mainMenu.setScreenMode(screenModeMainMenu)
d2screen.SetNextScreen(mainMenu) d2screen.SetNextScreen(mainMenu)
} }
@ -151,55 +170,71 @@ func (v *Credits) addNextItem() {
text := v.creditsText[0] text := v.creditsText[0]
v.creditsText = v.creditsText[1:] v.creditsText = v.creditsText[1:]
if len(text) == 0 && v.creditsText[0][0] != '*' {
if text == "" {
if v.creditsText[0][0] == '*' {
v.cyclesTillNextLine = 38
return
}
v.cyclesTillNextLine = 19 v.cyclesTillNextLine = 19
return
} else if len(text) == 0 && v.creditsText[0][0] == '*' {
v.cyclesTillNextLine = 38
return return
} }
isHeading := text[0] == '*' isHeading := text[0] == '*'
isNextHeading := len(v.creditsText) > 0 && len(v.creditsText[0]) > 0 && v.creditsText[0][0] == '*' isNextHeading := len(v.creditsText) > 0 && len(v.creditsText[0]) > 0 && v.creditsText[0][0] == '*'
isNextSpace := len(v.creditsText) > 0 && len(v.creditsText[0]) == 0 isNextSpace := len(v.creditsText) > 0 && v.creditsText[0] == ""
var label = v.getNewFontLabel(isHeading) var label = v.getNewFontLabel(isHeading)
if isHeading { if isHeading {
label.SetText(text[1:]) label.SetText(text[1:])
} else { } else {
label.SetText(text) label.SetText(text)
} }
isDoubled, isNextHeading := v.setItemLabelPosition(label, isHeading, isNextHeading, isNextSpace)
switch {
case isHeading && isNextHeading:
v.cyclesTillNextLine = 38
case isNextHeading:
if isDoubled {
v.cyclesTillNextLine = 38
} else {
v.cyclesTillNextLine = 57
}
case isHeading:
v.cyclesTillNextLine = 38
default:
v.cyclesTillNextLine = 19
}
}
func (v *Credits) setItemLabelPosition(label *d2ui.Label, isHeading, isNextHeading, isNextSpace bool) (isDoubled, nextHeading bool) {
width, _ := label.GetSize() width, _ := label.GetSize()
isDoubled := false
if !isHeading && !isNextHeading && !isNextSpace { if !isHeading && !isNextHeading && !isNextSpace {
isDoubled = true isDoubled = true
// Gotta go side by side // Gotta go side by side
label.SetPosition(400-width, 605) label.SetPosition(400-width, 605)
text2 := v.creditsText[0] text2 := v.creditsText[0]
v.creditsText = v.creditsText[1:] v.creditsText = v.creditsText[1:]
isNextHeading = len(v.creditsText) > 0 && len(v.creditsText[0]) > 0 && v.creditsText[0][0] == '*' nextHeading = len(v.creditsText) > 0 && len(v.creditsText[0]) > 0 && v.creditsText[0][0] == '*'
label2 := v.getNewFontLabel(isHeading) label2 := v.getNewFontLabel(isHeading)
label2.SetText(text2) label2.SetText(text2)
label2.SetPosition(410, 605) label2.SetPosition(410, 605)
} else {
label.SetPosition(405-width/2, 605) return isDoubled, nextHeading
} }
if isHeading && isNextHeading { label.SetPosition(405-width/2, 605)
v.cyclesTillNextLine = 38
} else if isNextHeading { return isDoubled, isNextHeading
if isDoubled {
v.cyclesTillNextLine = 38
} else {
v.cyclesTillNextLine = 57
}
} else if isHeading {
v.cyclesTillNextLine = 38
} else {
v.cyclesTillNextLine = 19
}
} }
func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label { func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
@ -211,6 +246,7 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
} else { } else {
label.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255} label.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255}
} }
return &label.Label return &label.Label
} }
} }
@ -225,9 +261,9 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
newLabelItem.Label.Color = color.RGBA{R: 255, G: 88, B: 82, A: 255} newLabelItem.Label.Color = color.RGBA{R: 255, G: 88, B: 82, A: 255}
} else { } else {
newLabelItem.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255} newLabelItem.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255}
} }
v.labels = append(v.labels, newLabelItem) v.labels = append(v.labels, newLabelItem)
return &newLabelItem.Label return &newLabelItem.Label
} }

View File

@ -26,35 +26,38 @@ const (
menuSize = 500 menuSize = 500
// layouts // layouts
noLayoutID layoutID = -2 noLayoutID layoutID = iota - 2
saveLayoutID = -1 saveLayoutID
mainLayoutID = 0 mainLayoutID
optionsLayoutID = 1 optionsLayoutID
soundOptionsLayoutID = 2 soundOptionsLayoutID
videoOptionsLayoutID = 3 videoOptionsLayoutID
automapOptionsLayoutID = 4 automapOptionsLayoutID
configureControlsLayoutID = 5 configureControlsLayoutID
// options // audio
optAudioSoundVolume optionID = 0 // audio optAudioSoundVolume optionID = iota
optAudioMusicVolume = 1 optAudioMusicVolume
optAudio3dSound = 2 optAudio3dSound
optAudioHardwareAcceleration = 3 optAudioHardwareAcceleration
optAudioEnvEffects = 4 optAudioEnvEffects
optAudioNpcSpeech = 5 optAudioNpcSpeech
optVideoResolution = 6 // video // video
optVideoLightingQuality = 7 optVideoResolution
optVideoBlendedShadows = 8 optVideoLightingQuality
optVideoPerspective = 9 optVideoBlendedShadows
optVideoGamma = 10 optVideoPerspective
optVideoContrast = 11 optVideoGamma
optAutomapSize = 12 // automap optVideoContrast
optAutomapFade = 13 // automap
optAutomapCenterWhenCleared = 14 optAutomapSize
optAutomapShowParty = 15 optAutomapFade
optAutomapShowNames = 16 optAutomapCenterWhenCleared
optAutomapShowParty
optAutomapShowNames
) )
// EscapeMenu represents the in-game menu that shows up when the esc key is pressed
type EscapeMenu struct { type EscapeMenu struct {
isOpen bool isOpen bool
selectSound d2interface.SoundEffect selectSound d2interface.SoundEffect
@ -104,8 +107,13 @@ func (l *enumLabel) Trigger() {
l.playSound() l.playSound()
next := (l.current + 1) % len(l.values) next := (l.current + 1) % len(l.values)
l.current = next l.current = next
l.textChangingLabel.SetText(l.values[l.current])
l.updateValue(l.optionID, l.values[l.current]) currentValue := l.values[l.current]
if err := l.textChangingLabel.SetText(currentValue); err != nil {
fmt.Printf("could not change the label text to: %s\n", currentValue)
}
l.updateValue(l.optionID, currentValue)
} }
type actionableElement interface { type actionableElement interface {
@ -113,6 +121,7 @@ type actionableElement interface {
Trigger() Trigger()
} }
// NewEscapeMenu creates a new escape menu
func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *EscapeMenu { func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *EscapeMenu {
m := &EscapeMenu{ m := &EscapeMenu{
audioProvider: audioProvider, audioProvider: audioProvider,
@ -127,6 +136,7 @@ func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Ter
automapOptionsLayoutID: m.newAutomapOptionsLayout(), automapOptionsLayoutID: m.newAutomapOptionsLayout(),
configureControlsLayoutID: m.newConfigureControlsLayout(), configureControlsLayoutID: m.newConfigureControlsLayout(),
} }
return m return m
} }
@ -157,7 +167,7 @@ func (m *EscapeMenu) newSoundOptionsLayout() *layout {
m.addEnumLabel(l, optAudioHardwareAcceleration, "HARDWARE ACCELERATION", []string{"ON", "OFF"}) m.addEnumLabel(l, optAudioHardwareAcceleration, "HARDWARE ACCELERATION", []string{"ON", "OFF"})
m.addEnumLabel(l, optAudioEnvEffects, "ENVIRONMENTAL EFFECTS", []string{"ON", "OFF"}) m.addEnumLabel(l, optAudioEnvEffects, "ENVIRONMENTAL EFFECTS", []string{"ON", "OFF"})
m.addEnumLabel(l, optAudioNpcSpeech, "NPC SPEECH", []string{"AUDIO AND TEXT", "AUDIO ONLY", "TEXT ONLY"}) m.addEnumLabel(l, optAudioNpcSpeech, "NPC SPEECH", []string{"AUDIO AND TEXT", "AUDIO ONLY", "TEXT ONLY"})
m.addPreviousMenuLabel(l, optionsLayoutID) m.addPreviousMenuLabel(l)
}) })
} }
@ -170,7 +180,7 @@ func (m *EscapeMenu) newVideoOptionsLayout() *layout {
m.addEnumLabel(l, optVideoPerspective, "PERSPECTIVE", []string{"ON", "OFF"}) m.addEnumLabel(l, optVideoPerspective, "PERSPECTIVE", []string{"ON", "OFF"})
m.addEnumLabel(l, optVideoGamma, "GAMMA", []string{"TODO"}) m.addEnumLabel(l, optVideoGamma, "GAMMA", []string{"TODO"})
m.addEnumLabel(l, optVideoContrast, "CONTRAST", []string{"TODO"}) m.addEnumLabel(l, optVideoContrast, "CONTRAST", []string{"TODO"})
m.addPreviousMenuLabel(l, optionsLayoutID) m.addPreviousMenuLabel(l)
}) })
} }
@ -182,14 +192,14 @@ func (m *EscapeMenu) newAutomapOptionsLayout() *layout {
m.addEnumLabel(l, optAutomapCenterWhenCleared, "CENTER WHEN CLEARED", []string{"YES", "NO"}) m.addEnumLabel(l, optAutomapCenterWhenCleared, "CENTER WHEN CLEARED", []string{"YES", "NO"})
m.addEnumLabel(l, optAutomapShowParty, "SHOW PARTY", []string{"YES", "NO"}) m.addEnumLabel(l, optAutomapShowParty, "SHOW PARTY", []string{"YES", "NO"})
m.addEnumLabel(l, optAutomapShowNames, "SHOW NAMES", []string{"YES", "NO"}) m.addEnumLabel(l, optAutomapShowNames, "SHOW NAMES", []string{"YES", "NO"})
m.addPreviousMenuLabel(l, optionsLayoutID) m.addPreviousMenuLabel(l)
}) })
} }
func (m *EscapeMenu) newConfigureControlsLayout() *layout { func (m *EscapeMenu) newConfigureControlsLayout() *layout {
return m.wrapLayout(func(l *layout) { return m.wrapLayout(func(l *layout) {
m.addTitle(l, "CONFIGURE CONTROLS") m.addTitle(l, "CONFIGURE CONTROLS")
m.addPreviousMenuLabel(l, optionsLayoutID) m.addPreviousMenuLabel(l)
}) })
} }
@ -221,6 +231,7 @@ func (m *EscapeMenu) wrapLayout(fn func(*layout)) *layout {
m.rightPent = rightPent m.rightPent = rightPent
wrapper.AddSpacerDynamic() wrapper.AddSpacerDynamic()
return &layout{ return &layout{
Layout: wrapper, Layout: wrapper,
leftPent: leftPent, leftPent: leftPent,
@ -230,7 +241,11 @@ func (m *EscapeMenu) wrapLayout(fn func(*layout)) *layout {
} }
func (m *EscapeMenu) addTitle(l *layout, text string) { func (m *EscapeMenu) addTitle(l *layout, text string) {
l.AddLabel(text, d2gui.FontStyle42Units) _, err := l.AddLabel(text, d2gui.FontStyle42Units)
if err != nil {
fmt.Printf("could not add label: %s to the escape menu\n", text)
}
l.AddSpacerStatic(10, labelGutter) l.AddSpacerStatic(10, labelGutter)
} }
@ -240,7 +255,9 @@ func (m *EscapeMenu) addBigSelectionLabel(l *layout, text string, targetLayout l
label.SetMouseClickHandler(func(_ d2input.MouseEvent) { label.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger() label.Trigger()
}) })
elID := len(l.actionableElements) elID := len(l.actionableElements)
label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID) m.onHoverElement(elID)
}) })
@ -248,17 +265,20 @@ func (m *EscapeMenu) addBigSelectionLabel(l *layout, text string, targetLayout l
l.actionableElements = append(l.actionableElements, label) l.actionableElements = append(l.actionableElements, label)
} }
func (m *EscapeMenu) addPreviousMenuLabel(l *layout, targetLayout layoutID) { func (m *EscapeMenu) addPreviousMenuLabel(l *layout) {
l.AddSpacerStatic(10, labelGutter) l.AddSpacerStatic(10, labelGutter)
guiLabel, _ := l.AddLabel("PREVIOUS MENU", d2gui.FontStyle30Units) guiLabel, _ := l.AddLabel("PREVIOUS MENU", d2gui.FontStyle30Units)
label := &showLayoutLabel{Label: guiLabel, target: targetLayout, showLayout: m.showLayout} label := &showLayoutLabel{Label: guiLabel, target: optionsLayoutID, showLayout: m.showLayout}
label.SetMouseClickHandler(func(_ d2input.MouseEvent) { label.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger() label.Trigger()
}) })
elID := len(l.actionableElements) elID := len(l.actionableElements)
label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID) m.onHoverElement(elID)
}) })
l.actionableElements = append(l.actionableElements, label) l.actionableElements = append(l.actionableElements, label)
} }
@ -266,8 +286,14 @@ func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values
guiLayout := l.AddLayout(d2gui.PositionTypeHorizontal) guiLayout := l.AddLayout(d2gui.PositionTypeHorizontal)
layout := &layout{Layout: guiLayout} layout := &layout{Layout: guiLayout}
layout.SetSize(menuSize, 0) layout.SetSize(menuSize, 0)
layout.AddLabel(text, d2gui.FontStyle30Units)
_, err := layout.AddLabel(text, d2gui.FontStyle30Units)
if err != nil {
fmt.Printf("could not add label: %s to the escape menu\n", text)
}
elID := len(l.actionableElements) elID := len(l.actionableElements)
layout.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { layout.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID) m.onHoverElement(elID)
}) })
@ -282,6 +308,7 @@ func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values
playSound: m.playSound, playSound: m.playSound,
updateValue: m.onUpdateValue, updateValue: m.onUpdateValue,
} }
layout.SetMouseClickHandler(func(_ d2input.MouseEvent) { layout.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger() label.Trigger()
}) })
@ -289,13 +316,13 @@ func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values
l.actionableElements = append(l.actionableElements, label) l.actionableElements = append(l.actionableElements, label)
} }
func (m *EscapeMenu) OnLoad() { func (m *EscapeMenu) onLoad() {
m.selectSound, _ = m.audioProvider.LoadSoundEffect(d2resource.SFXCursorSelect) m.selectSound, _ = m.audioProvider.LoadSoundEffect(d2resource.SFXCursorSelect)
} }
func (m *EscapeMenu) OnEscKey() { func (m *EscapeMenu) onEscKey() {
if !m.isOpen { if !m.isOpen {
m.Open() m.open()
return return
} }
@ -311,19 +338,16 @@ func (m *EscapeMenu) OnEscKey() {
return return
} }
m.Close() m.close()
} }
func (m *EscapeMenu) IsOpen() bool { func (m *EscapeMenu) close() {
return m.isOpen
}
func (m *EscapeMenu) Close() {
m.isOpen = false m.isOpen = false
d2gui.SetLayout(nil) d2gui.SetLayout(nil)
} }
func (m *EscapeMenu) Open() { func (m *EscapeMenu) open() {
m.isOpen = true m.isOpen = true
m.setLayout(mainLayoutID) m.setLayout(mainLayoutID)
} }
@ -336,14 +360,15 @@ func (m *EscapeMenu) showLayout(id layoutID) {
m.playSound() m.playSound()
if id == noLayoutID { if id == noLayoutID {
m.Close() m.close()
return return
} }
if id == saveLayoutID { if id == saveLayoutID {
mainMenu := CreateMainMenu(m.audioProvider, m.terminal) mainMenu := CreateMainMenu(m.audioProvider, m.terminal)
mainMenu.SetScreenMode(ScreenModeMainMenu) mainMenu.setScreenMode(screenModeMainMenu)
d2screen.SetNextScreen(mainMenu) d2screen.SetNextScreen(mainMenu)
return return
} }
@ -358,12 +383,10 @@ func (m *EscapeMenu) onHoverElement(id int) {
m.leftPent.SetPosition(x, y+10) m.leftPent.SetPosition(x, y+10)
x, _ = m.rightPent.GetPosition() x, _ = m.rightPent.GetPosition()
m.rightPent.SetPosition(x, y+10) m.rightPent.SetPosition(x, y+10)
return
} }
func (m *EscapeMenu) onUpdateValue(optID optionID, value string) { func (m *EscapeMenu) onUpdateValue(optID optionID, value string) {
fmt.Println(fmt.Sprintf("updating value %d with %s", optID, value)) fmt.Printf("updating value %d with %s\n", optID, value)
} }
func (m *EscapeMenu) setLayout(id layoutID) { func (m *EscapeMenu) setLayout(id layoutID) {
@ -379,6 +402,7 @@ func (m *EscapeMenu) onUpKey() {
if !m.isOpen { if !m.isOpen {
return return
} }
if m.layouts[m.currentLayout].currentEl == 0 { if m.layouts[m.currentLayout].currentEl == 0 {
return return
} }
@ -390,6 +414,7 @@ func (m *EscapeMenu) onDownKey() {
if !m.isOpen { if !m.isOpen {
return return
} }
if m.layouts[m.currentLayout].currentEl == len(m.layouts[m.currentLayout].actionableElements)-1 { if m.layouts[m.currentLayout].currentEl == len(m.layouts[m.currentLayout].actionableElements)-1 {
return return
} }
@ -401,13 +426,15 @@ func (m *EscapeMenu) onEnterKey() {
if !m.isOpen { if !m.isOpen {
return return
} }
m.layouts[m.currentLayout].actionableElements[m.layouts[m.currentLayout].currentEl].Trigger() m.layouts[m.currentLayout].actionableElements[m.layouts[m.currentLayout].currentEl].Trigger()
} }
// OnKeyDown defines the actions of the Escape Menu when a key is pressed
func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool { func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool {
switch event.Key { switch event.Key {
case d2input.KeyEscape: case d2input.KeyEscape:
m.OnEscKey() m.onEscKey()
case d2input.KeyUp: case d2input.KeyUp:
m.onUpKey() m.onUpKey()
case d2input.KeyDown: case d2input.KeyDown:
@ -417,5 +444,6 @@ func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool {
default: default:
return false return false
} }
return false
return true
} }

View File

@ -43,8 +43,9 @@ func CreateGame(audioProvider d2interface.AudioProvider, gameClient *d2client.Ga
audioProvider: audioProvider, audioProvider: audioProvider,
terminal: term, terminal: term,
} }
result.escapeMenu.OnLoad() result.escapeMenu.onLoad()
d2input.BindHandler(result.escapeMenu) d2input.BindHandler(result.escapeMenu)
return result return result
} }
@ -55,6 +56,7 @@ func (v *Game) OnLoad(loading d2screen.LoadingState) {
func (v *Game) OnUnload() error { func (v *Game) OnUnload() error {
d2input.UnbindHandler(v.gameControls) // TODO: hack d2input.UnbindHandler(v.gameControls) // TODO: hack
v.gameClient.Close() v.gameClient.Close()
return nil return nil
} }
@ -77,7 +79,7 @@ func (v *Game) Render(screen d2interface.Surface) error {
var hideZoneTextAfterSeconds = 2.0 var hideZoneTextAfterSeconds = 2.0
func (v *Game) Advance(tickTime float64) error { func (v *Game) Advance(tickTime float64) error {
if (v.escapeMenu != nil && !v.escapeMenu.IsOpen()) || len(v.gameClient.Players) != 1 { if (v.escapeMenu != nil && !v.escapeMenu.isOpen) || len(v.gameClient.Players) != 1 {
v.gameClient.MapEngine.Advance(tickTime) // TODO: Hack v.gameClient.MapEngine.Advance(tickTime) // TODO: Hack
} }
@ -101,6 +103,7 @@ func (v *Game) Advance(tickTime float64) error {
v.gameControls.ShowZoneChangeText() v.gameControls.ShowZoneChangeText()
v.gameControls.HideZoneChangeTextAfter(hideZoneTextAfterSeconds) v.gameControls.HideZoneChangeTextAfter(hideZoneTextAfterSeconds)
} }
v.lastRegionType = tile.RegionType v.lastRegionType = tile.RegionType
} }
} }
@ -112,6 +115,7 @@ func (v *Game) Advance(tickTime float64) error {
if player.Id != v.gameClient.PlayerId { if player.Id != v.gameClient.PlayerId {
continue continue
} }
v.localPlayer = player v.localPlayer = player
v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal) v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal)
v.gameControls.Load() v.gameControls.Load()
@ -126,6 +130,7 @@ func (v *Game) Advance(tickTime float64) error {
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5) rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)
v.mapRenderer.MoveCameraTo(rx, ry) v.mapRenderer.MoveCameraTo(rx, ry)
} }
return nil return nil
} }

View File

@ -14,6 +14,7 @@ func CreateGuiTestMain() *GuiTestMain {
func (g *GuiTestMain) OnLoad(loading d2screen.LoadingState) { func (g *GuiTestMain) OnLoad(loading d2screen.LoadingState) {
layout := d2gui.CreateLayout(d2gui.PositionTypeHorizontal) layout := d2gui.CreateLayout(d2gui.PositionTypeHorizontal)
loading.Progress(0.3) loading.Progress(0.3)
// //
layoutLeft := layout.AddLayout(d2gui.PositionTypeVertical) layoutLeft := layout.AddLayout(d2gui.PositionTypeVertical)

View File

@ -24,27 +24,27 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
) )
type MainMenuScreenMode int type mainMenuScreenMode int
const ( const (
ScreenModeUnknown MainMenuScreenMode = iota screenModeUnknown mainMenuScreenMode = iota
ScreenModeTrademark screenModeTrademark
ScreenModeMainMenu screenModeMainMenu
ScreenModeMultiplayer screenModeMultiplayer
ScreenModeTcpIp screenModeTCPIP
ScreenModeServerIp screenModeServerIP
) )
// MainMenu represents the main menu // MainMenu represents the main menu
type MainMenu struct { type MainMenu struct {
tcpIpBackground *d2ui.Sprite tcpIPBackground *d2ui.Sprite
trademarkBackground *d2ui.Sprite trademarkBackground *d2ui.Sprite
background *d2ui.Sprite background *d2ui.Sprite
diabloLogoLeft *d2ui.Sprite diabloLogoLeft *d2ui.Sprite
diabloLogoRight *d2ui.Sprite diabloLogoRight *d2ui.Sprite
diabloLogoLeftBack *d2ui.Sprite diabloLogoLeftBack *d2ui.Sprite
diabloLogoRightBack *d2ui.Sprite diabloLogoRightBack *d2ui.Sprite
serverIpBackground *d2ui.Sprite serverIPBackground *d2ui.Sprite
singlePlayerButton d2ui.Button singlePlayerButton d2ui.Button
multiplayerButton d2ui.Button multiplayerButton d2ui.Button
githubButton d2ui.Button githubButton d2ui.Button
@ -52,22 +52,22 @@ type MainMenu struct {
creditsButton d2ui.Button creditsButton d2ui.Button
cinematicsButton d2ui.Button cinematicsButton d2ui.Button
mapTestButton d2ui.Button mapTestButton d2ui.Button
networkTcpIpButton d2ui.Button networkTCPIPButton d2ui.Button
networkCancelButton d2ui.Button networkCancelButton d2ui.Button
btnTcpIpCancel d2ui.Button btnTCPIPCancel d2ui.Button
btnTcpIpHostGame d2ui.Button btnTCPIPHostGame d2ui.Button
btnTcpIpJoinGame d2ui.Button btnTCPIPJoinGame d2ui.Button
btnServerIpCancel d2ui.Button btnServerIPCancel d2ui.Button
btnServerIpOk d2ui.Button btnServerIPOk d2ui.Button
copyrightLabel d2ui.Label copyrightLabel d2ui.Label
copyrightLabel2 d2ui.Label copyrightLabel2 d2ui.Label
openDiabloLabel d2ui.Label openDiabloLabel d2ui.Label
versionLabel d2ui.Label versionLabel d2ui.Label
commitLabel d2ui.Label commitLabel d2ui.Label
tcpIpOptionsLabel d2ui.Label tcpIPOptionsLabel d2ui.Label
tcpJoinGameLabel d2ui.Label tcpJoinGameLabel d2ui.Label
tcpJoinGameEntry d2ui.TextBox tcpJoinGameEntry d2ui.TextBox
screenMode MainMenuScreenMode screenMode mainMenuScreenMode
leftButtonHeld bool leftButtonHeld bool
audioProvider d2interface.AudioProvider audioProvider d2interface.AudioProvider
terminal d2interface.Terminal terminal d2interface.Terminal
@ -76,18 +76,59 @@ type MainMenu struct {
// CreateMainMenu creates an instance of MainMenu // CreateMainMenu creates an instance of MainMenu
func CreateMainMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu { func CreateMainMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu {
return &MainMenu{ return &MainMenu{
screenMode: ScreenModeUnknown, screenMode: screenModeUnknown,
leftButtonHeld: true, leftButtonHeld: true,
audioProvider: audioProvider, audioProvider: audioProvider,
terminal: term, terminal: term,
} }
} }
// Load is called to load the resources for the main menu // OnLoad is called to load the resources for the main menu
func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
v.audioProvider.PlayBGM(d2resource.BGMTitle) v.audioProvider.PlayBGM(d2resource.BGMTitle)
loading.Progress(0.2) loading.Progress(0.2)
v.createLabels(loading)
v.loadBackgroundSprites()
v.createLogos(loading)
v.createButtons(loading)
v.tcpJoinGameEntry = d2ui.CreateTextbox()
v.tcpJoinGameEntry.SetPosition(318, 245)
v.tcpJoinGameEntry.SetFilter("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._:")
d2ui.AddWidget(&v.tcpJoinGameEntry)
loading.Progress(0.9)
if v.screenMode == screenModeUnknown {
v.setScreenMode(screenModeTrademark)
} else {
v.setScreenMode(screenModeMainMenu)
}
if err := d2input.BindHandler(v); err != nil {
fmt.Println("failed to add main menu as event handler")
}
}
func (v *MainMenu) loadBackgroundSprites() {
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.TCPIPBackground, d2resource.PaletteSky)
v.tcpIPBackground, _ = d2ui.LoadSprite(animation)
v.tcpIPBackground.SetPosition(0, 0)
animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
v.serverIPBackground, _ = d2ui.LoadSprite(animation)
v.serverIPBackground.SetPosition(270, 175)
}
func (v *MainMenu) createLabels(loading d2screen.LoadingState) {
v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
v.versionLabel.Alignment = d2ui.LabelAlignRight v.versionLabel.Alignment = d2ui.LabelAlignRight
v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch) v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch)
@ -120,19 +161,22 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
v.openDiabloLabel.SetPosition(400, 580) v.openDiabloLabel.SetPosition(400, 580)
loading.Progress(0.5) loading.Progress(0.5)
animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) v.tcpIPOptionsLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
v.background, _ = d2ui.LoadSprite(animation) v.tcpIPOptionsLabel.SetPosition(400, 23)
v.background.SetPosition(0, 0) v.tcpIPOptionsLabel.Alignment = d2ui.LabelAlignCenter
v.tcpIPOptionsLabel.SetText("TCP/IP Options")
animation, _ = d2asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky) v.tcpJoinGameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.trademarkBackground, _ = d2ui.LoadSprite(animation) v.tcpJoinGameLabel.Alignment = d2ui.LabelAlignCenter
v.trademarkBackground.SetPosition(0, 0) v.tcpJoinGameLabel.SetText(d2common.CombineStrings(
d2common.SplitIntoLinesWithMaxWidth("Enter Host IP Address to Join Game", 23)))
animation, _ = d2asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) v.tcpJoinGameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255}
v.tcpIpBackground, _ = d2ui.LoadSprite(animation) v.tcpJoinGameLabel.SetPosition(400, 190)
v.tcpIpBackground.SetPosition(0, 0) }
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) func (v *MainMenu) createLogos(loading d2screen.LoadingState) {
animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits)
v.diabloLogoLeft, _ = d2ui.LoadSprite(animation) v.diabloLogoLeft, _ = d2ui.LoadSprite(animation)
v.diabloLogoLeft.SetBlend(true) v.diabloLogoLeft.SetBlend(true)
v.diabloLogoLeft.PlayForward() v.diabloLogoLeft.PlayForward()
@ -152,7 +196,9 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits)
v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation) v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation)
v.diabloLogoRightBack.SetPosition(400, 120) v.diabloLogoRightBack.SetPosition(400, 120)
}
func (v *MainMenu) createButtons(loading d2screen.LoadingState) {
v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "EXIT DIABLO II") v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "EXIT DIABLO II")
v.exitDiabloButton.SetPosition(264, 535) v.exitDiabloButton.SetPosition(264, 535)
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
@ -173,11 +219,6 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() })
d2ui.AddWidget(&v.singlePlayerButton) d2ui.AddWidget(&v.singlePlayerButton)
v.multiplayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MULTIPLAYER")
v.multiplayerButton.SetPosition(264, 330)
v.multiplayerButton.OnActivated(func() { v.onMultiplayerClicked() })
d2ui.AddWidget(&v.multiplayerButton)
v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE") v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE")
v.githubButton.SetPosition(264, 400) v.githubButton.SetPosition(264, 400)
v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) v.githubButton.OnActivated(func() { v.onGithubButtonClicked() })
@ -188,78 +229,69 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) v.mapTestButton.OnActivated(func() { v.onMapTestClicked() })
d2ui.AddWidget(&v.mapTestButton) d2ui.AddWidget(&v.mapTestButton)
v.networkTcpIpButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "TCP/IP GAME") v.btnTCPIPCancel = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("cancel"))
v.networkTcpIpButton.SetPosition(264, 280) v.btnTCPIPCancel.SetPosition(33, 543)
v.networkTcpIpButton.OnActivated(func() { v.onNetworkTcpIpClicked() }) v.btnTCPIPCancel.OnActivated(func() { v.onTCPIPCancelClicked() })
d2ui.AddWidget(&v.networkTcpIpButton) d2ui.AddWidget(&v.btnTCPIPCancel)
v.btnServerIPCancel = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, "CANCEL")
v.btnServerIPCancel.SetPosition(285, 305)
v.btnServerIPCancel.OnActivated(func() { v.onBtnTCPIPCancelClicked() })
d2ui.AddWidget(&v.btnServerIPCancel)
v.btnServerIPOk = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, "OK")
v.btnServerIPOk.SetPosition(420, 305)
v.btnServerIPOk.OnActivated(func() { v.onBtnTCPIPOkClicked() })
d2ui.AddWidget(&v.btnServerIPOk)
v.createMultiplayerMenuButtons()
loading.Progress(0.8)
}
func (v *MainMenu) createMultiplayerMenuButtons() {
v.multiplayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MULTIPLAYER")
v.multiplayerButton.SetPosition(264, 330)
v.multiplayerButton.OnActivated(func() { v.onMultiplayerClicked() })
d2ui.AddWidget(&v.multiplayerButton)
v.networkTCPIPButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "TCP/IP GAME")
v.networkTCPIPButton.SetPosition(264, 280)
v.networkTCPIPButton.OnActivated(func() { v.onNetworkTCPIPClicked() })
d2ui.AddWidget(&v.networkTCPIPButton)
v.networkCancelButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("cancel")) v.networkCancelButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("cancel"))
v.networkCancelButton.SetPosition(264, 540) v.networkCancelButton.SetPosition(264, 540)
v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() }) v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() })
d2ui.AddWidget(&v.networkCancelButton) d2ui.AddWidget(&v.networkCancelButton)
v.btnTcpIpCancel = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("cancel")) v.btnTCPIPHostGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "HOST GAME")
v.btnTcpIpCancel.SetPosition(33, 543) v.btnTCPIPHostGame.SetPosition(264, 280)
v.btnTcpIpCancel.OnActivated(func() { v.onTcpIpCancelClicked() }) v.btnTCPIPHostGame.OnActivated(func() { v.onTCPIPHostGameClicked() })
d2ui.AddWidget(&v.btnTcpIpCancel) d2ui.AddWidget(&v.btnTCPIPHostGame)
v.btnTcpIpHostGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "HOST GAME") v.btnTCPIPJoinGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "JOIN GAME")
v.btnTcpIpHostGame.SetPosition(264, 280) v.btnTCPIPJoinGame.SetPosition(264, 320)
v.btnTcpIpHostGame.OnActivated(func() { v.onTcpIpHostGameClicked() }) v.btnTCPIPJoinGame.OnActivated(func() { v.onTCPIPJoinGameClicked() })
d2ui.AddWidget(&v.btnTcpIpHostGame) d2ui.AddWidget(&v.btnTCPIPJoinGame)
v.btnTcpIpJoinGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "JOIN GAME")
v.btnTcpIpJoinGame.SetPosition(264, 320)
v.btnTcpIpJoinGame.OnActivated(func() { v.onTcpIpJoinGameClicked() })
d2ui.AddWidget(&v.btnTcpIpJoinGame)
loading.Progress(0.8)
v.tcpIpOptionsLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
v.tcpIpOptionsLabel.SetPosition(400, 23)
v.tcpIpOptionsLabel.Alignment = d2ui.LabelAlignCenter
v.tcpIpOptionsLabel.SetText("TCP/IP Options")
animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
v.serverIpBackground, _ = d2ui.LoadSprite(animation)
v.serverIpBackground.SetPosition(270, 175)
v.tcpJoinGameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.tcpJoinGameLabel.Alignment = d2ui.LabelAlignCenter
v.tcpJoinGameLabel.SetText(d2common.CombineStrings(d2common.
SplitIntoLinesWithMaxWidth("Enter Host IP Address to Join Game", 23)))
v.tcpJoinGameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255}
v.tcpJoinGameLabel.SetPosition(400, 190)
v.tcpJoinGameEntry = d2ui.CreateTextbox()
v.tcpJoinGameEntry.SetPosition(318, 245)
v.tcpJoinGameEntry.SetFilter("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._:")
d2ui.AddWidget(&v.tcpJoinGameEntry)
loading.Progress(0.9)
v.btnServerIpCancel = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, "CANCEL")
v.btnServerIpCancel.SetPosition(285, 305)
v.btnServerIpCancel.OnActivated(func() { v.onBtnTcpIpCancelClicked() })
d2ui.AddWidget(&v.btnServerIpCancel)
v.btnServerIpOk = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, "OK")
v.btnServerIpOk.SetPosition(420, 305)
v.btnServerIpOk.OnActivated(func() { v.onBtnTcpIpOkClicked() })
d2ui.AddWidget(&v.btnServerIpOk)
if v.screenMode == ScreenModeUnknown {
v.SetScreenMode(ScreenModeTrademark)
} else {
v.SetScreenMode(ScreenModeMainMenu)
}
d2input.BindHandler(v)
} }
func (v *MainMenu) onMapTestClicked() { func (v *MainMenu) onMapTestClicked() {
d2screen.SetNextScreen(CreateMapEngineTest(0, 1, v.terminal)) d2screen.SetNextScreen(CreateMapEngineTest(0, 1, v.terminal))
} }
func openbrowser(url string) { func (v *MainMenu) onSinglePlayerClicked() {
// Go here only if existing characters are available to select
if d2player.HasGameStates() {
d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal))
return
}
d2screen.SetNextScreen(CreateSelectHeroClass(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText()))
}
func (v *MainMenu) onGithubButtonClicked() {
url := "https://www.github.com/OpenDiablo2/OpenDiablo2"
var err error var err error
switch runtime.GOOS { switch runtime.GOOS {
@ -272,23 +304,10 @@ func openbrowser(url string) {
default: default:
err = fmt.Errorf("unsupported platform") err = fmt.Errorf("unsupported platform")
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
}
func (v *MainMenu) onSinglePlayerClicked() {
// Go here only if existing characters are available to select
if d2player.HasGameStates() {
d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal))
return
}
d2screen.SetNextScreen(CreateSelectHeroClass(v.audioProvider, d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText()))
}
func (v *MainMenu) onGithubButtonClicked() {
openbrowser("https://www.github.com/OpenDiablo2/OpenDiablo2")
} }
func (v *MainMenu) onExitButtonClicked() { func (v *MainMenu) onExitButtonClicked() {
@ -301,40 +320,78 @@ func (v *MainMenu) onCreditsButtonClicked() {
// Render renders the main menu // Render renders the main menu
func (v *MainMenu) Render(screen d2interface.Surface) error { func (v *MainMenu) Render(screen d2interface.Surface) error {
if err := v.renderBackgrounds(screen); err != nil {
return err
}
if err := v.renderLogos(screen); err != nil {
return err
}
if err := v.renderLabels(screen); err != nil {
return err
}
return nil
}
func (v *MainMenu) renderBackgrounds(screen d2interface.Surface) error {
switch v.screenMode { switch v.screenMode {
case ScreenModeTrademark: case screenModeTrademark:
v.trademarkBackground.RenderSegmented(screen, 4, 3, 0) if err := v.trademarkBackground.RenderSegmented(screen, 4, 3, 0); err != nil {
case ScreenModeServerIp: return err
fallthrough }
case ScreenModeTcpIp: case screenModeServerIP:
v.tcpIpBackground.RenderSegmented(screen, 4, 3, 0) if err := v.serverIPBackground.RenderSegmented(screen, 2, 1, 0); err != nil {
return err
}
case screenModeTCPIP:
if err := v.tcpIPBackground.RenderSegmented(screen, 4, 3, 0); err != nil {
return err
}
default: default:
v.background.RenderSegmented(screen, 4, 3, 0) if err := v.background.RenderSegmented(screen, 4, 3, 0); err != nil {
return err
}
} }
return nil
}
func (v *MainMenu) renderLogos(screen d2interface.Surface) error {
switch v.screenMode { switch v.screenMode {
case ScreenModeTrademark: case screenModeTrademark, screenModeMainMenu, screenModeMultiplayer:
fallthrough if err := v.diabloLogoLeftBack.Render(screen); err != nil {
case ScreenModeMainMenu: return err
fallthrough }
case ScreenModeMultiplayer:
v.diabloLogoLeftBack.Render(screen) if err := v.diabloLogoRightBack.Render(screen); err != nil {
v.diabloLogoRightBack.Render(screen) return err
v.diabloLogoLeft.Render(screen) }
v.diabloLogoRight.Render(screen)
if err := v.diabloLogoLeft.Render(screen); err != nil {
return err
}
if err := v.diabloLogoRight.Render(screen); err != nil {
return err
}
} }
return nil
}
func (v *MainMenu) renderLabels(screen d2interface.Surface) error {
switch v.screenMode { switch v.screenMode {
case ScreenModeServerIp: case screenModeServerIP:
v.tcpIpOptionsLabel.Render(screen) v.tcpIPOptionsLabel.Render(screen)
v.serverIpBackground.RenderSegmented(screen, 2, 1, 0)
v.tcpJoinGameLabel.Render(screen) v.tcpJoinGameLabel.Render(screen)
case ScreenModeTcpIp: case screenModeTCPIP:
v.tcpIpOptionsLabel.Render(screen) v.tcpIPOptionsLabel.Render(screen)
case ScreenModeTrademark: case screenModeTrademark:
v.copyrightLabel.Render(screen) v.copyrightLabel.Render(screen)
v.copyrightLabel2.Render(screen) v.copyrightLabel2.Render(screen)
case ScreenModeMainMenu: case screenModeMainMenu:
v.openDiabloLabel.Render(screen) v.openDiabloLabel.Render(screen)
v.versionLabel.Render(screen) v.versionLabel.Render(screen)
v.commitLabel.Render(screen) v.commitLabel.Render(screen)
@ -343,37 +400,47 @@ func (v *MainMenu) Render(screen d2interface.Surface) error {
return nil return nil
} }
// Update runs the update logic on the main menu // Advance runs the update logic on the main menu
func (v *MainMenu) Advance(tickTime float64) error { func (v *MainMenu) Advance(tickTime float64) error {
switch v.screenMode { switch v.screenMode {
case ScreenModeMainMenu: case screenModeMainMenu, screenModeTrademark, screenModeMultiplayer:
fallthrough if err := v.diabloLogoLeftBack.Advance(tickTime); err != nil {
case ScreenModeTrademark: return err
fallthrough }
case ScreenModeMultiplayer:
v.diabloLogoLeftBack.Advance(tickTime) if err := v.diabloLogoRightBack.Advance(tickTime); err != nil {
v.diabloLogoRightBack.Advance(tickTime) return err
v.diabloLogoLeft.Advance(tickTime) }
v.diabloLogoRight.Advance(tickTime)
if err := v.diabloLogoLeft.Advance(tickTime); err != nil {
return err
}
if err := v.diabloLogoRight.Advance(tickTime); err != nil {
return err
}
} }
return nil return nil
} }
// OnMouseButtonDown is called when a mouse button is clicked
func (v *MainMenu) OnMouseButtonDown(event d2input.MouseEvent) bool { func (v *MainMenu) OnMouseButtonDown(event d2input.MouseEvent) bool {
if v.screenMode == ScreenModeTrademark && event.Button == d2input.MouseButtonLeft { if v.screenMode == screenModeTrademark && event.Button == d2input.MouseButtonLeft {
v.SetScreenMode(ScreenModeMainMenu) v.setScreenMode(screenModeMainMenu)
return true return true
} }
return false return false
} }
func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) { func (v *MainMenu) setScreenMode(screenMode mainMenuScreenMode) {
v.screenMode = screenMode v.screenMode = screenMode
isMainMenu := screenMode == ScreenModeMainMenu isMainMenu := screenMode == screenModeMainMenu
isMultiplayer := screenMode == ScreenModeMultiplayer isMultiplayer := screenMode == screenModeMultiplayer
isTcpIp := screenMode == ScreenModeTcpIp isTCPIP := screenMode == screenModeTCPIP
isServerIp := screenMode == ScreenModeServerIp isServerIP := screenMode == screenModeServerIP
v.exitDiabloButton.SetVisible(isMainMenu) v.exitDiabloButton.SetVisible(isMainMenu)
v.creditsButton.SetVisible(isMainMenu) v.creditsButton.SetVisible(isMainMenu)
v.cinematicsButton.SetVisible(isMainMenu) v.cinematicsButton.SetVisible(isMainMenu)
@ -381,47 +448,49 @@ func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.githubButton.SetVisible(isMainMenu) v.githubButton.SetVisible(isMainMenu)
v.mapTestButton.SetVisible(isMainMenu) v.mapTestButton.SetVisible(isMainMenu)
v.multiplayerButton.SetVisible(isMainMenu) v.multiplayerButton.SetVisible(isMainMenu)
v.networkTcpIpButton.SetVisible(isMultiplayer) v.networkTCPIPButton.SetVisible(isMultiplayer)
v.networkCancelButton.SetVisible(isMultiplayer) v.networkCancelButton.SetVisible(isMultiplayer)
v.btnTcpIpCancel.SetVisible(isTcpIp) v.btnTCPIPCancel.SetVisible(isTCPIP)
v.btnTcpIpHostGame.SetVisible(isTcpIp) v.btnTCPIPHostGame.SetVisible(isTCPIP)
v.btnTcpIpJoinGame.SetVisible(isTcpIp) v.btnTCPIPJoinGame.SetVisible(isTCPIP)
v.tcpJoinGameEntry.SetVisible(isServerIp) v.tcpJoinGameEntry.SetVisible(isServerIP)
if isServerIp {
if isServerIP {
v.tcpJoinGameEntry.Activate() v.tcpJoinGameEntry.Activate()
} }
v.btnServerIpOk.SetVisible(isServerIp)
v.btnServerIpCancel.SetVisible(isServerIp) v.btnServerIPOk.SetVisible(isServerIP)
v.btnServerIPCancel.SetVisible(isServerIP)
} }
func (v *MainMenu) onNetworkCancelClicked() { func (v *MainMenu) onNetworkCancelClicked() {
v.SetScreenMode(ScreenModeMainMenu) v.setScreenMode(screenModeMainMenu)
} }
func (v *MainMenu) onMultiplayerClicked() { func (v *MainMenu) onMultiplayerClicked() {
v.SetScreenMode(ScreenModeMultiplayer) v.setScreenMode(screenModeMultiplayer)
} }
func (v *MainMenu) onNetworkTcpIpClicked() { func (v *MainMenu) onNetworkTCPIPClicked() {
v.SetScreenMode(ScreenModeTcpIp) v.setScreenMode(screenModeTCPIP)
} }
func (v *MainMenu) onTcpIpCancelClicked() { func (v *MainMenu) onTCPIPCancelClicked() {
v.SetScreenMode(ScreenModeMultiplayer) v.setScreenMode(screenModeMultiplayer)
} }
func (v *MainMenu) onTcpIpHostGameClicked() { func (v *MainMenu) onTCPIPHostGameClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANServer, "", v.terminal)) d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANServer, "", v.terminal))
} }
func (v *MainMenu) onTcpIpJoinGameClicked() { func (v *MainMenu) onTCPIPJoinGameClicked() {
v.SetScreenMode(ScreenModeServerIp) v.setScreenMode(screenModeServerIP)
} }
func (v *MainMenu) onBtnTcpIpCancelClicked() { func (v *MainMenu) onBtnTCPIPCancelClicked() {
v.SetScreenMode(ScreenModeTcpIp) v.setScreenMode(screenModeTCPIP)
} }
func (v *MainMenu) onBtnTcpIpOkClicked() { func (v *MainMenu) onBtnTCPIPOkClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal)) d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal))
} }

View File

@ -1,6 +1,7 @@
package d2gamescreen package d2gamescreen
import ( import (
"fmt"
"log" "log"
"os" "os"
"time" "time"
@ -17,15 +18,15 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
) )
type RegionSpec struct { type regionSpec struct {
regionType d2enum.RegionIdType regionType d2enum.RegionIdType
startPresetIndex int startPresetIndex int
endPresetIndex int endPresetIndex int
extra []int extra []int
} }
var regions = []RegionSpec{ var regions = []regionSpec{
//Act I // Act I
{d2enum.RegionAct1Town, 1, 3, []int{}}, {d2enum.RegionAct1Town, 1, 3, []int{}},
{d2enum.RegionAct1Wilderness, 4, 52, []int{ {d2enum.RegionAct1Wilderness, 4, 52, []int{
108, 108,
@ -41,7 +42,7 @@ var regions = []RegionSpec{
{d2enum.RegionAct1Catacombs, 258, 299, []int{}}, {d2enum.RegionAct1Catacombs, 258, 299, []int{}},
{d2enum.RegionAct1Tristram, 300, 300, []int{}}, {d2enum.RegionAct1Tristram, 300, 300, []int{}},
//Act II // Act II
{d2enum.RegionAct2Town, 301, 301, []int{}}, {d2enum.RegionAct2Town, 301, 301, []int{}},
{d2enum.RegionAct2Sewer, 302, 352, []int{}}, {d2enum.RegionAct2Sewer, 302, 352, []int{}},
{d2enum.RegionAct2Harem, 353, 357, []int{}}, {d2enum.RegionAct2Harem, 353, 357, []int{}},
@ -51,24 +52,24 @@ var regions = []RegionSpec{
{d2enum.RegionAct2Lair, 482, 509, []int{}}, {d2enum.RegionAct2Lair, 482, 509, []int{}},
{d2enum.RegionAct2Arcane, 510, 528, []int{}}, {d2enum.RegionAct2Arcane, 510, 528, []int{}},
//Act III // Act III
{d2enum.RegionAct3Town, 529, 529, []int{}}, {d2enum.RegionAct3Town, 529, 529, []int{}},
{d2enum.RegionAct3Jungle, 530, 604, []int{}}, {d2enum.RegionAct3Jungle, 530, 604, []int{}},
{d2enum.RegionAct3Kurast, 605, 658, []int{ {d2enum.RegionAct3Kurast, 605, 658, []int{
748, 749, 750, 751, 752, 753, 754, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769,
755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791,
//yeah, i know =( 792, 793, 794, 795, 796,
}}, }},
{d2enum.RegionAct3Spider, 659, 664, []int{}}, {d2enum.RegionAct3Spider, 659, 664, []int{}},
{d2enum.RegionAct3Dungeon, 665, 704, []int{}}, {d2enum.RegionAct3Dungeon, 665, 704, []int{}},
{d2enum.RegionAct3Sewer, 705, 747, []int{}}, {d2enum.RegionAct3Sewer, 705, 747, []int{}},
//Act IV // Act IV
{d2enum.RegionAct4Town, 797, 798, []int{}}, {d2enum.RegionAct4Town, 797, 798, []int{}},
{d2enum.RegionAct4Mesa, 799, 835, []int{}}, {d2enum.RegionAct4Mesa, 799, 835, []int{}},
{d2enum.RegionAct4Lava, 836, 862, []int{}}, {d2enum.RegionAct4Lava, 836, 862, []int{}},
//Act V -- broken or wrong order // Act V -- broken or wrong order
{d2enum.RegonAct5Town, 863, 864, []int{}}, {d2enum.RegonAct5Town, 863, 864, []int{}},
{d2enum.RegionAct5Siege, 865, 879, []int{}}, {d2enum.RegionAct5Siege, 865, 879, []int{}},
{d2enum.RegionAct5Barricade, 880, 1002, []int{}}, {d2enum.RegionAct5Barricade, 880, 1002, []int{}},
@ -88,37 +89,42 @@ type MapEngineTest struct {
currentRegion int currentRegion int
levelPreset int levelPreset int
fileIndex int fileIndex int
regionSpec RegionSpec regionSpec regionSpec
filesCount int filesCount int
debugVisLevel int debugVisLevel int
} }
func CreateMapEngineTest(currentRegion int, levelPreset int, term d2interface.Terminal) *MapEngineTest { // CreateMapEngineTest creates the Map Engine Test screen and returns a pointer to it
func CreateMapEngineTest(currentRegion, levelPreset int, term d2interface.Terminal) *MapEngineTest {
result := &MapEngineTest{ result := &MapEngineTest{
currentRegion: currentRegion, currentRegion: currentRegion,
levelPreset: levelPreset, levelPreset: levelPreset,
fileIndex: 0, fileIndex: 0,
regionSpec: RegionSpec{}, regionSpec: regionSpec{},
filesCount: 0, filesCount: 0,
terminal: term, terminal: term,
} }
result.gameState = d2player.CreateTestGameState() result.gameState = d2player.CreateTestGameState()
return result return result
} }
func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) { func (met *MapEngineTest) loadRegionByIndex(n int, levelPreset, fileIndex int) {
log.Printf("Loaded region: Type(%d) LevelPreset(%d) FileIndex(%d)", n, levelPreset, fileIndex) log.Printf("Loaded region: Type(%d) LevelPreset(%d) FileIndex(%d)", n, levelPreset, fileIndex)
d2maprenderer.InvalidateImageCache() d2maprenderer.InvalidateImageCache()
for _, spec := range regions { for _, spec := range regions {
if spec.regionType == d2enum.RegionIdType(n) { if spec.regionType == d2enum.RegionIdType(n) {
met.regionSpec = spec met.regionSpec = spec
inExtra := false inExtra := false
for _, e := range spec.extra { for _, e := range spec.extra {
if e == levelPreset { if e == levelPreset {
inExtra = true inExtra = true
break break
} }
} }
if !inExtra { if !inExtra {
if levelPreset < spec.startPresetIndex { if levelPreset < spec.startPresetIndex {
levelPreset = spec.startPresetIndex levelPreset = spec.startPresetIndex
@ -128,6 +134,7 @@ func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
levelPreset = spec.endPresetIndex levelPreset = spec.endPresetIndex
} }
} }
met.levelPreset = levelPreset met.levelPreset = levelPreset
} }
} }
@ -141,25 +148,39 @@ func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
met.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex, true) met.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex, true)
met.mapEngine.RegenerateWalkPaths() met.mapEngine.RegenerateWalkPaths()
} }
met.mapRenderer.SetMapEngine(met.mapEngine) met.mapRenderer.SetMapEngine(met.mapEngine)
met.mapRenderer.MoveCameraTo(met.mapRenderer.WorldToOrtho(met.mapEngine.GetCenterPosition())) met.mapRenderer.MoveCameraTo(met.mapRenderer.WorldToOrtho(met.mapEngine.GetCenterPosition()))
} }
// OnLoad loads the resources for the Map Engine Test screen
func (met *MapEngineTest) OnLoad(loading d2screen.LoadingState) { func (met *MapEngineTest) OnLoad(loading d2screen.LoadingState) {
d2input.BindHandler(met) if err := d2input.BindHandler(met); err != nil {
fmt.Printf("could not add MapEngineTest as event handler")
}
loading.Progress(0.2) loading.Progress(0.2)
met.mapEngine = d2mapengine.CreateMapEngine() met.mapEngine = d2mapengine.CreateMapEngine()
loading.Progress(0.5) loading.Progress(0.5)
met.mapRenderer = d2maprenderer.CreateMapRenderer(met.mapEngine, met.terminal) met.mapRenderer = d2maprenderer.CreateMapRenderer(met.mapEngine, met.terminal)
loading.Progress(0.7) loading.Progress(0.7)
met.LoadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) met.loadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex)
} }
// OnUnload releases the resources for the Map Engine Test screen
func (met *MapEngineTest) OnUnload() error { func (met *MapEngineTest) OnUnload() error {
d2input.UnbindHandler(met) if err := d2input.UnbindHandler(met); err != nil {
return err
}
return nil return nil
} }
// Render renders the Map Engine Test screen
func (met *MapEngineTest) Render(screen d2interface.Surface) error { func (met *MapEngineTest) Render(screen d2interface.Surface) error {
met.mapRenderer.Render(screen) met.mapRenderer.Render(screen)
@ -260,12 +281,15 @@ func (met *MapEngineTest) Render(screen d2interface.Surface) error {
return nil return nil
} }
// Advance runs the update logic on the Map Engine Test screen
func (met *MapEngineTest) Advance(tickTime float64) error { func (met *MapEngineTest) Advance(tickTime float64) error {
met.mapEngine.Advance(tickTime) met.mapEngine.Advance(tickTime)
met.mapRenderer.Advance(tickTime) met.mapRenderer.Advance(tickTime)
return nil return nil
} }
// OnKeyRepeat is called to handle repeated key presses
func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool { func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool {
var moveSpeed float64 = 8 var moveSpeed float64 = 8
if event.KeyMod == d2input.KeyModShift { if event.KeyMod == d2input.KeyModShift {
@ -295,6 +319,7 @@ func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool {
return false return false
} }
// OnKeyDown defines the actions of the Map Engine Test screen when a key is pressed
func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool { func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyEscape { if event.Key == d2input.KeyEscape {
os.Exit(0) os.Exit(0)
@ -302,14 +327,14 @@ func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool {
} }
if event.Key == d2input.KeyN { if event.Key == d2input.KeyN {
if event.KeyMod == d2input.KeyModControl { switch event.KeyMod {
//met.fileIndex = increment(met.fileIndex, 0, met.filesCount-1) case d2input.KeyModControl:
met.fileIndex++ met.fileIndex++
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} else if event.KeyMod == d2input.KeyModShift { case d2input.KeyModShift:
met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex) met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} else { default:
met.currentRegion = increment(met.currentRegion, 0, len(regions)) met.currentRegion = increment(met.currentRegion, 0, len(regions))
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} }
@ -318,14 +343,14 @@ func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool {
} }
if event.Key == d2input.KeyP { if event.Key == d2input.KeyP {
if event.KeyMod == d2input.KeyModControl { switch event.KeyMod {
//met.fileIndex = decrement(met.fileIndex, 0, met.filesCount-1) case d2input.KeyModControl:
met.fileIndex-- met.fileIndex--
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} else if event.KeyMod == d2input.KeyModShift { case d2input.KeyModShift:
met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex) met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} else { default:
met.currentRegion = decrement(met.currentRegion, 0, len(regions)) met.currentRegion = decrement(met.currentRegion, 0, len(regions))
d2screen.SetNextScreen(met) d2screen.SetNextScreen(met)
} }
@ -341,6 +366,7 @@ func increment(v, min, max int) int {
if v > max { if v > max {
return min return min
} }
return v return v
} }
@ -349,5 +375,6 @@ func decrement(v, min, max int) int {
if v < min { if v < min {
return max return max
} }
return v return v
} }

View File

@ -80,6 +80,7 @@ func CreateSelectHeroClass(audioProvider d2interface.AudioProvider,
connectionHost: connectionHost, connectionHost: connectionHost,
audioProvider: audioProvider, audioProvider: audioProvider,
} }
return result return result
} }