mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-06-20 06:05:23 +00:00
3bdbd5c358
remove unused struct vars that were only stored in order to pass to the next screens consolidate create game code to single method export ScreenMode consts and SetScreenMode method to allow app to create the correct screens Co-authored-by: carrelda <carrelda@git>
366 lines
11 KiB
Go
366 lines
11 KiB
Go
package d2gamescreen
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"math"
|
|
"os"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
|
)
|
|
|
|
// CharacterSelect represents the character select screen
|
|
type CharacterSelect struct {
|
|
background *d2ui.Sprite
|
|
newCharButton d2ui.Button
|
|
convertCharButton d2ui.Button
|
|
deleteCharButton d2ui.Button
|
|
exitButton d2ui.Button
|
|
okButton d2ui.Button
|
|
deleteCharCancelButton d2ui.Button
|
|
deleteCharOkButton d2ui.Button
|
|
selectionBox *d2ui.Sprite
|
|
okCancelBox *d2ui.Sprite
|
|
d2HeroTitle d2ui.Label
|
|
deleteCharConfirmLabel d2ui.Label
|
|
charScrollbar d2ui.Scrollbar
|
|
characterNameLabel [8]d2ui.Label
|
|
characterStatsLabel [8]d2ui.Label
|
|
characterExpLabel [8]d2ui.Label
|
|
characterImage [8]*d2mapentity.Player
|
|
gameStates []*d2player.PlayerState
|
|
selectedCharacter int
|
|
showDeleteConfirmation bool
|
|
connectionType d2clientconnectiontype.ClientConnectionType
|
|
connectionHost string
|
|
|
|
inputManager d2interface.InputManager
|
|
audioProvider d2interface.AudioProvider
|
|
renderer d2interface.Renderer
|
|
navigator Navigator
|
|
}
|
|
|
|
// CreateCharacterSelect creates the character select screen and returns a pointer to it
|
|
func CreateCharacterSelect(
|
|
navigator Navigator,
|
|
renderer d2interface.Renderer,
|
|
inputManager d2interface.InputManager,
|
|
audioProvider d2interface.AudioProvider,
|
|
connectionType d2clientconnectiontype.ClientConnectionType,
|
|
connectionHost string,
|
|
) *CharacterSelect {
|
|
return &CharacterSelect{
|
|
selectedCharacter: -1,
|
|
renderer: renderer,
|
|
connectionType: connectionType,
|
|
connectionHost: connectionHost,
|
|
inputManager: inputManager,
|
|
audioProvider: audioProvider,
|
|
navigator: navigator,
|
|
}
|
|
}
|
|
|
|
// OnLoad loads the resources for the Character Select screen
|
|
func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
|
|
v.audioProvider.PlayBGM(d2resource.BGMTitle)
|
|
|
|
if err := v.inputManager.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.createButtons(loading)
|
|
|
|
v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
|
|
v.d2HeroTitle.SetPosition(320, 23)
|
|
v.d2HeroTitle.Alignment = d2gui.HorizontalAlignCenter
|
|
|
|
loading.Progress(0.3)
|
|
|
|
v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
|
lines := "Are you sure that you want\nto delete this character?\nTake note: this will delete all\nversions of this Character."
|
|
v.deleteCharConfirmLabel.SetText(lines)
|
|
v.deleteCharConfirmLabel.Alignment = d2gui.HorizontalAlignCenter
|
|
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(
|
|
v.renderer,
|
|
d2ui.ButtonTypeTall,
|
|
"CREATE NEW\nCHARACTER",
|
|
)
|
|
v.newCharButton.SetPosition(33, 468)
|
|
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
|
|
d2ui.AddWidget(&v.newCharButton)
|
|
|
|
v.convertCharButton = d2ui.CreateButton(
|
|
v.renderer,
|
|
d2ui.ButtonTypeTall,
|
|
"CONVERT TO\nEXPANSION",
|
|
)
|
|
v.convertCharButton.SetPosition(233, 468)
|
|
v.convertCharButton.SetEnabled(false)
|
|
d2ui.AddWidget(&v.convertCharButton)
|
|
|
|
v.deleteCharButton = d2ui.CreateButton(
|
|
v.renderer,
|
|
d2ui.ButtonTypeTall,
|
|
"DELETE\nCHARACTER",
|
|
)
|
|
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
|
|
v.deleteCharButton.SetPosition(433, 468)
|
|
d2ui.AddWidget(&v.deleteCharButton)
|
|
|
|
v.exitButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "EXIT")
|
|
v.exitButton.SetPosition(33, 537)
|
|
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
|
d2ui.AddWidget(&v.exitButton)
|
|
loading.Progress(0.2)
|
|
|
|
v.deleteCharCancelButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "NO")
|
|
v.deleteCharCancelButton.SetPosition(282, 308)
|
|
v.deleteCharCancelButton.SetVisible(false)
|
|
v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() })
|
|
d2ui.AddWidget(&v.deleteCharCancelButton)
|
|
|
|
v.deleteCharOkButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "YES")
|
|
v.deleteCharOkButton.SetPosition(422, 308)
|
|
v.deleteCharOkButton.SetVisible(false)
|
|
v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() })
|
|
d2ui.AddWidget(&v.deleteCharOkButton)
|
|
|
|
v.okButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "OK")
|
|
v.okButton.SetPosition(625, 537)
|
|
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
|
|
d2ui.AddWidget(&v.okButton)
|
|
}
|
|
|
|
func (v *CharacterSelect) onScrollUpdate() {
|
|
v.moveSelectionBox()
|
|
v.updateCharacterBoxes()
|
|
}
|
|
|
|
func (v *CharacterSelect) updateCharacterBoxes() {
|
|
expText := "EXPANSION CHARACTER"
|
|
|
|
for i := 0; i < 8; i++ {
|
|
idx := i + (v.charScrollbar.GetCurrentOffset() * 2)
|
|
if idx >= len(v.gameStates) {
|
|
v.characterNameLabel[i].SetText("")
|
|
v.characterStatsLabel[i].SetText("")
|
|
v.characterExpLabel[i].SetText("")
|
|
v.characterImage[i] = nil
|
|
|
|
continue
|
|
}
|
|
|
|
v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName)
|
|
v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String())
|
|
v.characterExpLabel[i].SetText(expText)
|
|
// TODO: Generate or load the object from the actual player data...
|
|
v.characterImage[i] = d2mapentity.CreatePlayer("", "", 0, 0, 0,
|
|
v.gameStates[idx].HeroType,
|
|
*v.gameStates[idx].Stats,
|
|
d2inventory.HeroObjects[v.gameStates[idx].HeroType],
|
|
)
|
|
}
|
|
}
|
|
|
|
func (v *CharacterSelect) onNewCharButtonClicked() {
|
|
v.navigator.ToSelectHero(v.connectionType, v.connectionHost)
|
|
}
|
|
|
|
func (v *CharacterSelect) onExitButtonClicked() {
|
|
v.navigator.ToMainMenu()
|
|
}
|
|
|
|
// Render renders the Character Select screen
|
|
func (v *CharacterSelect) Render(screen d2interface.Surface) error {
|
|
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 {
|
|
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)
|
|
screen.PushTranslation(v.characterNameLabel[i].X-40, v.characterNameLabel[i].Y+50)
|
|
v.characterImage[i].Render(screen)
|
|
screen.Pop()
|
|
}
|
|
|
|
if v.showDeleteConfirmation {
|
|
screen.DrawRect(800, 600, color.RGBA{A: 128})
|
|
|
|
if err := v.okCancelBox.RenderSegmented(screen, 2, 1, 0); err != nil {
|
|
return err
|
|
}
|
|
|
|
v.deleteCharConfirmLabel.Render(screen)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *CharacterSelect) moveSelectionBox() {
|
|
if v.selectedCharacter == -1 {
|
|
v.d2HeroTitle.SetText("")
|
|
return
|
|
}
|
|
|
|
bw := 272
|
|
bh := 92
|
|
selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
|
|
v.selectionBox.SetPosition(37+((selectedIndex&1)*bw), 86+(bh*(selectedIndex/2)))
|
|
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
|
|
}
|
|
|
|
// OnMouseButtonDown is called when a mouse button is clicked
|
|
func (v *CharacterSelect) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
|
if !v.showDeleteConfirmation {
|
|
if event.Button() == d2enum.MouseButtonLeft {
|
|
mx, my := event.X(), event.Y()
|
|
bw := 272
|
|
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++
|
|
}
|
|
|
|
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 {
|
|
hero.Advance(tickTime)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *CharacterSelect) onDeleteCharButtonClicked() {
|
|
v.toggleDeleteCharacterDialog(true)
|
|
}
|
|
|
|
func (v *CharacterSelect) onDeleteCharacterConfirmClicked() {
|
|
_ = os.Remove(v.gameStates[v.selectedCharacter].FilePath)
|
|
v.charScrollbar.SetCurrentOffset(0)
|
|
v.refreshGameStates()
|
|
v.toggleDeleteCharacterDialog(false)
|
|
v.deleteCharButton.SetEnabled(len(v.gameStates) > 0)
|
|
v.okButton.SetEnabled(len(v.gameStates) > 0)
|
|
}
|
|
|
|
func (v *CharacterSelect) onDeleteCharacterCancelClicked() {
|
|
v.toggleDeleteCharacterDialog(false)
|
|
}
|
|
|
|
func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) {
|
|
v.showDeleteConfirmation = showDialog
|
|
v.okButton.SetEnabled(!showDialog)
|
|
v.deleteCharButton.SetEnabled(!showDialog)
|
|
v.exitButton.SetEnabled(!showDialog)
|
|
v.newCharButton.SetEnabled(!showDialog)
|
|
v.deleteCharOkButton.SetVisible(showDialog)
|
|
v.deleteCharCancelButton.SetVisible(showDialog)
|
|
}
|
|
|
|
func (v *CharacterSelect) refreshGameStates() {
|
|
v.gameStates = d2player.GetAllPlayerStates()
|
|
v.updateCharacterBoxes()
|
|
|
|
if len(v.gameStates) > 0 {
|
|
v.selectedCharacter = 0
|
|
v.d2HeroTitle.SetText(v.gameStates[0].HeroName)
|
|
v.charScrollbar.SetMaxOffset(int(math.Ceil(float64(len(v.gameStates)-8) / float64(2))))
|
|
} else {
|
|
v.selectedCharacter = -1
|
|
v.charScrollbar.SetMaxOffset(0)
|
|
}
|
|
|
|
v.moveSelectionBox()
|
|
}
|
|
|
|
func (v *CharacterSelect) onOkButtonClicked() {
|
|
v.navigator.ToCreateGame(v.gameStates[v.selectedCharacter].FilePath, v.connectionType, v.connectionHost)
|
|
}
|