1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-09 17:30:43 +00:00

Added text entry. Fixed performance issue. Added error checking. (#132)

This commit is contained in:
Tim Sarbin 2019-11-10 12:28:41 -05:00 committed by GitHub
parent e8292e9c42
commit f156475731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 275 additions and 70 deletions

View File

@ -19,11 +19,11 @@ type CharacterSelect struct {
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
background d2render.Sprite
newCharButton *d2ui.Button
convertCharButton *d2ui.Button
deleteCharButton *d2ui.Button
exitButton *d2ui.Button
okButton *d2ui.Button
newCharButton d2ui.Button
convertCharButton d2ui.Button
deleteCharButton d2ui.Button
exitButton d2ui.Button
okButton d2ui.Button
}
func CreateCharacterSelect(
@ -52,31 +52,31 @@ func (v *CharacterSelect) Load() []func() {
v.newCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#831"), 15)))
v.newCharButton.MoveTo(33, 468)
v.newCharButton.OnActivated(func() { v.onNewCharButtonClicked() })
v.uiManager.AddWidget(v.newCharButton)
v.uiManager.AddWidget(&v.newCharButton)
},
func() {
v.convertCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#825"), 15)))
v.convertCharButton.MoveTo(233, 468)
v.convertCharButton.SetEnabled(false)
v.uiManager.AddWidget(v.convertCharButton)
v.uiManager.AddWidget(&v.convertCharButton)
},
func() {
v.deleteCharButton = d2ui.CreateButton(d2ui.ButtonTypeTall, v.fileProvider, dh.CombineStrings(dh.SplitIntoLinesWithMaxWidth(d2common.TranslateString("#832"), 15)))
v.deleteCharButton.MoveTo(433, 468)
v.deleteCharButton.SetEnabled(false)
v.uiManager.AddWidget(v.deleteCharButton)
v.uiManager.AddWidget(&v.deleteCharButton)
},
func() {
v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970"))
v.exitButton.MoveTo(33, 537)
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(v.exitButton)
v.uiManager.AddWidget(&v.exitButton)
},
func() {
v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971"))
v.okButton.MoveTo(625, 537)
v.okButton.SetEnabled(false)
v.uiManager.AddWidget(v.okButton)
v.uiManager.AddWidget(&v.okButton)
},
}
}

View File

@ -22,7 +22,7 @@ import (
)
type labelItem struct {
Label *d2ui.Label
Label d2ui.Label
IsHeading bool
Available bool
}
@ -34,7 +34,7 @@ type Credits struct {
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
creditsBackground d2render.Sprite
exitButton *d2ui.Button
exitButton d2ui.Button
creditsText []string
labels []*labelItem
cycleTime float64
@ -68,7 +68,7 @@ func (v *Credits) Load() []func() {
v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970"))
v.exitButton.MoveTo(30, 550)
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(v.exitButton)
v.uiManager.AddWidget(&v.exitButton)
},
func() {
fileData, _ := dh.Utf16BytesToString(v.fileProvider.LoadFile(d2resource.CreditsText)[2:])
@ -192,7 +192,7 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
} else {
label.Label.Color = color.RGBA{198, 178, 150, 255}
}
return label.Label
return &label.Label
}
}
@ -210,5 +210,5 @@ func (v *Credits) getNewFontLabel(isHeading bool) *d2ui.Label {
}
v.labels = append(v.labels, newLabelItem)
return newLabelItem.Label
return &newLabelItem.Label
}

View File

@ -37,17 +37,17 @@ type MainMenu struct {
diabloLogoRight d2render.Sprite
diabloLogoLeftBack d2render.Sprite
diabloLogoRightBack d2render.Sprite
singlePlayerButton *d2ui.Button
githubButton *d2ui.Button
exitDiabloButton *d2ui.Button
creditsButton *d2ui.Button
cinematicsButton *d2ui.Button
mapTestButton *d2ui.Button
copyrightLabel *d2ui.Label
copyrightLabel2 *d2ui.Label
openDiabloLabel *d2ui.Label
versionLabel *d2ui.Label
commitLabel *d2ui.Label
singlePlayerButton d2ui.Button
githubButton d2ui.Button
exitDiabloButton d2ui.Button
creditsButton d2ui.Button
cinematicsButton d2ui.Button
mapTestButton d2ui.Button
copyrightLabel d2ui.Label
copyrightLabel2 d2ui.Label
openDiabloLabel d2ui.Label
versionLabel d2ui.Label
commitLabel d2ui.Label
ShowTrademarkScreen bool
leftButtonHeld bool
@ -138,41 +138,41 @@ func (v *MainMenu) Load() []func() {
v.exitDiabloButton.MoveTo(264, 535)
v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen)
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(v.exitDiabloButton)
v.uiManager.AddWidget(&v.exitDiabloButton)
},
func() {
v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, v.fileProvider, d2common.TranslateString("#1627"))
v.creditsButton.MoveTo(264, 505)
v.creditsButton.SetVisible(!v.ShowTrademarkScreen)
v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() })
v.uiManager.AddWidget(v.creditsButton)
v.uiManager.AddWidget(&v.creditsButton)
},
func() {
v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, v.fileProvider, d2common.TranslateString("#1639"))
v.cinematicsButton.MoveTo(401, 505)
v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen)
v.uiManager.AddWidget(v.cinematicsButton)
v.uiManager.AddWidget(&v.cinematicsButton)
},
func() {
v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, d2common.TranslateString("#1620"))
v.singlePlayerButton.MoveTo(264, 290)
v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen)
v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() })
v.uiManager.AddWidget(v.singlePlayerButton)
v.uiManager.AddWidget(&v.singlePlayerButton)
},
func() {
v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, "PROJECT WEBSITE")
v.githubButton.MoveTo(264, 330)
v.githubButton.SetVisible(!v.ShowTrademarkScreen)
v.githubButton.OnActivated(func() { v.onGithubButtonClicked() })
v.uiManager.AddWidget(v.githubButton)
v.uiManager.AddWidget(&v.githubButton)
},
func() {
v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, v.fileProvider, "MAP ENGINE TEST")
v.mapTestButton.MoveTo(264, 450)
v.mapTestButton.SetVisible(!v.ShowTrademarkScreen)
v.mapTestButton.OnActivated(func() { v.onMapTestClicked() })
v.uiManager.AddWidget(v.mapTestButton)
v.uiManager.AddWidget(&v.mapTestButton)
},
}
}

View File

@ -2,6 +2,7 @@ package d2scene
import (
"image"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
@ -35,20 +36,23 @@ type HeroRenderInfo struct {
}
type SelectHeroClass struct {
uiManager *d2ui.Manager
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
bgImage d2render.Sprite
campfire d2render.Sprite
headingLabel *d2ui.Label
heroClassLabel *d2ui.Label
heroDesc1Label *d2ui.Label
heroDesc2Label *d2ui.Label
heroDesc3Label *d2ui.Label
heroRenderInfo map[d2enum.Hero]*HeroRenderInfo
selectedHero d2enum.Hero
exitButton *d2ui.Button
uiManager *d2ui.Manager
soundManager *d2audio.Manager
fileProvider d2interface.FileProvider
sceneProvider d2interface.SceneProvider
bgImage d2render.Sprite
campfire d2render.Sprite
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
}
func CreateSelectHeroClass(
@ -115,7 +119,28 @@ func (v *SelectHeroClass) Load() []func() {
v.exitButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#970"))
v.exitButton.MoveTo(33, 537)
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(v.exitButton)
v.uiManager.AddWidget(&v.exitButton)
},
func() {
v.okButton = d2ui.CreateButton(d2ui.ButtonTypeMedium, v.fileProvider, d2common.TranslateString("#971"))
v.okButton.MoveTo(630, 537)
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
v.okButton.SetVisible(false)
v.okButton.SetEnabled(false)
v.uiManager.AddWidget(&v.okButton)
},
func() {
v.heroNameLabel = d2ui.CreateLabel(v.fileProvider, d2resource.Font16, d2enum.Units)
v.heroNameLabel.Alignment = d2ui.LabelAlignLeft
v.heroNameLabel.Color = color.RGBA{216, 196, 128, 255}
v.heroNameLabel.SetText(d2common.TranslateString("#1694"))
v.heroNameLabel.MoveTo(321, 475)
},
func() {
v.heroNameTextbox = d2ui.CreateTextbox(v.fileProvider)
v.heroNameTextbox.MoveTo(318, 493)
v.heroNameTextbox.SetVisible(false)
v.uiManager.AddWidget(&v.heroNameTextbox)
},
func() {
v.heroRenderInfo[d2enum.HeroBarbarian] = &HeroRenderInfo{
@ -368,10 +393,14 @@ func (v *SelectHeroClass) Unload() {
v.heroRenderInfo = nil
}
func (v *SelectHeroClass) onExitButtonClicked() {
func (v SelectHeroClass) onExitButtonClicked() {
v.sceneProvider.SetNextScene(CreateCharacterSelect(v.fileProvider, v.sceneProvider, v.uiManager, v.soundManager))
}
func (v SelectHeroClass) onOkButtonClicked() {
// TODO: Start the game
}
func (v *SelectHeroClass) Render(screen *ebiten.Image) {
v.bgImage.DrawSegments(screen, 4, 3, 0)
v.headingLabel.Draw(screen)
@ -392,6 +421,9 @@ func (v *SelectHeroClass) Render(screen *ebiten.Image) {
}
}
v.campfire.Draw(screen)
if v.heroNameTextbox.GetVisible() {
v.heroNameLabel.Draw(screen)
}
}
func (v *SelectHeroClass) Update(tickTime float64) {
@ -412,6 +444,8 @@ func (v *SelectHeroClass) Update(tickTime float64) {
if v.selectedHero != d2enum.HeroNone && allIdle {
v.selectedHero = d2enum.HeroNone
}
v.heroNameTextbox.Update()
v.okButton.SetEnabled(len(v.heroNameTextbox.GetText()) >= 2)
}
func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect bool) {
@ -444,7 +478,8 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
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 && v.uiManager.CursorButtonPressed(d2ui.CursorButtonLeft) {
// showEntryUi = true;
v.heroNameTextbox.SetVisible(true)
v.okButton.SetVisible(true)
renderInfo.Stance = d2enum.HeroStanceApproaching
renderInfo.ForwardWalkSprite.ResetAnimation()
if renderInfo.ForwardWalkSpriteOverlay.IsValid() {

View File

@ -3,6 +3,7 @@ package d2render
import (
"fmt"
"image"
"log"
"strings"
"time"
@ -156,7 +157,9 @@ func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) {
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(v.frameLocations[frameName][v.currentFrame].Left+offsetX),
float64(v.frameLocations[frameName][v.currentFrame].Top+offsetY+40))
target.DrawImage(v.frames[frameName][v.currentFrame], opts)
if err := target.DrawImage(v.frames[frameName][v.currentFrame], opts); err != nil {
log.Panic(err.Error())
}
}
}

View File

@ -4,6 +4,7 @@ import (
"encoding/binary"
"image"
"image/color"
"log"
"sync"
"time"
@ -29,7 +30,6 @@ type Sprite struct {
LastFrameTime time.Time
Animate bool
ColorMod color.Color
visible bool
valid bool
}
@ -104,7 +104,7 @@ func CreateSprite(data []byte, palette d2datadict.PaletteRec) Sprite {
x := uint32(0)
y := result.Frames[i].Height - 1
for true {
for {
b := data[dataPointer]
dataPointer++
if b == 0x80 {
@ -197,7 +197,9 @@ func (v *Sprite) cacheFrame(frame int) {
v.atlasBytes[idx+3] = v.Frames[frame].FrameData[pix+3]
}
}
v.atlas.ReplacePixels(v.atlasBytes)
if err := v.atlas.ReplacePixels(v.atlasBytes); err != nil {
log.Panic(err.Error())
}
v.Frames[frame].cached = true
}
@ -218,7 +220,7 @@ func (v *Sprite) updateAnimation() {
} else {
timePerFrame = time.Duration(float64(time.Second) * (1.0 / float64(len(v.Frames))))
}
for time.Now().Sub(v.LastFrameTime) >= timePerFrame {
for time.Since(v.LastFrameTime) >= timePerFrame {
v.LastFrameTime = v.LastFrameTime.Add(timePerFrame)
v.Frame++
if v.Frame >= uint8(v.FramesPerDirection) {
@ -272,7 +274,9 @@ func (v *Sprite) Draw(target *ebiten.Image) {
if v.ColorMod != nil {
opts.ColorM = d2helper.ColorToColorM(v.ColorMod)
}
target.DrawImage(frame.Image, opts)
if err := target.DrawImage(frame.Image, opts); err != nil {
log.Panic(err.Error())
}
}
// DrawSegments draws the sprite via a grid of segments
@ -300,7 +304,9 @@ func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset
if v.ColorMod != nil {
opts.ColorM = d2helper.ColorToColorM(v.ColorMod)
}
target.DrawImage(frame.Image, opts)
if err := target.DrawImage(frame.Image, opts); err != nil {
log.Panic(err.Error())
}
xOffset += int32(frame.Width)
biggestYOffset = d2helper.MaxInt32(biggestYOffset, int32(frame.Height))
}

View File

@ -108,8 +108,8 @@ type Button struct {
}
// CreateButton creates an instance of Button
func CreateButton(buttonType ButtonType, fileProvider d2interface.FileProvider, text string) *Button {
result := &Button{
func CreateButton(buttonType ButtonType, fileProvider d2interface.FileProvider, text string) Button {
result := Button{
fileProvider: fileProvider,
width: 0,
height: 0,
@ -159,7 +159,7 @@ func CreateButton(buttonType ButtonType, fileProvider d2interface.FileProvider,
if buttonLayout.DisabledFrame != -1 {
result.disabledImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest)
buttonSprite.DrawSegments(result.disabledImage, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame)
font.Draw(0, textY-1, text, color.RGBA{100, 100, 100, 255}, result.disabledImage)
font.Draw(0, textY, text, color.RGBA{100, 100, 100, 255}, result.disabledImage)
}
}
return result
@ -171,7 +171,7 @@ func (v *Button) OnActivated(callback func()) {
}
// Activate calls the on activated callback handler, if any
func (v *Button) Activate() {
func (v Button) Activate() {
if v.onClick == nil {
return
}
@ -179,7 +179,7 @@ func (v *Button) Activate() {
}
// Draw renders the button
func (v *Button) Draw(target *ebiten.Image) {
func (v Button) Draw(target *ebiten.Image) {
opts := &ebiten.DrawImageOptions{
CompositeMode: ebiten.CompositeModeSourceAtop,
Filter: ebiten.FilterNearest,
@ -202,7 +202,7 @@ func (v *Button) Draw(target *ebiten.Image) {
}
// GetEnabled returns the enabled state
func (v *Button) GetEnabled() bool {
func (v Button) GetEnabled() bool {
return v.enabled
}
@ -212,7 +212,7 @@ func (v *Button) SetEnabled(enabled bool) {
}
// GetSize returns the size of the button
func (v *Button) GetSize() (uint32, uint32) {
func (v Button) GetSize() (uint32, uint32) {
return v.width, v.height
}
@ -223,12 +223,12 @@ func (v *Button) MoveTo(x, y int) {
}
// GetLocation returns the location of the button
func (v *Button) GetLocation() (x, y int) {
func (v Button) GetLocation() (x, y int) {
return v.x, v.y
}
// GetVisible returns the visibility of the button
func (v *Button) GetVisible() bool {
func (v Button) GetVisible() bool {
return v.visible
}
@ -238,7 +238,7 @@ func (v *Button) SetVisible(visible bool) {
}
// GetPressed returns the pressed state of the button
func (v *Button) GetPressed() bool {
func (v Button) GetPressed() bool {
return v.pressed
}

View File

@ -36,13 +36,12 @@ type Label struct {
}
// CreateLabel creates a new instance of a UI label
func CreateLabel(provider d2interface.FileProvider, font string, palette d2enum.PaletteType) *Label {
result := &Label{
func CreateLabel(provider d2interface.FileProvider, font string, palette d2enum.PaletteType) Label {
result := Label{
Alignment: LabelAlignLeft,
Color: color.White,
font: GetFont(font, palette, provider),
}
return result
}
@ -72,6 +71,10 @@ func (v *Label) MoveTo(x, y int) {
v.Y = y
}
func (v *Label) GetTextMetrics(text string) (width, height uint32) {
return v.font.GetTextMetrics(text)
}
func (v *Label) cacheImage() {
if v.imageData != nil {
return
@ -93,7 +96,7 @@ func (v *Label) SetText(newText string) {
}
// GetSize returns the size of the label
func (v *Label) GetSize() (width, height uint32) {
func (v Label) GetSize() (width, height uint32) {
v.cacheImage()
width = v.Width
height = v.Height

158
d2render/d2ui/TextBox.go Normal file
View File

@ -0,0 +1,158 @@
package d2ui
import (
"strings"
"time"
"github.com/hajimehoshi/ebiten/inpututil"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/hajimehoshi/ebiten"
)
// TextBox represents a text input box
type TextBox struct {
text string
x int
y int
visible bool
enabled bool
bgSprite d2render.Sprite
textLabel Label
lineBar Label
}
func CreateTextbox(fileProvider d2interface.FileProvider) TextBox {
result := TextBox{
bgSprite: d2render.CreateSprite(fileProvider.LoadFile(d2resource.TextBox2), d2datadict.Palettes[d2enum.Units]),
textLabel: CreateLabel(fileProvider, d2resource.FontFormal11, d2enum.Units),
lineBar: CreateLabel(fileProvider, d2resource.FontFormal11, d2enum.Units),
enabled: true,
visible: true,
}
result.lineBar.SetText("_")
return result
}
func repeatingKeyPressed(key ebiten.Key) bool {
const (
delay = 30
interval = 3
)
d := inpututil.KeyPressDuration(key)
if d == 1 {
return true
}
if d >= delay && (d-delay)%interval == 0 {
return true
}
return false
}
func (v TextBox) Draw(target *ebiten.Image) {
if !v.visible {
return
}
v.bgSprite.Draw(target)
v.textLabel.Draw(target)
if (time.Now().UnixNano()/1e6)&(1<<8) > 0 {
v.lineBar.Draw(target)
}
}
func (v *TextBox) Update() {
if !v.visible || !v.enabled {
return
}
newText := string(ebiten.InputChars())
if len(newText) > 0 {
v.text += newText
v.SetText(v.text)
}
if repeatingKeyPressed(ebiten.KeyBackspace) {
if len(v.text) >= 1 {
v.text = v.text[:len(v.text)-1]
}
v.SetText(v.text)
}
}
func (v TextBox) GetText() string {
return v.text
}
func (v *TextBox) SetText(newText string) {
result := ""
for _, c := range newText {
if !strings.Contains("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", string(c)) {
continue
}
result += string(c)
}
if len(result) > 15 {
result = result[0:15]
}
v.text = result
for {
tw, _ := v.textLabel.GetTextMetrics(result)
if tw > 150 {
result = result[1:]
continue
}
v.lineBar.MoveTo(v.x+6+int(tw), v.y+3)
v.textLabel.SetText(result)
break
}
}
func (v TextBox) GetSize() (width, height uint32) {
return v.bgSprite.GetSize()
}
func (v *TextBox) MoveTo(x, y int) {
v.x = x
v.y = y
v.textLabel.MoveTo(v.x+6, v.y+3)
v.lineBar.MoveTo(v.x+6+int(v.textLabel.Width), v.y+3)
v.bgSprite.MoveTo(v.x, v.y+26)
}
func (v TextBox) GetLocation() (x, y int) {
return v.x, v.y
}
func (v TextBox) GetVisible() bool {
return v.visible
}
func (v *TextBox) SetVisible(visible bool) {
v.visible = visible
}
func (v TextBox) GetEnabled() bool {
return v.enabled
}
func (v *TextBox) SetEnabled(enabled bool) {
v.enabled = enabled
}
func (v *TextBox) SetPressed(pressed bool) {
// no op
}
func (v TextBox) GetPressed() bool {
return false
}
func (v *TextBox) OnActivated(callback func()) {
// no op
}
func (v TextBox) Activate() {
//no op
}