mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-24 19:18:12 -05:00
d2ui refactor (#699)
* fixed lint errors in button.go * fixed lint errors in checkbox.go * Removed d2ui singleton, fixed nearly all lint errors - Changed `UI` struct to `UIManager`, removed singleton - UI element provider functions are now methods of the UI Manager - Screens now use the UI manager to create UI elements - game panels in d2player now use the UI Manager to create UI elements - Only the UI manager knows about "widgets"; calls to `d2ui.AddWidget` in Screen instances have been removed * changed ui element provider methods from `Create` to `New`
This commit is contained in:
parent
c1ed5a2381
commit
acc4c7a13e
27
d2app/app.go
27
d2app/app.go
@ -64,6 +64,7 @@ type App struct {
|
||||
audio d2interface.AudioProvider
|
||||
renderer d2interface.Renderer
|
||||
screen *d2screen.ScreenManager
|
||||
ui *d2ui.UIManager
|
||||
tAllocSamples *ring.Ring
|
||||
}
|
||||
|
||||
@ -85,7 +86,11 @@ func Create(gitBranch, gitCommit string,
|
||||
terminal d2interface.Terminal,
|
||||
scriptEngine *d2script.ScriptEngine,
|
||||
audio d2interface.AudioProvider,
|
||||
renderer d2interface.Renderer) *App {
|
||||
renderer d2interface.Renderer,
|
||||
) *App {
|
||||
uiManager := d2ui.NewUIManager(renderer, inputManager, audio)
|
||||
screenManager := d2screen.NewScreenManager(uiManager)
|
||||
|
||||
result := &App{
|
||||
gitBranch: gitBranch,
|
||||
gitCommit: gitCommit,
|
||||
@ -94,7 +99,8 @@ func Create(gitBranch, gitCommit string,
|
||||
scriptEngine: scriptEngine,
|
||||
audio: audio,
|
||||
renderer: renderer,
|
||||
screen: d2screen.NewScreenManager(),
|
||||
ui: uiManager,
|
||||
screen: screenManager,
|
||||
tAllocSamples: createZeroedRing(nSamplesTAlloc),
|
||||
}
|
||||
|
||||
@ -187,7 +193,7 @@ func (a *App) initialize() error {
|
||||
|
||||
d2inventory.LoadHeroObjects()
|
||||
|
||||
d2ui.Initialize(a.inputManager, a.audio)
|
||||
a.ui.Initialize()
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -368,7 +374,7 @@ func (a *App) render(target d2interface.Surface) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d2ui.Render(target)
|
||||
a.ui.Render(target)
|
||||
|
||||
if err := d2gui.Render(target); err != nil {
|
||||
return err
|
||||
@ -398,7 +404,7 @@ func (a *App) advance(elapsed, elapsedUnscaled, current float64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d2ui.Advance(elapsed)
|
||||
a.ui.Advance(elapsed)
|
||||
|
||||
if err := a.inputManager.Advance(elapsed, current); err != nil {
|
||||
return err
|
||||
@ -668,14 +674,14 @@ func updateInitError(target d2interface.Surface) error {
|
||||
// ToMainMenu forces the game to transition to the Main Menu
|
||||
func (a *App) ToMainMenu() {
|
||||
buildInfo := d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit}
|
||||
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, buildInfo)
|
||||
mainMenu.SetScreenMode(d2gamescreen.ScreenModeMainMenu)
|
||||
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, a.ui, buildInfo)
|
||||
// mainMenu.SetScreenMode(d2gamescreen.ScreenModeMainMenu)
|
||||
a.screen.SetNextScreen(mainMenu)
|
||||
}
|
||||
|
||||
// ToSelectHero forces the game to transition to the Select Hero (create character) screen
|
||||
func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, host string) {
|
||||
selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, connType, host)
|
||||
selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, a.ui, connType, host)
|
||||
a.screen.SetNextScreen(selectHero)
|
||||
}
|
||||
|
||||
@ -693,7 +699,8 @@ func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.Clie
|
||||
|
||||
// ToCharacterSelect forces the game to transition to the Character Select (load character) screen
|
||||
func (a *App) ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) {
|
||||
characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio, connType, connHost)
|
||||
characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio,
|
||||
a.ui, connType, connHost)
|
||||
a.screen.SetNextScreen(characterSelect)
|
||||
}
|
||||
|
||||
@ -706,5 +713,5 @@ func (a *App) ToMapEngineTest(region, level int) {
|
||||
|
||||
// ToCredits forces the game to transition to the credits screen
|
||||
func (a *App) ToCredits() {
|
||||
a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
|
||||
a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer, a.ui))
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func NewPlayer(id, name string, x, y, direction int, heroType d2enum.Hero,
|
||||
Stats: stats,
|
||||
name: name,
|
||||
Class: heroType,
|
||||
//nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
|
||||
//nameLabel: d2ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
|
||||
isRunToggled: true,
|
||||
isInTown: true,
|
||||
isRunning: true,
|
||||
|
@ -2,11 +2,16 @@ package d2screen
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
|
||||
// NewScreenManager creates a screen manager
|
||||
func NewScreenManager() *ScreenManager {
|
||||
return &ScreenManager{}
|
||||
func NewScreenManager(ui *d2ui.UIManager) *ScreenManager {
|
||||
sm := &ScreenManager{
|
||||
uiManager: ui,
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// Screen is an exported interface
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
// ScreenManager manages game screens (main menu, credits, character select, game, etc)
|
||||
type ScreenManager struct {
|
||||
uiManager *d2ui.UIManager
|
||||
nextScreen Screen
|
||||
loadingScreen Screen
|
||||
loadingState LoadingState
|
||||
@ -52,7 +53,7 @@ func (sm *ScreenManager) Advance(elapsed float64) error {
|
||||
}
|
||||
}
|
||||
|
||||
d2ui.Reset()
|
||||
sm.uiManager.Reset()
|
||||
d2gui.SetLayout(nil)
|
||||
|
||||
if handler, ok := sm.nextScreen.(ScreenLoadHandler); ok {
|
||||
|
@ -3,8 +3,8 @@ package d2ui
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
@ -43,6 +43,11 @@ const (
|
||||
ButtonTypeMinipanelMen ButtonType = 19
|
||||
)
|
||||
|
||||
const (
|
||||
greyAlpha100 = 0x646464_ff
|
||||
lightGreyAlpha75 = 0x808080_c3
|
||||
)
|
||||
|
||||
// ButtonLayout defines the type of buttons
|
||||
type ButtonLayout struct {
|
||||
ResourceName string
|
||||
@ -58,31 +63,101 @@ type ButtonLayout struct {
|
||||
AllowFrameChange bool
|
||||
}
|
||||
|
||||
const (
|
||||
buttonWideSegmentsX = 2
|
||||
buttonWideSegmentsY = 1
|
||||
buttonWideDisabledFrame = -1
|
||||
buttonWideTextOffset = 1
|
||||
|
||||
buttonShortSegmentsX = 1
|
||||
buttonShortSegmentsY = 1
|
||||
buttonShortDisabledFrame = -1
|
||||
buttonShortTextOffset = -1
|
||||
|
||||
buttonMediumSegmentsX = 1
|
||||
buttonMediumSegmentsY = 1
|
||||
|
||||
buttonTallSegmentsX = 1
|
||||
buttonTallSegmentsY = 1
|
||||
buttonTallTextOffset = 5
|
||||
|
||||
buttonOkCancelSegmentsX = 1
|
||||
buttonOkCancelSegmentsY = 1
|
||||
buttonOkCancelDisabledFrame = -1
|
||||
|
||||
buttonRunSegmentsX = 1
|
||||
buttonRunSegmentsY = 1
|
||||
buttonRunDisabledFrame = -1
|
||||
|
||||
pressedButtonOffset = 2
|
||||
)
|
||||
|
||||
func getButtonLayouts() map[ButtonType]ButtonLayout {
|
||||
return map[ButtonType]ButtonLayout{
|
||||
ButtonTypeWide: {
|
||||
XSegments: 2, YSegments: 1, ResourceName: d2resource.WideButtonBlank, PaletteName: d2resource.PaletteUnits,
|
||||
DisabledFrame: -1, FontPath: d2resource.FontExocet10, AllowFrameChange: true, TextOffset: 1},
|
||||
XSegments: buttonWideSegmentsX,
|
||||
YSegments: buttonWideSegmentsY,
|
||||
DisabledFrame: buttonWideDisabledFrame,
|
||||
TextOffset: buttonWideTextOffset,
|
||||
ResourceName: d2resource.WideButtonBlank,
|
||||
PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontExocet10,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
ButtonTypeShort: {
|
||||
XSegments: 1, YSegments: 1, ResourceName: d2resource.ShortButtonBlank, PaletteName: d2resource.PaletteUnits,
|
||||
DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true, TextOffset: -1},
|
||||
XSegments: buttonShortSegmentsX,
|
||||
YSegments: buttonShortSegmentsY,
|
||||
DisabledFrame: buttonShortDisabledFrame,
|
||||
TextOffset: buttonShortTextOffset,
|
||||
ResourceName: d2resource.ShortButtonBlank,
|
||||
PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontRediculous,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
ButtonTypeMedium: {
|
||||
XSegments: 1, YSegments: 1, ResourceName: d2resource.MediumButtonBlank, PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontExocet10, AllowFrameChange: true},
|
||||
XSegments: buttonMediumSegmentsX,
|
||||
YSegments: buttonMediumSegmentsY,
|
||||
ResourceName: d2resource.MediumButtonBlank,
|
||||
PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontExocet10,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
ButtonTypeTall: {
|
||||
XSegments: 1, YSegments: 1, ResourceName: d2resource.TallButtonBlank, PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontExocet10, AllowFrameChange: true, TextOffset: 5},
|
||||
XSegments: buttonTallSegmentsX,
|
||||
YSegments: buttonTallSegmentsY,
|
||||
TextOffset: buttonTallTextOffset,
|
||||
ResourceName: d2resource.TallButtonBlank,
|
||||
PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontExocet10,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
ButtonTypeOkCancel: {
|
||||
XSegments: 1, YSegments: 1, ResourceName: d2resource.CancelButton, PaletteName: d2resource.PaletteUnits,
|
||||
DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true},
|
||||
XSegments: buttonOkCancelSegmentsX,
|
||||
YSegments: buttonOkCancelSegmentsY,
|
||||
DisabledFrame: buttonOkCancelDisabledFrame,
|
||||
ResourceName: d2resource.CancelButton,
|
||||
PaletteName: d2resource.PaletteUnits,
|
||||
FontPath: d2resource.FontRediculous,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
ButtonTypeRun: {
|
||||
XSegments: 1, YSegments: 1, ResourceName: d2resource.RunButton, PaletteName: d2resource.PaletteSky,
|
||||
Toggleable: true, DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true},
|
||||
XSegments: buttonRunSegmentsX,
|
||||
YSegments: buttonRunSegmentsY,
|
||||
DisabledFrame: buttonRunDisabledFrame,
|
||||
ResourceName: d2resource.RunButton,
|
||||
PaletteName: d2resource.PaletteSky,
|
||||
Toggleable: true,
|
||||
FontPath: d2resource.FontRediculous,
|
||||
AllowFrameChange: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var _ Widget = &Button{} // static check to ensure button implements widget
|
||||
|
||||
// Button defines a standard wide UI button
|
||||
type Button struct {
|
||||
manager *UIManager
|
||||
buttonLayout ButtonLayout
|
||||
normalSurface d2interface.Surface
|
||||
pressedSurface d2interface.Surface
|
||||
@ -100,80 +175,93 @@ type Button struct {
|
||||
toggled bool
|
||||
}
|
||||
|
||||
// CreateButton creates an instance of Button
|
||||
func CreateButton(renderer d2interface.Renderer, buttonType ButtonType, text string) Button {
|
||||
result := Button{
|
||||
// NewButton creates an instance of Button
|
||||
func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button {
|
||||
btn := &Button{
|
||||
width: 0,
|
||||
height: 0,
|
||||
visible: true,
|
||||
enabled: true,
|
||||
pressed: false,
|
||||
}
|
||||
buttonLayout := getButtonLayouts()[buttonType]
|
||||
result.buttonLayout = buttonLayout
|
||||
lbl := CreateLabel(buttonLayout.FontPath, d2resource.PaletteUnits)
|
||||
|
||||
buttonLayout := getButtonLayouts()[buttonType]
|
||||
btn.buttonLayout = buttonLayout
|
||||
lbl := ui.NewLabel(buttonLayout.FontPath, d2resource.PaletteUnits)
|
||||
|
||||
lbl.SetText(text)
|
||||
lbl.Color[0] = color.RGBA{R: 100, G: 100, B: 100, A: 255}
|
||||
lbl.Color[0] = d2common.Color(greyAlpha100)
|
||||
lbl.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
animation, _ := d2asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName)
|
||||
buttonSprite, _ := LoadSprite(animation)
|
||||
buttonSprite, _ := ui.NewSprite(animation)
|
||||
|
||||
for i := 0; i < buttonLayout.XSegments; i++ {
|
||||
w, _, _ := buttonSprite.GetFrameSize(i)
|
||||
result.width += w
|
||||
btn.width += w
|
||||
}
|
||||
|
||||
for i := 0; i < buttonLayout.YSegments; i++ {
|
||||
_, h, _ := buttonSprite.GetFrameSize(i * buttonLayout.YSegments)
|
||||
result.height += h
|
||||
btn.height += h
|
||||
}
|
||||
|
||||
result.normalSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
btn.normalSurface, _ = ui.renderer.NewSurface(btn.width, btn.height, d2enum.FilterNearest)
|
||||
|
||||
buttonSprite.SetPosition(0, 0)
|
||||
buttonSprite.SetEffect(d2enum.DrawEffectModulate)
|
||||
|
||||
result.renderFrames(renderer, buttonSprite, &buttonLayout, &lbl)
|
||||
ui.addWidget(btn) // important that this comes before renderFrames!
|
||||
|
||||
return result
|
||||
btn.renderFrames(buttonSprite, &buttonLayout, lbl)
|
||||
|
||||
return btn
|
||||
}
|
||||
|
||||
func (v *Button) renderFrames(renderer d2interface.Renderer, buttonSprite *Sprite, buttonLayout *ButtonLayout, label *Label) {
|
||||
totalButtonTypes := buttonSprite.GetFrameCount() / (buttonLayout.XSegments * buttonLayout.YSegments)
|
||||
func (v *Button) renderFrames(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) {
|
||||
totalButtonTypes := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments)
|
||||
|
||||
var err error
|
||||
err = buttonSprite.RenderSegmented(v.normalSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame)
|
||||
err = btnSprite.RenderSegmented(v.normalSurface, btnLayout.XSegments, btnLayout.YSegments, btnLayout.BaseFrame)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button normalSurface, err: %v\n", err)
|
||||
}
|
||||
|
||||
_, labelHeight := label.GetSize()
|
||||
textY := v.height/2 - labelHeight/2
|
||||
xOffset := v.width / 2
|
||||
textY := half(v.height - labelHeight)
|
||||
xOffset := half(v.width)
|
||||
|
||||
label.SetPosition(xOffset, textY)
|
||||
label.Render(v.normalSurface)
|
||||
|
||||
if buttonLayout.AllowFrameChange {
|
||||
if totalButtonTypes > 1 {
|
||||
v.pressedSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
|
||||
err = buttonSprite.RenderSegmented(v.pressedSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1)
|
||||
if btnLayout.AllowFrameChange {
|
||||
frameOffset := 0
|
||||
xSeg, ySeg, baseFrame := btnLayout.XSegments, btnLayout.YSegments, btnLayout.BaseFrame
|
||||
|
||||
totalButtonTypes--
|
||||
if totalButtonTypes > 0 { // button has more than one type
|
||||
frameOffset++
|
||||
|
||||
v.pressedSurface, _ = v.manager.renderer.NewSurface(v.width, v.height,
|
||||
d2enum.FilterNearest)
|
||||
err = btnSprite.RenderSegmented(v.pressedSurface, xSeg, ySeg, baseFrame+frameOffset)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button pressedSurface, err: %v\n", err)
|
||||
}
|
||||
|
||||
label.SetPosition(xOffset-2, textY+2)
|
||||
label.SetPosition(xOffset-pressedButtonOffset, textY+pressedButtonOffset)
|
||||
label.Render(v.pressedSurface)
|
||||
}
|
||||
|
||||
if totalButtonTypes > 2 {
|
||||
v.toggledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
|
||||
err = buttonSprite.RenderSegmented(v.toggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2)
|
||||
totalButtonTypes--
|
||||
if totalButtonTypes > 0 { // button has more than two types
|
||||
frameOffset++
|
||||
|
||||
v.toggledSurface, _ = v.manager.renderer.NewSurface(v.width, v.height,
|
||||
d2enum.FilterNearest)
|
||||
err = btnSprite.RenderSegmented(v.pressedSurface, xSeg, ySeg, baseFrame+frameOffset)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button toggledSurface, err: %v\n", err)
|
||||
@ -183,9 +271,13 @@ func (v *Button) renderFrames(renderer d2interface.Renderer, buttonSprite *Sprit
|
||||
label.Render(v.toggledSurface)
|
||||
}
|
||||
|
||||
if totalButtonTypes > 3 {
|
||||
v.pressedToggledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
|
||||
err = buttonSprite.RenderSegmented(v.pressedToggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3)
|
||||
totalButtonTypes--
|
||||
if totalButtonTypes > 0 { // button has more than three types
|
||||
frameOffset++
|
||||
|
||||
v.pressedToggledSurface, _ = v.manager.renderer.NewSurface(v.width, v.height,
|
||||
d2enum.FilterNearest)
|
||||
err = btnSprite.RenderSegmented(v.pressedSurface, xSeg, ySeg, baseFrame+frameOffset)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button pressedToggledSurface, err: %v\n", err)
|
||||
@ -195,9 +287,10 @@ func (v *Button) renderFrames(renderer d2interface.Renderer, buttonSprite *Sprit
|
||||
label.Render(v.pressedToggledSurface)
|
||||
}
|
||||
|
||||
if buttonLayout.DisabledFrame != -1 {
|
||||
v.disabledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
|
||||
err = buttonSprite.RenderSegmented(v.disabledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame)
|
||||
if btnLayout.DisabledFrame != -1 {
|
||||
v.disabledSurface, _ = v.manager.renderer.NewSurface(v.width, v.height,
|
||||
d2enum.FilterNearest)
|
||||
err = btnSprite.RenderSegmented(v.disabledSurface, xSeg, ySeg, btnLayout.DisabledFrame)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button disabledSurface, err: %v\n", err)
|
||||
@ -209,6 +302,11 @@ func (v *Button) renderFrames(renderer d2interface.Renderer, buttonSprite *Sprit
|
||||
}
|
||||
}
|
||||
|
||||
// bindManager binds the button to the UI manager
|
||||
func (v *Button) bindManager(manager *UIManager) {
|
||||
v.manager = manager
|
||||
}
|
||||
|
||||
// OnActivated defines the callback handler for the activate event
|
||||
func (v *Button) OnActivated(callback func()) {
|
||||
v.onClick = callback
|
||||
@ -226,15 +324,16 @@ func (v *Button) Activate() {
|
||||
// Render renders the button
|
||||
func (v *Button) Render(target d2interface.Surface) error {
|
||||
target.PushFilter(d2enum.FilterNearest)
|
||||
target.PushTranslation(v.x, v.y)
|
||||
defer target.Pop()
|
||||
|
||||
defer target.PopN(2)
|
||||
target.PushTranslation(v.x, v.y)
|
||||
defer target.Pop()
|
||||
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case !v.enabled:
|
||||
target.PushColor(color.RGBA{R: 128, G: 128, B: 128, A: 195})
|
||||
target.PushColor(d2common.Color(lightGreyAlpha75))
|
||||
defer target.Pop()
|
||||
err = target.Render(v.disabledSurface)
|
||||
case v.toggled && v.pressed:
|
||||
@ -260,8 +359,8 @@ func (v *Button) Toggle() {
|
||||
}
|
||||
|
||||
// Advance advances the button state
|
||||
func (v *Button) Advance(elapsed float64) {
|
||||
|
||||
func (v *Button) Advance(_ float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnabled returns the enabled state
|
||||
@ -309,3 +408,7 @@ func (v *Button) GetPressed() bool {
|
||||
func (v *Button) SetPressed(pressed bool) {
|
||||
v.pressed = pressed
|
||||
}
|
||||
|
||||
func half(n int) int {
|
||||
return n / 2
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
// Checkbox represents a checkbox UI element
|
||||
type Checkbox struct {
|
||||
manager *UIManager
|
||||
Image d2interface.Surface
|
||||
checkedImage d2interface.Surface
|
||||
x int
|
||||
@ -21,9 +22,9 @@ type Checkbox struct {
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// CreateCheckbox creates a new instance of a checkbox
|
||||
func CreateCheckbox(renderer d2interface.Renderer, checkState bool) Checkbox {
|
||||
result := Checkbox{
|
||||
// NewCheckbox creates a new instance of a checkbox
|
||||
func (ui *UIManager) NewCheckbox(checkState bool) *Checkbox {
|
||||
result := &Checkbox{
|
||||
checkState: checkState,
|
||||
visible: true,
|
||||
width: 0,
|
||||
@ -32,27 +33,35 @@ func CreateCheckbox(renderer d2interface.Renderer, checkState bool) Checkbox {
|
||||
}
|
||||
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Checkbox, d2resource.PaletteFechar)
|
||||
checkboxSprite, _ := LoadSprite(animation)
|
||||
checkboxSprite, _ := ui.NewSprite(animation)
|
||||
result.width, result.height, _ = checkboxSprite.GetFrameSize(0)
|
||||
checkboxSprite.SetPosition(0, 0)
|
||||
|
||||
result.Image, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
result.Image, _ = ui.renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
|
||||
_ = checkboxSprite.RenderSegmented(result.Image, 1, 1, 0)
|
||||
|
||||
result.checkedImage, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
result.checkedImage, _ = ui.renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
|
||||
_ = checkboxSprite.RenderSegmented(result.checkedImage, 1, 1, 1)
|
||||
|
||||
ui.addWidget(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// bindManager binds the checkbox to the UI manager
|
||||
func (v *Checkbox) bindManager(manager *UIManager) {
|
||||
v.manager = manager
|
||||
}
|
||||
|
||||
// Render renders the checkbox
|
||||
func (v *Checkbox) Render(target d2interface.Surface) error {
|
||||
target.PushTranslation(v.x, v.y)
|
||||
target.PushFilter(d2enum.FilterNearest)
|
||||
defer target.Pop()
|
||||
|
||||
defer target.PopN(2)
|
||||
target.PushFilter(d2enum.FilterNearest)
|
||||
defer target.Pop()
|
||||
|
||||
if v.checkState {
|
||||
_ = target.Render(v.checkedImage)
|
||||
@ -64,8 +73,8 @@ func (v *Checkbox) Render(target d2interface.Surface) error {
|
||||
}
|
||||
|
||||
// Advance does nothing for checkboxes
|
||||
func (v *Checkbox) Advance(elapsed float64) {
|
||||
|
||||
func (v *Checkbox) Advance(_ float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnabled returns the enabled state of the checkbox
|
||||
@ -97,7 +106,7 @@ func (v *Checkbox) GetPressed() bool {
|
||||
return v.checkState
|
||||
}
|
||||
|
||||
// OnACtivated sets the callback function of the click event for the checkbox
|
||||
// OnActivated sets the callback function of the click event for the checkbox
|
||||
func (v *Checkbox) OnActivated(callback func()) {
|
||||
v.onClick = callback
|
||||
}
|
||||
@ -108,16 +117,17 @@ func (v *Checkbox) Activate() {
|
||||
if v.onClick == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.onClick()
|
||||
}
|
||||
|
||||
// GetPosition returns the position of the checkbox
|
||||
func (v *Checkbox) GetPosition() (int, int) {
|
||||
func (v *Checkbox) GetPosition() (x, y int) {
|
||||
return v.x, v.y
|
||||
}
|
||||
|
||||
// GetSize returns the size of the checkbox
|
||||
func (v *Checkbox) GetSize() (int, int) {
|
||||
func (v *Checkbox) GetSize() (width, height int) {
|
||||
return v.width, v.height
|
||||
}
|
||||
|
||||
@ -127,7 +137,7 @@ func (v *Checkbox) GetVisible() bool {
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the checkbox
|
||||
func (v *Checkbox) SetPosition(x int, y int) {
|
||||
func (v *Checkbox) SetPosition(x, y int) {
|
||||
v.x = x
|
||||
v.y = y
|
||||
}
|
||||
|
58
d2core/d2ui/color_tokens.go
Normal file
58
d2core/d2ui/color_tokens.go
Normal file
@ -0,0 +1,58 @@
|
||||
package d2ui
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ColorToken is a string which is used inside of label strings to set font color.
|
||||
type ColorToken string
|
||||
|
||||
const (
|
||||
colorTokenFmt = `%s%s`
|
||||
colorTokenMatch = `\[[^\]]+\]` // nolint:gosec // has nothing to to with credentials
|
||||
colorStrMatch = colorTokenMatch + `[^\[]+`
|
||||
)
|
||||
|
||||
// Color tokens for colored labels
|
||||
const (
|
||||
ColorTokenGrey ColorToken = "[grey]"
|
||||
ColorTokenRed ColorToken = "[red]"
|
||||
ColorTokenWhite ColorToken = "[white]"
|
||||
ColorTokenBlue ColorToken = "[blue]"
|
||||
ColorTokenYellow ColorToken = "[yellow]"
|
||||
ColorTokenGreen ColorToken = "[green]"
|
||||
ColorTokenGold ColorToken = "[gold]"
|
||||
ColorTokenOrange ColorToken = "[orange]"
|
||||
ColorTokenBlack ColorToken = "[black]"
|
||||
)
|
||||
|
||||
// Color tokens for specific use-cases
|
||||
const (
|
||||
ColorTokenSocketedItem = ColorTokenGrey
|
||||
ColorTokenNormalItem = ColorTokenWhite
|
||||
ColorTokenMagicItem = ColorTokenBlue
|
||||
ColorTokenRareItem = ColorTokenYellow
|
||||
ColorTokenSetItem = ColorTokenGreen
|
||||
ColorTokenUniqueItem = ColorTokenGold
|
||||
ColorTokenCraftedItem = ColorTokenOrange
|
||||
ColorTokenServer = ColorTokenRed
|
||||
ColorTokenButton = ColorTokenBlack
|
||||
ColorTokenCharacterName = ColorTokenGold
|
||||
ColorTokenCharacterDesc = ColorTokenWhite
|
||||
ColorTokenCharacterType = ColorTokenGreen
|
||||
)
|
||||
|
||||
const (
|
||||
colorGrey100Alpha = 0x69_69_69_ff
|
||||
colorWhite100Alpha = 0xff_ff_ff_ff
|
||||
colorBlue100Alpha = 0x69_69_ff_ff
|
||||
colorYellow100Alpha = 0xff_ff_64_ff
|
||||
colorGreen100Alpha = 0x00_ff_00_ff
|
||||
colorGold100Alpha = 0xc7_b3_77_ff
|
||||
colorOrange100Alpha = 0xff_a8_00_ff
|
||||
colorRed100Alpha = 0xff_77_77_ff
|
||||
colorBlack100Alpha = 0x00_00_00_ff
|
||||
)
|
||||
|
||||
// ColorTokenize formats the string with the given color token
|
||||
func ColorTokenize(s string, t ColorToken) string {
|
||||
return fmt.Sprintf(colorTokenFmt, t, s)
|
||||
}
|
@ -1,13 +1,7 @@
|
||||
package d2ui
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
)
|
||||
|
||||
// CursorButton represents a mouse button
|
||||
@ -20,110 +14,17 @@ const (
|
||||
CursorButtonRight CursorButton = 2
|
||||
)
|
||||
|
||||
type UI struct {
|
||||
inputManager d2interface.InputManager
|
||||
widgets []Widget
|
||||
cursorButtons CursorButton // TODO (carrelld) convert dependent code and remove
|
||||
CursorX int // TODO (carrelld) convert dependent code and remove
|
||||
CursorY int // TODO (carrelld) convert dependent code and remove
|
||||
pressedWidget Widget
|
||||
clickSfx d2interface.SoundEffect
|
||||
}
|
||||
|
||||
var singleton UI
|
||||
|
||||
func Initialize(inputManager d2interface.InputManager, audioProvider d2interface.AudioProvider) {
|
||||
sfx, err := audioProvider.LoadSound(d2resource.SFXButtonClick, false, false)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize ui: %v", err)
|
||||
// NewUIManager creates a UIManager instance with the given input and audio provider
|
||||
func NewUIManager(
|
||||
renderer d2interface.Renderer,
|
||||
input d2interface.InputManager,
|
||||
audio d2interface.AudioProvider,
|
||||
) *UIManager {
|
||||
ui := &UIManager{
|
||||
renderer: renderer,
|
||||
inputManager: input,
|
||||
audio: audio,
|
||||
}
|
||||
singleton.clickSfx = sfx
|
||||
|
||||
singleton.inputManager = inputManager
|
||||
if err := singleton.inputManager.BindHandler(&singleton); err != nil {
|
||||
log.Fatalf("failed to initialize ui: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the state of the UI manager. Typically called for new screens
|
||||
func Reset() {
|
||||
singleton.widgets = nil
|
||||
singleton.pressedWidget = nil
|
||||
}
|
||||
|
||||
// AddWidget adds a widget to the UI manager
|
||||
func AddWidget(widget Widget) {
|
||||
singleton.inputManager.BindHandler(widget)
|
||||
singleton.widgets = append(singleton.widgets, widget)
|
||||
}
|
||||
|
||||
func (u *UI) OnMouseButtonUp(event d2interface.MouseEvent) bool {
|
||||
singleton.CursorX, singleton.CursorY = event.X(), event.Y()
|
||||
if event.Button() == d2enum.MouseButtonLeft {
|
||||
singleton.cursorButtons |= CursorButtonLeft
|
||||
// activate previously pressed widget if cursor is still hovering
|
||||
w := singleton.pressedWidget
|
||||
if w != nil && contains(w, singleton.CursorX, singleton.CursorY) && w.GetVisible() && w.GetEnabled() {
|
||||
w.Activate()
|
||||
}
|
||||
// unpress all widgets that are pressed
|
||||
for _, w := range singleton.widgets {
|
||||
w.SetPressed(false)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UI) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
||||
singleton.CursorX, singleton.CursorY = event.X(), event.Y()
|
||||
if event.Button() == d2enum.MouseButtonLeft {
|
||||
// find and press a widget on screen
|
||||
singleton.pressedWidget = nil
|
||||
for _, w := range singleton.widgets {
|
||||
if contains(w, singleton.CursorX, singleton.CursorY) && w.GetVisible() && w.GetEnabled() {
|
||||
w.SetPressed(true)
|
||||
singleton.pressedWidget = w
|
||||
u.clickSfx.Play()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if event.Button() == d2enum.MouseButtonRight {
|
||||
singleton.cursorButtons |= CursorButtonRight
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Render renders all of the UI elements
|
||||
func Render(target d2interface.Surface) {
|
||||
for _, widget := range singleton.widgets {
|
||||
if widget.GetVisible() {
|
||||
widget.Render(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// contains determines whether a given x,y coordinate lands within a Widget
|
||||
func contains(w Widget, x, y int) bool {
|
||||
wx, wy := w.GetPosition()
|
||||
ww, wh := w.GetSize()
|
||||
return x >= wx && x <= wx+ww && y >= wy && y <= wy+wh
|
||||
}
|
||||
|
||||
// Update updates all of the UI elements
|
||||
func Advance(elapsed float64) {
|
||||
for _, widget := range singleton.widgets {
|
||||
if widget.GetVisible() {
|
||||
widget.Advance(elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CursorButtonPressed determines if the specified button has been pressed
|
||||
func CursorButtonPressed(button CursorButton) bool {
|
||||
return singleton.cursorButtons&button > 0
|
||||
}
|
||||
|
||||
func CursorPosition() (x, y int) {
|
||||
return singleton.CursorX, singleton.CursorY
|
||||
return ui
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
// Drawable represents an instance that can be drawn
|
||||
type Drawable interface {
|
||||
Render(target d2interface.Surface) error
|
||||
Advance(elapsed float64)
|
||||
Advance(elapsed float64) error
|
||||
GetSize() (width, height int)
|
||||
SetPosition(x, y int)
|
||||
GetPosition() (x, y int)
|
||||
|
@ -1,66 +1,20 @@
|
||||
package d2ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"image/color"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
)
|
||||
|
||||
// ColorToken is a string which is used inside of label strings to set font color.
|
||||
type ColorToken string
|
||||
|
||||
const (
|
||||
colorTokenFmt = `%s%s`
|
||||
colorTokenMatch = `\[[^\]]+\]`
|
||||
colorStrMatch = colorTokenMatch + `[^\[]+`
|
||||
)
|
||||
|
||||
const (
|
||||
ColorTokenGrey ColorToken = "[grey]"
|
||||
ColorTokenRed ColorToken = "[red]"
|
||||
ColorTokenWhite ColorToken = "[white]"
|
||||
ColorTokenBlue ColorToken = "[blue]"
|
||||
ColorTokenYellow ColorToken = "[yellow]"
|
||||
ColorTokenGreen ColorToken = "[green]"
|
||||
ColorTokenGold ColorToken = "[gold]"
|
||||
ColorTokenOrange ColorToken = "[orange]"
|
||||
ColorTokenBlack ColorToken = "[black]"
|
||||
)
|
||||
|
||||
// Color tokens for item labels
|
||||
const (
|
||||
ColorTokenSocketedItem = ColorTokenGrey
|
||||
ColorTokenNormalItem = ColorTokenWhite
|
||||
ColorTokenMagicItem = ColorTokenBlue
|
||||
ColorTokenRareItem = ColorTokenYellow
|
||||
ColorTokenSetItem = ColorTokenGreen
|
||||
ColorTokenUniqueItem = ColorTokenGold
|
||||
ColorTokenCraftedItem = ColorTokenOrange
|
||||
)
|
||||
|
||||
const (
|
||||
ColorTokenServer = ColorTokenRed
|
||||
ColorTokenButton = ColorTokenBlack
|
||||
)
|
||||
|
||||
const (
|
||||
ColorTokenCharacterName = ColorTokenGold
|
||||
ColorTokenCharacterDesc = ColorTokenWhite
|
||||
ColorTokenCharacterType = ColorTokenGreen
|
||||
)
|
||||
|
||||
// ColorTokenize formats the string with the given color token
|
||||
func ColorTokenize(s string, t ColorToken) string {
|
||||
return fmt.Sprintf(colorTokenFmt, t, s)
|
||||
}
|
||||
|
||||
// Label represents a user interface label
|
||||
type Label struct {
|
||||
manager *UIManager
|
||||
text string
|
||||
X int
|
||||
Y int
|
||||
@ -70,15 +24,17 @@ type Label struct {
|
||||
backgroundColor color.Color
|
||||
}
|
||||
|
||||
// CreateLabel creates a new instance of a UI label
|
||||
func CreateLabel(fontPath, palettePath string) Label {
|
||||
// NewLabel creates a new instance of a UI label
|
||||
func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label {
|
||||
font, _ := d2asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath)
|
||||
result := Label{
|
||||
result := &Label{
|
||||
Alignment: d2gui.HorizontalAlignLeft,
|
||||
Color: map[int]color.Color{0: color.White},
|
||||
font: font,
|
||||
}
|
||||
|
||||
result.bindManager(ui)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -126,6 +82,11 @@ func (v *Label) Render(target d2interface.Surface) {
|
||||
target.Pop()
|
||||
}
|
||||
|
||||
// bindManager binds the label to the UI manager
|
||||
func (v *Label) bindManager(manager *UIManager) {
|
||||
v.manager = manager
|
||||
}
|
||||
|
||||
// SetPosition moves the label to the specified location
|
||||
func (v *Label) SetPosition(x, y int) {
|
||||
v.X = x
|
||||
@ -148,7 +109,7 @@ func (v *Label) SetText(newText string) {
|
||||
}
|
||||
|
||||
// SetBackgroundColor sets the background highlight color
|
||||
func (v *Label) SetBackgroundColor(c color.RGBA) {
|
||||
func (v *Label) SetBackgroundColor(c color.Color) {
|
||||
v.backgroundColor = c
|
||||
}
|
||||
|
||||
@ -203,19 +164,17 @@ func (v *Label) getAlignOffset(textWidth int) int {
|
||||
}
|
||||
|
||||
func getColor(token ColorToken) color.Color {
|
||||
alpha := uint8(255)
|
||||
|
||||
// todo this should really come from the PL2 files
|
||||
colors := map[ColorToken]color.Color{
|
||||
ColorTokenGrey: color.RGBA{105, 105, 105, alpha},
|
||||
ColorTokenWhite: color.RGBA{255, 255, 255, alpha},
|
||||
ColorTokenBlue: color.RGBA{105, 105, 255, alpha},
|
||||
ColorTokenYellow: color.RGBA{255, 255, 100, alpha},
|
||||
ColorTokenGreen: color.RGBA{0, 255, 0, alpha},
|
||||
ColorTokenGold: color.RGBA{199, 179, 119, alpha},
|
||||
ColorTokenOrange: color.RGBA{255, 168, 0, alpha},
|
||||
ColorTokenRed: color.RGBA{255, 77, 77, alpha},
|
||||
ColorTokenBlack: color.RGBA{0, 0, 0, alpha},
|
||||
ColorTokenGrey: d2common.Color(colorGrey100Alpha),
|
||||
ColorTokenWhite: d2common.Color(colorWhite100Alpha),
|
||||
ColorTokenBlue: d2common.Color(colorBlue100Alpha),
|
||||
ColorTokenYellow: d2common.Color(colorYellow100Alpha),
|
||||
ColorTokenGreen: d2common.Color(colorGreen100Alpha),
|
||||
ColorTokenGold: d2common.Color(colorGold100Alpha),
|
||||
ColorTokenOrange: d2common.Color(colorOrange100Alpha),
|
||||
ColorTokenRed: d2common.Color(colorRed100Alpha),
|
||||
ColorTokenBlack: d2common.Color(colorBlack100Alpha),
|
||||
}
|
||||
|
||||
chosen := colors[token]
|
||||
|
@ -6,7 +6,17 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
|
||||
const (
|
||||
scrollbarOffsetY = 30
|
||||
halfScrollbarOffsetY = scrollbarOffsetY / 2
|
||||
scrollbarSpriteOffsetY = 10
|
||||
scrollbarFrameOffset = 4
|
||||
scrollbarWidth = 10
|
||||
)
|
||||
|
||||
// Scrollbar is a vertical slider ui element
|
||||
type Scrollbar struct {
|
||||
manager *UIManager
|
||||
x, y, height int
|
||||
visible bool
|
||||
enabled bool
|
||||
@ -17,10 +27,11 @@ type Scrollbar struct {
|
||||
scrollbarSprite *Sprite
|
||||
}
|
||||
|
||||
func CreateScrollbar(x, y, height int) Scrollbar {
|
||||
// NewScrollbar creates a scrollbar instance
|
||||
func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Scrollbar, d2resource.PaletteSky)
|
||||
scrollbarSprite, _ := LoadSprite(animation)
|
||||
result := Scrollbar{
|
||||
scrollbarSprite, _ := ui.NewSprite(animation)
|
||||
result := &Scrollbar{
|
||||
visible: true,
|
||||
enabled: true,
|
||||
x: x,
|
||||
@ -28,32 +39,44 @@ func CreateScrollbar(x, y, height int) Scrollbar {
|
||||
height: height,
|
||||
scrollbarSprite: scrollbarSprite,
|
||||
}
|
||||
|
||||
ui.addWidget(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (v Scrollbar) GetEnabled() bool {
|
||||
// GetEnabled returns whether or not the scrollbar is enabled
|
||||
func (v *Scrollbar) GetEnabled() bool {
|
||||
return v.enabled
|
||||
}
|
||||
|
||||
// SetEnabled sets the enabled state
|
||||
func (v *Scrollbar) SetEnabled(enabled bool) {
|
||||
v.enabled = enabled
|
||||
}
|
||||
|
||||
func (v *Scrollbar) SetPressed(pressed bool) {}
|
||||
func (v *Scrollbar) GetPressed() bool { return false }
|
||||
// SetPressed is not used by the scrollbar, but is present to satisfy the ui widget interface
|
||||
func (v *Scrollbar) SetPressed(_ bool) {}
|
||||
|
||||
// GetPressed is not used by the scrollbar, but is present to satisfy the ui widget interface
|
||||
func (v *Scrollbar) GetPressed() bool { return false }
|
||||
|
||||
// OnActivated sets the onActivate callback function for the scrollbar
|
||||
func (v *Scrollbar) OnActivated(callback func()) {
|
||||
v.onActivate = callback
|
||||
}
|
||||
|
||||
func (v Scrollbar) getBarPosition() int {
|
||||
return int((float32(v.currentOffset) / float32(v.maxOffset)) * float32(v.height-30))
|
||||
func (v *Scrollbar) getBarPosition() int {
|
||||
maxOffset := float32(v.maxOffset) * float32(v.height-scrollbarOffsetY)
|
||||
return int(float32(v.currentOffset) / maxOffset)
|
||||
}
|
||||
|
||||
// Activate will call the onActivate callback (if set)
|
||||
func (v *Scrollbar) Activate() {
|
||||
_, my := CursorPosition()
|
||||
_, my := v.manager.CursorPosition()
|
||||
barPosition := v.getBarPosition()
|
||||
if my <= v.y+barPosition+15 {
|
||||
|
||||
if my <= v.y+barPosition+halfScrollbarOffsetY {
|
||||
if v.currentOffset > 0 {
|
||||
v.currentOffset--
|
||||
v.lastDirChange = -1
|
||||
@ -70,10 +93,12 @@ func (v *Scrollbar) Activate() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastDirChange get the last direction change
|
||||
func (v *Scrollbar) GetLastDirChange() int {
|
||||
return v.lastDirChange
|
||||
}
|
||||
|
||||
// Render renders the scrollbar to the given surface
|
||||
func (v *Scrollbar) Render(target d2interface.Surface) error {
|
||||
if !v.visible || v.maxOffset == 0 {
|
||||
return nil
|
||||
@ -91,7 +116,7 @@ func (v *Scrollbar) Render(target d2interface.Surface) error {
|
||||
return err
|
||||
}
|
||||
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+v.height-10)
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+v.height-scrollbarSpriteOffsetY) // what is the magic?
|
||||
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, 1+offset); err != nil {
|
||||
return err
|
||||
@ -103,65 +128,82 @@ func (v *Scrollbar) Render(target d2interface.Surface) error {
|
||||
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+10+v.getBarPosition())
|
||||
|
||||
offset = 0
|
||||
offset = scrollbarFrameOffset
|
||||
|
||||
if !v.enabled {
|
||||
offset = 1
|
||||
offset++
|
||||
}
|
||||
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, 4+offset); err != nil {
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Scrollbar) Advance(elapsed float64) {
|
||||
v.scrollbarSprite.Advance(elapsed)
|
||||
// bindManager binds the scrollbar to the UI manager
|
||||
func (v *Scrollbar) bindManager(manager *UIManager) {
|
||||
v.manager = manager
|
||||
}
|
||||
|
||||
// Advance advances the scrollbar sprite
|
||||
func (v *Scrollbar) Advance(elapsed float64) error {
|
||||
return v.scrollbarSprite.Advance(elapsed)
|
||||
}
|
||||
|
||||
// GetSize returns the scrollbar width and height
|
||||
func (v *Scrollbar) GetSize() (width, height int) {
|
||||
return 10, v.height
|
||||
return scrollbarWidth, v.height
|
||||
}
|
||||
|
||||
// SetPosition sets the scrollbar x,y position
|
||||
func (v *Scrollbar) SetPosition(x, y int) {
|
||||
v.x = x
|
||||
v.y = y
|
||||
}
|
||||
|
||||
// GetPosition returns the scrollbar x,y position
|
||||
func (v *Scrollbar) GetPosition() (x, y int) {
|
||||
return v.x, v.y
|
||||
}
|
||||
|
||||
// GetVisible returns whether or not the scrollbar is visible
|
||||
func (v *Scrollbar) GetVisible() bool {
|
||||
return v.visible
|
||||
}
|
||||
|
||||
// SetVisible sets the scrollbar visibility state
|
||||
func (v *Scrollbar) SetVisible(visible bool) {
|
||||
v.visible = visible
|
||||
}
|
||||
|
||||
// SetMaxOffset sets the maximum offset of the scrollbar
|
||||
func (v *Scrollbar) SetMaxOffset(maxOffset int) {
|
||||
v.maxOffset = maxOffset
|
||||
if v.maxOffset < 0 {
|
||||
v.maxOffset = 0
|
||||
}
|
||||
|
||||
if v.currentOffset > v.maxOffset {
|
||||
v.currentOffset = v.maxOffset
|
||||
}
|
||||
|
||||
if v.maxOffset == 0 {
|
||||
v.currentOffset = 0
|
||||
}
|
||||
}
|
||||
|
||||
// SetCurrentOffset sets the scrollbar's current offset
|
||||
func (v *Scrollbar) SetCurrentOffset(currentOffset int) {
|
||||
v.currentOffset = currentOffset
|
||||
}
|
||||
|
||||
// GetMaxOffset returns the max offset
|
||||
func (v *Scrollbar) GetMaxOffset() int {
|
||||
return v.maxOffset
|
||||
}
|
||||
|
||||
// GetCurrentOffset gets the current max offset of the scrollbar
|
||||
func (v *Scrollbar) GetCurrentOffset() int {
|
||||
return v.currentOffset
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package d2ui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||
)
|
||||
|
||||
// Sprite is a positioned visual object.
|
||||
@ -17,17 +17,20 @@ type Sprite struct {
|
||||
animation d2interface.Animation
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNoAnimation = errors.New("no animation was specified")
|
||||
const (
|
||||
errNoAnimation = "no animation was specified"
|
||||
)
|
||||
|
||||
func LoadSprite(animation d2interface.Animation) (*Sprite, error) {
|
||||
// NewSprite creates a new Sprite
|
||||
func (ui *UIManager) NewSprite(animation d2interface.Animation) (*Sprite, error) {
|
||||
if animation == nil {
|
||||
return nil, ErrNoAnimation
|
||||
return nil, fmt.Errorf(errNoAnimation)
|
||||
}
|
||||
|
||||
return &Sprite{animation: animation}, nil
|
||||
}
|
||||
|
||||
// Render renders the sprite on the given surface
|
||||
func (s *Sprite) Render(target d2interface.Surface) error {
|
||||
_, frameHeight := s.animation.GetCurrentFrameSize()
|
||||
|
||||
@ -45,12 +48,12 @@ func (s *Sprite) RenderSection(sfc d2interface.Surface, bound image.Rectangle) e
|
||||
return s.animation.RenderSection(sfc, bound)
|
||||
}
|
||||
|
||||
// RenderSegmented renders a sprite that is internally segmented as frames
|
||||
func (s *Sprite) RenderSegmented(target d2interface.Surface, segmentsX, segmentsY, frameOffset int) error {
|
||||
var currentY int
|
||||
|
||||
for y := 0; y < segmentsY; y++ {
|
||||
var currentX int
|
||||
var maxFrameHeight int
|
||||
var currentX, maxFrameHeight int
|
||||
|
||||
for x := 0; x < segmentsX; x++ {
|
||||
if err := s.animation.SetCurrentFrame(x + y*segmentsX + frameOffset*segmentsX*segmentsY); err != nil {
|
||||
@ -83,22 +86,22 @@ func (s *Sprite) SetPosition(x, y int) {
|
||||
}
|
||||
|
||||
// GetPosition retrieves the 2D position of the sprite
|
||||
func (s *Sprite) GetPosition() (int, int) {
|
||||
func (s *Sprite) GetPosition() (x, y int) {
|
||||
return s.x, s.y
|
||||
}
|
||||
|
||||
// GetFrameSize gets the Size(width, height) of a indexed frame.
|
||||
func (s *Sprite) GetFrameSize(frameIndex int) (int, int, error) {
|
||||
func (s *Sprite) GetFrameSize(frameIndex int) (x, y int, err error) {
|
||||
return s.animation.GetFrameSize(frameIndex)
|
||||
}
|
||||
|
||||
// GetCurrentFrameSize gets the Size(width, height) of the current frame.
|
||||
func (s *Sprite) GetCurrentFrameSize() (int, int) {
|
||||
func (s *Sprite) GetCurrentFrameSize() (width, height int) {
|
||||
return s.animation.GetCurrentFrameSize()
|
||||
}
|
||||
|
||||
// GetFrameBounds gets maximum Size(width, height) of all frame.
|
||||
func (s *Sprite) GetFrameBounds() (int, int) {
|
||||
func (s *Sprite) GetFrameBounds() (width, height int) {
|
||||
return s.animation.GetFrameBounds()
|
||||
}
|
||||
|
||||
@ -144,7 +147,7 @@ func (s *Sprite) SetCurrentFrame(frameIndex int) error {
|
||||
|
||||
// Rewind sprite to beginning
|
||||
func (s *Sprite) Rewind() {
|
||||
s.animation.SetCurrentFrame(0)
|
||||
_ = s.animation.SetCurrentFrame(0)
|
||||
}
|
||||
|
||||
// PlayForward plays sprite forward
|
||||
@ -167,22 +170,27 @@ func (s *Sprite) SetPlayLoop(loop bool) {
|
||||
s.animation.SetPlayLoop(loop)
|
||||
}
|
||||
|
||||
// SetPlayLength sets the play length of the sprite animation
|
||||
func (s *Sprite) SetPlayLength(playLength float64) {
|
||||
s.animation.SetPlayLength(playLength)
|
||||
}
|
||||
|
||||
// SetPlayLengthMs sets the play length of the sprite animation in milliseconds
|
||||
func (s *Sprite) SetPlayLengthMs(playLengthMs int) {
|
||||
s.animation.SetPlayLengthMs(playLengthMs)
|
||||
}
|
||||
|
||||
func (s *Sprite) SetColorMod(color color.Color) {
|
||||
s.animation.SetColorMod(color)
|
||||
// SetColorMod sets the color modifier
|
||||
func (s *Sprite) SetColorMod(c color.Color) {
|
||||
s.animation.SetColorMod(c)
|
||||
}
|
||||
|
||||
// Advance advances the animation
|
||||
func (s *Sprite) Advance(elapsed float64) error {
|
||||
return s.animation.Advance(elapsed)
|
||||
}
|
||||
|
||||
// SetEffect sets the draw effect type
|
||||
func (s *Sprite) SetEffect(e d2enum.DrawEffect) {
|
||||
s.animation.SetEffect(e)
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ import (
|
||||
|
||||
// TextBox represents a text input box
|
||||
type TextBox struct {
|
||||
textLabel Label
|
||||
lineBar Label
|
||||
manager *UIManager
|
||||
textLabel *Label
|
||||
lineBar *Label
|
||||
text string
|
||||
filter string
|
||||
x int
|
||||
@ -24,20 +25,22 @@ type TextBox struct {
|
||||
isFocused bool
|
||||
}
|
||||
|
||||
// CreateTextbox creates a new instance of a text box
|
||||
func CreateTextbox() TextBox {
|
||||
// NewTextbox creates a new instance of a text box
|
||||
func (ui *UIManager) NewTextbox() *TextBox {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits)
|
||||
bgSprite, _ := LoadSprite(animation)
|
||||
tb := TextBox{
|
||||
bgSprite, _ := ui.NewSprite(animation)
|
||||
tb := &TextBox{
|
||||
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
bgSprite: bgSprite,
|
||||
textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
||||
lineBar: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
||||
textLabel: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
||||
lineBar: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
||||
enabled: true,
|
||||
visible: true,
|
||||
}
|
||||
tb.lineBar.SetText("_")
|
||||
|
||||
ui.addWidget(tb)
|
||||
|
||||
return tb
|
||||
}
|
||||
|
||||
@ -65,6 +68,11 @@ func (v *TextBox) Render(target d2interface.Surface) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindManager binds the textbox to the UI manager
|
||||
func (v *TextBox) bindManager(manager *UIManager) {
|
||||
v.manager = manager
|
||||
}
|
||||
|
||||
// OnKeyChars handles key character events
|
||||
func (v *TextBox) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
||||
if !v.isFocused || !v.visible || !v.enabled {
|
||||
@ -115,13 +123,11 @@ func debounceEvents(numFrames int) bool {
|
||||
}
|
||||
|
||||
// Advance updates the text box
|
||||
func (v *TextBox) Advance(_ float64) {
|
||||
if !v.visible || !v.enabled {
|
||||
return
|
||||
}
|
||||
func (v *TextBox) Advance(_ float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates the textbox
|
||||
// Update updates the textbox (not currently implemented)
|
||||
func (v *TextBox) Update() {
|
||||
}
|
||||
|
||||
|
140
d2core/d2ui/ui_manager.go
Normal file
140
d2core/d2ui/ui_manager.go
Normal file
@ -0,0 +1,140 @@
|
||||
package d2ui
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
)
|
||||
|
||||
// UIManager manages a collection of UI elements (buttons, textboxes, labels)
|
||||
type UIManager struct {
|
||||
renderer d2interface.Renderer
|
||||
inputManager d2interface.InputManager
|
||||
audio d2interface.AudioProvider
|
||||
widgets []Widget
|
||||
cursorButtons CursorButton // TODO (carrelld) convert dependent code and remove
|
||||
CursorX int // TODO (carrelld) convert dependent code and remove
|
||||
CursorY int // TODO (carrelld) convert dependent code and remove
|
||||
pressedWidget Widget
|
||||
clickSfx d2interface.SoundEffect
|
||||
}
|
||||
|
||||
// Note: methods for creating buttons and stuff are in their respective files
|
||||
|
||||
// Initialize is meant to be called after the game loads all of the necessary files
|
||||
// for sprites and audio
|
||||
func (ui *UIManager) Initialize() {
|
||||
sfx, err := ui.audio.LoadSound(d2resource.SFXButtonClick, false, false)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize ui: %v", err)
|
||||
}
|
||||
|
||||
ui.clickSfx = sfx
|
||||
|
||||
if err := ui.inputManager.BindHandler(ui); err != nil {
|
||||
log.Fatalf("failed to initialize ui: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the state of the UI manager. Typically called for new screens
|
||||
func (ui *UIManager) Reset() {
|
||||
ui.widgets = nil
|
||||
ui.pressedWidget = nil
|
||||
}
|
||||
|
||||
// addWidget adds a widget to the UI manager
|
||||
func (ui *UIManager) addWidget(widget Widget) {
|
||||
_ = ui.inputManager.BindHandler(widget)
|
||||
ui.widgets = append(ui.widgets, widget)
|
||||
|
||||
widget.bindManager(ui)
|
||||
}
|
||||
|
||||
// OnMouseButtonUp is an event handler for input
|
||||
func (ui *UIManager) OnMouseButtonUp(event d2interface.MouseEvent) bool {
|
||||
ui.CursorX, ui.CursorY = event.X(), event.Y()
|
||||
if event.Button() == d2enum.MouseButtonLeft {
|
||||
ui.cursorButtons |= CursorButtonLeft
|
||||
// activate previously pressed widget if cursor is still hovering
|
||||
w := ui.pressedWidget
|
||||
|
||||
if w != nil && ui.contains(w, ui.CursorX, ui.CursorY) && w.GetVisible() && w.
|
||||
GetEnabled() {
|
||||
w.Activate()
|
||||
}
|
||||
|
||||
// unpress all widgets that are pressed
|
||||
for _, w := range ui.widgets {
|
||||
w.SetPressed(false)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// OnMouseButtonDown is the mouse button down event handler
|
||||
func (ui *UIManager) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
||||
ui.CursorX, ui.CursorY = event.X(), event.Y()
|
||||
if event.Button() == d2enum.MouseButtonLeft {
|
||||
// find and press a widget on screen
|
||||
ui.pressedWidget = nil
|
||||
for _, w := range ui.widgets {
|
||||
if ui.contains(w, ui.CursorX, ui.CursorY) && w.GetVisible() && w.GetEnabled() {
|
||||
w.SetPressed(true)
|
||||
ui.pressedWidget = w
|
||||
ui.clickSfx.Play()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if event.Button() == d2enum.MouseButtonRight {
|
||||
ui.cursorButtons |= CursorButtonRight
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Render renders all of the UI elements
|
||||
func (ui *UIManager) Render(target d2interface.Surface) {
|
||||
for _, widget := range ui.widgets {
|
||||
if widget.GetVisible() {
|
||||
_ = widget.Render(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// contains determines whether a given x,y coordinate lands within a Widget
|
||||
func (ui *UIManager) contains(w Widget, x, y int) bool {
|
||||
wx, wy := w.GetPosition()
|
||||
ww, wh := w.GetSize()
|
||||
|
||||
return x >= wx && x <= wx+ww && y >= wy && y <= wy+wh
|
||||
}
|
||||
|
||||
// Advance updates all of the UI elements
|
||||
func (ui *UIManager) Advance(elapsed float64) {
|
||||
for _, widget := range ui.widgets {
|
||||
if widget.GetVisible() {
|
||||
_ = widget.Advance(elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CursorButtonPressed determines if the specified button has been pressed
|
||||
func (ui *UIManager) CursorButtonPressed(button CursorButton) bool {
|
||||
return ui.cursorButtons&button > 0
|
||||
}
|
||||
|
||||
// CursorPosition returns the current cursor position
|
||||
func (ui *UIManager) CursorPosition() (x, y int) {
|
||||
return ui.CursorX, ui.CursorY
|
||||
}
|
||||
|
||||
// Renderer returns the renderer for this ui manager
|
||||
func (ui *UIManager) Renderer() d2interface.Renderer {
|
||||
return ui.renderer
|
||||
}
|
@ -3,6 +3,7 @@ package d2ui
|
||||
// Widget defines an object that is a UI widget
|
||||
type Widget interface {
|
||||
Drawable
|
||||
bindManager(ui *UIManager)
|
||||
GetEnabled() bool
|
||||
SetEnabled(enabled bool)
|
||||
SetPressed(pressed bool)
|
||||
|
@ -22,21 +22,21 @@ import (
|
||||
// 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
|
||||
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
|
||||
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
|
||||
@ -44,6 +44,7 @@ type CharacterSelect struct {
|
||||
connectionType d2clientconnectiontype.ClientConnectionType
|
||||
connectionHost string
|
||||
|
||||
uiManager *d2ui.UIManager
|
||||
inputManager d2interface.InputManager
|
||||
audioProvider d2interface.AudioProvider
|
||||
renderer d2interface.Renderer
|
||||
@ -56,6 +57,7 @@ func CreateCharacterSelect(
|
||||
renderer d2interface.Renderer,
|
||||
inputManager d2interface.InputManager,
|
||||
audioProvider d2interface.AudioProvider,
|
||||
ui *d2ui.UIManager,
|
||||
connectionType d2clientconnectiontype.ClientConnectionType,
|
||||
connectionHost string,
|
||||
) *CharacterSelect {
|
||||
@ -67,6 +69,7 @@ func CreateCharacterSelect(
|
||||
inputManager: inputManager,
|
||||
audioProvider: audioProvider,
|
||||
navigator: navigator,
|
||||
uiManager: ui,
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,19 +135,19 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
|
||||
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky)
|
||||
bgX, bgY := 0, 0
|
||||
v.background, _ = d2ui.LoadSprite(animation)
|
||||
v.background, _ = v.uiManager.NewSprite(animation)
|
||||
v.background.SetPosition(bgX, bgY)
|
||||
|
||||
v.createButtons(loading)
|
||||
|
||||
heroTitleX, heroTitleY := 320, 23
|
||||
v.d2HeroTitle = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
|
||||
v.d2HeroTitle = v.uiManager.NewLabel(d2resource.Font42, d2resource.PaletteUnits)
|
||||
v.d2HeroTitle.SetPosition(heroTitleX, heroTitleY)
|
||||
v.d2HeroTitle.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
loading.Progress(thirtyPercent)
|
||||
|
||||
v.deleteCharConfirmLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.deleteCharConfirmLabel = v.uiManager.NewLabel(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
|
||||
@ -152,19 +155,18 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
|
||||
v.deleteCharConfirmLabel.SetPosition(deleteConfirmX, deleteConfirmY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.CharacterSelectionSelectBox, d2resource.PaletteSky)
|
||||
v.selectionBox, _ = d2ui.LoadSprite(animation)
|
||||
v.selectionBox, _ = v.uiManager.NewSprite(animation)
|
||||
selBoxX, selBoxY := 37, 86
|
||||
v.selectionBox.SetPosition(selBoxX, selBoxY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
|
||||
v.okCancelBox, _ = d2ui.LoadSprite(animation)
|
||||
v.okCancelBox, _ = v.uiManager.NewSprite(animation)
|
||||
okCancelX, okCancelY := 270, 175
|
||||
v.okCancelBox.SetPosition(okCancelX, okCancelY)
|
||||
|
||||
scrollBarX, scrollBarY, scrollBarHeight := 586, 87, 369
|
||||
v.charScrollbar = d2ui.CreateScrollbar(scrollBarX, scrollBarY, scrollBarHeight)
|
||||
v.charScrollbar = v.uiManager.NewScrollbar(scrollBarX, scrollBarY, scrollBarHeight)
|
||||
v.charScrollbar.OnActivated(func() { v.onScrollUpdate() })
|
||||
d2ui.AddWidget(&v.charScrollbar)
|
||||
|
||||
loading.Progress(fiftyPercent)
|
||||
|
||||
@ -174,16 +176,16 @@ func (v *CharacterSelect) OnLoad(loading d2screen.LoadingState) {
|
||||
offsetX = 385
|
||||
}
|
||||
|
||||
v.characterNameLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.characterNameLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.characterNameLabel[i].SetPosition(offsetX, offsetY)
|
||||
v.characterNameLabel[i].Color[0] = rgbaColor(lightBrown)
|
||||
|
||||
offsetY += labelHeight
|
||||
v.characterStatsLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.characterStatsLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.characterStatsLabel[i].SetPosition(offsetX, offsetY)
|
||||
|
||||
offsetY += labelHeight
|
||||
v.characterExpLabel[i] = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteStatic)
|
||||
v.characterExpLabel[i] = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
|
||||
v.characterExpLabel[i].SetPosition(offsetX, offsetY)
|
||||
v.characterExpLabel[i].Color[0] = rgbaColor(lightGreen)
|
||||
}
|
||||
@ -216,58 +218,39 @@ func rgbaColor(rgba uint32) color.RGBA {
|
||||
}
|
||||
|
||||
func (v *CharacterSelect) createButtons(loading d2screen.LoadingState) {
|
||||
v.newCharButton = d2ui.CreateButton(
|
||||
v.renderer,
|
||||
d2ui.ButtonTypeTall,
|
||||
"CREATE NEW\nCHARACTER",
|
||||
)
|
||||
v.newCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "CREATE NEW\nCHARACTER")
|
||||
|
||||
v.newCharButton.SetPosition(newCharBtnX, newCharBtnY)
|
||||
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
|
||||
d2ui.AddWidget(&v.newCharButton)
|
||||
|
||||
v.convertCharButton = d2ui.CreateButton(
|
||||
v.renderer,
|
||||
d2ui.ButtonTypeTall,
|
||||
"CONVERT TO\nEXPANSION",
|
||||
)
|
||||
v.convertCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "CONVERT TO\nEXPANSION")
|
||||
|
||||
v.convertCharButton.SetPosition(convertCharBtnX, convertCharBtnY)
|
||||
v.convertCharButton.SetEnabled(false)
|
||||
d2ui.AddWidget(&v.convertCharButton)
|
||||
|
||||
v.deleteCharButton = d2ui.CreateButton(
|
||||
v.renderer,
|
||||
d2ui.ButtonTypeTall,
|
||||
"DELETE\nCHARACTER",
|
||||
)
|
||||
v.deleteCharButton = v.uiManager.NewButton(d2ui.ButtonTypeTall, "DELETE\nCHARACTER")
|
||||
v.deleteCharButton.OnActivated(func() { v.onDeleteCharButtonClicked() })
|
||||
v.deleteCharButton.SetPosition(deleteCharBtnX, deleteCharBtnY)
|
||||
d2ui.AddWidget(&v.deleteCharButton)
|
||||
|
||||
v.exitButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton.SetPosition(exitBtnX, exitBtnY)
|
||||
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
||||
d2ui.AddWidget(&v.exitButton)
|
||||
|
||||
loading.Progress(twentyPercent)
|
||||
|
||||
v.deleteCharCancelButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "NO")
|
||||
v.deleteCharCancelButton = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "NO")
|
||||
v.deleteCharCancelButton.SetPosition(deleteCancelX, deleteCancelY)
|
||||
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 = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "YES")
|
||||
v.deleteCharOkButton.SetPosition(deleteOkX, deleteOkY)
|
||||
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 = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "OK")
|
||||
v.okButton.SetPosition(okBtnX, okBtnY)
|
||||
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
|
||||
d2ui.AddWidget(&v.okButton)
|
||||
}
|
||||
|
||||
func (v *CharacterSelect) onScrollUpdate() {
|
||||
|
@ -17,14 +17,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
creditsX, creditsY = 0, 0
|
||||
creditsX, creditsY = 0, 0
|
||||
charSelExitBtnX, charSelExitBtnY = 33, 543
|
||||
)
|
||||
|
||||
const secondsPerCycle float64 = 0.02
|
||||
|
||||
type labelItem struct {
|
||||
Label d2ui.Label
|
||||
Label *d2ui.Label
|
||||
IsHeading bool
|
||||
Available bool
|
||||
}
|
||||
@ -32,7 +32,7 @@ type labelItem struct {
|
||||
// Credits represents the credits screen
|
||||
type Credits struct {
|
||||
creditsBackground *d2ui.Sprite
|
||||
exitButton d2ui.Button
|
||||
exitButton *d2ui.Button
|
||||
creditsText []string
|
||||
labels []*labelItem
|
||||
cycleTime float64
|
||||
@ -41,10 +41,11 @@ type Credits struct {
|
||||
|
||||
renderer d2interface.Renderer
|
||||
navigator Navigator
|
||||
uiManager *d2ui.UIManager
|
||||
}
|
||||
|
||||
// CreateCredits creates an instance of the credits screen
|
||||
func CreateCredits(navigator Navigator, renderer d2interface.Renderer) *Credits {
|
||||
func CreateCredits(navigator Navigator, renderer d2interface.Renderer, ui *d2ui.UIManager) *Credits {
|
||||
result := &Credits{
|
||||
labels: make([]*labelItem, 0),
|
||||
cycleTime: 0,
|
||||
@ -52,6 +53,7 @@ func CreateCredits(navigator Navigator, renderer d2interface.Renderer) *Credits
|
||||
cyclesTillNextLine: 0,
|
||||
renderer: renderer,
|
||||
navigator: navigator,
|
||||
uiManager: ui,
|
||||
}
|
||||
|
||||
return result
|
||||
@ -85,14 +87,13 @@ func (v *Credits) LoadContributors() []string {
|
||||
// OnLoad is called to load the resources for the credits screen
|
||||
func (v *Credits) OnLoad(loading d2screen.LoadingState) {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.CreditsBackground, d2resource.PaletteSky)
|
||||
v.creditsBackground, _ = d2ui.LoadSprite(animation)
|
||||
v.creditsBackground, _ = v.uiManager.NewSprite(animation)
|
||||
v.creditsBackground.SetPosition(creditsX, creditsY)
|
||||
loading.Progress(twentyPercent)
|
||||
|
||||
v.exitButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton.SetPosition(charSelExitBtnX, charSelExitBtnY)
|
||||
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
||||
d2ui.AddWidget(&v.exitButton)
|
||||
loading.Progress(fourtyPercent)
|
||||
|
||||
fileData, err := d2asset.LoadFile(d2resource.CreditsText)
|
||||
@ -214,17 +215,17 @@ func (v *Credits) addNextItem() {
|
||||
}
|
||||
}
|
||||
|
||||
const(
|
||||
itemLabelY = 605
|
||||
itemLabelX = 400
|
||||
itemLabel2offsetX = 10
|
||||
halfItemLabel2offsetX = itemLabel2offsetX/2
|
||||
const (
|
||||
itemLabelY = 605
|
||||
itemLabelX = 400
|
||||
itemLabel2offsetX = 10
|
||||
halfItemLabel2offsetX = itemLabel2offsetX / 2
|
||||
)
|
||||
|
||||
func (v *Credits) setItemLabelPosition(label *d2ui.Label, isHeading, isNextHeading, isNextSpace bool) (isDoubled, nextHeading bool) {
|
||||
width, _ := label.GetSize()
|
||||
half := 2
|
||||
halfWidth := width/half
|
||||
halfWidth := width / half
|
||||
|
||||
if !isHeading && !isNextHeading && !isNextSpace {
|
||||
isDoubled = true
|
||||
@ -250,7 +251,7 @@ func (v *Credits) setItemLabelPosition(label *d2ui.Label, isHeading, isNextHeadi
|
||||
|
||||
const (
|
||||
lightRed = 0xff5852ff
|
||||
beige = 0xc6b296ff
|
||||
beige = 0xc6b296ff
|
||||
)
|
||||
|
||||
func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
|
||||
@ -263,14 +264,14 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
|
||||
label.Label.Color[0] = rgbaColor(beige)
|
||||
}
|
||||
|
||||
return &label.Label
|
||||
return label.Label
|
||||
}
|
||||
}
|
||||
|
||||
newLabelItem := &labelItem{
|
||||
Available: false,
|
||||
IsHeading: isHeading,
|
||||
Label: d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteSky),
|
||||
Label: v.uiManager.NewLabel(d2resource.FontFormal10, d2resource.PaletteSky),
|
||||
}
|
||||
|
||||
if isHeading {
|
||||
@ -281,5 +282,5 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
|
||||
|
||||
v.labels = append(v.labels, newLabelItem)
|
||||
|
||||
return &newLabelItem.Label
|
||||
return newLabelItem.Label
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package d2gamescreen
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
||||
@ -30,6 +31,7 @@ const (
|
||||
type Game struct {
|
||||
gameClient *d2client.GameClient
|
||||
mapRenderer *d2maprenderer.MapRenderer
|
||||
uiManager *d2ui.UIManager
|
||||
gameControls *d2player.GameControls // TODO: Hack
|
||||
localPlayer *d2mapentity.Player
|
||||
lastRegionType d2enum.RegionIdType
|
||||
@ -77,6 +79,7 @@ func CreateGame(
|
||||
renderer: renderer,
|
||||
terminal: term,
|
||||
soundEngine: d2audio.NewSoundEngine(audioProvider, term),
|
||||
uiManager: d2ui.NewUIManager(renderer, inputManager, audioProvider),
|
||||
}
|
||||
result.soundEnv = d2audio.NewSoundEnvironment(result.soundEngine)
|
||||
|
||||
@ -244,7 +247,8 @@ func (v *Game) bindGameControls() error {
|
||||
v.localPlayer = player
|
||||
|
||||
var err error
|
||||
v.gameControls, err = d2player.NewGameControls(v.renderer, player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal)
|
||||
v.gameControls, err = d2player.NewGameControls(v.renderer, player,
|
||||
v.gameClient.MapEngine, v.mapRenderer, v, v.terminal, v.uiManager)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -86,28 +86,28 @@ type MainMenu struct {
|
||||
diabloLogoLeftBack *d2ui.Sprite
|
||||
diabloLogoRightBack *d2ui.Sprite
|
||||
serverIPBackground *d2ui.Sprite
|
||||
singlePlayerButton d2ui.Button
|
||||
multiplayerButton d2ui.Button
|
||||
githubButton d2ui.Button
|
||||
exitDiabloButton d2ui.Button
|
||||
creditsButton d2ui.Button
|
||||
cinematicsButton d2ui.Button
|
||||
mapTestButton d2ui.Button
|
||||
networkTCPIPButton d2ui.Button
|
||||
networkCancelButton d2ui.Button
|
||||
btnTCPIPCancel d2ui.Button
|
||||
btnTCPIPHostGame d2ui.Button
|
||||
btnTCPIPJoinGame d2ui.Button
|
||||
btnServerIPCancel d2ui.Button
|
||||
btnServerIPOk d2ui.Button
|
||||
copyrightLabel d2ui.Label
|
||||
copyrightLabel2 d2ui.Label
|
||||
openDiabloLabel d2ui.Label
|
||||
versionLabel d2ui.Label
|
||||
commitLabel d2ui.Label
|
||||
tcpIPOptionsLabel d2ui.Label
|
||||
tcpJoinGameLabel d2ui.Label
|
||||
tcpJoinGameEntry d2ui.TextBox
|
||||
singlePlayerButton *d2ui.Button
|
||||
multiplayerButton *d2ui.Button
|
||||
githubButton *d2ui.Button
|
||||
exitDiabloButton *d2ui.Button
|
||||
creditsButton *d2ui.Button
|
||||
cinematicsButton *d2ui.Button
|
||||
mapTestButton *d2ui.Button
|
||||
networkTCPIPButton *d2ui.Button
|
||||
networkCancelButton *d2ui.Button
|
||||
btnTCPIPCancel *d2ui.Button
|
||||
btnTCPIPHostGame *d2ui.Button
|
||||
btnTCPIPJoinGame *d2ui.Button
|
||||
btnServerIPCancel *d2ui.Button
|
||||
btnServerIPOk *d2ui.Button
|
||||
copyrightLabel *d2ui.Label
|
||||
copyrightLabel2 *d2ui.Label
|
||||
openDiabloLabel *d2ui.Label
|
||||
versionLabel *d2ui.Label
|
||||
commitLabel *d2ui.Label
|
||||
tcpIPOptionsLabel *d2ui.Label
|
||||
tcpJoinGameLabel *d2ui.Label
|
||||
tcpJoinGameEntry *d2ui.TextBox
|
||||
screenMode mainMenuScreenMode
|
||||
leftButtonHeld bool
|
||||
|
||||
@ -116,6 +116,7 @@ type MainMenu struct {
|
||||
audioProvider d2interface.AudioProvider
|
||||
scriptEngine *d2script.ScriptEngine
|
||||
navigator Navigator
|
||||
uiManager *d2ui.UIManager
|
||||
|
||||
buildInfo BuildInfo
|
||||
}
|
||||
@ -126,6 +127,7 @@ func CreateMainMenu(
|
||||
renderer d2interface.Renderer,
|
||||
inputManager d2interface.InputManager,
|
||||
audioProvider d2interface.AudioProvider,
|
||||
ui *d2ui.UIManager,
|
||||
buildInfo BuildInfo,
|
||||
) *MainMenu {
|
||||
return &MainMenu{
|
||||
@ -135,7 +137,8 @@ func CreateMainMenu(
|
||||
inputManager: inputManager,
|
||||
audioProvider: audioProvider,
|
||||
navigator: navigator,
|
||||
buildInfo: buildInfo,
|
||||
buildInfo: buildInfo,
|
||||
uiManager: ui,
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,10 +152,9 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
|
||||
v.createLogos(loading)
|
||||
v.createButtons(loading)
|
||||
|
||||
v.tcpJoinGameEntry = d2ui.CreateTextbox()
|
||||
v.tcpJoinGameEntry = v.uiManager.NewTextbox()
|
||||
v.tcpJoinGameEntry.SetPosition(joinGameDialogX, joinGameDialogY)
|
||||
v.tcpJoinGameEntry.SetFilter(joinGameCharacterFilter)
|
||||
d2ui.AddWidget(&v.tcpJoinGameEntry)
|
||||
loading.Progress(ninetyPercent)
|
||||
|
||||
if v.screenMode == ScreenModeUnknown {
|
||||
@ -168,61 +170,61 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
|
||||
|
||||
func (v *MainMenu) loadBackgroundSprites() {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.GameSelectScreen, d2resource.PaletteSky)
|
||||
v.background, _ = d2ui.LoadSprite(animation)
|
||||
v.background, _ = v.uiManager.NewSprite(animation)
|
||||
v.background.SetPosition(backgroundX, backgroundY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.TrademarkScreen, d2resource.PaletteSky)
|
||||
v.trademarkBackground, _ = d2ui.LoadSprite(animation)
|
||||
v.trademarkBackground, _ = v.uiManager.NewSprite(animation)
|
||||
v.trademarkBackground.SetPosition(backgroundX, backgroundY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.TCPIPBackground, d2resource.PaletteSky)
|
||||
v.tcpIPBackground, _ = d2ui.LoadSprite(animation)
|
||||
v.tcpIPBackground, _ = v.uiManager.NewSprite(animation)
|
||||
v.tcpIPBackground.SetPosition(backgroundX, backgroundY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
|
||||
v.serverIPBackground, _ = d2ui.LoadSprite(animation)
|
||||
v.serverIPBackground, _ = v.uiManager.NewSprite(animation)
|
||||
v.serverIPBackground.SetPosition(serverIPbackgroundX, serverIPbackgroundY)
|
||||
}
|
||||
|
||||
func (v *MainMenu) createLabels(loading d2screen.LoadingState) {
|
||||
v.versionLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.versionLabel = v.uiManager.NewLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.versionLabel.Alignment = d2gui.HorizontalAlignRight
|
||||
v.versionLabel.SetText("OpenDiablo2 - " + v.buildInfo.Branch)
|
||||
v.versionLabel.Color[0] = rgbaColor(white)
|
||||
v.versionLabel.SetPosition(versionLabelX, versionLabelY)
|
||||
|
||||
v.commitLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic)
|
||||
v.commitLabel = v.uiManager.NewLabel(d2resource.FontFormal10, d2resource.PaletteStatic)
|
||||
v.commitLabel.Alignment = d2gui.HorizontalAlignLeft
|
||||
v.commitLabel.SetText(v.buildInfo.Commit)
|
||||
v.commitLabel.Color[0] = rgbaColor(white)
|
||||
v.commitLabel.SetPosition(commitLabelX, commitLabelY)
|
||||
|
||||
v.copyrightLabel = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.copyrightLabel = v.uiManager.NewLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.copyrightLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment")
|
||||
v.copyrightLabel.Color[0] = rgbaColor(lightBrown)
|
||||
v.copyrightLabel.SetPosition(copyrightX, copyrightY)
|
||||
loading.Progress(thirtyPercent)
|
||||
|
||||
v.copyrightLabel2 = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.copyrightLabel2 = v.uiManager.NewLabel(d2resource.FontFormal12, d2resource.PaletteStatic)
|
||||
v.copyrightLabel2.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.copyrightLabel2.SetText("All Rights Reserved.")
|
||||
v.copyrightLabel2.Color[0] = rgbaColor(lightBrown)
|
||||
v.copyrightLabel2.SetPosition(copyright2X, copyright2Y)
|
||||
|
||||
v.openDiabloLabel = d2ui.CreateLabel(d2resource.FontFormal10, d2resource.PaletteStatic)
|
||||
v.openDiabloLabel = v.uiManager.NewLabel(d2resource.FontFormal10, d2resource.PaletteStatic)
|
||||
v.openDiabloLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.openDiabloLabel.SetText("OpenDiablo2 is neither developed by, nor endorsed by Blizzard or its parent company Activision")
|
||||
v.openDiabloLabel.Color[0] = rgbaColor(lightYellow)
|
||||
v.openDiabloLabel.SetPosition(od2LabelX, od2LabelY)
|
||||
loading.Progress(fiftyPercent)
|
||||
|
||||
v.tcpIPOptionsLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
|
||||
v.tcpIPOptionsLabel = v.uiManager.NewLabel(d2resource.Font42, d2resource.PaletteUnits)
|
||||
v.tcpIPOptionsLabel.SetPosition(tcpOptionsX, tcpOptionsY)
|
||||
v.tcpIPOptionsLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.tcpIPOptionsLabel.SetText("TCP/IP Options")
|
||||
|
||||
v.tcpJoinGameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.tcpJoinGameLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.tcpJoinGameLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.tcpJoinGameLabel.SetText("Enter Host IP Address\nto Join Game")
|
||||
|
||||
@ -232,102 +234,90 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) {
|
||||
|
||||
func (v *MainMenu) createLogos(loading d2screen.LoadingState) {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits)
|
||||
v.diabloLogoLeft, _ = d2ui.LoadSprite(animation)
|
||||
v.diabloLogoLeft, _ = v.uiManager.NewSprite(animation)
|
||||
v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate)
|
||||
v.diabloLogoLeft.PlayForward()
|
||||
v.diabloLogoLeft.SetPosition(diabloLogoX, diabloLogoY)
|
||||
loading.Progress(sixtyPercent)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits)
|
||||
v.diabloLogoRight, _ = d2ui.LoadSprite(animation)
|
||||
v.diabloLogoRight, _ = v.uiManager.NewSprite(animation)
|
||||
v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate)
|
||||
v.diabloLogoRight.PlayForward()
|
||||
v.diabloLogoRight.SetPosition(diabloLogoX, diabloLogoY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackLeft, d2resource.PaletteUnits)
|
||||
v.diabloLogoLeftBack, _ = d2ui.LoadSprite(animation)
|
||||
v.diabloLogoLeftBack, _ = v.uiManager.NewSprite(animation)
|
||||
v.diabloLogoLeftBack.SetPosition(diabloLogoX, diabloLogoY)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoBlackRight, d2resource.PaletteUnits)
|
||||
v.diabloLogoRightBack, _ = d2ui.LoadSprite(animation)
|
||||
v.diabloLogoRightBack, _ = v.uiManager.NewSprite(animation)
|
||||
v.diabloLogoRightBack.SetPosition(diabloLogoX, diabloLogoY)
|
||||
}
|
||||
|
||||
func (v *MainMenu) createButtons(loading d2screen.LoadingState) {
|
||||
v.exitDiabloButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "EXIT DIABLO II")
|
||||
v.exitDiabloButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "EXIT DIABLO II")
|
||||
v.exitDiabloButton.SetPosition(exitDiabloBtnX, exitDiabloBtnY)
|
||||
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
|
||||
d2ui.AddWidget(&v.exitDiabloButton)
|
||||
|
||||
v.creditsButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeShort, "CREDITS")
|
||||
v.creditsButton = v.uiManager.NewButton(d2ui.ButtonTypeShort, "CREDITS")
|
||||
v.creditsButton.SetPosition(creditBtnX, creditBtnY)
|
||||
v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() })
|
||||
d2ui.AddWidget(&v.creditsButton)
|
||||
|
||||
v.cinematicsButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeShort, "CINEMATICS")
|
||||
v.cinematicsButton = v.uiManager.NewButton(d2ui.ButtonTypeShort, "CINEMATICS")
|
||||
v.cinematicsButton.SetPosition(cineBtnX, cineBtnY)
|
||||
d2ui.AddWidget(&v.cinematicsButton)
|
||||
loading.Progress(seventyPercent)
|
||||
|
||||
v.singlePlayerButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "SINGLE PLAYER")
|
||||
v.singlePlayerButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "SINGLE PLAYER")
|
||||
v.singlePlayerButton.SetPosition(singlePlayerBtnX, singlePlayerBtnY)
|
||||
v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() })
|
||||
d2ui.AddWidget(&v.singlePlayerButton)
|
||||
|
||||
v.githubButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "PROJECT WEBSITE")
|
||||
v.githubButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE")
|
||||
v.githubButton.SetPosition(githubBtnX, githubBtnY)
|
||||
v.githubButton.OnActivated(func() { v.onGithubButtonClicked() })
|
||||
d2ui.AddWidget(&v.githubButton)
|
||||
|
||||
v.mapTestButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "MAP ENGINE TEST")
|
||||
v.mapTestButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST")
|
||||
v.mapTestButton.SetPosition(mapTestBtnX, mapTestBtnY)
|
||||
v.mapTestButton.OnActivated(func() { v.onMapTestClicked() })
|
||||
d2ui.AddWidget(&v.mapTestButton)
|
||||
|
||||
v.btnTCPIPCancel = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, d2common.TranslateString("cancel"))
|
||||
v.btnTCPIPCancel = v.uiManager.NewButton(d2ui.ButtonTypeMedium,
|
||||
d2common.TranslateString("cancel"))
|
||||
v.btnTCPIPCancel.SetPosition(tcpBtnX, tcpBtnY)
|
||||
v.btnTCPIPCancel.OnActivated(func() { v.onTCPIPCancelClicked() })
|
||||
d2ui.AddWidget(&v.btnTCPIPCancel)
|
||||
|
||||
v.btnServerIPCancel = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "CANCEL")
|
||||
v.btnServerIPCancel = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "CANCEL")
|
||||
v.btnServerIPCancel.SetPosition(srvCancelBtnX, srvCancelBtnY)
|
||||
v.btnServerIPCancel.OnActivated(func() { v.onBtnTCPIPCancelClicked() })
|
||||
d2ui.AddWidget(&v.btnServerIPCancel)
|
||||
|
||||
v.btnServerIPOk = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeOkCancel, "OK")
|
||||
v.btnServerIPOk = v.uiManager.NewButton(d2ui.ButtonTypeOkCancel, "OK")
|
||||
v.btnServerIPOk.SetPosition(srvOkBtnX, srvOkBtnY)
|
||||
v.btnServerIPOk.OnActivated(func() { v.onBtnTCPIPOkClicked() })
|
||||
d2ui.AddWidget(&v.btnServerIPOk)
|
||||
|
||||
v.createMultiplayerMenuButtons()
|
||||
loading.Progress(eightyPercent)
|
||||
}
|
||||
|
||||
func (v *MainMenu) createMultiplayerMenuButtons() {
|
||||
v.multiplayerButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "MULTIPLAYER")
|
||||
v.multiplayerButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "MULTIPLAYER")
|
||||
v.multiplayerButton.SetPosition(multiplayerBtnX, multiplayerBtnY)
|
||||
v.multiplayerButton.OnActivated(func() { v.onMultiplayerClicked() })
|
||||
d2ui.AddWidget(&v.multiplayerButton)
|
||||
|
||||
v.networkTCPIPButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "TCP/IP GAME")
|
||||
v.networkTCPIPButton = v.uiManager.NewButton(d2ui.ButtonTypeWide, "TCP/IP GAME")
|
||||
v.networkTCPIPButton.SetPosition(tcpNetBtnX, tcpNetBtnY)
|
||||
v.networkTCPIPButton.OnActivated(func() { v.onNetworkTCPIPClicked() })
|
||||
d2ui.AddWidget(&v.networkTCPIPButton)
|
||||
|
||||
v.networkCancelButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, d2common.TranslateString("cancel"))
|
||||
v.networkCancelButton = v.uiManager.NewButton(d2ui.ButtonTypeWide,
|
||||
d2common.TranslateString("cancel"))
|
||||
v.networkCancelButton.SetPosition(networkCancelBtnX, networkCancelBtnY)
|
||||
v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() })
|
||||
d2ui.AddWidget(&v.networkCancelButton)
|
||||
|
||||
v.btnTCPIPHostGame = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "HOST GAME")
|
||||
v.btnTCPIPHostGame = v.uiManager.NewButton(d2ui.ButtonTypeWide, "HOST GAME")
|
||||
v.btnTCPIPHostGame.SetPosition(tcpHostBtnX, tcpHostBtnY)
|
||||
v.btnTCPIPHostGame.OnActivated(func() { v.onTCPIPHostGameClicked() })
|
||||
d2ui.AddWidget(&v.btnTCPIPHostGame)
|
||||
|
||||
v.btnTCPIPJoinGame = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeWide, "JOIN GAME")
|
||||
v.btnTCPIPJoinGame = v.uiManager.NewButton(d2ui.ButtonTypeWide, "JOIN GAME")
|
||||
v.btnTCPIPJoinGame.SetPosition(tcpJoinBtnX, tcpJoinBtnY)
|
||||
v.btnTCPIPJoinGame.OnActivated(func() { v.onTCPIPJoinGameClicked() })
|
||||
d2ui.AddWidget(&v.btnTCPIPJoinGame)
|
||||
}
|
||||
|
||||
func (v *MainMenu) onMapTestClicked() {
|
||||
|
@ -267,23 +267,24 @@ func (hri *HeroRenderInfo) advance(elapsed float64) {
|
||||
|
||||
// SelectHeroClass represents the Select Hero Class screen
|
||||
type SelectHeroClass struct {
|
||||
uiManager *d2ui.UIManager
|
||||
bgImage *d2ui.Sprite
|
||||
campfire *d2ui.Sprite
|
||||
headingLabel d2ui.Label
|
||||
heroClassLabel d2ui.Label
|
||||
heroDesc1Label d2ui.Label
|
||||
heroDesc2Label d2ui.Label
|
||||
heroDesc3Label d2ui.Label
|
||||
heroNameTextbox d2ui.TextBox
|
||||
heroNameLabel d2ui.Label
|
||||
headingLabel *d2ui.Label
|
||||
heroClassLabel *d2ui.Label
|
||||
heroDesc1Label *d2ui.Label
|
||||
heroDesc2Label *d2ui.Label
|
||||
heroDesc3Label *d2ui.Label
|
||||
heroNameTextbox *d2ui.TextBox
|
||||
heroNameLabel *d2ui.Label
|
||||
heroRenderInfo map[d2enum.Hero]*HeroRenderInfo
|
||||
selectedHero d2enum.Hero
|
||||
exitButton d2ui.Button
|
||||
okButton d2ui.Button
|
||||
expansionCheckbox d2ui.Checkbox
|
||||
expansionCharLabel d2ui.Label
|
||||
hardcoreCheckbox d2ui.Checkbox
|
||||
hardcoreCharLabel d2ui.Label
|
||||
exitButton *d2ui.Button
|
||||
okButton *d2ui.Button
|
||||
expansionCheckbox *d2ui.Checkbox
|
||||
expansionCharLabel *d2ui.Label
|
||||
hardcoreCheckbox *d2ui.Checkbox
|
||||
hardcoreCharLabel *d2ui.Label
|
||||
connectionType d2clientconnectiontype.ClientConnectionType
|
||||
connectionHost string
|
||||
|
||||
@ -297,6 +298,7 @@ func CreateSelectHeroClass(
|
||||
navigator Navigator,
|
||||
renderer d2interface.Renderer,
|
||||
audioProvider d2interface.AudioProvider,
|
||||
ui *d2ui.UIManager,
|
||||
connectionType d2clientconnectiontype.ClientConnectionType,
|
||||
connectionHost string,
|
||||
) *SelectHeroClass {
|
||||
@ -308,6 +310,7 @@ func CreateSelectHeroClass(
|
||||
audioProvider: audioProvider,
|
||||
renderer: renderer,
|
||||
navigator: navigator,
|
||||
uiManager: ui,
|
||||
}
|
||||
|
||||
return result
|
||||
@ -318,7 +321,7 @@ func (v *SelectHeroClass) OnLoad(loading d2screen.LoadingState) {
|
||||
v.audioProvider.PlayBGM(d2resource.BGMTitle)
|
||||
loading.Progress(tenPercent)
|
||||
|
||||
v.bgImage = loadSprite(
|
||||
v.bgImage = v.loadSprite(
|
||||
d2resource.CharacterSelectBackground,
|
||||
point(0, 0),
|
||||
0,
|
||||
@ -332,7 +335,7 @@ func (v *SelectHeroClass) OnLoad(loading d2screen.LoadingState) {
|
||||
loading.Progress(fourtyPercent)
|
||||
v.createButtons()
|
||||
|
||||
v.campfire = loadSprite(
|
||||
v.campfire = v.loadSprite(
|
||||
d2resource.CharacterSelectCampfire,
|
||||
point(campfirePosX, campfirePosY),
|
||||
0,
|
||||
@ -340,12 +343,12 @@ func (v *SelectHeroClass) OnLoad(loading d2screen.LoadingState) {
|
||||
true,
|
||||
)
|
||||
|
||||
v.createCheckboxes(v.renderer)
|
||||
v.createCheckboxes()
|
||||
loading.Progress(fiftyPercent)
|
||||
|
||||
for hero, config := range getHeroRenderConfiguration() {
|
||||
position := config.position
|
||||
forwardWalkOverlaySprite := loadSprite(
|
||||
forwardWalkOverlaySprite := v.loadSprite(
|
||||
config.forwardWalkOverlayAnimationPath,
|
||||
position,
|
||||
config.forwardWalkPlayLengthMs,
|
||||
@ -353,24 +356,32 @@ func (v *SelectHeroClass) OnLoad(loading d2screen.LoadingState) {
|
||||
config.forwardWalkOverlayBlend,
|
||||
)
|
||||
v.heroRenderInfo[hero] = &HeroRenderInfo{
|
||||
Stance: d2enum.HeroStanceIdle,
|
||||
IdleSprite: loadSprite(config.idleAnimationPath, position, config.idlePlayLengthMs, true, false),
|
||||
IdleSelectedSprite: loadSprite(config.idleSelectedAnimationPath, position, config.idlePlayLengthMs, true, false),
|
||||
ForwardWalkSprite: loadSprite(config.forwardWalkAnimationPath, position, config.forwardWalkPlayLengthMs, false, false),
|
||||
Stance: d2enum.HeroStanceIdle,
|
||||
IdleSprite: v.loadSprite(config.idleAnimationPath, position,
|
||||
config.idlePlayLengthMs, true, false),
|
||||
IdleSelectedSprite: v.loadSprite(config.idleSelectedAnimationPath,
|
||||
position,
|
||||
config.idlePlayLengthMs, true, false),
|
||||
ForwardWalkSprite: v.loadSprite(config.forwardWalkAnimationPath, position,
|
||||
config.forwardWalkPlayLengthMs, false, false),
|
||||
ForwardWalkSpriteOverlay: forwardWalkOverlaySprite,
|
||||
SelectedSprite: loadSprite(config.selectedAnimationPath, position, config.idlePlayLengthMs, true, false),
|
||||
SelectedSpriteOverlay: loadSprite(config.selectedOverlayAnimationPath, position, config.idlePlayLengthMs, true, true),
|
||||
BackWalkSprite: loadSprite(config.backWalkAnimationPath, position, config.backWalkPlayLengthMs, false, false),
|
||||
BackWalkSpriteOverlay: loadSprite(config.backWalkOverlayAnimationPath, position, config.backWalkPlayLengthMs, false, true),
|
||||
SelectionBounds: config.selectionBounds,
|
||||
SelectSfx: v.loadSoundEffect(config.selectSfx),
|
||||
DeselectSfx: v.loadSoundEffect(config.deselectSfx),
|
||||
SelectedSprite: v.loadSprite(config.selectedAnimationPath, position,
|
||||
config.idlePlayLengthMs, true, false),
|
||||
SelectedSpriteOverlay: v.loadSprite(config.selectedOverlayAnimationPath, position,
|
||||
config.idlePlayLengthMs, true, true),
|
||||
BackWalkSprite: v.loadSprite(config.backWalkAnimationPath, position,
|
||||
config.backWalkPlayLengthMs, false, false),
|
||||
BackWalkSpriteOverlay: v.loadSprite(config.backWalkOverlayAnimationPath, position,
|
||||
config.backWalkPlayLengthMs, false, true),
|
||||
SelectionBounds: config.selectionBounds,
|
||||
SelectSfx: v.loadSoundEffect(config.selectSfx),
|
||||
DeselectSfx: v.loadSoundEffect(config.deselectSfx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *SelectHeroClass) createLabels() {
|
||||
v.headingLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
v.headingLabel = v.uiManager.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
fontWidth, _ := v.headingLabel.GetSize()
|
||||
half := 2
|
||||
halfFontWidth := fontWidth / half
|
||||
@ -379,67 +390,62 @@ func (v *SelectHeroClass) createLabels() {
|
||||
v.headingLabel.SetText("Select Hero Class")
|
||||
v.headingLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
v.heroClassLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
v.heroClassLabel = v.uiManager.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
v.heroClassLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.heroClassLabel.SetPosition(heroClassLabelX, heroClassLabelY)
|
||||
|
||||
v.heroDesc1Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc1Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc1Label.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.heroDesc1Label.SetPosition(heroDescLine1X, heroDescLine1Y)
|
||||
|
||||
v.heroDesc2Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc2Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc2Label.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.heroDesc2Label.SetPosition(heroDescLine2X, heroDescLine2Y)
|
||||
|
||||
v.heroDesc3Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc3Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroDesc3Label.Alignment = d2gui.HorizontalAlignCenter
|
||||
v.heroDesc3Label.SetPosition(heroDescLine3X, heroDescLine3Y)
|
||||
|
||||
v.heroNameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroNameLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.heroNameLabel.Alignment = d2gui.HorizontalAlignLeft
|
||||
v.heroNameLabel.SetText(d2ui.ColorTokenize("Character Name", d2ui.ColorTokenGold))
|
||||
v.heroNameLabel.SetPosition(heroNameLabelX, heroNameLabelY)
|
||||
|
||||
v.expansionCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.expansionCharLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.expansionCharLabel.Alignment = d2gui.HorizontalAlignLeft
|
||||
v.expansionCharLabel.SetText(d2ui.ColorTokenize("EXPANSION CHARACTER", d2ui.ColorTokenGold))
|
||||
v.expansionCharLabel.SetPosition(expansionLabelX, expansionLabelY)
|
||||
|
||||
v.hardcoreCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.hardcoreCharLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
||||
v.hardcoreCharLabel.Alignment = d2gui.HorizontalAlignLeft
|
||||
v.hardcoreCharLabel.SetText(d2ui.ColorTokenize("Hardcore", d2ui.ColorTokenGold))
|
||||
v.hardcoreCharLabel.SetPosition(hardcoreLabelX, hardcoreLabelY)
|
||||
}
|
||||
|
||||
func (v *SelectHeroClass) createButtons() {
|
||||
v.exitButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "EXIT")
|
||||
v.exitButton.SetPosition(selHeroExitBtnX, selHeroExitBtnY)
|
||||
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
|
||||
d2ui.AddWidget(&v.exitButton)
|
||||
|
||||
v.okButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "OK")
|
||||
v.okButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, "OK")
|
||||
v.okButton.SetPosition(selHeroOkBtnX, selHeroOkBtnY)
|
||||
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
|
||||
v.okButton.SetVisible(false)
|
||||
v.okButton.SetEnabled(false)
|
||||
d2ui.AddWidget(&v.okButton)
|
||||
}
|
||||
|
||||
func (v *SelectHeroClass) createCheckboxes(renderer d2interface.Renderer) {
|
||||
v.heroNameTextbox = d2ui.CreateTextbox()
|
||||
func (v *SelectHeroClass) createCheckboxes() {
|
||||
v.heroNameTextbox = v.uiManager.NewTextbox()
|
||||
v.heroNameTextbox.SetPosition(heroNameTextBoxX, heoNameTextBoxY)
|
||||
v.heroNameTextbox.SetVisible(false)
|
||||
d2ui.AddWidget(&v.heroNameTextbox)
|
||||
|
||||
v.expansionCheckbox = d2ui.CreateCheckbox(v.renderer, true)
|
||||
v.expansionCheckbox = v.uiManager.NewCheckbox(true)
|
||||
v.expansionCheckbox.SetPosition(expandsionCheckboxX, expansionCheckboxY)
|
||||
v.expansionCheckbox.SetVisible(false)
|
||||
d2ui.AddWidget(&v.expansionCheckbox)
|
||||
|
||||
v.hardcoreCheckbox = d2ui.CreateCheckbox(renderer, false)
|
||||
v.hardcoreCheckbox = v.uiManager.NewCheckbox(false)
|
||||
v.hardcoreCheckbox.SetPosition(hardcoreCheckoxX, hardcoreCheckboxY)
|
||||
v.hardcoreCheckbox.SetVisible(false)
|
||||
d2ui.AddWidget(&v.hardcoreCheckbox)
|
||||
}
|
||||
|
||||
// OnUnload releases the resources of the Select Hero Class screen
|
||||
@ -559,11 +565,11 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
|
||||
return
|
||||
}
|
||||
|
||||
mouseX, mouseY := d2ui.CursorPosition()
|
||||
mouseX, mouseY := v.uiManager.CursorPosition()
|
||||
b := renderInfo.SelectionBounds
|
||||
mouseHover := (mouseX >= b.Min.X) && (mouseX <= b.Min.X+b.Max.X) && (mouseY >= b.Min.Y) && (mouseY <= b.Min.Y+b.Max.Y)
|
||||
|
||||
if mouseHover && d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) {
|
||||
if mouseHover && v.uiManager.CursorButtonPressed(d2ui.CursorButtonLeft) {
|
||||
v.handleCursorButtonPress(hero, renderInfo)
|
||||
return
|
||||
}
|
||||
@ -717,7 +723,9 @@ func advanceSprite(sprite *d2ui.Sprite, elapsed float64) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadSprite(animationPath string, position image.Point, playLength int, playLoop,
|
||||
func (v *SelectHeroClass) loadSprite(animationPath string, position image.Point,
|
||||
playLength int,
|
||||
playLoop,
|
||||
blend bool) *d2ui.Sprite {
|
||||
if animationPath == "" {
|
||||
return nil
|
||||
@ -740,7 +748,7 @@ func loadSprite(animationPath string, position image.Point, playLength int, play
|
||||
animation.SetPlayLengthMs(playLength)
|
||||
}
|
||||
|
||||
sprite, err := d2ui.LoadSprite(animation)
|
||||
sprite, err := v.uiManager.NewSprite(animation)
|
||||
if err != nil {
|
||||
fmt.Printf("could not load sprite for the animation: %s\n", animationPath)
|
||||
return nil
|
||||
|
@ -46,6 +46,7 @@ type GameControls struct {
|
||||
hero *d2mapentity.Player
|
||||
mapEngine *d2mapengine.MapEngine
|
||||
mapRenderer *d2maprenderer.MapRenderer
|
||||
uiManager *d2ui.UIManager
|
||||
inventory *Inventory
|
||||
heroStatsPanel *HeroStatsPanel
|
||||
inputListener InputCallbackListener
|
||||
@ -62,7 +63,7 @@ type GameControls struct {
|
||||
skillIcon *d2ui.Sprite
|
||||
zoneChangeText *d2ui.Label
|
||||
nameLabel *d2ui.Label
|
||||
runButton d2ui.Button
|
||||
runButton *d2ui.Button
|
||||
isZoneTextShown bool
|
||||
actionableRegions []ActionableRegion
|
||||
}
|
||||
@ -86,17 +87,24 @@ const (
|
||||
rightSkill = ActionableType(iota)
|
||||
)
|
||||
|
||||
func NewGameControls(renderer d2interface.Renderer, hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine,
|
||||
mapRenderer *d2maprenderer.MapRenderer, inputListener InputCallbackListener, term d2interface.Terminal) (*GameControls, error) {
|
||||
func NewGameControls(
|
||||
renderer d2interface.Renderer,
|
||||
hero *d2mapentity.Player,
|
||||
mapEngine *d2mapengine.MapEngine,
|
||||
mapRenderer *d2maprenderer.MapRenderer,
|
||||
inputListener InputCallbackListener,
|
||||
term d2interface.Terminal,
|
||||
ui *d2ui.UIManager,
|
||||
) (*GameControls, error) {
|
||||
missileID := initialMissileID
|
||||
term.BindAction("setmissile", "set missile id to summon on right click", func(id int) {
|
||||
missileID = id
|
||||
})
|
||||
|
||||
zoneLabel := d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
zoneLabel := ui.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
|
||||
zoneLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
nameLabel := d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
|
||||
nameLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
|
||||
nameLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
nameLabel.SetText(d2ui.ColorTokenize("", d2ui.ColorTokenServer))
|
||||
|
||||
@ -124,20 +132,21 @@ func NewGameControls(renderer d2interface.Renderer, hero *d2mapentity.Player, ma
|
||||
|
||||
inventoryRecord := d2datadict.Inventory[inventoryRecordKey]
|
||||
|
||||
hoverLabel := &nameLabel
|
||||
hoverLabel.SetBackgroundColor(color.RGBA{0,0,0, uint8(128)})
|
||||
hoverLabel := nameLabel
|
||||
hoverLabel.SetBackgroundColor(color.RGBA{0, 0, 0, uint8(128)})
|
||||
|
||||
gc := &GameControls{
|
||||
uiManager: ui,
|
||||
renderer: renderer,
|
||||
hero: hero,
|
||||
mapEngine: mapEngine,
|
||||
inputListener: inputListener,
|
||||
mapRenderer: mapRenderer,
|
||||
inventory: NewInventory(inventoryRecord),
|
||||
heroStatsPanel: NewHeroStatsPanel(renderer, hero.Name(), hero.Class, hero.Stats),
|
||||
inventory: NewInventory(ui, inventoryRecord),
|
||||
heroStatsPanel: NewHeroStatsPanel(ui, hero.Name(), hero.Class, hero.Stats),
|
||||
missileID: missileID,
|
||||
nameLabel: hoverLabel,
|
||||
zoneChangeText: &zoneLabel,
|
||||
zoneChangeText: zoneLabel,
|
||||
actionableRegions: []ActionableRegion{
|
||||
{leftSkill, d2common.Rectangle{Left: 115, Top: 550, Width: 50, Height: 50}},
|
||||
{leftSelec, d2common.Rectangle{Left: 206, Top: 563, Width: 30, Height: 30}},
|
||||
@ -328,19 +337,19 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
||||
|
||||
func (g *GameControls) Load() {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky)
|
||||
g.globeSprite, _ = d2ui.LoadSprite(animation)
|
||||
g.globeSprite, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.HealthManaIndicator, d2resource.PaletteSky)
|
||||
g.hpManaStatusSprite, _ = d2ui.LoadSprite(animation)
|
||||
g.hpManaStatusSprite, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.GamePanels, d2resource.PaletteSky)
|
||||
g.mainPanel, _ = d2ui.LoadSprite(animation)
|
||||
g.mainPanel, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.MenuButton, d2resource.PaletteSky)
|
||||
g.menuButton, _ = d2ui.LoadSprite(animation)
|
||||
g.menuButton, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.GenericSkills, d2resource.PaletteSky)
|
||||
g.skillIcon, _ = d2ui.LoadSprite(animation)
|
||||
g.skillIcon, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
g.loadUIButtons()
|
||||
|
||||
@ -350,7 +359,7 @@ func (g *GameControls) Load() {
|
||||
|
||||
func (g *GameControls) loadUIButtons() {
|
||||
// Run button
|
||||
g.runButton = d2ui.CreateButton(g.renderer, d2ui.ButtonTypeRun, "")
|
||||
g.runButton = g.uiManager.NewButton(d2ui.ButtonTypeRun, "")
|
||||
|
||||
g.runButton.SetPosition(255, 570)
|
||||
g.runButton.OnActivated(func() { g.onToggleRunButton() })
|
||||
@ -358,8 +367,6 @@ func (g *GameControls) loadUIButtons() {
|
||||
if g.hero.IsRunToggled() {
|
||||
g.runButton.Toggle()
|
||||
}
|
||||
|
||||
d2ui.AddWidget(&g.runButton)
|
||||
}
|
||||
|
||||
func (g *GameControls) onToggleRunButton() {
|
||||
|
@ -24,19 +24,19 @@ type PanelText struct {
|
||||
|
||||
// StatsPanelLabels represents the labels in the status panel
|
||||
type StatsPanelLabels struct {
|
||||
Level d2ui.Label
|
||||
Experience d2ui.Label
|
||||
NextLevelExp d2ui.Label
|
||||
Strength d2ui.Label
|
||||
Dexterity d2ui.Label
|
||||
Vitality d2ui.Label
|
||||
Energy d2ui.Label
|
||||
Health d2ui.Label
|
||||
MaxHealth d2ui.Label
|
||||
Mana d2ui.Label
|
||||
MaxMana d2ui.Label
|
||||
MaxStamina d2ui.Label
|
||||
Stamina d2ui.Label
|
||||
Level *d2ui.Label
|
||||
Experience *d2ui.Label
|
||||
NextLevelExp *d2ui.Label
|
||||
Strength *d2ui.Label
|
||||
Dexterity *d2ui.Label
|
||||
Vitality *d2ui.Label
|
||||
Energy *d2ui.Label
|
||||
Health *d2ui.Label
|
||||
MaxHealth *d2ui.Label
|
||||
Mana *d2ui.Label
|
||||
MaxMana *d2ui.Label
|
||||
MaxStamina *d2ui.Label
|
||||
Stamina *d2ui.Label
|
||||
}
|
||||
|
||||
// stores all the labels that can change during gameplay(e.g. current level, current hp, mana, etc.)
|
||||
@ -44,6 +44,7 @@ var StatValueLabels = make([]d2ui.Label, 13)
|
||||
|
||||
// HeroStatsPanel represents the hero status panel
|
||||
type HeroStatsPanel struct {
|
||||
uiManager *d2ui.UIManager
|
||||
frame *d2ui.Sprite
|
||||
panel *d2ui.Sprite
|
||||
heroState *d2hero.HeroStatsState
|
||||
@ -59,13 +60,14 @@ type HeroStatsPanel struct {
|
||||
}
|
||||
|
||||
// NewHeroStatsPanel creates a new hero status panel
|
||||
func NewHeroStatsPanel(renderer d2interface.Renderer, heroName string, heroClass d2enum.Hero,
|
||||
func NewHeroStatsPanel(ui *d2ui.UIManager, heroName string, heroClass d2enum.Hero,
|
||||
heroState *d2hero.HeroStatsState) *HeroStatsPanel {
|
||||
originX := 0
|
||||
originY := 0
|
||||
|
||||
return &HeroStatsPanel{
|
||||
renderer: renderer,
|
||||
uiManager: ui,
|
||||
renderer: ui.Renderer(),
|
||||
originX: originX,
|
||||
originY: originY,
|
||||
heroState: heroState,
|
||||
@ -78,9 +80,9 @@ func NewHeroStatsPanel(renderer d2interface.Renderer, heroName string, heroClass
|
||||
// Load loads the data for the hero status panel
|
||||
func (s *HeroStatsPanel) Load() {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky)
|
||||
s.frame, _ = d2ui.LoadSprite(animation)
|
||||
s.frame, _ = s.uiManager.NewSprite(animation)
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
|
||||
s.panel, _ = d2ui.LoadSprite(animation)
|
||||
s.panel, _ = s.uiManager.NewSprite(animation)
|
||||
s.initStatValueLabels()
|
||||
}
|
||||
|
||||
@ -273,7 +275,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var label d2ui.Label
|
||||
var label *d2ui.Label
|
||||
|
||||
// all static labels are not stored since we use them only once to generate the image cache
|
||||
|
||||
@ -360,23 +362,25 @@ func (s *HeroStatsPanel) renderStatValues(target d2interface.Surface) {
|
||||
s.renderStatValueNum(s.labels.Mana, s.heroState.Mana, target)
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) renderStatValueNum(label d2ui.Label, value int, target d2interface.Surface) {
|
||||
func (s *HeroStatsPanel) renderStatValueNum(label *d2ui.Label, value int,
|
||||
target d2interface.Surface) {
|
||||
label.SetText(strconv.Itoa(value))
|
||||
label.Render(target)
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) createStatValueLabel(stat int, x int, y int) d2ui.Label {
|
||||
func (s *HeroStatsPanel) createStatValueLabel(stat int, x int, y int) *d2ui.Label {
|
||||
text := strconv.Itoa(stat)
|
||||
return s.createTextLabel(PanelText{X: x, Y: y, Text: text, Font: d2resource.Font16, AlignCenter: true})
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) createTextLabel(element PanelText) d2ui.Label {
|
||||
label := d2ui.CreateLabel(element.Font, d2resource.PaletteStatic)
|
||||
func (s *HeroStatsPanel) createTextLabel(element PanelText) *d2ui.Label {
|
||||
label := s.uiManager.NewLabel(element.Font, d2resource.PaletteStatic)
|
||||
if element.AlignCenter {
|
||||
label.Alignment = d2gui.HorizontalAlignCenter
|
||||
}
|
||||
|
||||
label.SetText(element.Text)
|
||||
label.SetPosition(element.X, element.Y)
|
||||
|
||||
return label
|
||||
}
|
||||
|
@ -14,9 +14,10 @@ import (
|
||||
)
|
||||
|
||||
type Inventory struct {
|
||||
frame *d2ui.Sprite
|
||||
panel *d2ui.Sprite
|
||||
grid *ItemGrid
|
||||
uiManager *d2ui.UIManager
|
||||
frame *d2ui.Sprite
|
||||
panel *d2ui.Sprite
|
||||
grid *ItemGrid
|
||||
|
||||
hoverLabel *d2ui.Label
|
||||
hoverX, hoverY int
|
||||
@ -28,15 +29,16 @@ type Inventory struct {
|
||||
isOpen bool
|
||||
}
|
||||
|
||||
func NewInventory(record *d2datadict.InventoryRecord) *Inventory {
|
||||
func NewInventory(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *Inventory {
|
||||
|
||||
hoverLabel := d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
|
||||
hoverLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
|
||||
hoverLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
return &Inventory{
|
||||
grid: NewItemGrid(record),
|
||||
uiManager: ui,
|
||||
grid: NewItemGrid(ui, record),
|
||||
originX: record.Panel.Left,
|
||||
hoverLabel: &hoverLabel,
|
||||
hoverLabel: hoverLabel,
|
||||
// originY: record.Panel.Top,
|
||||
originY: 0, // expansion data has these all offset by +60 ...
|
||||
}
|
||||
@ -60,10 +62,10 @@ func (g *Inventory) Close() {
|
||||
|
||||
func (g *Inventory) Load() {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky)
|
||||
g.frame, _ = d2ui.LoadSprite(animation)
|
||||
g.frame, _ = g.uiManager.NewSprite(animation)
|
||||
|
||||
animation, _ = d2asset.LoadAnimation(d2resource.InventoryCharacterPanel, d2resource.PaletteSky)
|
||||
g.panel, _ = d2ui.LoadSprite(animation)
|
||||
g.panel, _ = g.uiManager.NewSprite(animation)
|
||||
items := []InventoryItem{
|
||||
diablo2item.NewItem("kit", "Crimson", "of the Bat", "of Frost").Identify(),
|
||||
diablo2item.NewItem("rin", "Steel", "of Shock").Identify(),
|
||||
@ -246,6 +248,7 @@ func (g *Inventory) Render(target d2interface.Surface) error {
|
||||
}
|
||||
|
||||
g.renderItemDescription(target, item)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -272,7 +275,7 @@ func (g *Inventory) renderItemDescription(target d2interface.Surface, i Inventor
|
||||
}
|
||||
|
||||
halfW, halfH := maxW/2, maxH/2
|
||||
centerX, centerY := g.hoverX, iy - halfH
|
||||
centerX, centerY := g.hoverX, iy-halfH
|
||||
|
||||
if (centerX + halfW) > 800 {
|
||||
centerX = 800 - halfW
|
||||
|
@ -33,6 +33,7 @@ var ErrorInventoryFull = errors.New("inventory full")
|
||||
// Reusable grid for use with player and merchant inventory.
|
||||
// Handles layout and rendering item icons based on code.
|
||||
type ItemGrid struct {
|
||||
uiManager *d2ui.UIManager
|
||||
items []InventoryItem
|
||||
equipmentSlots map[d2enum.EquippedSlot]EquipmentSlot
|
||||
width int
|
||||
@ -43,10 +44,11 @@ type ItemGrid struct {
|
||||
slotSize int
|
||||
}
|
||||
|
||||
func NewItemGrid(record *d2datadict.InventoryRecord) *ItemGrid {
|
||||
func NewItemGrid(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *ItemGrid {
|
||||
grid := record.Grid
|
||||
|
||||
return &ItemGrid{
|
||||
uiManager: ui,
|
||||
width: grid.Box.Width,
|
||||
height: grid.Box.Height,
|
||||
originX: grid.Box.Left,
|
||||
@ -126,7 +128,7 @@ func (g *ItemGrid) loadItem(item InventoryItem) {
|
||||
return
|
||||
}
|
||||
|
||||
itemSprite, err = d2ui.LoadSprite(animation)
|
||||
itemSprite, err = g.uiManager.NewSprite(animation)
|
||||
if err != nil {
|
||||
log.Printf("Failed to load sprite, error: " + err.Error())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user