2020-06-13 20:36:20 -04:00
|
|
|
package d2gamescreen
|
2019-10-26 23:59:27 -04:00
|
|
|
|
|
|
|
import (
|
2020-07-02 13:55:43 -04:00
|
|
|
"fmt"
|
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"
|
2020-06-28 19:31:10 -04:00
|
|
|
|
2020-07-11 11:24:04 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2020-02-01 21:06:22 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2020-02-01 18:55:56 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
2020-07-11 11:24:04 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
2020-02-01 18:55:56 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
|
2020-06-23 18:12:08 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
2020-06-13 20:36:20 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
|
2020-01-31 23:18:11 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
2020-06-23 18:12:08 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
2019-10-26 23:59:27 -04:00
|
|
|
)
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// CharacterSelect represents the character select screen
|
2019-10-26 23:59:27 -04:00
|
|
|
type CharacterSelect struct {
|
2020-09-12 16:51:30 -04:00
|
|
|
asset *d2asset.AssetManager
|
|
|
|
*d2mapentity.MapEntityFactory
|
2020-02-01 18:55:56 -05:00
|
|
|
background *d2ui.Sprite
|
2020-08-06 10:30:23 -04:00
|
|
|
newCharButton *d2ui.Button
|
|
|
|
convertCharButton *d2ui.Button
|
|
|
|
deleteCharButton *d2ui.Button
|
|
|
|
exitButton *d2ui.Button
|
|
|
|
okButton *d2ui.Button
|
|
|
|
deleteCharCancelButton *d2ui.Button
|
|
|
|
deleteCharOkButton *d2ui.Button
|
2020-02-01 18:55:56 -05:00
|
|
|
selectionBox *d2ui.Sprite
|
|
|
|
okCancelBox *d2ui.Sprite
|
2020-08-06 10:30:23 -04:00
|
|
|
d2HeroTitle *d2ui.Label
|
|
|
|
deleteCharConfirmLabel *d2ui.Label
|
|
|
|
charScrollbar *d2ui.Scrollbar
|
|
|
|
characterNameLabel [8]*d2ui.Label
|
|
|
|
characterStatsLabel [8]*d2ui.Label
|
|
|
|
characterExpLabel [8]*d2ui.Label
|
2020-06-21 18:40:37 -04:00
|
|
|
characterImage [8]*d2mapentity.Player
|
2020-06-18 14:11:04 -04:00
|
|
|
gameStates []*d2player.PlayerState
|
2019-11-11 23:48:55 -05:00
|
|
|
selectedCharacter int
|
|
|
|
showDeleteConfirmation bool
|
2020-06-18 14:11:04 -04:00
|
|
|
connectionType d2clientconnectiontype.ClientConnectionType
|
|
|
|
connectionHost string
|
2020-07-14 13:11:23 -04:00
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
uiManager *d2ui.UIManager
|
2020-07-14 13:11:23 -04:00
|
|
|
inputManager d2interface.InputManager
|
|
|
|
audioProvider d2interface.AudioProvider
|
|
|
|
renderer d2interface.Renderer
|
|
|
|
navigator Navigator
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// CreateCharacterSelect creates the character select screen and returns a pointer to it
|
|
|
|
func CreateCharacterSelect(
|
2020-07-14 13:11:23 -04:00
|
|
|
navigator Navigator,
|
2020-09-12 16:51:30 -04:00
|
|
|
asset *d2asset.AssetManager,
|
2020-07-03 14:00:56 -04:00
|
|
|
renderer d2interface.Renderer,
|
2020-07-13 20:29:17 -04:00
|
|
|
inputManager d2interface.InputManager,
|
2020-07-02 13:55:43 -04:00
|
|
|
audioProvider d2interface.AudioProvider,
|
2020-08-06 10:30:23 -04:00
|
|
|
ui *d2ui.UIManager,
|
2020-07-02 13:55:43 -04:00
|
|
|
connectionType d2clientconnectiontype.ClientConnectionType,
|
2020-07-14 13:11:23 -04:00
|
|
|
connectionHost string,
|
2020-07-02 13:55:43 -04:00
|
|
|
) *CharacterSelect {
|
2020-06-18 14:11:04 -04:00
|
|
|
return &CharacterSelect{
|
|
|
|
selectedCharacter: -1,
|
2020-09-12 16:51:30 -04:00
|
|
|
asset: asset,
|
|
|
|
MapEntityFactory: d2mapentity.NewMapEntityFactory(asset),
|
2020-07-03 14:00:56 -04:00
|
|
|
renderer: renderer,
|
2020-06-18 14:11:04 -04:00
|
|
|
connectionType: connectionType,
|
|
|
|
connectionHost: connectionHost,
|
2020-07-13 20:29:17 -04:00
|
|
|
inputManager: inputManager,
|
2020-06-28 19:31:10 -04:00
|
|
|
audioProvider: audioProvider,
|
2020-07-14 13:11:23 -04:00
|
|
|
navigator: navigator,
|
2020-08-06 10:30:23 -04:00
|
|
|
uiManager: ui,
|
2020-06-18 14:11:04 -04:00
|
|
|
}
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
const (
|
|
|
|
tenPercent = 0.1 * iota
|
|
|
|
twentyPercent
|
|
|
|
thirtyPercent
|
|
|
|
fourtyPercent
|
|
|
|
fiftyPercent
|
|
|
|
sixtyPercent
|
|
|
|
seventyPercent
|
|
|
|
eightyPercent
|
|
|
|
ninetyPercent
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
rootLabelOffsetX = 115
|
|
|
|
rootLabelOffsetY = 100
|
|
|
|
labelHeight = 15
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
selectionBoxNumColumns = 2
|
|
|
|
selectionBoxNumRows = 4
|
|
|
|
selectionBoxWidth = 272
|
|
|
|
selectionBoxHeight = 92
|
|
|
|
selectionBoxOffsetX = 37
|
|
|
|
selectionBoxOffsetY = 86
|
|
|
|
selectionBoxImageOffsetX = 40
|
|
|
|
selectionBoxImageOffsetY = 50
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
blackHalfOpacity = 0x0000007f
|
|
|
|
lightBrown = 0xbca88cff
|
|
|
|
lightGreen = 0x18ff00ff
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
screenWidth = 800
|
|
|
|
screenHeight = 600
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
newCharBtnX, newCharBtnY = 33, 468
|
|
|
|
convertCharBtnX, convertCharBtnY = 233, 468
|
|
|
|
deleteCharBtnX, deleteCharBtnY = 433, 468
|
|
|
|
deleteCancelX, deleteCancelY = 282, 308
|
|
|
|
deleteOkX, deleteOkY = 422, 308
|
|
|
|
exitBtnX, exitBtnY = 33, 537
|
|
|
|
okBtnX, okBtnY = 625, 537
|
|
|
|
)
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// OnLoad loads the resources for the Character Select screen
|
2020-06-24 18:46:03 -04:00
|
|
|
func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
|
2020-06-28 19:31:10 -04:00
|
|
|
v.audioProvider.PlayBGM(d2resource.BGMTitle)
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-07-13 20:29:17 -04:00
|
|
|
if err := v.inputManager.BindHandler(v); err != nil {
|
2020-07-02 13:55:43 -04:00
|
|
|
fmt.Println("failed to add Character Select screen as event handler")
|
|
|
|
}
|
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
loading.Progress(tenPercent)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
bgX, bgY := 0, 0
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
v.background, _ = v.uiManager.NewSprite(d2resource.CharacterSelectionBackground, d2resource.PaletteSky)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.background.SetPosition(bgX, bgY)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
v.createButtons(loading)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
heroTitleX, heroTitleY := 320, 23
|
2020-08-06 10:30:23 -04:00
|
|
|
v.d2HeroTitle = v.uiManager.NewLabel(d2resource.Font42, d2resource.PaletteUnits)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.d2HeroTitle.SetPosition(heroTitleX, heroTitleY)
|
2020-07-07 20:16:22 -04:00
|
|
|
v.d2HeroTitle.Alignment = d2gui.HorizontalAlignCenter
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
loading.Progress(thirtyPercent)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.deleteCharConfirmLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
2020-07-07 20:16:22 -04:00
|
|
|
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
|
2020-07-18 09:54:10 -04:00
|
|
|
deleteConfirmX, deleteConfirmY := 400, 185
|
|
|
|
v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
v.selectionBox, _ = v.uiManager.NewSprite(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky)
|
2020-07-18 09:54:10 -04:00
|
|
|
selBoxX, selBoxY := 37, 86
|
|
|
|
v.selectionBox.SetPosition(selBoxX, selBoxY)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
Decouple asset manager from renderer (#730)
* improve AssetManager implementation
Notable changes are:
* removed the individual managers inside of d2asset, only one asset manager
* AssetManager now has caches for the types of files it loads
* created a type for TextDictionary (the txt file structs)
* fixed a file path bug in d2loader Source
* fixed a asset stream bug in d2loader Asset
* d2loader.Loader now needs a d2config.Config on creation (for resolving locale files)
* updated the mpq file in d2asset test data, added test case for "sub-directory"
* added a Data method to d2asset.Asset. The data is cached on first full read.
* renamed ArchiveDataStream to DataStream in d2interface
* moved palette utility func out of d2asset and into d2util
* bugfix for MacOS mpq loader issue
* lint fixes, added data caching to filesystem asset
* adding comment for mpq asset close
* Decouple d2asset from d2render
Notable changes in d2common:
* d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct
* un-exported dcc.decodeDirection, it is only used in d2dcc
* removed font interface from d2interface, we only have one font implementation
* added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need
* added `BindRenderer` method to animation interface
Notable changes in d2common/d2asset:
* **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render
* exported Animation
* Animation implementation binds to the renderer to create surfaces only on the first time it is rendered
* font, dcc, dc6 initialization logic moved out of asset_manager.go
* for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods
* the d2asset.Font struct now stores font table data for initialization purposes
Notable changes in d2core/d2render:
* Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time
**These last changes should have been a separate PR, sorry.**
Notable changes in d2core/d2ui:
* ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments
Notable Changes in d2game:
Because of the change in d2ui, all instances of this code pattern...
```golang
animation, err := screen.asset.LoadAnimation(imgPath, palettePath)
sprite, err := screen.ui.NewSprite(animation)
```
... becomes this ...
```golang
sprite, err := screen.ui.NewSprite(imgPath, palettePath)
```
2020-09-14 17:31:45 -04:00
|
|
|
v.okCancelBox, _ = v.uiManager.NewSprite(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
|
2020-07-18 09:54:10 -04:00
|
|
|
okCancelX, okCancelY := 270, 175
|
|
|
|
v.okCancelBox.SetPosition(okCancelX, okCancelY)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
scrollBarX, scrollBarY, scrollBarHeight := 586, 87, 369
|
2020-08-06 10:30:23 -04:00
|
|
|
v.charScrollbar = v.uiManager.NewScrollbar(scrollBarX, scrollBarY, scrollBarHeight)
|
2020-02-08 21:02:37 -05:00
|
|
|
v.charScrollbar.OnActivated(func() { v.onScrollUpdate() })
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
loading.Progress(fiftyPercent)
|
2020-02-08 21:02:37 -05:00
|
|
|
|
|
|
|
for i := 0; i < 8; i++ {
|
2020-07-18 09:54:10 -04:00
|
|
|
offsetX, offsetY := rootLabelOffsetX, rootLabelOffsetY+((i/2)*95)
|
2020-02-08 21:02:37 -05:00
|
|
|
if i&1 > 0 {
|
2020-07-18 09:54:10 -04:00
|
|
|
offsetX = 385
|
2020-02-08 21:02:37 -05:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.characterNameLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.characterNameLabel[i].SetPosition(offsetX, offsetY)
|
2020-08-02 21:26:07 -04:00
|
|
|
v.characterNameLabel[i].Color[0] = rgbaColor(lightBrown)
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
offsetY += labelHeight
|
2020-08-06 10:30:23 -04:00
|
|
|
v.characterStatsLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.characterStatsLabel[i].SetPosition(offsetX, offsetY)
|
|
|
|
|
|
|
|
offsetY += labelHeight
|
2020-08-06 10:30:23 -04:00
|
|
|
v.characterExpLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.characterExpLabel[i].SetPosition(offsetX, offsetY)
|
2020-08-02 21:26:07 -04:00
|
|
|
v.characterExpLabel[i].Color[0] = rgbaColor(lightGreen)
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
2020-02-08 21:02:37 -05:00
|
|
|
v.refreshGameStates()
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-18 09:54:10 -04:00
|
|
|
func rgbaColor(rgba uint32) color.RGBA {
|
|
|
|
result := color.RGBA{}
|
|
|
|
a, b, g, r := 0, 1, 2, 3
|
|
|
|
byteWidth := 8
|
|
|
|
byteMask := 0xff
|
|
|
|
|
|
|
|
for idx := 0; idx < 4; idx++ {
|
|
|
|
shift := idx * byteWidth
|
|
|
|
component := uint8(rgba>>shift) & uint8(byteMask)
|
|
|
|
|
|
|
|
switch idx {
|
|
|
|
case a:
|
|
|
|
result.A = component
|
|
|
|
case b:
|
|
|
|
result.B = component
|
|
|
|
case g:
|
|
|
|
result.G = component
|
|
|
|
case r:
|
|
|
|
result.R = component
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
func (v *CharacterSelect) createButtons(loading d2screen.LoadingState) {
|
2020-08-06 10:30:23 -04:00
|
|
|
v.newCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "CREATE NEW\nCHARACTER")
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
v.newCharButton.SetPosition(newCharBtnX, newCharBtnY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.convertCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "CONVERT TO\nEXPANSION")
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
v.convertCharButton.SetPosition(convertCharBtnX, convertCharBtnY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.convertCharButton.SetEnabled(false)
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.deleteCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "DELETE\nCHARACTER")
|
2020-07-02 13:55:43 -04:00
|
|
|
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
|
2020-07-18 09:54:10 -04:00
|
|
|
v.deleteCharButton.SetPosition(deleteCharBtnX, deleteCharBtnY)
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.exitButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "EXIT")
|
2020-07-18 09:54:10 -04:00
|
|
|
v.exitButton.SetPosition(exitBtnX, exitBtnY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
loading.Progress(twentyPercent)
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.deleteCharCancelButton = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "NO")
|
2020-07-18 09:54:10 -04:00
|
|
|
v.deleteCharCancelButton.SetPosition(deleteCancelX, deleteCancelY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.deleteCharCancelButton.SetVisible(false)
|
|
|
|
v.deleteCharCancelButton.OnActivated(func() { v.onDeleteCharacterCancelClicked() })
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.deleteCharOkButton = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "YES")
|
2020-07-18 09:54:10 -04:00
|
|
|
v.deleteCharOkButton.SetPosition(deleteOkX, deleteOkY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.deleteCharOkButton.SetVisible(false)
|
|
|
|
v.deleteCharOkButton.OnActivated(func() { v.onDeleteCharacterConfirmClicked() })
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
v.okButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "OK")
|
2020-07-18 09:54:10 -04:00
|
|
|
v.okButton.SetPosition(okBtnX, okBtnY)
|
2020-07-02 13:55:43 -04:00
|
|
|
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
|
|
|
|
}
|
|
|
|
|
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() {
|
2020-06-20 15:14:52 -04:00
|
|
|
expText := "EXPANSION CHARACTER"
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
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
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
continue
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-08-02 21:26:07 -04:00
|
|
|
heroName := v.gameStates[idx].HeroName
|
|
|
|
heroInfo := "Level 1 " + v.gameStates[idx].HeroType.String()
|
|
|
|
|
|
|
|
v.characterNameLabel[i].SetText(d2ui.ColorTokenize(heroName, d2ui.ColorTokenGold))
|
|
|
|
v.characterStatsLabel[i].SetText(d2ui.ColorTokenize(heroInfo, d2ui.ColorTokenWhite))
|
|
|
|
v.characterExpLabel[i].SetText(d2ui.ColorTokenize(expText, d2ui.ColorTokenGreen))
|
2020-07-17 22:11:16 -04:00
|
|
|
|
|
|
|
heroType := v.gameStates[idx].HeroType
|
|
|
|
equipment := d2inventory.HeroObjects[heroType]
|
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
// TODO: Generate or load the object from the actual player data...
|
2020-09-12 16:51:30 -04:00
|
|
|
v.characterImage[i] = v.NewPlayer("", "", 0, 0, 0,
|
2020-06-26 17:12:19 -04:00
|
|
|
v.gameStates[idx].HeroType,
|
2020-07-17 22:11:16 -04:00
|
|
|
v.gameStates[idx].Stats,
|
|
|
|
&equipment,
|
2019-11-14 22:20:01 -05:00
|
|
|
)
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-27 02:58:37 -04:00
|
|
|
func (v *CharacterSelect) onNewCharButtonClicked() {
|
2020-07-14 13:11:23 -04:00
|
|
|
v.navigator.ToSelectHero(v.connectionType, v.connectionHost)
|
2019-10-27 02:58:37 -04:00
|
|
|
}
|
|
|
|
|
2019-10-26 23:59:27 -04:00
|
|
|
func (v *CharacterSelect) onExitButtonClicked() {
|
2020-07-14 13:11:23 -04:00
|
|
|
v.navigator.ToMainMenu()
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// Render renders the Character Select screen
|
2020-06-29 00:41:58 -04:00
|
|
|
func (v *CharacterSelect) Render(screen d2interface.Surface) error {
|
2020-07-02 13:55:43 -04:00
|
|
|
if err := v.background.RenderSegmented(screen, 4, 3, 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
v.d2HeroTitle.Render(screen)
|
2019-11-12 23:44:04 -05:00
|
|
|
actualSelectionIndex := v.selectedCharacter - (v.charScrollbar.GetCurrentOffset() * 2)
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-12 23:44:04 -05:00
|
|
|
if v.selectedCharacter > -1 && actualSelectionIndex >= 0 && actualSelectionIndex < 8 {
|
2020-07-02 13:55:43 -04:00
|
|
|
if err := v.selectionBox.RenderSegmented(screen, 2, 1, 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
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
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
v.characterNameLabel[i].Render(screen)
|
|
|
|
v.characterStatsLabel[i].Render(screen)
|
|
|
|
v.characterExpLabel[i].Render(screen)
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
charImgX := v.characterNameLabel[i].X - selectionBoxImageOffsetX
|
|
|
|
charImgY := v.characterNameLabel[i].Y + selectionBoxImageOffsetY
|
|
|
|
screen.PushTranslation(charImgX, charImgY)
|
2019-12-28 16:46:08 -05:00
|
|
|
v.characterImage[i].Render(screen)
|
|
|
|
screen.Pop()
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
if v.showDeleteConfirmation {
|
2020-07-18 09:54:10 -04:00
|
|
|
screen.DrawRect(screenWidth, screenHeight, rgbaColor(blackHalfOpacity))
|
2020-07-02 13:55:43 -04:00
|
|
|
|
|
|
|
if err := v.okCancelBox.RenderSegmented(screen, 2, 1, 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
v.deleteCharConfirmLabel.Render(screen)
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
2020-02-08 21:02:37 -05:00
|
|
|
|
|
|
|
return nil
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *CharacterSelect) moveSelectionBox() {
|
2019-11-12 23:44:04 -05:00
|
|
|
if v.selectedCharacter == -1 {
|
|
|
|
v.d2HeroTitle.SetText("")
|
|
|
|
return
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
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)
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
selBoxX := selectionBoxOffsetX + ((selectedIndex & 1) * bw)
|
|
|
|
selBoxY := selectionBoxOffsetY + (bh * (selectedIndex / 2))
|
|
|
|
v.selectionBox.SetPosition(selBoxX, selBoxY)
|
2019-11-11 23:48:55 -05:00
|
|
|
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// OnMouseButtonDown is called when a mouse button is clicked
|
2020-07-03 15:09:16 -04:00
|
|
|
func (v *CharacterSelect) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
2019-11-11 23:48:55 -05:00
|
|
|
if !v.showDeleteConfirmation {
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Button() == d2enum.MouseButtonLeft {
|
2020-07-03 15:09:16 -04:00
|
|
|
mx, my := event.X(), event.Y()
|
2020-07-18 09:54:10 -04:00
|
|
|
|
|
|
|
bw := selectionBoxWidth
|
|
|
|
bh := selectionBoxHeight
|
|
|
|
localMouseX := mx - selectionBoxOffsetX
|
|
|
|
localMouseY := my - selectionBoxOffsetY
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 {
|
|
|
|
adjustY := localMouseY / bh
|
2020-07-18 09:54:10 -04:00
|
|
|
selectedIndex := adjustY * selectionBoxNumColumns
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
if localMouseX > bw {
|
2020-07-02 13:55:43 -04:00
|
|
|
selectedIndex++
|
2020-06-23 18:12:08 -04:00
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) {
|
|
|
|
v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex
|
|
|
|
v.moveSelectionBox()
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return true
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
}
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
// Advance runs the update logic on the Character Select screen
|
2020-06-23 18:12:08 -04:00
|
|
|
func (v *CharacterSelect) Advance(tickTime float64) error {
|
2019-12-26 11:14:26 -05:00
|
|
|
for _, hero := range v.characterImage {
|
|
|
|
if hero != nil {
|
2020-06-24 13:49:13 -04:00
|
|
|
hero.Advance(tickTime)
|
2019-12-26 11:14:26 -05:00
|
|
|
}
|
|
|
|
}
|
2020-02-08 21:02:37 -05:00
|
|
|
|
|
|
|
return nil
|
2019-11-11 23:48:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2020-06-18 14:11:04 -04:00
|
|
|
v.gameStates = d2player.GetAllPlayerStates()
|
2019-11-11 23:48:55 -05:00
|
|
|
v.updateCharacterBoxes()
|
2020-07-02 13:55:43 -04:00
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
if len(v.gameStates) > 0 {
|
|
|
|
v.selectedCharacter = 0
|
2020-07-18 09:54:10 -04:00
|
|
|
numStates := selectionBoxNumColumns * selectionBoxNumRows
|
|
|
|
byHalf := 2.0
|
|
|
|
|
2019-11-11 23:48:55 -05:00
|
|
|
v.d2HeroTitle.SetText(v.gameStates[0].HeroName)
|
2020-07-18 09:54:10 -04:00
|
|
|
v.charScrollbar.SetMaxOffset(int(math.Ceil(float64(len(v.gameStates)-numStates) / byHalf)))
|
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
|
|
|
}
|
|
|
|
|
2020-07-02 13:55:43 -04:00
|
|
|
v.moveSelectionBox()
|
2019-10-26 23:59:27 -04:00
|
|
|
}
|
2019-11-13 00:31:52 -05:00
|
|
|
|
|
|
|
func (v *CharacterSelect) onOkButtonClicked() {
|
2020-07-14 13:11:23 -04:00
|
|
|
v.navigator.ToCreateGame(v.gameStates[v.selectedCharacter].FilePath, v.connectionType, v.connectionHost)
|
2019-11-13 00:31:52 -05:00
|
|
|
}
|