2020-06-13 20:36:20 -04:00
package d2gamescreen
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"
2020-06-23 18:12:08 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
2020-02-01 21:06:22 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
2020-02-01 18:55:56 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
2020-02-01 21:06:22 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
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-02-01 20:39:28 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
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"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
2019-10-26 23:59:27 -04:00
)
type CharacterSelect struct {
2020-02-01 18:55:56 -05:00
background * d2ui . Sprite
2019-11-11 23:48:55 -05: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
2019-11-11 23:48:55 -05:00
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
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
2019-10-26 23:59:27 -04:00
}
2020-06-18 14:11:04 -04:00
func CreateCharacterSelect ( connectionType d2clientconnectiontype . ClientConnectionType , connectionHost string ) * CharacterSelect {
return & CharacterSelect {
selectedCharacter : - 1 ,
connectionType : connectionType ,
connectionHost : connectionHost ,
}
2019-10-26 23:59:27 -04:00
}
2020-06-24 18:46:03 -04:00
func ( v * CharacterSelect ) OnLoad ( loading d2screen . LoadingState ) {
2020-01-31 23:18:11 -05:00
d2audio . PlayBGM ( d2resource . BGMTitle )
2020-06-23 18:12:08 -04:00
d2input . BindHandler ( v )
2020-06-24 18:46:03 -04:00
loading . Progress ( 0.1 )
2020-02-08 21:02:37 -05:00
animation , _ := d2asset . LoadAnimation ( d2resource . CharacterSelectionBackground , d2resource . PaletteSky )
v . background , _ = d2ui . LoadSprite ( animation )
v . background . SetPosition ( 0 , 0 )
2020-06-22 11:53:44 -04:00
v . newCharButton = d2ui . CreateButton ( d2ui . ButtonTypeTall , d2common . CombineStrings ( d2common . SplitIntoLinesWithMaxWidth ( "CREATE NEW CHARACTER" , 15 ) ) )
2020-02-08 21:02:37 -05:00
v . newCharButton . SetPosition ( 33 , 468 )
v . newCharButton . OnActivated ( func ( ) { v . onNewCharButtonClicked ( ) } )
d2ui . AddWidget ( & v . newCharButton )
2020-06-22 11:53:44 -04:00
v . convertCharButton = d2ui . CreateButton ( d2ui . ButtonTypeTall , d2common . CombineStrings ( d2common . SplitIntoLinesWithMaxWidth ( "CONVERT TO EXPANSION" , 15 ) ) )
2020-02-08 21:02:37 -05:00
v . convertCharButton . SetPosition ( 233 , 468 )
v . convertCharButton . SetEnabled ( false )
d2ui . AddWidget ( & v . convertCharButton )
2020-06-22 11:53:44 -04:00
v . deleteCharButton = d2ui . CreateButton ( d2ui . ButtonTypeTall , d2common . CombineStrings ( d2common . SplitIntoLinesWithMaxWidth ( "DELETE CHARACTER" , 15 ) ) )
2020-02-08 21:02:37 -05:00
v . deleteCharButton . OnActivated ( func ( ) { v . onDeleteCharButtonClicked ( ) } )
v . deleteCharButton . SetPosition ( 433 , 468 )
d2ui . AddWidget ( & v . deleteCharButton )
2020-06-20 15:14:52 -04:00
v . exitButton = d2ui . CreateButton ( d2ui . ButtonTypeMedium , "EXIT" )
2020-02-08 21:02:37 -05:00
v . exitButton . SetPosition ( 33 , 537 )
v . exitButton . OnActivated ( func ( ) { v . onExitButtonClicked ( ) } )
d2ui . AddWidget ( & v . exitButton )
2020-06-24 18:46:03 -04:00
loading . Progress ( 0.2 )
2020-02-08 21:02:37 -05:00
2020-06-20 15:14:52 -04:00
v . deleteCharCancelButton = d2ui . CreateButton ( d2ui . ButtonTypeOkCancel , "NO" )
2020-02-08 21:02:37 -05:00
v . deleteCharCancelButton . SetPosition ( 282 , 308 )
v . deleteCharCancelButton . SetVisible ( false )
v . deleteCharCancelButton . OnActivated ( func ( ) { v . onDeleteCharacterCancelClicked ( ) } )
d2ui . AddWidget ( & v . deleteCharCancelButton )
2020-06-20 15:14:52 -04:00
v . deleteCharOkButton = d2ui . CreateButton ( d2ui . ButtonTypeOkCancel , "YES" )
2020-02-08 21:02:37 -05:00
v . deleteCharOkButton . SetPosition ( 422 , 308 )
v . deleteCharOkButton . SetVisible ( false )
v . deleteCharOkButton . OnActivated ( func ( ) { v . onDeleteCharacterConfirmClicked ( ) } )
d2ui . AddWidget ( & v . deleteCharOkButton )
2020-06-20 15:14:52 -04:00
v . okButton = d2ui . CreateButton ( d2ui . ButtonTypeMedium , "OK" )
2020-02-08 21:02:37 -05:00
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
2020-06-24 18:46:03 -04:00
loading . Progress ( 0.3 )
2020-02-08 21:02:37 -05:00
v . deleteCharConfirmLabel = d2ui . CreateLabel ( d2resource . Font16 , d2resource . PaletteUnits )
2020-06-22 11:53:44 -04:00
lines := d2common . SplitIntoLinesWithMaxWidth ( "Are you sure that you want to delete this character? Take note: this will delete all versions of this Character." , 29 )
2020-02-08 21:02:37 -05:00
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 )
2020-06-24 18:46:03 -04:00
loading . Progress ( 0.5 )
2020-02-08 21:02:37 -05:00
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 ) )
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
}
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"
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
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...
2020-06-21 18:40:37 -04:00
v . characterImage [ i ] = d2mapentity . CreatePlayer ( "" , "" , 0 , 0 , 0 ,
2019-11-14 22:20:01 -05:00
v . gameStates [ idx ] . HeroType ,
2020-02-01 18:55:56 -05:00
d2inventory . HeroObjects [ v . gameStates [ idx ] . HeroType ] ,
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-06-18 14:11:04 -04:00
d2screen . SetNextScreen ( CreateSelectHeroClass ( 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-01-31 23:18:11 -05:00
mainMenu := CreateMainMenu ( )
2020-06-18 14:11:04 -04:00
mainMenu . SetScreenMode ( ScreenModeMainMenu )
2020-06-13 20:36:20 -04:00
d2screen . SetNextScreen ( mainMenu )
2019-10-26 23:59:27 -04:00
}
2020-02-08 21:02:37 -05:00
func ( v * CharacterSelect ) Render ( screen d2render . Surface ) error {
2019-12-21 20:53:18 -05:00
v . background . RenderSegmented ( screen , 4 , 3 , 0 )
v . d2HeroTitle . Render ( 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-12-21 20:53:18 -05:00
v . selectionBox . RenderSegmented ( screen , 2 , 1 , 0 )
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
}
2019-12-21 20:53:18 -05:00
v . characterNameLabel [ i ] . Render ( screen )
v . characterStatsLabel [ i ] . Render ( screen )
v . characterExpLabel [ i ] . Render ( screen )
2019-12-28 16:46:08 -05:00
screen . PushTranslation ( v . characterNameLabel [ i ] . X - 40 , v . characterNameLabel [ i ] . Y + 50 )
v . characterImage [ i ] . Render ( screen )
screen . Pop ( )
2019-11-11 23:48:55 -05:00
}
if v . showDeleteConfirmation {
2020-02-01 21:51:49 -05:00
screen . DrawRect ( 800 , 600 , color . RGBA { A : 128 } )
2019-12-21 20:53:18 -05:00
v . okCancelBox . RenderSegmented ( screen , 2 , 1 , 0 )
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
}
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-02-01 21:51:49 -05:00
v . selectionBox . SetPosition ( 37 + ( ( selectedIndex & 1 ) * bw ) , 86 + ( bh * ( selectedIndex / 2 ) ) )
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-06-23 18:12:08 -04:00
func ( v * CharacterSelect ) OnMouseButtonDown ( event d2input . MouseEvent ) bool {
2019-11-11 23:48:55 -05:00
if ! v . showDeleteConfirmation {
2020-06-23 18:12:08 -04:00
if event . Button == d2input . 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 += 1
}
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-06-23 18:12:08 -04:00
return true
2019-11-11 23:48:55 -05:00
}
}
2020-06-23 18:12:08 -04:00
return false
}
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 ( )
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 ( ) {
2020-06-18 14:11:04 -04:00
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 )
}
2020-06-13 20:36:20 -04:00
d2screen . SetNextScreen ( CreateGame ( gameClient ) )
2019-11-13 00:31:52 -05:00
}