diff --git a/d2core/d2term/terminal.go b/d2core/d2term/terminal.go index bd673bb6..54a6b908 100644 --- a/d2core/d2term/terminal.go +++ b/d2core/d2term/terminal.go @@ -1,3 +1,4 @@ +// Package d2term provides a in-game terminal that allows executing custom commands for debugging package d2term import ( @@ -45,6 +46,11 @@ const ( termVisHiding ) +const ( + maxVisAnim = 1.0 + minVisAnim = 0.0 +) + type termHistoryEntry struct { text string category d2interface.TermCategory @@ -79,13 +85,13 @@ type terminal struct { func (t *terminal) Advance(elapsed float64) error { switch t.visState { case termVisShowing: - t.visAnim = math.Min(1.0, t.visAnim+elapsed/termAnimLength) - if t.visAnim == 1.0 { + t.visAnim = math.Min(maxVisAnim, t.visAnim+elapsed/termAnimLength) + if t.visAnim == maxVisAnim { t.visState = termVisShown } case termVisHiding: - t.visAnim = math.Max(0.0, t.visAnim-elapsed/termAnimLength) - if t.visAnim == 0.0 { + t.visAnim = math.Max(minVisAnim, t.visAnim-elapsed/termAnimLength) + if t.visAnim == minVisAnim { t.visState = termVisHidden } } @@ -122,12 +128,8 @@ func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool { if t.outputIndex -= t.lineCount; t.outputIndex < 0 { t.outputIndex = 0 } - case d2input.KeyUp: - t.handleKeyUp(event.KeyMod) - case d2input.KeyDown: - if event.KeyMod == d2input.KeyModControl { - t.lineCount = d2common.MinInt(t.lineCount+1, termRowCountMax) - } + case d2input.KeyUp, d2input.KeyDown: + t.handleControlKey(event.Key, event.KeyMod) case d2input.KeyEnter: t.processCommand() case d2input.KeyBackspace: @@ -166,15 +168,22 @@ func (t *terminal) processCommand() { t.command = "" } -func (t *terminal) handleKeyUp(keyMod d2input.KeyMod) { - if keyMod == d2input.KeyModControl { - t.lineCount = d2common.MaxInt(0, t.lineCount-1) - } else if len(t.commandHistory) > 0 { - t.command = t.commandHistory[t.commandIndex] - if t.commandIndex == 0 { - t.commandIndex = len(t.commandHistory) - 1 - } else { - t.commandIndex-- +func (t *terminal) handleControlKey(eventKey d2input.Key, keyMod d2input.KeyMod) { + switch eventKey { + case d2input.KeyUp: + if keyMod == d2input.KeyModControl { + t.lineCount = d2common.MaxInt(0, t.lineCount-1) + } else if len(t.commandHistory) > 0 { + t.command = t.commandHistory[t.commandIndex] + if t.commandIndex == 0 { + 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") } - 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 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") - } + paramValues, err := parseActionParams(actionType, actionParams) + if err != nil { + return err } actionValue := reflect.ValueOf(actionEntry.action) @@ -331,6 +304,51 @@ func (t *terminal) Execute(command string) error { 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) { var line string diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index bcfe8bc8..7252038c 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -1,6 +1,7 @@ package d2gamescreen import ( + "fmt" "image/color" "math" "os" @@ -22,6 +23,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" ) +// CharacterSelect represents the character select screen type CharacterSelect struct { background *d2ui.Sprite newCharButton d2ui.Button @@ -49,8 +51,12 @@ type CharacterSelect struct { terminal d2interface.Terminal } -func CreateCharacterSelect(audioProvider d2interface.AudioProvider, - connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string, term d2interface.Terminal) *CharacterSelect { +// CreateCharacterSelect creates the character select screen and returns a pointer to it +func CreateCharacterSelect( + audioProvider d2interface.AudioProvider, + connectionType d2clientconnectiontype.ClientConnectionType, + connectionHost string, term d2interface.Terminal, +) *CharacterSelect { return &CharacterSelect{ selectedCharacter: -1, 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) { 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) 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(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.OnActivated(func() { v.onNewCharButtonClicked() }) 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.SetEnabled(false) 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.SetPosition(433, 468) d2ui.AddWidget(&v.deleteCharButton) @@ -106,46 +175,6 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) { 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 - 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() { @@ -155,6 +184,7 @@ func (v *CharacterSelect) onScrollUpdate() { func (v *CharacterSelect) updateCharacterBoxes() { expText := "EXPANSION CHARACTER" + for i := 0; i < 8; i++ { idx := i + (v.charScrollbar.GetCurrentOffset() * 2) if idx >= len(v.gameStates) { @@ -162,8 +192,10 @@ func (v *CharacterSelect) updateCharacterBoxes() { v.characterStatsLabel[i].SetText("") v.characterExpLabel[i].SetText("") v.characterImage[i] = nil + continue } + v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName) v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String()) v.characterExpLabel[i].SetText(expText) @@ -182,22 +214,31 @@ func (v *CharacterSelect) onNewCharButtonClicked() { func (v *CharacterSelect) onExitButtonClicked() { mainMenu := CreateMainMenu(v.audioProvider, v.terminal) - mainMenu.SetScreenMode(ScreenModeMainMenu) + mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) } +// Render renders the Character Select screen 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) actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) + 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++ { idx := i + (v.charScrollbar.GetCurrentOffset() * 2) if idx >= len(v.gameStates) { continue } + v.characterNameLabel[i].Render(screen) v.characterStatsLabel[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) screen.Pop() } + if v.showDeleteConfirmation { 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) } @@ -219,6 +265,7 @@ func (v *CharacterSelect) moveSelectionBox() { v.d2HeroTitle.SetText("") return } + bw := 272 bh := 92 selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2) @@ -226,6 +273,7 @@ func (v *CharacterSelect) moveSelectionBox() { 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 { if !v.showDeleteConfirmation { if event.Button == d2input.MouseButtonLeft { @@ -234,23 +282,29 @@ func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool { bh := 92 localMouseX := mx - 37 localMouseY := my - 86 + if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 { adjustY := localMouseY / bh selectedIndex := adjustY * 2 + if localMouseX > bw { - selectedIndex += 1 + selectedIndex++ } + if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) { v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex v.moveSelectionBox() } } + return true } } + return false } +// Advance runs the update logic on the Character Select screen func (v *CharacterSelect) Advance(tickTime float64) error { for _, hero := range v.characterImage { if hero != nil { @@ -291,6 +345,7 @@ func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) { func (v *CharacterSelect) refreshGameStates() { v.gameStates = d2player.GetAllPlayerStates() v.updateCharacterBoxes() + if len(v.gameStates) > 0 { v.selectedCharacter = 0 v.d2HeroTitle.SetText(v.gameStates[0].HeroName) @@ -299,17 +354,21 @@ func (v *CharacterSelect) refreshGameStates() { v.selectedCharacter = -1 v.charScrollbar.SetMaxOffset(0) } - v.moveSelectionBox() + v.moveSelectionBox() } func (v *CharacterSelect) onOkButtonClicked() { gameClient, _ := d2client.Create(v.connectionType) - switch v.connectionType { - case d2clientconnectiontype.LANClient: - gameClient.Open(v.connectionHost, v.gameStates[v.selectedCharacter].FilePath) - default: - gameClient.Open("", v.gameStates[v.selectedCharacter].FilePath) + + host := "" + if v.connectionType == d2clientconnectiontype.LANClient { + host = v.connectionHost + } + + 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)) diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index 2de2682b..78ffcce9 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -2,6 +2,7 @@ package d2gamescreen import ( "bufio" + "fmt" "image/color" "log" "os" @@ -46,24 +47,32 @@ func CreateCredits(audioProvider d2interface.AudioProvider) *Credits { cyclesTillNextLine: 0, audioProvider: audioProvider, } + 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 func (v *Credits) LoadContributors() []string { - var contributors []string file, err := os.Open(path.Join("./", "CONTRIBUTORS")) if err != nil || file == nil { log.Print("CONTRIBUTORS file is missing") 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) + + var contributors []string for scanner.Scan() { contributors = append(contributors, strings.Trim(scanner.Text(), " ")) } + return contributors } @@ -85,13 +94,16 @@ func (v *Credits) OnLoad(loading d2screen.LoadingState) { loading.Error(err) return } + loading.Progress(0.6) creditData, _ := d2common.Utf16BytesToString(fileData[2:]) v.creditsText = strings.Split(creditData, "\r\n") + for i := range v.creditsText { v.creditsText[i] = strings.Trim(v.creditsText[i], " ") } + loading.Progress(0.8) v.creditsText = append(v.LoadContributors(), v.creditsText...) @@ -99,11 +111,16 @@ func (v *Credits) OnLoad(loading d2screen.LoadingState) { // Render renders the credits screen 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 { if label.Available { continue } + label.Label.Render(screen) } @@ -112,12 +129,13 @@ func (v *Credits) Render(screen d2interface.Surface) error { 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 { v.cycleTime += tickTime for v.cycleTime >= secondsPerCycle { v.cycleTime -= secondsPerCycle v.cyclesTillNextLine-- + if !v.doneWithCredits && v.cyclesTillNextLine <= 0 { v.addNextItem() } @@ -126,6 +144,7 @@ func (v *Credits) Advance(tickTime float64) error { if label.Available { continue } + if label.Label.Y-1 < -15 { label.Available = true continue @@ -139,7 +158,7 @@ func (v *Credits) Advance(tickTime float64) error { func (v *Credits) onExitButtonClicked() { mainMenu := CreateMainMenu(v.audioProvider, v.terminal) - mainMenu.SetScreenMode(ScreenModeMainMenu) + mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) } @@ -151,55 +170,71 @@ func (v *Credits) addNextItem() { text := v.creditsText[0] 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 - return - } else if len(text) == 0 && v.creditsText[0][0] == '*' { - v.cyclesTillNextLine = 38 + return } + isHeading := text[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) + if isHeading { label.SetText(text[1:]) } else { 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() - isDoubled := false + if !isHeading && !isNextHeading && !isNextSpace { isDoubled = true - // Gotta go side by side label.SetPosition(400-width, 605) text2 := v.creditsText[0] 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.SetText(text2) label2.SetPosition(410, 605) - } else { - label.SetPosition(405-width/2, 605) + + return isDoubled, nextHeading } - if isHeading && isNextHeading { - v.cyclesTillNextLine = 38 - } else if isNextHeading { - if isDoubled { - v.cyclesTillNextLine = 38 - } else { - v.cyclesTillNextLine = 57 - } - } else if isHeading { - v.cyclesTillNextLine = 38 - } else { - v.cyclesTillNextLine = 19 - } + label.SetPosition(405-width/2, 605) + + return isDoubled, isNextHeading } func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label { @@ -211,6 +246,7 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label { } else { label.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255} } + 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} } else { newLabelItem.Label.Color = color.RGBA{R: 198, G: 178, B: 150, A: 255} - } v.labels = append(v.labels, newLabelItem) + return &newLabelItem.Label } diff --git a/d2game/d2gamescreen/escape_menu.go b/d2game/d2gamescreen/escape_menu.go index b63176c7..66fc3c62 100644 --- a/d2game/d2gamescreen/escape_menu.go +++ b/d2game/d2gamescreen/escape_menu.go @@ -26,35 +26,38 @@ const ( menuSize = 500 // layouts - noLayoutID layoutID = -2 - saveLayoutID = -1 - mainLayoutID = 0 - optionsLayoutID = 1 - soundOptionsLayoutID = 2 - videoOptionsLayoutID = 3 - automapOptionsLayoutID = 4 - configureControlsLayoutID = 5 + noLayoutID layoutID = iota - 2 + saveLayoutID + mainLayoutID + optionsLayoutID + soundOptionsLayoutID + videoOptionsLayoutID + automapOptionsLayoutID + configureControlsLayoutID - // options - optAudioSoundVolume optionID = 0 // audio - optAudioMusicVolume = 1 - optAudio3dSound = 2 - optAudioHardwareAcceleration = 3 - optAudioEnvEffects = 4 - optAudioNpcSpeech = 5 - optVideoResolution = 6 // video - optVideoLightingQuality = 7 - optVideoBlendedShadows = 8 - optVideoPerspective = 9 - optVideoGamma = 10 - optVideoContrast = 11 - optAutomapSize = 12 // automap - optAutomapFade = 13 - optAutomapCenterWhenCleared = 14 - optAutomapShowParty = 15 - optAutomapShowNames = 16 + // audio + optAudioSoundVolume optionID = iota + optAudioMusicVolume + optAudio3dSound + optAudioHardwareAcceleration + optAudioEnvEffects + optAudioNpcSpeech + // video + optVideoResolution + optVideoLightingQuality + optVideoBlendedShadows + optVideoPerspective + optVideoGamma + optVideoContrast + // automap + optAutomapSize + optAutomapFade + optAutomapCenterWhenCleared + optAutomapShowParty + optAutomapShowNames ) +// EscapeMenu represents the in-game menu that shows up when the esc key is pressed type EscapeMenu struct { isOpen bool selectSound d2interface.SoundEffect @@ -104,8 +107,13 @@ func (l *enumLabel) Trigger() { l.playSound() next := (l.current + 1) % len(l.values) 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 { @@ -113,6 +121,7 @@ type actionableElement interface { Trigger() } +// NewEscapeMenu creates a new escape menu func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *EscapeMenu { m := &EscapeMenu{ audioProvider: audioProvider, @@ -127,6 +136,7 @@ func NewEscapeMenu(audioProvider d2interface.AudioProvider, term d2interface.Ter automapOptionsLayoutID: m.newAutomapOptionsLayout(), configureControlsLayoutID: m.newConfigureControlsLayout(), } + return m } @@ -157,7 +167,7 @@ func (m *EscapeMenu) newSoundOptionsLayout() *layout { m.addEnumLabel(l, optAudioHardwareAcceleration, "HARDWARE ACCELERATION", []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.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, optVideoGamma, "GAMMA", []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, optAutomapShowParty, "SHOW PARTY", []string{"YES", "NO"}) m.addEnumLabel(l, optAutomapShowNames, "SHOW NAMES", []string{"YES", "NO"}) - m.addPreviousMenuLabel(l, optionsLayoutID) + m.addPreviousMenuLabel(l) }) } func (m *EscapeMenu) newConfigureControlsLayout() *layout { return m.wrapLayout(func(l *layout) { 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 wrapper.AddSpacerDynamic() + return &layout{ Layout: wrapper, leftPent: leftPent, @@ -230,7 +241,11 @@ func (m *EscapeMenu) wrapLayout(fn func(*layout)) *layout { } 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) } @@ -240,7 +255,9 @@ func (m *EscapeMenu) addBigSelectionLabel(l *layout, text string, targetLayout l label.SetMouseClickHandler(func(_ d2input.MouseEvent) { label.Trigger() }) + elID := len(l.actionableElements) + label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { m.onHoverElement(elID) }) @@ -248,17 +265,20 @@ func (m *EscapeMenu) addBigSelectionLabel(l *layout, text string, targetLayout l l.actionableElements = append(l.actionableElements, label) } -func (m *EscapeMenu) addPreviousMenuLabel(l *layout, targetLayout layoutID) { +func (m *EscapeMenu) addPreviousMenuLabel(l *layout) { l.AddSpacerStatic(10, labelGutter) 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.Trigger() }) + elID := len(l.actionableElements) + label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { m.onHoverElement(elID) }) + 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) layout := &layout{Layout: guiLayout} 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) + layout.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) { m.onHoverElement(elID) }) @@ -282,6 +308,7 @@ func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values playSound: m.playSound, updateValue: m.onUpdateValue, } + layout.SetMouseClickHandler(func(_ d2input.MouseEvent) { label.Trigger() }) @@ -289,13 +316,13 @@ func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values l.actionableElements = append(l.actionableElements, label) } -func (m *EscapeMenu) OnLoad() { +func (m *EscapeMenu) onLoad() { m.selectSound, _ = m.audioProvider.LoadSoundEffect(d2resource.SFXCursorSelect) } -func (m *EscapeMenu) OnEscKey() { +func (m *EscapeMenu) onEscKey() { if !m.isOpen { - m.Open() + m.open() return } @@ -311,19 +338,16 @@ func (m *EscapeMenu) OnEscKey() { return } - m.Close() + m.close() } -func (m *EscapeMenu) IsOpen() bool { - return m.isOpen -} - -func (m *EscapeMenu) Close() { +func (m *EscapeMenu) close() { m.isOpen = false + d2gui.SetLayout(nil) } -func (m *EscapeMenu) Open() { +func (m *EscapeMenu) open() { m.isOpen = true m.setLayout(mainLayoutID) } @@ -336,14 +360,15 @@ func (m *EscapeMenu) showLayout(id layoutID) { m.playSound() if id == noLayoutID { - m.Close() + m.close() return } if id == saveLayoutID { mainMenu := CreateMainMenu(m.audioProvider, m.terminal) - mainMenu.SetScreenMode(ScreenModeMainMenu) + mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) + return } @@ -358,12 +383,10 @@ func (m *EscapeMenu) onHoverElement(id int) { m.leftPent.SetPosition(x, y+10) x, _ = m.rightPent.GetPosition() m.rightPent.SetPosition(x, y+10) - - return } 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) { @@ -379,6 +402,7 @@ func (m *EscapeMenu) onUpKey() { if !m.isOpen { return } + if m.layouts[m.currentLayout].currentEl == 0 { return } @@ -390,6 +414,7 @@ func (m *EscapeMenu) onDownKey() { if !m.isOpen { return } + if m.layouts[m.currentLayout].currentEl == len(m.layouts[m.currentLayout].actionableElements)-1 { return } @@ -401,13 +426,15 @@ func (m *EscapeMenu) onEnterKey() { if !m.isOpen { return } + 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 { switch event.Key { case d2input.KeyEscape: - m.OnEscKey() + m.onEscKey() case d2input.KeyUp: m.onUpKey() case d2input.KeyDown: @@ -417,5 +444,6 @@ func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool { default: return false } - return false + + return true } diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index fb8f1da5..fd7abcd7 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -43,8 +43,9 @@ func CreateGame(audioProvider d2interface.AudioProvider, gameClient *d2client.Ga audioProvider: audioProvider, terminal: term, } - result.escapeMenu.OnLoad() + result.escapeMenu.onLoad() d2input.BindHandler(result.escapeMenu) + return result } @@ -55,6 +56,7 @@ func (v *Game) OnLoad(loading d2screen.LoadingState) { func (v *Game) OnUnload() error { d2input.UnbindHandler(v.gameControls) // TODO: hack v.gameClient.Close() + return nil } @@ -77,7 +79,7 @@ func (v *Game) Render(screen d2interface.Surface) error { var hideZoneTextAfterSeconds = 2.0 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 } @@ -101,6 +103,7 @@ func (v *Game) Advance(tickTime float64) error { v.gameControls.ShowZoneChangeText() v.gameControls.HideZoneChangeTextAfter(hideZoneTextAfterSeconds) } + v.lastRegionType = tile.RegionType } } @@ -112,6 +115,7 @@ func (v *Game) Advance(tickTime float64) error { if player.Id != v.gameClient.PlayerId { continue } + v.localPlayer = player v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal) 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) v.mapRenderer.MoveCameraTo(rx, ry) } + return nil } diff --git a/d2game/d2gamescreen/gui_testing.go b/d2game/d2gamescreen/gui_testing.go index 9b97c260..7f7bff84 100644 --- a/d2game/d2gamescreen/gui_testing.go +++ b/d2game/d2gamescreen/gui_testing.go @@ -14,6 +14,7 @@ func CreateGuiTestMain() *GuiTestMain { func (g *GuiTestMain) OnLoad(loading d2screen.LoadingState) { layout := d2gui.CreateLayout(d2gui.PositionTypeHorizontal) + loading.Progress(0.3) // layoutLeft := layout.AddLayout(d2gui.PositionTypeVertical) diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index ae96c285..9aff199a 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -24,27 +24,27 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) -type MainMenuScreenMode int +type mainMenuScreenMode int const ( - ScreenModeUnknown MainMenuScreenMode = iota - ScreenModeTrademark - ScreenModeMainMenu - ScreenModeMultiplayer - ScreenModeTcpIp - ScreenModeServerIp + screenModeUnknown mainMenuScreenMode = iota + screenModeTrademark + screenModeMainMenu + screenModeMultiplayer + screenModeTCPIP + screenModeServerIP ) // MainMenu represents the main menu type MainMenu struct { - tcpIpBackground *d2ui.Sprite + tcpIPBackground *d2ui.Sprite trademarkBackground *d2ui.Sprite background *d2ui.Sprite diabloLogoLeft *d2ui.Sprite diabloLogoRight *d2ui.Sprite diabloLogoLeftBack *d2ui.Sprite diabloLogoRightBack *d2ui.Sprite - serverIpBackground *d2ui.Sprite + serverIPBackground *d2ui.Sprite singlePlayerButton d2ui.Button multiplayerButton d2ui.Button githubButton d2ui.Button @@ -52,22 +52,22 @@ type MainMenu struct { creditsButton d2ui.Button cinematicsButton d2ui.Button mapTestButton d2ui.Button - networkTcpIpButton d2ui.Button + networkTCPIPButton d2ui.Button networkCancelButton d2ui.Button - btnTcpIpCancel d2ui.Button - btnTcpIpHostGame d2ui.Button - btnTcpIpJoinGame d2ui.Button - btnServerIpCancel d2ui.Button - btnServerIpOk d2ui.Button + btnTCPIPCancel d2ui.Button + btnTCPIPHostGame d2ui.Button + btnTCPIPJoinGame d2ui.Button + btnServerIPCancel d2ui.Button + btnServerIPOk d2ui.Button copyrightLabel d2ui.Label copyrightLabel2 d2ui.Label openDiabloLabel d2ui.Label versionLabel d2ui.Label commitLabel d2ui.Label - tcpIpOptionsLabel d2ui.Label + tcpIPOptionsLabel d2ui.Label tcpJoinGameLabel d2ui.Label tcpJoinGameEntry d2ui.TextBox - screenMode MainMenuScreenMode + screenMode mainMenuScreenMode leftButtonHeld bool audioProvider d2interface.AudioProvider terminal d2interface.Terminal @@ -76,18 +76,59 @@ type MainMenu struct { // CreateMainMenu creates an instance of MainMenu func CreateMainMenu(audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu { return &MainMenu{ - screenMode: ScreenModeUnknown, + screenMode: screenModeUnknown, leftButtonHeld: true, audioProvider: audioProvider, 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) { v.audioProvider.PlayBGM(d2resource.BGMTitle) 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.Alignment = d2ui.LabelAlignRight v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch) @@ -120,19 +161,22 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { v.openDiabloLabel.SetPosition(400, 580) loading.Progress(0.5) - animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky) - v.background, _ = d2ui.LoadSprite(animation) - v.background.SetPosition(0, 0) + 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.TrademarkScreen, d2resource.PaletteSky) - v.trademarkBackground, _ = d2ui.LoadSprite(animation) - v.trademarkBackground.SetPosition(0, 0) + 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))) - animation, _ = d2asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky) - v.tcpIpBackground, _ = d2ui.LoadSprite(animation) - v.tcpIpBackground.SetPosition(0, 0) + v.tcpJoinGameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255} + v.tcpJoinGameLabel.SetPosition(400, 190) +} - 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.SetBlend(true) v.diabloLogoLeft.PlayForward() @@ -152,7 +196,9 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits) v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation) v.diabloLogoRightBack.SetPosition(400, 120) +} +func (v *MainMenu) createButtons(loading d2screen.LoadingState) { v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "EXIT DIABLO II") v.exitDiabloButton.SetPosition(264, 535) v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) @@ -173,11 +219,6 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) 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.SetPosition(264, 400) v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) @@ -188,78 +229,69 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) { v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) d2ui.AddWidget(&v.mapTestButton) - 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.btnTCPIPCancel = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("cancel")) + v.btnTCPIPCancel.SetPosition(33, 543) + v.btnTCPIPCancel.OnActivated(func() { v.onTCPIPCancelClicked() }) + 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.SetPosition(264, 540) v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() }) d2ui.AddWidget(&v.networkCancelButton) - v.btnTcpIpCancel = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("cancel")) - v.btnTcpIpCancel.SetPosition(33, 543) - v.btnTcpIpCancel.OnActivated(func() { v.onTcpIpCancelClicked() }) - d2ui.AddWidget(&v.btnTcpIpCancel) + v.btnTCPIPHostGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "HOST GAME") + v.btnTCPIPHostGame.SetPosition(264, 280) + v.btnTCPIPHostGame.OnActivated(func() { v.onTCPIPHostGameClicked() }) + d2ui.AddWidget(&v.btnTCPIPHostGame) - v.btnTcpIpHostGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "HOST GAME") - v.btnTcpIpHostGame.SetPosition(264, 280) - v.btnTcpIpHostGame.OnActivated(func() { v.onTcpIpHostGameClicked() }) - d2ui.AddWidget(&v.btnTcpIpHostGame) - - 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) + v.btnTCPIPJoinGame = d2ui.CreateButton(d2ui.ButtonTypeWide, "JOIN GAME") + v.btnTCPIPJoinGame.SetPosition(264, 320) + v.btnTCPIPJoinGame.OnActivated(func() { v.onTCPIPJoinGameClicked() }) + d2ui.AddWidget(&v.btnTCPIPJoinGame) } func (v *MainMenu) onMapTestClicked() { 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 switch runtime.GOOS { @@ -272,23 +304,10 @@ func openbrowser(url string) { default: err = fmt.Errorf("unsupported platform") } + if err != nil { 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() { @@ -301,40 +320,78 @@ func (v *MainMenu) onCreditsButtonClicked() { // Render renders the main menu 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 { - case ScreenModeTrademark: - v.trademarkBackground.RenderSegmented(screen, 4, 3, 0) - case ScreenModeServerIp: - fallthrough - case ScreenModeTcpIp: - v.tcpIpBackground.RenderSegmented(screen, 4, 3, 0) + case screenModeTrademark: + if err := v.trademarkBackground.RenderSegmented(screen, 4, 3, 0); err != nil { + return err + } + case screenModeServerIP: + 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: - 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 { - case ScreenModeTrademark: - fallthrough - case ScreenModeMainMenu: - fallthrough - case ScreenModeMultiplayer: - v.diabloLogoLeftBack.Render(screen) - v.diabloLogoRightBack.Render(screen) - v.diabloLogoLeft.Render(screen) - v.diabloLogoRight.Render(screen) + case screenModeTrademark, screenModeMainMenu, screenModeMultiplayer: + if err := v.diabloLogoLeftBack.Render(screen); err != nil { + return err + } + + if err := v.diabloLogoRightBack.Render(screen); err != nil { + return err + } + + 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 { - case ScreenModeServerIp: - v.tcpIpOptionsLabel.Render(screen) - v.serverIpBackground.RenderSegmented(screen, 2, 1, 0) + case screenModeServerIP: + v.tcpIPOptionsLabel.Render(screen) v.tcpJoinGameLabel.Render(screen) - case ScreenModeTcpIp: - v.tcpIpOptionsLabel.Render(screen) - case ScreenModeTrademark: + case screenModeTCPIP: + v.tcpIPOptionsLabel.Render(screen) + case screenModeTrademark: v.copyrightLabel.Render(screen) v.copyrightLabel2.Render(screen) - case ScreenModeMainMenu: + case screenModeMainMenu: v.openDiabloLabel.Render(screen) v.versionLabel.Render(screen) v.commitLabel.Render(screen) @@ -343,37 +400,47 @@ func (v *MainMenu) Render(screen d2interface.Surface) error { 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 { switch v.screenMode { - case ScreenModeMainMenu: - fallthrough - case ScreenModeTrademark: - fallthrough - case ScreenModeMultiplayer: - v.diabloLogoLeftBack.Advance(tickTime) - v.diabloLogoRightBack.Advance(tickTime) - v.diabloLogoLeft.Advance(tickTime) - v.diabloLogoRight.Advance(tickTime) + case screenModeMainMenu, screenModeTrademark, screenModeMultiplayer: + if err := v.diabloLogoLeftBack.Advance(tickTime); err != nil { + return err + } + + if err := v.diabloLogoRightBack.Advance(tickTime); err != nil { + return err + } + + if err := v.diabloLogoLeft.Advance(tickTime); err != nil { + return err + } + + if err := v.diabloLogoRight.Advance(tickTime); err != nil { + return err + } } return nil } +// OnMouseButtonDown is called when a mouse button is clicked func (v *MainMenu) OnMouseButtonDown(event d2input.MouseEvent) bool { - if v.screenMode == ScreenModeTrademark && event.Button == d2input.MouseButtonLeft { - v.SetScreenMode(ScreenModeMainMenu) + if v.screenMode == screenModeTrademark && event.Button == d2input.MouseButtonLeft { + v.setScreenMode(screenModeMainMenu) return true } + return false } -func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) { +func (v *MainMenu) setScreenMode(screenMode mainMenuScreenMode) { v.screenMode = screenMode - isMainMenu := screenMode == ScreenModeMainMenu - isMultiplayer := screenMode == ScreenModeMultiplayer - isTcpIp := screenMode == ScreenModeTcpIp - isServerIp := screenMode == ScreenModeServerIp + isMainMenu := screenMode == screenModeMainMenu + isMultiplayer := screenMode == screenModeMultiplayer + isTCPIP := screenMode == screenModeTCPIP + isServerIP := screenMode == screenModeServerIP + v.exitDiabloButton.SetVisible(isMainMenu) v.creditsButton.SetVisible(isMainMenu) v.cinematicsButton.SetVisible(isMainMenu) @@ -381,47 +448,49 @@ func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) { v.githubButton.SetVisible(isMainMenu) v.mapTestButton.SetVisible(isMainMenu) v.multiplayerButton.SetVisible(isMainMenu) - v.networkTcpIpButton.SetVisible(isMultiplayer) + v.networkTCPIPButton.SetVisible(isMultiplayer) v.networkCancelButton.SetVisible(isMultiplayer) - v.btnTcpIpCancel.SetVisible(isTcpIp) - v.btnTcpIpHostGame.SetVisible(isTcpIp) - v.btnTcpIpJoinGame.SetVisible(isTcpIp) - v.tcpJoinGameEntry.SetVisible(isServerIp) - if isServerIp { + v.btnTCPIPCancel.SetVisible(isTCPIP) + v.btnTCPIPHostGame.SetVisible(isTCPIP) + v.btnTCPIPJoinGame.SetVisible(isTCPIP) + v.tcpJoinGameEntry.SetVisible(isServerIP) + + if isServerIP { v.tcpJoinGameEntry.Activate() } - v.btnServerIpOk.SetVisible(isServerIp) - v.btnServerIpCancel.SetVisible(isServerIp) + + v.btnServerIPOk.SetVisible(isServerIP) + v.btnServerIPCancel.SetVisible(isServerIP) } func (v *MainMenu) onNetworkCancelClicked() { - v.SetScreenMode(ScreenModeMainMenu) + v.setScreenMode(screenModeMainMenu) } func (v *MainMenu) onMultiplayerClicked() { - v.SetScreenMode(ScreenModeMultiplayer) + v.setScreenMode(screenModeMultiplayer) } -func (v *MainMenu) onNetworkTcpIpClicked() { - v.SetScreenMode(ScreenModeTcpIp) +func (v *MainMenu) onNetworkTCPIPClicked() { + v.setScreenMode(screenModeTCPIP) } -func (v *MainMenu) onTcpIpCancelClicked() { - v.SetScreenMode(ScreenModeMultiplayer) +func (v *MainMenu) onTCPIPCancelClicked() { + v.setScreenMode(screenModeMultiplayer) } -func (v *MainMenu) onTcpIpHostGameClicked() { +func (v *MainMenu) onTCPIPHostGameClicked() { d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANServer, "", v.terminal)) } -func (v *MainMenu) onTcpIpJoinGameClicked() { - v.SetScreenMode(ScreenModeServerIp) +func (v *MainMenu) onTCPIPJoinGameClicked() { + v.setScreenMode(screenModeServerIP) } -func (v *MainMenu) onBtnTcpIpCancelClicked() { - v.SetScreenMode(ScreenModeTcpIp) +func (v *MainMenu) onBtnTCPIPCancelClicked() { + v.setScreenMode(screenModeTCPIP) } -func (v *MainMenu) onBtnTcpIpOkClicked() { +func (v *MainMenu) onBtnTCPIPOkClicked() { d2screen.SetNextScreen(CreateCharacterSelect(v.audioProvider, d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal)) } diff --git a/d2game/d2gamescreen/map_engine_testing.go b/d2game/d2gamescreen/map_engine_testing.go index ca5a4f01..833d7382 100644 --- a/d2game/d2gamescreen/map_engine_testing.go +++ b/d2game/d2gamescreen/map_engine_testing.go @@ -1,6 +1,7 @@ package d2gamescreen import ( + "fmt" "log" "os" "time" @@ -17,15 +18,15 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" ) -type RegionSpec struct { +type regionSpec struct { regionType d2enum.RegionIdType startPresetIndex int endPresetIndex int extra []int } -var regions = []RegionSpec{ - //Act I +var regions = []regionSpec{ + // Act I {d2enum.RegionAct1Town, 1, 3, []int{}}, {d2enum.RegionAct1Wilderness, 4, 52, []int{ 108, @@ -41,7 +42,7 @@ var regions = []RegionSpec{ {d2enum.RegionAct1Catacombs, 258, 299, []int{}}, {d2enum.RegionAct1Tristram, 300, 300, []int{}}, - //Act II + // Act II {d2enum.RegionAct2Town, 301, 301, []int{}}, {d2enum.RegionAct2Sewer, 302, 352, []int{}}, {d2enum.RegionAct2Harem, 353, 357, []int{}}, @@ -51,24 +52,24 @@ var regions = []RegionSpec{ {d2enum.RegionAct2Lair, 482, 509, []int{}}, {d2enum.RegionAct2Arcane, 510, 528, []int{}}, - //Act III + // Act III {d2enum.RegionAct3Town, 529, 529, []int{}}, {d2enum.RegionAct3Jungle, 530, 604, []int{}}, {d2enum.RegionAct3Kurast, 605, 658, []int{ - 748, 749, 750, 751, 752, 753, 754, - 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, - //yeah, i know =( + 748, 749, 750, 751, 752, 753, 754, 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, }}, {d2enum.RegionAct3Spider, 659, 664, []int{}}, {d2enum.RegionAct3Dungeon, 665, 704, []int{}}, {d2enum.RegionAct3Sewer, 705, 747, []int{}}, - //Act IV + // Act IV {d2enum.RegionAct4Town, 797, 798, []int{}}, {d2enum.RegionAct4Mesa, 799, 835, []int{}}, {d2enum.RegionAct4Lava, 836, 862, []int{}}, - //Act V -- broken or wrong order + // Act V -- broken or wrong order {d2enum.RegonAct5Town, 863, 864, []int{}}, {d2enum.RegionAct5Siege, 865, 879, []int{}}, {d2enum.RegionAct5Barricade, 880, 1002, []int{}}, @@ -88,37 +89,42 @@ type MapEngineTest struct { currentRegion int levelPreset int fileIndex int - regionSpec RegionSpec + regionSpec regionSpec filesCount 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{ currentRegion: currentRegion, levelPreset: levelPreset, fileIndex: 0, - regionSpec: RegionSpec{}, + regionSpec: regionSpec{}, filesCount: 0, terminal: term, } result.gameState = d2player.CreateTestGameState() + 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) d2maprenderer.InvalidateImageCache() + for _, spec := range regions { if spec.regionType == d2enum.RegionIdType(n) { met.regionSpec = spec inExtra := false + for _, e := range spec.extra { if e == levelPreset { inExtra = true break } } + if !inExtra { if levelPreset < spec.startPresetIndex { levelPreset = spec.startPresetIndex @@ -128,6 +134,7 @@ func (met *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) { levelPreset = spec.endPresetIndex } } + 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.RegenerateWalkPaths() } + met.mapRenderer.SetMapEngine(met.mapEngine) 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) { - d2input.BindHandler(met) + if err := d2input.BindHandler(met); err != nil { + fmt.Printf("could not add MapEngineTest as event handler") + } + loading.Progress(0.2) + met.mapEngine = d2mapengine.CreateMapEngine() + loading.Progress(0.5) + met.mapRenderer = d2maprenderer.CreateMapRenderer(met.mapEngine, met.terminal) + 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 { - d2input.UnbindHandler(met) + if err := d2input.UnbindHandler(met); err != nil { + return err + } + return nil } +// Render renders the Map Engine Test screen func (met *MapEngineTest) Render(screen d2interface.Surface) error { met.mapRenderer.Render(screen) @@ -260,12 +281,15 @@ func (met *MapEngineTest) Render(screen d2interface.Surface) error { return nil } +// Advance runs the update logic on the Map Engine Test screen func (met *MapEngineTest) Advance(tickTime float64) error { met.mapEngine.Advance(tickTime) met.mapRenderer.Advance(tickTime) + return nil } +// OnKeyRepeat is called to handle repeated key presses func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool { var moveSpeed float64 = 8 if event.KeyMod == d2input.KeyModShift { @@ -295,6 +319,7 @@ func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool { 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 { if event.Key == d2input.KeyEscape { os.Exit(0) @@ -302,14 +327,14 @@ func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool { } if event.Key == d2input.KeyN { - if event.KeyMod == d2input.KeyModControl { - //met.fileIndex = increment(met.fileIndex, 0, met.filesCount-1) + switch event.KeyMod { + case d2input.KeyModControl: met.fileIndex++ d2screen.SetNextScreen(met) - } else if event.KeyMod == d2input.KeyModShift { + case d2input.KeyModShift: met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex) d2screen.SetNextScreen(met) - } else { + default: met.currentRegion = increment(met.currentRegion, 0, len(regions)) d2screen.SetNextScreen(met) } @@ -318,14 +343,14 @@ func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool { } if event.Key == d2input.KeyP { - if event.KeyMod == d2input.KeyModControl { - //met.fileIndex = decrement(met.fileIndex, 0, met.filesCount-1) + switch event.KeyMod { + case d2input.KeyModControl: met.fileIndex-- d2screen.SetNextScreen(met) - } else if event.KeyMod == d2input.KeyModShift { + case d2input.KeyModShift: met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex) d2screen.SetNextScreen(met) - } else { + default: met.currentRegion = decrement(met.currentRegion, 0, len(regions)) d2screen.SetNextScreen(met) } @@ -341,6 +366,7 @@ func increment(v, min, max int) int { if v > max { return min } + return v } @@ -349,5 +375,6 @@ func decrement(v, min, max int) int { if v < min { return max } + return v } diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index 9e7eee79..6652bb45 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -80,6 +80,7 @@ func CreateSelectHeroClass(audioProvider d2interface.AudioProvider, connectionHost: connectionHost, audioProvider: audioProvider, } + return result }