2019-11-10 08:51:02 -05:00
|
|
|
package d2scene
|
2019-10-26 23:59:27 -04:00
|
|
|
|
|
|
|
import (
|
2019-11-11 23:48:55 -05:00
|
|
|
"image/color"
|
2019-11-12 23:44:04 -05:00
|
|
|
"math"
|
2019-11-11 23:48:55 -05:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
|
|
|
|
2019-11-10 03:36:53 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2019-11-10 08:51:02 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2019-11-11 23:48:55 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core"
|
2019-11-10 08:51:02 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
|
2019-11-10 03:36:53 -05:00
|
|
|
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
2019-11-10 08:51:02 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
|
2019-10-26 23:59:27 -04:00
|
|
|
"github.com/hajimehoshi/ebiten"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CharacterSelect struct {
|
2019-11-11 23:48:55 -05:00
|
|
|
uiManager *d2ui.Manager
|
|
|
|
soundManager *d2audio.Manager
|
|
|
|
fileProvider d2interface.FileProvider
|
|
|
|
sceneProvider d2interface.SceneProvider
|
|
|
|
background d2render.Sprite
|
|
|
|
newCharButton d2ui.Button
|
|
|
|
convertCharButton d2ui.Button
|
|
|
|
deleteCharButton d2ui.Button
|
|
|
|
exitButton d2ui.Button
|
|
|
|
okButton d2ui.Button
|
|
|
|
deleteCharCancelButton d2ui.Button
|
|
|
|
deleteCharOkButton d2ui.Button
|
|
|
|
selectionBox d2render.Sprite
|
|
|
|
okCancelBox d2render.Sprite
|
|
|
|
d2HeroTitle d2ui.Label
|
|
|
|
deleteCharConfirmLabel d2ui.Label
|
2019-11-12 23:44:04 -05:00
|
|
|
charScrollbar d2ui.Scrollbar
|
2019-11-11 23:48:55 -05:00
|
|
|
characterNameLabel [8]d2ui.Label
|
|
|
|
characterStatsLabel [8]d2ui.Label
|
|
|
|
characterExpLabel [8]d2ui.Label
|
|
|
|
characterImage [8]*d2core.NPC
|
|
|
|
gameStates []*d2core.GameState
|
|
|
|
selectedCharacter int
|
|
|
|
mouseButtonPressed bool
|
|
|
|
showDeleteConfirmation bool
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func CreateCharacterSelect(
|
2019-11-10 03:36:53 -05:00
|
|
|
fileProvider d2interface.FileProvider,
|
|
|
|
sceneProvider d2interface.SceneProvider,
|
2019-11-10 08:51:02 -05:00
|
|
|
uiManager *d2ui.Manager,
|
2019-11-10 03:36:53 -05:00
|
|
|
soundManager *d2audio.Manager,
|
2019-10-26 23:59:27 -04:00
|
|
|
) *CharacterSelect {
|
|
|
|
result := &CharacterSelect{
|
2019-11-11 23:48:55 -05:00
|
|
|
selectedCharacter: -1,
|
|
|
|
uiManager: uiManager,
|
|
|
|
sceneProvider: sceneProvider,
|
|
|
|
fileProvider: fileProvider,
|
|
|
|
soundManager: soundManager,
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) Load() []func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.soundManager.PlayBGM(d2resource.BGMTitle)
|
2019-10-26 23:59:27 -04:00
|
|
|
return []func(){
|
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.background = d2render.CreateSprite(v.fileProvider.LoadFile(d2resource.CharacterSelectionBackground), d2datadict.Palettes[d2enum.Sky])
|
2019-10-26 23:59:27 -04:00
|
|
|
v.background.MoveTo(0, 0)
|
|
|
|
},
|
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15)))
|
2019-10-26 23:59:27 -04:00
|
|
|
v.newCharButton.MoveTo(33, 468)
|
2019-10-27 02:58:37 -04:00
|
|
|
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
|
2019-11-10 12:28:41 -05:00
|
|
|
v.uiManager.AddWidget(&v.newCharButton)
|
2019-10-26 23:59:27 -04:00
|
|
|
},
|
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15)))
|
2019-10-26 23:59:27 -04:00
|
|
|
v.convertCharButton.MoveTo(233, 468)
|
|
|
|
v.convertCharButton.SetEnabled(false)
|
2019-11-10 12:28:41 -05:00
|
|
|
v.uiManager.AddWidget(&v.convertCharButton)
|
2019-10-26 23:59:27 -04:00
|
|
|
},
|
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15)))
|
2019-11-11 23:48:55 -05:00
|
|
|
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
|
2019-10-26 23:59:27 -04:00
|
|
|
v.deleteCharButton.MoveTo(433, 468)
|
2019-11-10 12:28:41 -05:00
|
|
|
v.uiManager.AddWidget(&v.deleteCharButton)
|
2019-10-26 23:59:27 -04:00
|
|
|
},
|
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970"))
|
2019-10-26 23:59:27 -04:00
|
|
|
v.exitButton.MoveTo(33, 537)
|
|
|
|
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
2019-11-10 12:28:41 -05:00
|
|
|
v.uiManager.AddWidget(&v.exitButton)
|
2019-10-26 23:59:27 -04:00
|
|
|
},
|
2019-11-11 23:48:55 -05:00
|
|
|
func() {
|
|
|
|
v.deleteCharCancelButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4231"))
|
|
|
|
v.deleteCharCancelButton.MoveTo(282, 308)
|
|
|
|
v.deleteCharCancelButton.SetVisible(false)
|
|
|
|
v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() })
|
|
|
|
v.uiManager.AddWidget(&v.deleteCharCancelButton)
|
|
|
|
},
|
|
|
|
func() {
|
|
|
|
v.deleteCharOkButton = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, v.fileProvider, d2common.TranslateString("#4227"))
|
|
|
|
v.deleteCharOkButton.MoveTo(422, 308)
|
|
|
|
v.deleteCharOkButton.SetVisible(false)
|
|
|
|
v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() })
|
|
|
|
v.uiManager.AddWidget(&v.deleteCharOkButton)
|
|
|
|
},
|
2019-10-26 23:59:27 -04:00
|
|
|
func() {
|
2019-11-10 08:51:02 -05:00
|
|
|
v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971"))
|
2019-10-26 23:59:27 -04:00
|
|
|
v.okButton.MoveTo(625, 537)
|
2019-11-13 00:31:52 -05:00
|
|
|
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
|
2019-11-10 12:28:41 -05:00
|
|
|
v.uiManager.AddWidget(&v.okButton)
|
2019-10-26 23:59:27 -04:00
|
|
|
},
|
2019-11-11 23:48:55 -05:00
|
|
|
func() {
|
|
|
|
v.d2HeroTitle = d2ui.CreateLabel(v.fileProvider, d2resource.Font42, d2enum.Units)
|
|
|
|
v.d2HeroTitle.MoveTo(320, 23)
|
|
|
|
v.d2HeroTitle.Alignment = d2ui.LabelAlignCenter
|
|
|
|
},
|
|
|
|
func() {
|
|
|
|
v.deleteCharConfirmLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
|
|
|
|
lines := dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#1878"), 29)
|
|
|
|
v.deleteCharConfirmLabel.SetText(strings.Join(lines, "\n"))
|
|
|
|
v.deleteCharConfirmLabel.Alignment = d2ui.LabelAlignCenter
|
|
|
|
v.deleteCharConfirmLabel.MoveTo(400, 185)
|
|
|
|
},
|
|
|
|
func() {
|
|
|
|
v.selectionBox = d2render.CreateSprite(v.fileProvider.LoadFile(d2resource.CharacterSelectionSelectBox), d2datadict.Palettes[d2enum.Sky])
|
|
|
|
v.selectionBox.MoveTo(37, 86)
|
|
|
|
},
|
|
|
|
func() {
|
|
|
|
v.okCancelBox = d2render.CreateSprite(v.fileProvider.LoadFile(d2resource.PopUpOkCancel), d2datadict.Palettes[d2enum.Fechar])
|
|
|
|
v.okCancelBox.MoveTo(270, 175)
|
|
|
|
},
|
2019-11-12 23:44:04 -05:00
|
|
|
func() {
|
|
|
|
v.charScrollbar = d2ui.CreateScrollbar(v.fileProvider, 586, 87, 369)
|
|
|
|
v.charScrollbar.OnActivated(func() { v.onScrollUpdate() })
|
|
|
|
v.uiManager.AddWidget(&v.charScrollbar)
|
|
|
|
},
|
2019-11-11 23:48:55 -05:00
|
|
|
func() {
|
|
|
|
for i := 0; i < 8; i++ {
|
|
|
|
xOffset := 115
|
|
|
|
if i&1 > 0 {
|
|
|
|
xOffset = 385
|
|
|
|
}
|
|
|
|
v.characterNameLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
|
|
|
|
v.characterNameLabel[i].Color = color.RGBA{188, 168, 140, 255}
|
|
|
|
v.characterNameLabel[i].MoveTo(xOffset, 100+((i/2)*95))
|
|
|
|
v.characterStatsLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
|
|
|
|
v.characterStatsLabel[i].MoveTo(xOffset, 115+((i/2)*95))
|
|
|
|
v.characterExpLabel[i] = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Static)
|
|
|
|
v.characterExpLabel[i].Color = color.RGBA{24, 255, 0, 255}
|
|
|
|
v.characterExpLabel[i].MoveTo(xOffset, 130+((i/2)*95))
|
|
|
|
}
|
|
|
|
v.refreshGameStates()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-12 23:44:04 -05:00
|
|
|
func (v *CharacterSelect) onScrollUpdate() {
|
|
|
|
v.moveSelectionBox()
|
|
|
|
v.updateCharacterBoxes()
|
|
|
|
}
|
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
func (v *CharacterSelect) updateCharacterBoxes() {
|
|
|
|
expText := d2common.TranslateString("expansionchar2x")
|
|
|
|
for i := 0; i < 8; i++ {
|
2019-11-12 23:44:04 -05:00
|
|
|
idx := i + (v.charScrollbar.GetCurrentOffset() * 2)
|
2019-11-11 23:48:55 -05:00
|
|
|
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] = d2core.CreateNPC(0, 0, d2datadict.HeroObjects[v.gameStates[idx].HeroType], v.fileProvider, 5)
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:58:37 -04:00
|
|
|
func (v *CharacterSelect) onNewCharButtonClicked() {
|
|
|
|
v.sceneProvider.SetNextScene(CreateSelectHeroClass(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager))
|
|
|
|
}
|
|
|
|
|
2019-10-26 23:59:27 -04:00
|
|
|
func (v *CharacterSelect) onExitButtonClicked() {
|
2019-10-27 19:00:38 -04:00
|
|
|
mainMenu := CreateMainMenu(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)
|
|
|
|
mainMenu.ShowTrademarkScreen = false
|
|
|
|
v.sceneProvider.SetNextScene(mainMenu)
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) Unload() {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) Render(screen *ebiten.Image) {
|
|
|
|
v.background.DrawSegments(screen, 4, 3, 0)
|
2019-11-11 23:48:55 -05:00
|
|
|
v.d2HeroTitle.Draw(screen)
|
2019-11-12 23:44:04 -05:00
|
|
|
actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
|
|
|
|
if v.selectedCharacter > -1 && actualSelectionIndex >= 0 && actualSelectionIndex < 8 {
|
2019-11-11 23:48:55 -05:00
|
|
|
v.selectionBox.DrawSegments(screen, 2, 1, 0)
|
|
|
|
}
|
|
|
|
for i := 0; i < 8; i++ {
|
2019-11-12 23:44:04 -05:00
|
|
|
idx := i + (v.charScrollbar.GetCurrentOffset() * 2)
|
2019-11-11 23:48:55 -05:00
|
|
|
if idx >= len(v.gameStates) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
v.characterNameLabel[i].Draw(screen)
|
|
|
|
v.characterStatsLabel[i].Draw(screen)
|
|
|
|
v.characterExpLabel[i].Draw(screen)
|
|
|
|
v.characterImage[i].Render(screen, v.characterNameLabel[i].X-40, v.characterNameLabel[i].Y+30)
|
|
|
|
}
|
|
|
|
if v.showDeleteConfirmation {
|
|
|
|
ebitenutil.DrawRect(screen, 0.0, 0.0, 800.0, 600.0, color.RGBA{0, 0, 0, 128})
|
|
|
|
v.okCancelBox.DrawSegments(screen, 2, 1, 0)
|
|
|
|
v.deleteCharConfirmLabel.Draw(screen)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) moveSelectionBox() {
|
2019-11-12 23:44:04 -05:00
|
|
|
if v.selectedCharacter == -1 {
|
|
|
|
v.d2HeroTitle.SetText("")
|
|
|
|
return
|
|
|
|
}
|
2019-11-11 23:48:55 -05:00
|
|
|
bw := 272
|
|
|
|
bh := 92
|
2019-11-12 23:44:04 -05:00
|
|
|
selectedIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
|
2019-11-11 23:48:55 -05:00
|
|
|
v.selectionBox.MoveTo(37+((selectedIndex&1)*int(bw)), 86+(int(bh)*(selectedIndex/2)))
|
|
|
|
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) Update(tickTime float64) {
|
2019-11-11 23:48:55 -05:00
|
|
|
if !v.showDeleteConfirmation {
|
|
|
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
|
|
|
if !v.mouseButtonPressed {
|
|
|
|
v.mouseButtonPressed = true
|
|
|
|
mx, my := ebiten.CursorPosition()
|
|
|
|
bw := 272
|
|
|
|
bh := 92
|
|
|
|
localMouseX := mx - 37
|
|
|
|
localMouseY := my - 86
|
|
|
|
if localMouseX > 0 && localMouseX < int(bw*2) && localMouseY >= 0 && localMouseY < int(bh*4) {
|
|
|
|
adjustY := localMouseY / int(bh)
|
|
|
|
selectedIndex := adjustY * 2
|
|
|
|
if localMouseX > int(bw) {
|
|
|
|
selectedIndex += 1
|
|
|
|
}
|
2019-11-12 23:44:04 -05:00
|
|
|
if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) {
|
|
|
|
v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex
|
2019-11-11 23:48:55 -05:00
|
|
|
v.moveSelectionBox()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
v.mouseButtonPressed = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) onDeleteCharButtonClicked() {
|
|
|
|
v.toggleDeleteCharacterDialog(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) onDeleteCharacterConfirmClicked() {
|
|
|
|
_ = os.Remove(v.gameStates[v.selectedCharacter].FilePath)
|
2019-11-12 23:44:04 -05:00
|
|
|
v.charScrollbar.SetCurrentOffset(0)
|
2019-11-11 23:48:55 -05:00
|
|
|
v.refreshGameStates()
|
|
|
|
v.toggleDeleteCharacterDialog(false)
|
2019-11-12 23:44:04 -05:00
|
|
|
v.deleteCharButton.SetEnabled(len(v.gameStates) > 0)
|
|
|
|
v.okButton.SetEnabled(len(v.gameStates) > 0)
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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 = d2core.GetAllGameStates()
|
|
|
|
v.updateCharacterBoxes()
|
|
|
|
if len(v.gameStates) > 0 {
|
|
|
|
v.selectedCharacter = 0
|
|
|
|
v.d2HeroTitle.SetText(v.gameStates[0].HeroName)
|
2019-11-12 23:44:04 -05:00
|
|
|
v.charScrollbar.SetMaxOffset(int(math.Ceil(float64(len(v.gameStates)-8) / float64(2))))
|
2019-11-11 23:48:55 -05:00
|
|
|
} else {
|
|
|
|
v.selectedCharacter = -1
|
2019-11-12 23:44:04 -05:00
|
|
|
v.charScrollbar.SetMaxOffset(0)
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
v.moveSelectionBox()
|
|
|
|
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
2019-11-13 00:31:52 -05:00
|
|
|
|
|
|
|
func (v *CharacterSelect) onOkButtonClicked() {
|
|
|
|
v.sceneProvider.SetNextScene(CreateGame(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager, v.gameStates[v.selectedCharacter]))
|
|
|
|
}
|