mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-18 02:16:23 -05:00
Work in progress on GUI (#304)
* Work in progress on GUI refactor * Remove WIP file * Remove WIP style
This commit is contained in:
parent
1983ec395d
commit
810b168ebf
@ -19,6 +19,7 @@ type assetManager struct {
|
||||
fileManager *fileManager
|
||||
paletteManager *paletteManager
|
||||
animationManager *animationManager
|
||||
fontManager *fontManager
|
||||
}
|
||||
|
||||
func loadPalette(palettePath string) (*d2datadict.PaletteRec, error) {
|
||||
|
@ -21,6 +21,7 @@ func Initialize() error {
|
||||
fileManager = createFileManager(config, archiveManager)
|
||||
paletteManager = createPaletteManager()
|
||||
animationManager = createAnimationManager()
|
||||
fontManager = createFontManager()
|
||||
)
|
||||
|
||||
singleton = &assetManager{
|
||||
@ -28,6 +29,7 @@ func Initialize() error {
|
||||
fileManager,
|
||||
paletteManager,
|
||||
animationManager,
|
||||
fontManager,
|
||||
}
|
||||
|
||||
d2term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) {
|
||||
@ -44,10 +46,11 @@ func Initialize() error {
|
||||
})
|
||||
|
||||
d2term.BindAction("assetstat", "display asset manager cache statistics", func() {
|
||||
d2term.OutputInfo("archive cache: %f%%", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("file cache: %f%%", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("palette cache: %f%%", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("animation cache: %f%%", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("archive cache: %f", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("file cache: %f", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("palette cache: %f", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("animation cache: %f", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0)
|
||||
d2term.OutputInfo("font cache: %f", float64(fontManager.cache.GetWeight())/float64(fontManager.cache.GetBudget())*100.0)
|
||||
})
|
||||
|
||||
d2term.BindAction("assetclear", "clear asset manager cache", func() {
|
||||
@ -55,6 +58,7 @@ func Initialize() error {
|
||||
fileManager.cache.Clear()
|
||||
paletteManager.cache.Clear()
|
||||
animationManager.cache.Clear()
|
||||
fontManager.cache.Clear()
|
||||
})
|
||||
|
||||
return nil
|
||||
@ -86,6 +90,7 @@ func FileExists(filePath string) (bool, error) {
|
||||
}
|
||||
|
||||
func LoadAnimation(animationPath, palettePath string) (*Animation, error) {
|
||||
verifyWasInit()
|
||||
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
|
||||
}
|
||||
|
||||
@ -95,9 +100,15 @@ func LoadAnimationWithTransparency(animationPath, palettePath string, transparen
|
||||
}
|
||||
|
||||
func LoadComposite(object *d2datadict.ObjectLookupRecord, palettePath string) (*Composite, error) {
|
||||
verifyWasInit()
|
||||
return CreateComposite(object, palettePath), nil
|
||||
}
|
||||
|
||||
func LoadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
||||
verifyWasInit()
|
||||
return singleton.fontManager.loadFont(tablePath, spritePath, palettePath)
|
||||
}
|
||||
|
||||
func verifyWasInit() {
|
||||
if singleton == nil {
|
||||
panic(ErrNotInit)
|
||||
|
126
d2core/d2asset/font.go
Normal file
126
d2core/d2asset/font.go
Normal file
@ -0,0 +1,126 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"image/color"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type fontGlyph struct {
|
||||
frame int
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
type Font struct {
|
||||
sheet *Animation
|
||||
glyphs map[rune]fontGlyph
|
||||
color color.Color
|
||||
}
|
||||
|
||||
func loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
||||
sheet, err := LoadAnimation(spritePath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := LoadFile(tablePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if string(data[:5]) != "Woo!\x01" {
|
||||
return nil, errors.New("invalid font table format")
|
||||
}
|
||||
|
||||
_, maxCharHeight := sheet.GetFrameBounds()
|
||||
|
||||
glyphs := make(map[rune]fontGlyph)
|
||||
for i := 12; i < len(data); i += 14 {
|
||||
code := rune(binary.LittleEndian.Uint16(data[i : i+2]))
|
||||
|
||||
var glyph fontGlyph
|
||||
glyph.frame = int(binary.LittleEndian.Uint16(data[i+8 : i+10]))
|
||||
glyph.width = int(data[i+3])
|
||||
glyph.height = maxCharHeight // int(data[i+4])
|
||||
|
||||
glyphs[code] = glyph
|
||||
}
|
||||
|
||||
font := &Font{
|
||||
sheet: sheet,
|
||||
glyphs: glyphs,
|
||||
color: color.White,
|
||||
}
|
||||
|
||||
return font, nil
|
||||
}
|
||||
|
||||
func (f *Font) GetTextMetrics(text string) (int, int) {
|
||||
var (
|
||||
lineWidth int
|
||||
lineHeight int
|
||||
totalWidth int
|
||||
totalHeight int
|
||||
)
|
||||
|
||||
for _, c := range text {
|
||||
if c == '\n' {
|
||||
totalWidth = d2common.MaxInt(totalWidth, lineWidth)
|
||||
totalHeight += lineHeight
|
||||
lineWidth = 0
|
||||
lineHeight = 0
|
||||
} else if glyph, ok := f.glyphs[c]; ok {
|
||||
lineWidth += glyph.width
|
||||
lineHeight = d2common.MaxInt(lineHeight, glyph.height)
|
||||
}
|
||||
}
|
||||
|
||||
totalWidth = d2common.MaxInt(totalWidth, lineWidth)
|
||||
totalHeight += lineHeight
|
||||
|
||||
return totalWidth, totalHeight
|
||||
}
|
||||
|
||||
func (f *Font) Clone() *Font {
|
||||
return &Font{
|
||||
sheet: f.sheet,
|
||||
glyphs: f.glyphs,
|
||||
color: f.color,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Font) RenderText(text string, target d2render.Surface) error {
|
||||
f.sheet.SetColorMod(f.color)
|
||||
f.sheet.SetBlend(false)
|
||||
|
||||
lines := strings.Split(text, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
var (
|
||||
lineHeight int
|
||||
lineLength int
|
||||
)
|
||||
|
||||
for _, c := range line {
|
||||
if glyph, ok := f.glyphs[c]; ok {
|
||||
f.sheet.SetCurrentFrame(glyph.frame)
|
||||
f.sheet.Render(target)
|
||||
lineHeight = d2common.MaxInt(lineHeight, glyph.height)
|
||||
target.PushTranslation(glyph.width, 0)
|
||||
lineLength++
|
||||
}
|
||||
}
|
||||
|
||||
target.PopN(lineLength)
|
||||
target.PushTranslation(0, lineHeight)
|
||||
}
|
||||
|
||||
target.PopN(len(lines))
|
||||
|
||||
return nil
|
||||
}
|
37
d2core/d2asset/font_manager.go
Normal file
37
d2core/d2asset/font_manager.go
Normal file
@ -0,0 +1,37 @@
|
||||
package d2asset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
const (
|
||||
fontBudget = 64
|
||||
)
|
||||
|
||||
type fontManager struct {
|
||||
cache *d2common.Cache
|
||||
}
|
||||
|
||||
func createFontManager() *fontManager {
|
||||
return &fontManager{d2common.CreateCache(fontBudget)}
|
||||
}
|
||||
|
||||
func (fm *fontManager) loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
||||
cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath)
|
||||
if font, found := fm.cache.Retrieve(cachePath); found {
|
||||
return font.(*Font).Clone(), nil
|
||||
}
|
||||
|
||||
font, err := loadFont(tablePath, spritePath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := fm.cache.Insert(cachePath, font.Clone(), 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return font, nil
|
||||
}
|
@ -11,7 +11,7 @@ var (
|
||||
ErrNotInit = errors.New("gui system is not initialized")
|
||||
)
|
||||
|
||||
var singleton *guiManager
|
||||
var singleton *manager
|
||||
|
||||
func Initialize() error {
|
||||
verifyNotInit()
|
||||
@ -34,6 +34,18 @@ func Advance(elapsed float64) error {
|
||||
return singleton.advance(elapsed)
|
||||
}
|
||||
|
||||
func AddLayout() *Layout {
|
||||
return singleton.addLayout()
|
||||
}
|
||||
|
||||
func AddSprite(imagePath, palettePath string) *Sprite {
|
||||
return singleton.addSprite(imagePath, palettePath)
|
||||
}
|
||||
|
||||
func AddLabel(text string, fontStyle FontStyle) *Label {
|
||||
return singleton.addLabel(text, fontStyle)
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
verifyWasInit()
|
||||
singleton.clear()
|
||||
|
@ -1,125 +0,0 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type guiWidget interface {
|
||||
getLayer() int
|
||||
render(target d2render.Surface) error
|
||||
advance(elapsed float64) error
|
||||
}
|
||||
|
||||
type guiManager struct {
|
||||
cursorSprite *Sprite
|
||||
loadSprite *Sprite
|
||||
widgets []guiWidget
|
||||
loading bool
|
||||
}
|
||||
|
||||
func createGuiManager() (*guiManager, error) {
|
||||
cursorSprite, err := CreateSprite(d2resource.CursorDefault, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loadSprite, err := CreateSprite(d2resource.LoadingScreen, d2resource.PaletteLoading)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
width, height := loadSprite.getSize()
|
||||
loadSprite.SetPosition(400-width/2, 300+height/2)
|
||||
|
||||
manager := &guiManager{
|
||||
cursorSprite: cursorSprite,
|
||||
loadSprite: loadSprite,
|
||||
}
|
||||
|
||||
if err := d2input.BindHandler(manager); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (gui *guiManager) OnMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
gui.cursorSprite.SetPosition(event.X, event.Y)
|
||||
return false
|
||||
}
|
||||
|
||||
func (gui *guiManager) render(target d2render.Surface) error {
|
||||
if gui.loading {
|
||||
target.Clear(color.Black)
|
||||
if err := gui.loadSprite.render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for _, widget := range gui.widgets {
|
||||
if err := widget.render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.cursorSprite.render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *guiManager) advance(elapsed float64) error {
|
||||
if gui.loading {
|
||||
gui.loadSprite.Show()
|
||||
if err := gui.loadSprite.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
gui.loadSprite.Hide()
|
||||
for _, widget := range gui.widgets {
|
||||
if err := widget.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.loadSprite.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *guiManager) showLoadScreen(progress float64) {
|
||||
progress = math.Min(progress, 1.0)
|
||||
progress = math.Max(progress, 0.0)
|
||||
|
||||
animation := gui.loadSprite.animation
|
||||
frameCount := animation.GetFrameCount()
|
||||
animation.SetCurrentFrame(int(float64(frameCount-1.0) * progress))
|
||||
|
||||
gui.loading = true
|
||||
}
|
||||
|
||||
func (gui *guiManager) hideLoadScreen() {
|
||||
gui.loading = false
|
||||
}
|
||||
|
||||
func (gui *guiManager) showCursor() {
|
||||
gui.cursorSprite.Show()
|
||||
}
|
||||
|
||||
func (gui *guiManager) hideCursor() {
|
||||
gui.cursorSprite.Hide()
|
||||
}
|
||||
|
||||
func (gui *guiManager) clear() {
|
||||
gui.widgets = nil
|
||||
gui.hideLoadScreen()
|
||||
}
|
60
d2core/d2gui/label.go
Normal file
60
d2core/d2gui/label.go
Normal file
@ -0,0 +1,60 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
widgetBase
|
||||
|
||||
text string
|
||||
font *d2asset.Font
|
||||
surface d2render.Surface
|
||||
}
|
||||
|
||||
func createLabel(text string, fontStyle FontStyle) *Label {
|
||||
font, _ := loadFont(fontStyle)
|
||||
label := &Label{font: font}
|
||||
label.SetText(text)
|
||||
label.visible = true
|
||||
return label
|
||||
}
|
||||
|
||||
func (l *Label) SetText(text string) *Label {
|
||||
l.text = text
|
||||
l.cache()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Label) render(target d2render.Surface) error {
|
||||
if l.surface == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return target.Render(l.surface)
|
||||
}
|
||||
|
||||
func (l *Label) cache() error {
|
||||
l.surface = nil
|
||||
if l.font == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
width, height := l.font.GetTextMetrics(l.text)
|
||||
|
||||
var err error
|
||||
if l.surface, err = d2render.NewSurface(width, height, d2render.FilterNearest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.font.RenderText(l.text, l.surface)
|
||||
}
|
||||
|
||||
func (l *Label) getSize() (int, int) {
|
||||
if l.surface == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return l.surface.GetSize()
|
||||
}
|
163
d2core/d2gui/layout.go
Normal file
163
d2core/d2gui/layout.go
Normal file
@ -0,0 +1,163 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type layoutEntry struct {
|
||||
widget widget
|
||||
|
||||
mouseOver bool
|
||||
mouseDown [3]bool
|
||||
}
|
||||
|
||||
type Layout struct {
|
||||
widgetBase
|
||||
entries []*layoutEntry
|
||||
}
|
||||
|
||||
func createLayout() *Layout {
|
||||
layout := new(Layout)
|
||||
layout.visible = true
|
||||
return layout
|
||||
}
|
||||
|
||||
func (l *Layout) render(target d2render.Surface) error {
|
||||
for _, entry := range l.entries {
|
||||
if entry.widget.isVisible() {
|
||||
l.renderWidget(entry.widget, target)
|
||||
l.renderWidgetDebug(entry.widget, target)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Layout) advance(elapsed float64) error {
|
||||
for _, entry := range l.entries {
|
||||
if entry.widget.isVisible() {
|
||||
if err := entry.widget.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Layout) renderWidget(widget widget, target d2render.Surface) {
|
||||
target.PushTranslation(widget.getPosition())
|
||||
defer target.Pop()
|
||||
|
||||
widget.render(target)
|
||||
}
|
||||
|
||||
func (l *Layout) renderWidgetDebug(widget widget, target d2render.Surface) {
|
||||
target.PushTranslation(widget.getPosition())
|
||||
defer target.Pop()
|
||||
|
||||
drawColor := color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xb0}
|
||||
|
||||
width, height := widget.getSize()
|
||||
target.DrawLine(width, 0, drawColor)
|
||||
target.DrawLine(0, height, drawColor)
|
||||
|
||||
target.PushTranslation(width, 0)
|
||||
target.DrawLine(0, height, drawColor)
|
||||
target.Pop()
|
||||
|
||||
target.PushTranslation(0, height)
|
||||
target.DrawLine(width, 0, drawColor)
|
||||
target.Pop()
|
||||
}
|
||||
|
||||
func (l *Layout) getSize() (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (l *Layout) onMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
entry.mouseDown[event.Button] = true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Layout) onMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
if entry.mouseDown[event.Button] {
|
||||
entry.widget.onMouseClick(eventLocal)
|
||||
}
|
||||
}
|
||||
|
||||
entry.mouseDown[event.Button] = false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Layout) onMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
if entry.mouseOver {
|
||||
entry.widget.onMouseOver(eventLocal)
|
||||
} else {
|
||||
entry.widget.onMouseEnter(eventLocal)
|
||||
}
|
||||
entry.mouseOver = true
|
||||
} else if entry.mouseOver {
|
||||
entry.widget.onMouseLeave(eventLocal)
|
||||
entry.mouseOver = false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Layout) adjustEventCoords(widget widget, eventX, eventY *int) bool {
|
||||
x, y := widget.getPosition()
|
||||
width, height := widget.getSize()
|
||||
|
||||
*eventX -= x
|
||||
*eventY -= y
|
||||
|
||||
if *eventX < 0 || *eventY < 0 || *eventX >= width || *eventY >= height {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *Layout) addLayout() *Layout {
|
||||
layout := createLayout()
|
||||
l.entries = append(l.entries, &layoutEntry{widget: layout})
|
||||
return layout
|
||||
}
|
||||
|
||||
func (l *Layout) addSprite(imagePath, palettePath string) *Sprite {
|
||||
sprite := createSprite(imagePath, palettePath)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: sprite})
|
||||
return sprite
|
||||
}
|
||||
|
||||
func (l *Layout) addLabel(text string, fontStyle FontStyle) *Label {
|
||||
label := createLabel(text, fontStyle)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: label})
|
||||
return label
|
||||
}
|
||||
|
||||
func (l *Layout) clear() {
|
||||
l.entries = nil
|
||||
}
|
141
d2core/d2gui/manager.go
Normal file
141
d2core/d2gui/manager.go
Normal file
@ -0,0 +1,141 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type manager struct {
|
||||
Layout
|
||||
|
||||
cursorAnim *d2asset.Animation
|
||||
cursorX int
|
||||
cursorY int
|
||||
cursorVisible bool
|
||||
|
||||
loadingAnim *d2asset.Animation
|
||||
loading bool
|
||||
}
|
||||
|
||||
func createGuiManager() (*manager, error) {
|
||||
cursorAnim, err := d2asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loadingAnim, err := d2asset.LoadAnimation(d2resource.LoadingScreen, d2resource.PaletteLoading)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager := &manager{
|
||||
cursorAnim: cursorAnim,
|
||||
loadingAnim: loadingAnim,
|
||||
cursorVisible: true,
|
||||
}
|
||||
|
||||
if err := d2input.BindHandler(manager); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
return m.Layout.onMouseButtonDown(event)
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
return m.Layout.onMouseButtonUp(event)
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
m.cursorX = event.X
|
||||
m.cursorY = event.Y
|
||||
|
||||
return m.Layout.onMouseMove(event)
|
||||
}
|
||||
|
||||
func (m *manager) render(target d2render.Surface) error {
|
||||
if m.loading {
|
||||
if err := m.renderLoadScreen(target); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := m.Layout.render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if m.cursorVisible {
|
||||
if err := m.renderCursor(target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *manager) renderLoadScreen(target d2render.Surface) error {
|
||||
target.Clear(color.Black)
|
||||
|
||||
screenWidth, screenHeight := target.GetSize()
|
||||
animWidth, animHeight := m.loadingAnim.GetCurrentFrameSize()
|
||||
target.PushTranslation(screenWidth/2-animWidth/2, screenHeight/2+animHeight/2)
|
||||
target.PushTranslation(0, -animHeight)
|
||||
defer target.PopN(2)
|
||||
|
||||
return m.loadingAnim.Render(target)
|
||||
}
|
||||
|
||||
func (m *manager) renderCursor(target d2render.Surface) error {
|
||||
_, height := m.cursorAnim.GetCurrentFrameSize()
|
||||
target.PushTranslation(m.cursorX, m.cursorY)
|
||||
target.PushTranslation(0, -height)
|
||||
defer target.PopN(2)
|
||||
|
||||
return m.cursorAnim.Render(target)
|
||||
}
|
||||
|
||||
func (m *manager) advance(elapsed float64) error {
|
||||
if !m.loading {
|
||||
if err := m.Layout.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *manager) showLoadScreen(progress float64) {
|
||||
progress = math.Min(progress, 1.0)
|
||||
progress = math.Max(progress, 0.0)
|
||||
|
||||
animation := m.loadingAnim
|
||||
frameCount := animation.GetFrameCount()
|
||||
animation.SetCurrentFrame(int(float64(frameCount-1.0) * progress))
|
||||
|
||||
m.loading = true
|
||||
}
|
||||
|
||||
func (m *manager) hideLoadScreen() {
|
||||
m.loading = false
|
||||
}
|
||||
|
||||
func (m *manager) showCursor() {
|
||||
m.cursorVisible = true
|
||||
}
|
||||
|
||||
func (m *manager) hideCursor() {
|
||||
m.cursorVisible = false
|
||||
}
|
||||
|
||||
func (m *manager) clear() {
|
||||
m.Layout.clear()
|
||||
m.hideLoadScreen()
|
||||
}
|
@ -7,29 +7,20 @@ import (
|
||||
)
|
||||
|
||||
type Sprite struct {
|
||||
x int
|
||||
y int
|
||||
widgetBase
|
||||
|
||||
segmentsX int
|
||||
segmentsY int
|
||||
frameOffset int
|
||||
|
||||
visible bool
|
||||
animation *d2asset.Animation
|
||||
}
|
||||
|
||||
func CreateSprite(imagePath, palettePath string) (*Sprite, error) {
|
||||
animation, err := d2asset.LoadAnimation(imagePath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sprite := &Sprite{
|
||||
animation: animation,
|
||||
visible: true,
|
||||
}
|
||||
|
||||
return sprite, nil
|
||||
func createSprite(imagePath, palettePath string) *Sprite {
|
||||
sprite := new(Sprite)
|
||||
sprite.animation, _ = d2asset.LoadAnimation(imagePath, palettePath)
|
||||
sprite.visible = true
|
||||
return sprite
|
||||
}
|
||||
|
||||
func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) {
|
||||
@ -38,34 +29,13 @@ func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) {
|
||||
s.frameOffset = frameOffset
|
||||
}
|
||||
|
||||
func (s *Sprite) SetPosition(x, y int) {
|
||||
s.x = x
|
||||
s.y = y
|
||||
}
|
||||
|
||||
func (s *Sprite) Show() {
|
||||
s.visible = true
|
||||
}
|
||||
|
||||
func (s *Sprite) Hide() {
|
||||
s.visible = false
|
||||
}
|
||||
|
||||
func (s *Sprite) getPosition() (int, int) {
|
||||
return s.x, s.y
|
||||
}
|
||||
|
||||
func (s *Sprite) getSize() (int, int) {
|
||||
return s.animation.GetCurrentFrameSize()
|
||||
}
|
||||
|
||||
func (s *Sprite) render(target d2render.Surface) error {
|
||||
if !s.visible {
|
||||
if s.animation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, height := s.animation.GetCurrentFrameSize()
|
||||
target.PushTranslation(s.x, s.y-height)
|
||||
target.PushTranslation(0, -height)
|
||||
defer target.Pop()
|
||||
|
||||
if s.segmentsX == 0 && s.segmentsY == 0 {
|
||||
@ -100,5 +70,17 @@ func (s *Sprite) render(target d2render.Surface) error {
|
||||
}
|
||||
|
||||
func (s *Sprite) advance(elapsed float64) error {
|
||||
if s.animation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.animation.Advance(elapsed)
|
||||
}
|
||||
|
||||
func (s *Sprite) getSize() (int, int) {
|
||||
if s.animation == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return s.animation.GetCurrentFrameSize()
|
||||
}
|
||||
|
36
d2core/d2gui/style.go
Normal file
36
d2core/d2gui/style.go
Normal file
@ -0,0 +1,36 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
|
||||
type FontStyle int
|
||||
|
||||
const (
|
||||
FontStyle16Units FontStyle = iota
|
||||
FontStyle30Units
|
||||
FontStyle42Units
|
||||
FontStyleFormal10Static
|
||||
FontStyleFormal11Units
|
||||
FontStyleFormal12Static
|
||||
)
|
||||
|
||||
type fontStyleConfig struct {
|
||||
fontBasePath string
|
||||
palettePath string
|
||||
}
|
||||
|
||||
var fontStyleConfigs = map[FontStyle]fontStyleConfig{
|
||||
FontStyle16Units: {d2resource.Font16, d2resource.PaletteUnits},
|
||||
FontStyle30Units: {d2resource.Font30, d2resource.PaletteUnits},
|
||||
FontStyle42Units: {d2resource.Font42, d2resource.PaletteUnits},
|
||||
FontStyleFormal10Static: {d2resource.FontFormal10, d2resource.PaletteStatic},
|
||||
FontStyleFormal11Units: {d2resource.FontFormal11, d2resource.PaletteUnits},
|
||||
FontStyleFormal12Static: {d2resource.FontFormal12, d2resource.PaletteStatic},
|
||||
}
|
||||
|
||||
func loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
|
||||
config := fontStyleConfigs[fontStyle]
|
||||
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath)
|
||||
}
|
105
d2core/d2gui/widget.go
Normal file
105
d2core/d2gui/widget.go
Normal file
@ -0,0 +1,105 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type MouseHandler func(d2input.MouseEvent)
|
||||
type MouseMoveHandler func(d2input.MouseMoveEvent)
|
||||
|
||||
type widget interface {
|
||||
render(target d2render.Surface) error
|
||||
advance(elapsed float64) error
|
||||
|
||||
onMouseEnter(event d2input.MouseMoveEvent)
|
||||
onMouseLeave(event d2input.MouseMoveEvent)
|
||||
onMouseOver(event d2input.MouseMoveEvent)
|
||||
onMouseClick(event d2input.MouseEvent)
|
||||
|
||||
getPosition() (int, int)
|
||||
getSize() (int, int)
|
||||
getLayer() int
|
||||
isVisible() bool
|
||||
}
|
||||
|
||||
type widgetBase struct {
|
||||
x int
|
||||
y int
|
||||
layer int
|
||||
visible bool
|
||||
|
||||
mouseEnterHandler MouseMoveHandler
|
||||
mouseLeaveHandler MouseMoveHandler
|
||||
mouseMoveHandler MouseMoveHandler
|
||||
mouseClickHandler MouseHandler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetPosition(x, y int) {
|
||||
w.x = x
|
||||
w.y = y
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetLayer(layer int) {
|
||||
w.layer = layer
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetVisible(visible bool) {
|
||||
w.visible = visible
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseEnterHandler(handler MouseMoveHandler) {
|
||||
w.mouseEnterHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseLeaveHandler(handler MouseMoveHandler) {
|
||||
w.mouseLeaveHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseMoveHandler(handler MouseMoveHandler) {
|
||||
w.mouseMoveHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseClickHandler(handler MouseHandler) {
|
||||
w.mouseClickHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) getPosition() (int, int) {
|
||||
return w.x, w.y
|
||||
}
|
||||
|
||||
func (w *widgetBase) getLayer() int {
|
||||
return w.layer
|
||||
}
|
||||
|
||||
func (w *widgetBase) isVisible() bool {
|
||||
return w.visible
|
||||
}
|
||||
|
||||
func (w *widgetBase) advance(elapsed float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) {
|
||||
if w.mouseEnterHandler != nil {
|
||||
w.mouseEnterHandler(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) {
|
||||
if w.mouseLeaveHandler != nil {
|
||||
w.mouseLeaveHandler(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseOver(event d2input.MouseMoveEvent) {
|
||||
if w.mouseMoveHandler != nil {
|
||||
w.mouseMoveHandler(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseClick(event d2input.MouseEvent) {
|
||||
if w.mouseClickHandler != nil {
|
||||
w.mouseClickHandler(event)
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type widget interface {
|
||||
Render(target d2render.Surface) error
|
||||
Advance(elapsed float64) error
|
||||
}
|
||||
|
||||
type widgetManager struct {
|
||||
}
|
Loading…
Reference in New Issue
Block a user