mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-12-24 11:06:51 -05:00
Fixed font rendering. Added basic button stuff.
This commit is contained in:
parent
f485f803c8
commit
b0de898fff
@ -5,9 +5,9 @@ import "github.com/hajimehoshi/ebiten"
|
||||
// Drawable represents an instance that can be drawn
|
||||
type Drawable interface {
|
||||
Draw(target *ebiten.Image)
|
||||
GetSize() (uint32, uint32)
|
||||
GetSize() (width, height uint32)
|
||||
MoveTo(x, y int)
|
||||
GetLocation() (int, int)
|
||||
GetLocation() (x, y int)
|
||||
GetVisible() bool
|
||||
SetVisible(bool)
|
||||
SetVisible(visible bool)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type Sprite struct {
|
||||
Directions uint32
|
||||
FramesPerDirection uint32
|
||||
Frames []SpriteFrame
|
||||
Frames []*SpriteFrame
|
||||
X, Y int
|
||||
Frame, Direction uint8
|
||||
Blend bool
|
||||
@ -34,6 +34,7 @@ type SpriteFrame struct {
|
||||
Length uint32
|
||||
ImageData []int16
|
||||
Image *ebiten.Image
|
||||
Loaded bool
|
||||
}
|
||||
|
||||
// CreateSprite creates an instance of a sprite
|
||||
@ -57,10 +58,10 @@ func CreateSprite(data []byte, palette Palette) *Sprite {
|
||||
framePointers[i] = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||
dataPointer += 4
|
||||
}
|
||||
result.Frames = make([]SpriteFrame, totalFrames)
|
||||
result.Frames = make([]*SpriteFrame, totalFrames)
|
||||
for i := uint32(0); i < totalFrames; i++ {
|
||||
dataPointer = framePointers[i]
|
||||
|
||||
result.Frames[i] = &SpriteFrame{}
|
||||
result.Frames[i].Flip = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||
dataPointer += 4
|
||||
result.Frames[i].Width = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||
@ -120,6 +121,7 @@ func CreateSprite(data []byte, palette Palette) *Sprite {
|
||||
newData[(ii*4)+3] = 0xFF
|
||||
}
|
||||
result.Frames[ix].Image.ReplacePixels(newData)
|
||||
result.Frames[ix].Loaded = true
|
||||
}(i, dataPointer)
|
||||
}
|
||||
return result
|
||||
@ -156,11 +158,16 @@ func (v *Sprite) Draw(target *ebiten.Image) {
|
||||
float64((int32(v.Y) - int32(frame.Height) + frame.OffsetY)),
|
||||
)
|
||||
if v.Blend {
|
||||
opts.CompositeMode = ebiten.CompositeModeLighter
|
||||
opts.CompositeMode = ebiten.CompositeModeSourceOver
|
||||
} else {
|
||||
opts.CompositeMode = ebiten.CompositeModeSourceOver
|
||||
}
|
||||
if v.ColorMod != nil {
|
||||
opts.ColorM = ColorToColorM(v.ColorMod)
|
||||
}
|
||||
for frame.Image == nil {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
target.DrawImage(frame.Image, opts)
|
||||
}
|
||||
|
||||
@ -180,10 +187,15 @@ func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset
|
||||
)
|
||||
if v.Blend {
|
||||
opts.CompositeMode = ebiten.CompositeModeLighter
|
||||
} else {
|
||||
opts.CompositeMode = ebiten.CompositeModeSourceOver
|
||||
}
|
||||
if v.ColorMod != nil {
|
||||
opts.ColorM = ColorToColorM(v.ColorMod)
|
||||
}
|
||||
for frame.Image == nil {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
target.DrawImage(frame.Image, opts)
|
||||
xOffset += int32(frame.Width)
|
||||
biggestYOffset = MaxInt32(biggestYOffset, int32(frame.Height))
|
||||
|
@ -104,8 +104,11 @@ func (v *Engine) mapMpqFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
// LoadFile loads a file from the specified mpq and returns the data as a byte array
|
||||
func (v *Engine) LoadFile(fileName string) []byte {
|
||||
mutex.Lock()
|
||||
// TODO: May want to cache some things if performance becomes an issue
|
||||
mpqFile := v.Files[strings.ToLower(fileName)]
|
||||
mpq, err := MPQ.Load(mpqFile)
|
||||
@ -120,7 +123,7 @@ func (v *Engine) LoadFile(fileName string) []byte {
|
||||
mpqStream := MPQ.CreateStream(mpq, blockTableEntry, fileName)
|
||||
result := make([]byte, blockTableEntry.UncompressedFileSize)
|
||||
mpqStream.Read(result, 0, blockTableEntry.UncompressedFileSize)
|
||||
|
||||
mutex.Unlock()
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ type MainMenu struct {
|
||||
diabloLogoRight *Common.Sprite
|
||||
diabloLogoLeftBack *Common.Sprite
|
||||
diabloLogoRightBack *Common.Sprite
|
||||
exitDiabloButton *UI.Button
|
||||
copyrightLabel *UI.Label
|
||||
copyrightLabel2 *UI.Label
|
||||
showTrademarkScreen bool
|
||||
@ -48,14 +49,14 @@ func (v *MainMenu) Load() []func() {
|
||||
v.copyrightLabel = UI.CreateLabel(v.fileProvider, ResourcePaths.FontFormal12, Palettes.Static)
|
||||
v.copyrightLabel.Alignment = UI.LabelAlignCenter
|
||||
v.copyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment")
|
||||
v.copyrightLabel.ColorMod = color.RGBA{188, 168, 140, 255}
|
||||
v.copyrightLabel.Color = color.RGBA{188, 168, 140, 255}
|
||||
v.copyrightLabel.MoveTo(400, 500)
|
||||
},
|
||||
func() {
|
||||
v.copyrightLabel2 = UI.CreateLabel(v.fileProvider, ResourcePaths.FontFormal12, Palettes.Static)
|
||||
v.copyrightLabel2.Alignment = UI.LabelAlignCenter
|
||||
v.copyrightLabel2.SetText("All Rights Reserved.")
|
||||
v.copyrightLabel2.ColorMod = color.RGBA{188, 168, 140, 255}
|
||||
v.copyrightLabel2.Color = color.RGBA{188, 168, 140, 255}
|
||||
v.copyrightLabel2.MoveTo(400, 525)
|
||||
},
|
||||
func() {
|
||||
@ -86,6 +87,12 @@ func (v *MainMenu) Load() []func() {
|
||||
v.diabloLogoRightBack = v.fileProvider.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, Palettes.Units)
|
||||
v.diabloLogoRightBack.MoveTo(400, 120)
|
||||
},
|
||||
func() {
|
||||
v.exitDiabloButton = UI.CreateButton(v.fileProvider, "EXIT DIABLO II")
|
||||
v.exitDiabloButton.MoveTo(264, 535)
|
||||
v.exitDiabloButton.SetVisible(false)
|
||||
v.uiManager.AddWidget(v.exitDiabloButton)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,6 +127,7 @@ func (v *MainMenu) Update() {
|
||||
if v.uiManager.CursorButtonPressed(UI.CursorButtonLeft) {
|
||||
v.leftButtonHeld = true
|
||||
v.showTrademarkScreen = false
|
||||
v.exitDiabloButton.SetVisible(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
100
UI/Button.go
100
UI/Button.go
@ -1,8 +1,98 @@
|
||||
package UI
|
||||
|
||||
// Button defines an object that acts like a button
|
||||
type Button interface {
|
||||
Widget
|
||||
isPressed() bool
|
||||
setPressed(bool)
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/essial/OpenDiablo2/Common"
|
||||
"github.com/essial/OpenDiablo2/Palettes"
|
||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
// Button defines a standard wide UI button
|
||||
type Button struct {
|
||||
enabled bool
|
||||
x, y int
|
||||
width, height uint32
|
||||
visible bool
|
||||
pressed bool
|
||||
fileProvider Common.FileProvider
|
||||
normalImage *ebiten.Image
|
||||
pressedImage *ebiten.Image
|
||||
}
|
||||
|
||||
// CreateButton creates an instance of Button
|
||||
func CreateButton(fileProvider Common.FileProvider, text string) *Button {
|
||||
result := &Button{
|
||||
fileProvider: fileProvider,
|
||||
width: 272,
|
||||
height: 35,
|
||||
visible: true,
|
||||
enabled: true,
|
||||
pressed: false,
|
||||
}
|
||||
font := GetFont(ResourcePaths.FontExocet10, Palettes.Units, fileProvider)
|
||||
result.normalImage, _ = ebiten.NewImage(272, 35, ebiten.FilterNearest)
|
||||
result.pressedImage, _ = ebiten.NewImage(272, 35, ebiten.FilterNearest)
|
||||
textWidth, textHeight := font.GetTextMetrics(text)
|
||||
textX := (272 / 2) - (textWidth / 2)
|
||||
textY := (35 / 2) - (textHeight / 2) + 5
|
||||
buttonSprite := fileProvider.LoadSprite(ResourcePaths.WideButtonBlank, Palettes.Units)
|
||||
buttonSprite.MoveTo(0, 0)
|
||||
buttonSprite.Blend = true
|
||||
buttonSprite.DrawSegments(result.normalImage, 2, 1, 0)
|
||||
font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.normalImage)
|
||||
buttonSprite.DrawSegments(result.pressedImage, 2, 1, 1)
|
||||
font.Draw(int(textX-2), int(textY+2), text, color.Black, result.pressedImage)
|
||||
return result
|
||||
}
|
||||
|
||||
// Draw renders the button
|
||||
func (v *Button) Draw(target *ebiten.Image) {
|
||||
opts := &ebiten.DrawImageOptions{
|
||||
CompositeMode: ebiten.CompositeModeSourceAtop,
|
||||
Filter: ebiten.FilterNearest,
|
||||
}
|
||||
opts.GeoM.Translate(float64(v.x), float64(v.y))
|
||||
if v.pressed {
|
||||
target.DrawImage(v.pressedImage, opts)
|
||||
return
|
||||
}
|
||||
target.DrawImage(v.normalImage, opts)
|
||||
}
|
||||
|
||||
// GetEnabled returns the enabled state
|
||||
func (v *Button) GetEnabled() bool {
|
||||
return v.enabled
|
||||
}
|
||||
|
||||
// SetEnabled sets the enabled state
|
||||
func (v *Button) SetEnabled(enabled bool) {
|
||||
v.enabled = enabled
|
||||
}
|
||||
|
||||
// GetSize returns the size of the button
|
||||
func (v *Button) GetSize() (uint32, uint32) {
|
||||
return v.width, v.height
|
||||
}
|
||||
|
||||
// MoveTo moves the button
|
||||
func (v *Button) MoveTo(x, y int) {
|
||||
v.x = x
|
||||
v.y = y
|
||||
}
|
||||
|
||||
// GetLocation returns the location of the button
|
||||
func (v *Button) GetLocation() (x, y int) {
|
||||
return v.x, v.y
|
||||
}
|
||||
|
||||
// GetVisible returns the visibility of the button
|
||||
func (v *Button) GetVisible() bool {
|
||||
return v.visible
|
||||
}
|
||||
|
||||
// SetVisible sets the visibility of the button
|
||||
func (v *Button) SetVisible(visible bool) {
|
||||
v.visible = visible
|
||||
}
|
||||
|
40
UI/Font.go
40
UI/Font.go
@ -1,8 +1,11 @@
|
||||
package UI
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/essial/OpenDiablo2/Common"
|
||||
"github.com/essial/OpenDiablo2/Palettes"
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
var fontCache = map[string]*Font{}
|
||||
@ -15,8 +18,8 @@ type FontSize struct {
|
||||
|
||||
// Font represents a font
|
||||
type Font struct {
|
||||
FontSprite *Common.Sprite
|
||||
Metrics map[uint8]FontSize
|
||||
fontSprite *Common.Sprite
|
||||
metrics map[uint8]FontSize
|
||||
}
|
||||
|
||||
// GetFont creates or loads an existing font
|
||||
@ -33,9 +36,9 @@ func GetFont(font string, palette Palettes.Palette, fileProvider Common.FileProv
|
||||
// CreateFont creates an instance of a MPQ Font
|
||||
func CreateFont(font string, palette Palettes.Palette, fileProvider Common.FileProvider) *Font {
|
||||
result := &Font{
|
||||
Metrics: make(map[uint8]FontSize),
|
||||
metrics: make(map[uint8]FontSize),
|
||||
}
|
||||
result.FontSprite = fileProvider.LoadSprite(font+".dc6", palette)
|
||||
result.fontSprite = fileProvider.LoadSprite(font+".dc6", palette)
|
||||
woo := "Woo!\x01"
|
||||
fontData := fileProvider.LoadFile(font + ".tbl")
|
||||
if string(fontData[0:5]) != woo {
|
||||
@ -46,7 +49,34 @@ func CreateFont(font string, palette Palettes.Palette, fileProvider Common.FileP
|
||||
Width: fontData[i+3],
|
||||
Height: fontData[i+4],
|
||||
}
|
||||
result.Metrics[fontData[i+8]] = fontSize
|
||||
result.metrics[fontData[i+8]] = fontSize
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetTextMetrics returns the size of the specified text
|
||||
func (v *Font) GetTextMetrics(text string) (width, height uint32) {
|
||||
width = uint32(0)
|
||||
height = uint32(0)
|
||||
for _, ch := range text {
|
||||
metric := v.metrics[uint8(ch)]
|
||||
width += uint32(metric.Width)
|
||||
height = Common.Max(height, uint32(metric.Height))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Draw draws the font on the target surface
|
||||
func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Image) {
|
||||
v.fontSprite.ColorMod = color
|
||||
v.fontSprite.Blend = true
|
||||
_, height := v.GetTextMetrics(text)
|
||||
for _, ch := range text {
|
||||
char := uint8(ch)
|
||||
metric := v.metrics[char]
|
||||
v.fontSprite.Frame = char
|
||||
v.fontSprite.MoveTo(x, y+int(height))
|
||||
v.fontSprite.Draw(target)
|
||||
x += int(metric.Width)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ const (
|
||||
|
||||
// Manager represents the UI manager
|
||||
type Manager struct {
|
||||
widgets []*Widget
|
||||
widgets []Widget
|
||||
cursorSprite *Common.Sprite
|
||||
cursorButtons CursorButton
|
||||
CursorX int
|
||||
@ -29,7 +29,7 @@ type Manager struct {
|
||||
// CreateManager creates a new instance of a UI manager
|
||||
func CreateManager(provider Common.FileProvider) *Manager {
|
||||
result := &Manager{
|
||||
widgets: make([]*Widget, 0),
|
||||
widgets: make([]Widget, 0),
|
||||
cursorSprite: provider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units),
|
||||
}
|
||||
return result
|
||||
@ -37,16 +37,23 @@ func CreateManager(provider Common.FileProvider) *Manager {
|
||||
|
||||
// Reset resets the state of the UI manager. Typically called for new scenes
|
||||
func (v *Manager) Reset() {
|
||||
v.widgets = make([]*Widget, 0)
|
||||
v.widgets = make([]Widget, 0)
|
||||
}
|
||||
|
||||
// AddWidget adds a widget to the UI manager
|
||||
func (v *Manager) AddWidget(widget *Widget) {
|
||||
func (v *Manager) AddWidget(widget Widget) {
|
||||
v.widgets = append(v.widgets, widget)
|
||||
}
|
||||
|
||||
// Draw renders all of the UI elements
|
||||
func (v *Manager) Draw(screen *ebiten.Image) {
|
||||
for _, widget := range v.widgets {
|
||||
if !widget.GetVisible() {
|
||||
continue
|
||||
}
|
||||
widget.Draw(screen)
|
||||
}
|
||||
|
||||
cx, cy := ebiten.CursorPosition()
|
||||
v.cursorSprite.MoveTo(cx, cy)
|
||||
v.cursorSprite.Draw(screen)
|
||||
|
@ -30,14 +30,14 @@ type Label struct {
|
||||
Alignment LabelAlignment
|
||||
font *Font
|
||||
imageData *ebiten.Image
|
||||
ColorMod color.Color
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// CreateLabel creates a new instance of a UI label
|
||||
func CreateLabel(provider Common.FileProvider, font string, palette Palettes.Palette) *Label {
|
||||
result := &Label{
|
||||
Alignment: LabelAlignLeft,
|
||||
ColorMod: nil,
|
||||
Color: color.White,
|
||||
font: GetFont(font, palette, provider),
|
||||
}
|
||||
|
||||
@ -64,17 +64,6 @@ func (v *Label) Draw(target *ebiten.Image) {
|
||||
target.DrawImage(v.imageData, opts)
|
||||
}
|
||||
|
||||
func (v *Label) calculateSize() (uint32, uint32) {
|
||||
width := uint32(0)
|
||||
height := uint32(0)
|
||||
for _, ch := range v.text {
|
||||
metric := v.font.Metrics[uint8(ch)]
|
||||
width += uint32(metric.Width)
|
||||
height = Common.Max(height, uint32(metric.Height))
|
||||
}
|
||||
return width, height
|
||||
}
|
||||
|
||||
// MoveTo moves the label to the specified location
|
||||
func (v *Label) MoveTo(x, y int) {
|
||||
v.X = x
|
||||
@ -85,20 +74,11 @@ func (v *Label) cacheImage() {
|
||||
if v.imageData != nil {
|
||||
return
|
||||
}
|
||||
width, height := v.calculateSize()
|
||||
width, height := v.font.GetTextMetrics(v.text)
|
||||
v.Width = width
|
||||
v.Height = height
|
||||
v.imageData, _ = ebiten.NewImage(int(width), int(height), ebiten.FilterNearest)
|
||||
x := uint32(0)
|
||||
v.font.FontSprite.ColorMod = v.ColorMod
|
||||
for _, ch := range v.text {
|
||||
char := uint8(ch)
|
||||
metric := v.font.Metrics[char]
|
||||
v.font.FontSprite.Frame = char
|
||||
v.font.FontSprite.MoveTo(int(x), int(height))
|
||||
v.font.FontSprite.Draw(v.imageData)
|
||||
x += uint32(metric.Width)
|
||||
}
|
||||
v.font.Draw(0, 0, v.text, v.Color, v.imageData)
|
||||
}
|
||||
|
||||
// SetText sets the label's text
|
||||
|
@ -7,6 +7,6 @@ import (
|
||||
// Widget defines an object that is a UI widget
|
||||
type Widget interface {
|
||||
Common.Drawable
|
||||
getEnabled() bool
|
||||
setEnabled(bool)
|
||||
GetEnabled() bool
|
||||
SetEnabled(enabled bool)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user