OpenDiablo2/d2game/d2gamescreen/main_menu.go

503 lines
16 KiB
Go

package d2gamescreen
import (
"fmt"
"image/color"
"log"
"os"
"os/exec"
"runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
type mainMenuScreenMode int
const (
screenModeUnknown mainMenuScreenMode = iota
screenModeTrademark
screenModeMainMenu
screenModeMultiplayer
screenModeTCPIP
screenModeServerIP
)
// MainMenu represents the main menu
type MainMenu struct {
tcpIPBackground *d2ui.Sprite
trademarkBackground *d2ui.Sprite
background *d2ui.Sprite
diabloLogoLeft *d2ui.Sprite
diabloLogoRight *d2ui.Sprite
diabloLogoLeftBack *d2ui.Sprite
diabloLogoRightBack *d2ui.Sprite
serverIPBackground *d2ui.Sprite
singlePlayerButton d2ui.Button
multiplayerButton d2ui.Button
githubButton d2ui.Button
exitDiabloButton d2ui.Button
creditsButton d2ui.Button
cinematicsButton d2ui.Button
mapTestButton d2ui.Button
networkTCPIPButton d2ui.Button
networkCancelButton 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
tcpJoinGameLabel d2ui.Label
tcpJoinGameEntry d2ui.TextBox
screenMode mainMenuScreenMode
leftButtonHeld bool
renderer d2interface.Renderer
audioProvider d2interface.AudioProvider
terminal d2interface.Terminal
}
// CreateMainMenu creates an instance of MainMenu
func CreateMainMenu(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu {
return &MainMenu{
screenMode: screenModeUnknown,
leftButtonHeld: true,
renderer: renderer,
audioProvider: audioProvider,
terminal: term,
}
}
// 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.renderer)
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(v.renderer, d2resource.FontFormal12, d2resource.PaletteStatic)
v.versionLabel.Alignment = d2ui.LabelAlignRight
v.versionLabel.SetText("OpenDiablo2 - " + d2common.BuildInfo.Branch)
v.versionLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255}
v.versionLabel.SetPosition(795, -10)
v.commitLabel = d2ui.CreateLabel(v.renderer, d2resource.FontFormal10, d2resource.PaletteStatic)
v.commitLabel.Alignment = d2ui.LabelAlignLeft
v.commitLabel.SetText(d2common.BuildInfo.Commit)
v.commitLabel.Color = color.RGBA{R: 255, G: 255, B: 255, A: 255}
v.commitLabel.SetPosition(2, 2)
v.copyrightLabel = d2ui.CreateLabel(v.renderer, d2resource.FontFormal12, d2resource.PaletteStatic)
v.copyrightLabel.Alignment = d2ui.LabelAlignCenter
v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment")
v.copyrightLabel.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255}
v.copyrightLabel.SetPosition(400, 500)
loading.Progress(0.3)
v.copyrightLabel2 = d2ui.CreateLabel(v.renderer, d2resource.FontFormal12, d2resource.PaletteStatic)
v.copyrightLabel2.Alignment = d2ui.LabelAlignCenter
v.copyrightLabel2.SetText("All Rights Reserved.")
v.copyrightLabel2.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255}
v.copyrightLabel2.SetPosition(400, 525)
v.openDiabloLabel = d2ui.CreateLabel(v.renderer, d2resource.FontFormal10, d2resource.PaletteStatic)
v.openDiabloLabel.Alignment = d2ui.LabelAlignCenter
v.openDiabloLabel.SetText("OpenDiablo2 is neither developed by, nor endorsed by Blizzard or its parent company Activision")
v.openDiabloLabel.Color = color.RGBA{R: 255, G: 255, B: 140, A: 255}
v.openDiabloLabel.SetPosition(400, 580)
loading.Progress(0.5)
v.tcpIPOptionsLabel = d2ui.CreateLabel(v.renderer, d2resource.Font42, d2resource.PaletteUnits)
v.tcpIPOptionsLabel.SetPosition(400, 23)
v.tcpIPOptionsLabel.Alignment = d2ui.LabelAlignCenter
v.tcpIPOptionsLabel.SetText("TCP/IP Options")
v.tcpJoinGameLabel = d2ui.CreateLabel(v.renderer, 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)
}
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()
v.diabloLogoLeft.SetPosition(400, 120)
loading.Progress(0.6)
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits)
v.diabloLogoRight, _ = d2ui.LoadSprite(animation)
v.diabloLogoRight.SetBlend(true)
v.diabloLogoRight.PlayForward()
v.diabloLogoRight.SetPosition(400, 120)
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits)
v.diabloLogoLeftBack, _ = d2ui.LoadSprite(animation)
v.diabloLogoLeftBack.SetPosition(400, 120)
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits)
v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation)
v.diabloLogoRightBack.SetPosition(400, 120)
}
func (v *MainMenu) createButtons(loading d2screen.LoadingState) {
v.exitDiabloButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "EXIT DIABLO II")
v.exitDiabloButton.SetPosition(264, 535)
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
d2ui.AddWidget(&v.exitDiabloButton)
v.creditsButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeShort, "CREDITS")
v.creditsButton.SetPosition(264, 505)
v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() })
d2ui.AddWidget(&v.creditsButton)
v.cinematicsButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeShort, "CINEMATICS")
v.cinematicsButton.SetPosition(401, 505)
d2ui.AddWidget(&v.cinematicsButton)
loading.Progress(0.7)
v.singlePlayerButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "SINGLE PLAYER")
v.singlePlayerButton.SetPosition(264, 290)
v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() })
d2ui.AddWidget(&v.singlePlayerButton)
v.githubButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "PROJECT WEBSITE")
v.githubButton.SetPosition(264, 400)
v.githubButton.OnActivated(func() { v.onGithubButtonClicked() })
d2ui.AddWidget(&v.githubButton)
v.mapTestButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "MAP ENGINE TEST")
v.mapTestButton.SetPosition(264, 440)
v.mapTestButton.OnActivated(func() { v.onMapTestClicked() })
d2ui.AddWidget(&v.mapTestButton)
v.btnTCPIPCancel = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, d2common.TranslateString("cancel"))
v.btnTCPIPCancel.SetPosition(33, 543)
v.btnTCPIPCancel.OnActivated(func() { v.onTCPIPCancelClicked() })
d2ui.AddWidget(&v.btnTCPIPCancel)
v.btnServerIPCancel = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "CANCEL")
v.btnServerIPCancel.SetPosition(285, 305)
v.btnServerIPCancel.OnActivated(func() { v.onBtnTCPIPCancelClicked() })
d2ui.AddWidget(&v.btnServerIPCancel)
v.btnServerIPOk = d2ui.CreateButton(v.renderer, 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(v.renderer, d2ui.ButtonTypeWide, "MULTIPLAYER")
v.multiplayerButton.SetPosition(264, 330)
v.multiplayerButton.OnActivated(func() { v.onMultiplayerClicked() })
d2ui.AddWidget(&v.multiplayerButton)
v.networkTCPIPButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "TCP/IP GAME")
v.networkTCPIPButton.SetPosition(264, 280)
v.networkTCPIPButton.OnActivated(func() { v.onNetworkTCPIPClicked() })
d2ui.AddWidget(&v.networkTCPIPButton)
v.networkCancelButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, d2common.TranslateString("cancel"))
v.networkCancelButton.SetPosition(264, 540)
v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() })
d2ui.AddWidget(&v.networkCancelButton)
v.btnTCPIPHostGame = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "HOST GAME")
v.btnTCPIPHostGame.SetPosition(264, 280)
v.btnTCPIPHostGame.OnActivated(func() { v.onTCPIPHostGameClicked() })
d2ui.AddWidget(&v.btnTCPIPHostGame)
v.btnTCPIPJoinGame = d2ui.CreateButton(v.renderer, 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, v.renderer))
}
func (v *MainMenu) onSinglePlayerClicked() {
// Go here only if existing characters are available to select
if d2player.HasGameStates() {
d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider, d2clientconnectiontype.Local,
v.tcpJoinGameEntry.GetText(), v.terminal))
return
}
d2screen.SetNextScreen(CreateSelectHeroClass(v.renderer, v.audioProvider,
d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal))
}
func (v *MainMenu) onGithubButtonClicked() {
url := "https://www.github.com/OpenDiablo2/OpenDiablo2"
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
if err != nil {
log.Fatal(err)
}
}
func (v *MainMenu) onExitButtonClicked() {
os.Exit(0)
}
func (v *MainMenu) onCreditsButtonClicked() {
d2screen.SetNextScreen(CreateCredits(v.renderer, v.audioProvider))
}
// 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:
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:
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, 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.tcpJoinGameLabel.Render(screen)
case screenModeTCPIP:
v.tcpIPOptionsLabel.Render(screen)
case screenModeTrademark:
v.copyrightLabel.Render(screen)
v.copyrightLabel2.Render(screen)
case screenModeMainMenu:
v.openDiabloLabel.Render(screen)
v.versionLabel.Render(screen)
v.commitLabel.Render(screen)
}
return nil
}
// Advance runs the update logic on the main menu
func (v *MainMenu) Advance(tickTime float64) error {
switch v.screenMode {
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 d2interface.MouseEvent) bool {
if v.screenMode == screenModeTrademark && event.Button() == d2interface.MouseButtonLeft {
v.setScreenMode(screenModeMainMenu)
return true
}
return false
}
func (v *MainMenu) setScreenMode(screenMode mainMenuScreenMode) {
v.screenMode = screenMode
isMainMenu := screenMode == screenModeMainMenu
isMultiplayer := screenMode == screenModeMultiplayer
isTCPIP := screenMode == screenModeTCPIP
isServerIP := screenMode == screenModeServerIP
v.exitDiabloButton.SetVisible(isMainMenu)
v.creditsButton.SetVisible(isMainMenu)
v.cinematicsButton.SetVisible(isMainMenu)
v.singlePlayerButton.SetVisible(isMainMenu)
v.githubButton.SetVisible(isMainMenu)
v.mapTestButton.SetVisible(isMainMenu)
v.multiplayerButton.SetVisible(isMainMenu)
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.tcpJoinGameEntry.Activate()
}
v.btnServerIPOk.SetVisible(isServerIP)
v.btnServerIPCancel.SetVisible(isServerIP)
}
func (v *MainMenu) onNetworkCancelClicked() {
v.setScreenMode(screenModeMainMenu)
}
func (v *MainMenu) onMultiplayerClicked() {
v.setScreenMode(screenModeMultiplayer)
}
func (v *MainMenu) onNetworkTCPIPClicked() {
v.setScreenMode(screenModeTCPIP)
}
func (v *MainMenu) onTCPIPCancelClicked() {
v.setScreenMode(screenModeMultiplayer)
}
func (v *MainMenu) onTCPIPHostGameClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider,
d2clientconnectiontype.LANServer, "", v.terminal))
}
func (v *MainMenu) onTCPIPJoinGameClicked() {
v.setScreenMode(screenModeServerIP)
}
func (v *MainMenu) onBtnTCPIPCancelClicked() {
v.setScreenMode(screenModeTCPIP)
}
func (v *MainMenu) onBtnTCPIPOkClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider,
d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal))
}