diff --git a/.travis.yml b/.travis.yml index b6a547b7..e5d8b5e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ go: - 1.13.3 before_install: - sudo apt-get -y install libx11-dev mesa-common-dev libglfw3-dev libgles2-mesa-dev libasound2-dev -script: go get && golangci-lint run . && go build ./cmd/Client +script: go get && go build . git: depth: 1 notifications: diff --git a/Scenes/CharacterSelect.go b/Scenes/CharacterSelect.go new file mode 100644 index 00000000..65d7621d --- /dev/null +++ b/Scenes/CharacterSelect.go @@ -0,0 +1,92 @@ +package Scenes + +import ( + "github.com/essial/OpenDiablo2/Common" + "github.com/essial/OpenDiablo2/Palettes" + "github.com/essial/OpenDiablo2/ResourcePaths" + "github.com/essial/OpenDiablo2/Sound" + "github.com/essial/OpenDiablo2/UI" + "github.com/hajimehoshi/ebiten" +) + +type CharacterSelect struct { + uiManager *UI.Manager + soundManager *Sound.Manager + fileProvider Common.FileProvider + sceneProvider SceneProvider + background *Common.Sprite + newCharButton *UI.Button + convertCharButton *UI.Button + deleteCharButton *UI.Button + exitButton *UI.Button + okButton *UI.Button +} + +func CreateCharacterSelect( + fileProvider Common.FileProvider, + sceneProvider SceneProvider, + uiManager *UI.Manager, soundManager *Sound.Manager, +) *CharacterSelect { + result := &CharacterSelect{ + uiManager: uiManager, + sceneProvider: sceneProvider, + fileProvider: fileProvider, + soundManager: soundManager, + } + return result +} + +func (v *CharacterSelect) Load() []func() { + v.soundManager.PlayBGM(ResourcePaths.BGMTitle) + return []func(){ + func() { + v.background = v.fileProvider.LoadSprite(ResourcePaths.CharacterSelectionBackground, Palettes.Sky) + v.background.MoveTo(0, 0) + }, + func() { + v.newCharButton = UI.CreateButton(UI.ButtonTypeTall, v.fileProvider, "CREATE NEW\nCHARACTER") + v.newCharButton.MoveTo(33, 468) + v.uiManager.AddWidget(v.newCharButton) + }, + func() { + v.convertCharButton = UI.CreateButton(UI.ButtonTypeTall, v.fileProvider, "CONVERT TO\nEXPANSION") + v.convertCharButton.MoveTo(233, 468) + v.convertCharButton.SetEnabled(false) + v.uiManager.AddWidget(v.convertCharButton) + }, + func() { + v.deleteCharButton = UI.CreateButton(UI.ButtonTypeTall, v.fileProvider, "DELETE\nCHARACTER") + v.deleteCharButton.MoveTo(433, 468) + v.deleteCharButton.SetEnabled(false) + v.uiManager.AddWidget(v.deleteCharButton) + }, + func() { + v.exitButton = UI.CreateButton(UI.ButtonTypeMedium, v.fileProvider, "EXIT") + v.exitButton.MoveTo(33, 537) + v.exitButton.OnActivated(func() { v.onExitButtonClicked() }) + v.uiManager.AddWidget(v.exitButton) + }, + func() { + v.okButton = UI.CreateButton(UI.ButtonTypeMedium, v.fileProvider, "OK") + v.okButton.MoveTo(625, 537) + v.okButton.SetEnabled(false) + v.uiManager.AddWidget(v.okButton) + }, + } +} + +func (v *CharacterSelect) onExitButtonClicked() { + v.sceneProvider.SetNextScene(CreateMainMenu(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) +} + +func (v *CharacterSelect) Unload() { + +} + +func (v *CharacterSelect) Render(screen *ebiten.Image) { + v.background.DrawSegments(screen, 4, 3, 0) +} + +func (v *CharacterSelect) Update(tickTime float64) { + +} diff --git a/Scenes/MainMenu.go b/Scenes/MainMenu.go index e3d0563b..b77bf1a8 100644 --- a/Scenes/MainMenu.go +++ b/Scenes/MainMenu.go @@ -50,6 +50,7 @@ func CreateMainMenu(fileProvider Common.FileProvider, sceneProvider SceneProvide soundManager: soundManager, sceneProvider: sceneProvider, ShowTrademarkScreen: true, + leftButtonHeld: true, } return result } @@ -131,6 +132,7 @@ func (v *MainMenu) Load() []func() { v.singlePlayerButton = UI.CreateButton(UI.ButtonTypeWide, v.fileProvider, "SINGLE PLAYER") v.singlePlayerButton.MoveTo(264, 290) v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen) + v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) v.uiManager.AddWidget(v.singlePlayerButton) }, func() { @@ -162,6 +164,10 @@ func openbrowser(url string) { } +func (v *MainMenu) onSinglePlayerClicked() { + v.sceneProvider.SetNextScene(CreateCharacterSelect(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager)) +} + func (v *MainMenu) onGithubButtonClicked() { openbrowser("https://www.github.com/essial/OpenDiablo2") } @@ -203,6 +209,9 @@ func (v *MainMenu) Render(screen *ebiten.Image) { func (v *MainMenu) Update(tickTime float64) { if v.ShowTrademarkScreen { if v.uiManager.CursorButtonPressed(UI.CursorButtonLeft) { + if v.leftButtonHeld { + return + } v.leftButtonHeld = true v.ShowTrademarkScreen = false v.exitDiabloButton.SetVisible(true) @@ -210,6 +219,8 @@ func (v *MainMenu) Update(tickTime float64) { v.cinematicsButton.SetVisible(true) v.singlePlayerButton.SetVisible(true) v.githubButton.SetVisible(true) + } else { + v.leftButtonHeld = false } return } diff --git a/Sound/AudioProvider.go b/Sound/AudioProvider.go index e2ae62d8..54724232 100644 --- a/Sound/AudioProvider.go +++ b/Sound/AudioProvider.go @@ -49,7 +49,7 @@ func (v *Manager) PlayBGM(song string) { if err != nil { log.Fatal(err) } - s := audio.NewInfiniteLoop(d, int64(len(audioData))) + s := audio.NewInfiniteLoop(d, d.Length()) v.bgmAudio, err = audio.NewPlayer(v.audioContext, s) if err != nil { log.Fatal(err) diff --git a/UI/Button.go b/UI/Button.go index c8ff427f..63b5593c 100644 --- a/UI/Button.go +++ b/UI/Button.go @@ -56,12 +56,11 @@ type ButtonLayout struct { var ButtonLayouts = map[ButtonType]ButtonLayout{ ButtonTypeWide: {2, 1, ResourcePaths.WideButtonBlank, Palettes.Units, false, 0, -1, ResourcePaths.FontExocet10, nil, true}, ButtonTypeShort: {1, 1, ResourcePaths.ShortButtonBlank, Palettes.Units, false, 0, -1, ResourcePaths.FontRediculous, nil, true}, - ButtonTypeMedium: {1, 1, ResourcePaths.MediumButtonBlank, Palettes.Units, false, 0, -1, ResourcePaths.FontExocet10, nil, true}, + ButtonTypeMedium: {1, 1, ResourcePaths.MediumButtonBlank, Palettes.Units, false, 0, 0, ResourcePaths.FontExocet10, nil, true}, + ButtonTypeTall: {1, 1, ResourcePaths.TallButtonBlank, Palettes.Units, false, 0, 0, ResourcePaths.FontExocet10, nil, true}, /* {eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } }, {eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = Palettes.Units } }, - {eButtonType.Tall, new ButtonLayout { ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } }, - {eButtonType.Cancel, new ButtonLayout { ResourceName = ResourcePaths.CancelButton, PaletteName = Palettes.Units } }, // Minipanel {eButtonType.MinipanelCharacter, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 0 } }, @@ -95,6 +94,7 @@ type Button struct { toggledImage *ebiten.Image pressedToggledImage *ebiten.Image disabledImage *ebiten.Image + buttonLayout ButtonLayout onClick func() } @@ -109,6 +109,7 @@ func CreateButton(buttonType ButtonType, fileProvider Common.FileProvider, text pressed: false, } buttonLayout := ButtonLayouts[buttonType] + result.buttonLayout = buttonLayout font := GetFont(buttonLayout.FontPath, Palettes.Units, fileProvider) buttonSprite := fileProvider.LoadSprite(buttonLayout.ResourceName, buttonLayout.PaletteName) totalButtonTypes := buttonSprite.GetTotalFrames() / (buttonLayout.XSegments * buttonLayout.YSegments) @@ -122,30 +123,36 @@ func CreateButton(buttonType ButtonType, fileProvider Common.FileProvider, text } result.normalImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - result.pressedImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) - textWidth, _ := font.GetTextMetrics(text) - textX := (result.width / 2) - (textWidth / 2) - textY := (result.height / 2) + _, fontHeight := font.GetTextMetrics(text) + textY := int((result.height/2)-(fontHeight/2)) + 6 + // Nasty size hack, please remove this + if buttonType == ButtonTypeShort { + textY -= 3 + } buttonSprite.MoveTo(0, 0) buttonSprite.Blend = true buttonSprite.DrawSegments(result.normalImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame) - font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.normalImage) + font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.normalImage) if buttonLayout.AllowFrameChange { if totalButtonTypes > 1 { + result.pressedImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) buttonSprite.DrawSegments(result.pressedImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1) - font.Draw(int(textX-2), int(textY+2), text, color.RGBA{100, 100, 100, 255}, result.pressedImage) + font.Draw(-2, textY+2, text, color.RGBA{100, 100, 100, 255}, result.pressedImage) } if totalButtonTypes > 2 { + result.toggledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) buttonSprite.DrawSegments(result.toggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2) - font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.toggledImage) + font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.toggledImage) } if totalButtonTypes > 3 { + result.pressedToggledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) buttonSprite.DrawSegments(result.pressedToggledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3) - font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.pressedToggledImage) + font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.pressedToggledImage) } if buttonLayout.DisabledFrame != -1 { + result.disabledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest) buttonSprite.DrawSegments(result.disabledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame) - font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.disabledImage) + font.Draw(0, textY-1, text, color.RGBA{100, 100, 100, 255}, result.disabledImage) } } return result @@ -173,6 +180,8 @@ func (v *Button) Draw(target *ebiten.Image) { opts.GeoM.Translate(float64(v.x), float64(v.y)) if !v.enabled { + //opts.CompositeMode = ebiten.CompositeModeLighter + opts.ColorM = Common.ColorToColorM(color.RGBA{128, 128, 128, 195}) target.DrawImage(v.disabledImage, opts) } else if v.toggled && v.pressed { target.DrawImage(v.pressedToggledImage, opts) diff --git a/UI/Font.go b/UI/Font.go index fb5d3097..42cbb91a 100644 --- a/UI/Font.go +++ b/UI/Font.go @@ -2,6 +2,7 @@ package UI import ( "image/color" + "strings" "github.com/essial/OpenDiablo2/Common" "github.com/essial/OpenDiablo2/Palettes" @@ -57,14 +58,25 @@ func CreateFont(font string, palette Palettes.Palette, fileProvider Common.FileP // GetTextMetrics returns the size of the specified text func (v *Font) GetTextMetrics(text string) (width, height uint32) { width = uint32(0) + curWidth := uint32(0) height = uint32(0) + maxCharHeight := uint32(0) + for _, m := range v.metrics { + maxCharHeight = Common.Max(maxCharHeight, uint32(m.Height)) + } for i := 0; i < len(text); i++ { ch := text[i] + if ch == '\n' { + width = Common.Max(width, curWidth) + curWidth = 0 + height += maxCharHeight + 6 + continue + } metric := v.metrics[uint8(ch)] - width += uint32(metric.Width) - //_, h := v.fontSprite.GetFrameSize(int(ch)) - height = Common.Max(height, uint32(metric.Height)) + curWidth += uint32(metric.Width) } + width = Common.Max(width, curWidth) + height += maxCharHeight return } @@ -72,13 +84,32 @@ func (v *Font) GetTextMetrics(text string) (width, height uint32) { func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Image) { v.fontSprite.ColorMod = color v.fontSprite.Blend = false - _, height := v.GetTextMetrics(text) - for _, ch := range text { - char := uint8(ch) - metric := v.metrics[char] - v.fontSprite.Frame = char - v.fontSprite.MoveTo(x, y+int(height)) - v.fontSprite.Draw(target) - x += int(metric.Width) + + maxCharHeight := uint32(0) + for _, m := range v.metrics { + maxCharHeight = Common.Max(maxCharHeight, uint32(m.Height)) + } + + targetWidth, _ := target.Size() + lines := strings.Split(text, "\n") + for lineIdx, line := range lines { + lineWidth, _ := v.GetTextMetrics(line) + xPos := x + ((targetWidth / 2) - int(lineWidth/2)) + + for _, ch := range line { + char := uint8(ch) + metric := v.metrics[char] + v.fontSprite.Frame = char + v.fontSprite.MoveTo(xPos, y+int(metric.Height)) + v.fontSprite.Draw(target) + xPos += int(metric.Width) + } + + if lineIdx >= len(lines)-1 { + break + } + + xPos = x + y += int(maxCharHeight + 6) } }