Display overlay text in the UI

We still have fonts to do, so this is very ugly, but it at least shows
*something* on the screen now.
This commit is contained in:
2020-03-26 22:09:26 +00:00
parent a0fd653c24
commit e4ce932324
7 changed files with 107 additions and 34 deletions

View File

@@ -94,7 +94,6 @@ func (o *Object) Sprite(idx int) (*Sprite, error) {
if sprite := o.sprites[idx]; sprite != nil { if sprite := o.sprites[idx]; sprite != nil {
return sprite, nil return sprite, nil
} }
log.Printf("Loading sprite %v:%v", o.raw.Name, idx)
if o.raw.Sprites[idx] == nil { if o.raw.Sprites[idx] == nil {
if err := o.raw.LoadSprite(idx); err != nil { if err := o.raw.LoadSprite(idx); err != nil {

View File

@@ -13,7 +13,7 @@ import (
// file that maps string IDs to messages // file that maps string IDs to messages
type I18n struct { type I18n struct {
Name string Name string
mapping map[int]string mapping map[int]Entry
} }
// FIXME: this should be put into the config file maybe, or detected from a list // FIXME: this should be put into the config file maybe, or detected from a list
@@ -22,6 +22,15 @@ const (
I18nFile = "USEng.dta" I18nFile = "USEng.dta"
) )
// I18n entries may have 1 or 2 items. If two, the first is a menu item and the
// second is a help string for that menu item.
//
// The text or help may contain some magic strings, as mentioned in USEng.data
type Entry struct {
Text string
Help string
}
func LoadI18n(filename string) (*I18n, error) { func LoadI18n(filename string) (*I18n, error) {
f, err := os.Open(filename) f, err := os.Open(filename)
if err != nil { if err != nil {
@@ -32,7 +41,7 @@ func LoadI18n(filename string) (*I18n, error) {
out := &I18n{ out := &I18n{
Name: filepath.Base(filename), Name: filepath.Base(filename),
mapping: make(map[int]string), mapping: make(map[int]Entry),
} }
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
@@ -63,7 +72,12 @@ func LoadI18n(filename string) (*I18n, error) {
// TODO: Replace certain escape characters with their literals? // TODO: Replace certain escape characters with their literals?
out.mapping[num] = string(val) if entry, ok := out.mapping[num]; !ok { // first time we've seen this
out.mapping[num] = Entry{Text: string(val)}
} else { // Second time, the item is help text
entry.Help = string(val)
out.mapping[num] = entry
}
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
@@ -78,8 +92,14 @@ func (n *I18n) Len() int {
} }
// Puts the internationalized string into `out` if `in` matches a known ID // Puts the internationalized string into `out` if `in` matches a known ID
func (n *I18n) Replace(in int, out *string) { func (n *I18n) ReplaceText(in int, out *string) {
if str, ok := n.mapping[in]; ok { if str, ok := n.mapping[in]; ok {
*out = str *out = str.Text
}
}
func (n *I18n) ReplaceHelp(in int, out *string) {
if str, ok := n.mapping[in]; ok {
*out = str.Help
} }
} }

View File

@@ -41,6 +41,7 @@ const (
) )
type Record struct { type Record struct {
Menu *Menu
Parent *Record Parent *Record
Children []*Record Children []*Record
@@ -53,7 +54,10 @@ type Record struct {
Share int Share int
X int X int
Y int Y int
Desc string
// From i18n
Text string
Help string
// FIXME: turn these into first-class data // FIXME: turn these into first-class data
properties map[string]string properties map[string]string
@@ -75,6 +79,8 @@ type Menu struct {
func LoadMenu(filename string) (*Menu, error) { func LoadMenu(filename string) (*Menu, error) {
name := filepath.Base(filename) name := filepath.Base(filename)
name = strings.Replace(name, filepath.Ext(name), "", -1)
name = strings.ToLower(name)
// FIXME: this needs turning into a real parser sometime // FIXME: this needs turning into a real parser sometime
scanner, err := asciiscan.New(filename) scanner, err := asciiscan.New(filename)
@@ -132,9 +138,9 @@ func LoadMenu(filename string) (*Menu, error) {
k, v := asciiscan.ConsumeProperty(str) k, v := asciiscan.ConsumeProperty(str)
switch k { switch k {
case "MENUID": case "MENUID":
record = newRecord(nil) record = newRecord(out, nil)
case "SUBMENUID": case "SUBMENUID":
record = newRecord(record.Toplevel()) record = newRecord(out, record.Toplevel())
} }
setProperty(record, k, v) setProperty(record, k, v)
} }
@@ -172,8 +178,9 @@ func LoadMenus(dir string) (map[string]*Menu, error) {
return out, nil return out, nil
} }
func newRecord(parent *Record) *Record { func newRecord(menu *Menu, parent *Record) *Record {
out := &Record{ out := &Record{
Menu: menu,
Parent: parent, Parent: parent,
properties: map[string]string{}, properties: map[string]string{},
} }
@@ -215,8 +222,6 @@ func setProperty(r *Record, k, v string) {
r.X = vInt r.X = vInt
case "Y-CORD": case "Y-CORD":
r.Y = vInt r.Y = vInt
case "DESC":
r.Desc = v
case "FONTTYPE": case "FONTTYPE":
r.FontType = vInt r.FontType = vInt
case "DRAW TYPE": case "DRAW TYPE":
@@ -229,13 +234,26 @@ func setProperty(r *Record, k, v string) {
} }
type Replacer interface { type Replacer interface {
Replace(int, *string) ReplaceText(int, *string)
ReplaceHelp(int, *string)
} }
func (r *Record) Internationalize(replacer Replacer) { func (r *Record) Internationalize(replacer Replacer) {
id, err := strconv.Atoi(r.Desc) // FIXME: I can't see how else this would happen in the original
if r.Menu.Name == "main" {
switch r.Path() {
case "2.6":
r.properties["DESC"] = "50992" // Chaos Gate
case "2.7":
r.Text = "ordoor-0.0.0"
}
}
id, err := strconv.Atoi(r.properties["DESC"])
if err == nil { if err == nil {
replacer.Replace(id, &r.Desc) delete(r.properties, "DESC")
replacer.ReplaceText(id, &r.Text)
replacer.ReplaceHelp(id, &r.Help)
} }
for _, child := range r.Children { for _, child := range r.Children {

View File

@@ -66,7 +66,7 @@ func registerMainButton(d *Driver, r *menus.Record) error {
baseSpr: sprites[0], baseSpr: sprites[0],
clickSpr: sprites[1], clickSpr: sprites[1],
frozenSpr: sprites[2], frozenSpr: sprites[2],
hoverImpl: hoverImpl{text: r.Desc}, hoverImpl: hoverImpl{text: r.Text},
}, },
} }
@@ -89,7 +89,7 @@ func registerButton(d *Driver, r *menus.Record, spriteId int) error {
baseSpr: sprites[0], baseSpr: sprites[0],
clickSpr: sprites[1], clickSpr: sprites[1],
frozenSpr: sprites[2], frozenSpr: sprites[2],
hoverImpl: hoverImpl{text: r.Desc}, hoverImpl: hoverImpl{text: r.Text},
} }
d.clickables = append(d.clickables, btn) d.clickables = append(d.clickables, btn)

View File

@@ -291,7 +291,7 @@ func (d *Driver) Draw(screen *ebiten.Image) error {
} }
func (d *Driver) addRecord(record *menus.Record) error { func (d *Driver) addRecord(record *menus.Record) error {
log.Printf("Adding record: %#+v", record) //log.Printf("Adding record: %#+v", record)
handler, ok := widgetBuilders[record.Type] handler, ok := widgetBuilders[record.Type]
if !ok { if !ok {
@@ -316,28 +316,28 @@ func (d *Driver) addRecord(record *menus.Record) error {
func (d *Driver) hoverStartEvent(h hoverable, inBounds bool) { func (d *Driver) hoverStartEvent(h hoverable, inBounds bool) {
if inBounds && !h.hoverState() { if inBounds && !h.hoverState() {
log.Printf("hoverable false -> true") //log.Printf("hoverable false -> true")
h.setHoverState(true) h.setHoverState(true)
} }
} }
func (d *Driver) hoverEndEvent(h hoverable, inBounds bool) { func (d *Driver) hoverEndEvent(h hoverable, inBounds bool) {
if !inBounds && h.hoverState() { if !inBounds && h.hoverState() {
log.Printf("hoverable true -> false") //log.Printf("hoverable true -> false")
h.setHoverState(false) h.setHoverState(false)
} }
} }
func (d *Driver) mouseDownEvent(c clickable, inBounds, wasDown, isDown bool) { func (d *Driver) mouseDownEvent(c clickable, inBounds, wasDown, isDown bool) {
if inBounds && !wasDown && isDown { if inBounds && !wasDown && isDown {
log.Printf("mouse down false -> true") //log.Printf("mouse down false -> true")
c.setMouseDownState(true) c.setMouseDownState(true)
} }
} }
func (d *Driver) mouseClickEvent(c clickable, inBounds, wasDown, isDown bool) { func (d *Driver) mouseClickEvent(c clickable, inBounds, wasDown, isDown bool) {
if inBounds && wasDown && !isDown { if inBounds && wasDown && !isDown {
log.Printf("mouse click") //log.Printf("mouse click")
c.registerMouseClick() c.registerMouseClick()
} }
} }
@@ -345,12 +345,12 @@ func (d *Driver) mouseClickEvent(c clickable, inBounds, wasDown, isDown bool) {
func (d *Driver) mouseUpEvent(c clickable, inBounds, wasDown, isDown bool) { func (d *Driver) mouseUpEvent(c clickable, inBounds, wasDown, isDown bool) {
if inBounds { if inBounds {
if wasDown && !isDown { if wasDown && !isDown {
log.Printf("mouse down true -> false") //log.Printf("mouse down true -> false")
c.setMouseDownState(false) c.setMouseDownState(false)
} }
} else { } else {
if wasDown { if wasDown {
log.Printf("mouse down true -> false") //log.Printf("mouse down true -> false")
c.setMouseDownState(false) c.setMouseDownState(false)
} }
} }

View File

@@ -3,13 +3,16 @@ package ui
import ( import (
"image" "image"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
"code.ur.gs/lupine/ordoor/internal/menus" "code.ur.gs/lupine/ordoor/internal/menus"
) )
func init() { func init() {
registerBuilder(menus.TypeStatic, registerStatic) registerBuilder(menus.TypeStatic, registerStatic)
registerBuilder(menus.TypeHypertext, registerHypertext) registerBuilder(menus.TypeHypertext, registerHypertext)
registerBuilder(menus.TypeOverlay, registerStatic) registerBuilder(menus.TypeOverlay, registerOverlay)
registerBuilder(menus.TypeAnimationSample, registerAnimation) registerBuilder(menus.TypeAnimationSample, registerAnimation)
} }
@@ -21,6 +24,9 @@ type noninteractive struct {
frames animation frames animation
rect image.Rectangle rect image.Rectangle
// Some non-interactives, e.g., overlays, are an image + text to be shown
textImg *ebiten.Image
hoverImpl hoverImpl
} }
@@ -38,7 +44,7 @@ func registerStatic(d *Driver, r *menus.Record) error {
ni := &noninteractive{ ni := &noninteractive{
frames: animation{sprite.Image}, frames: animation{sprite.Image},
hoverImpl: hoverImpl{text: r.Desc}, hoverImpl: hoverImpl{text: r.Text},
rect: sprite.Rect, rect: sprite.Rect,
} }
@@ -55,8 +61,7 @@ func registerHypertext(d *Driver, r *menus.Record) error {
} }
ni := &noninteractive{ ni := &noninteractive{
frames: nil, hoverImpl: hoverImpl{text: r.Text},
hoverImpl: hoverImpl{text: r.Desc},
rect: sprite.Rect, rect: sprite.Rect,
} }
@@ -65,6 +70,34 @@ func registerHypertext(d *Driver, r *menus.Record) error {
return nil return nil
} }
// An overlay is a static image + some text that needs to be rendered
func registerOverlay(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.Share)
if err != nil {
return err
}
ni := &noninteractive{
frames: animation{sprite.Image},
rect: sprite.Rect,
}
if r.Text != "" {
// FIXME: we should be rendering the text much more nicely than this
textImg, err := ebiten.NewImage(sprite.Rect.Dx(), sprite.Rect.Dy(), ebiten.FilterDefault)
if err != nil {
return err
}
ebitenutil.DebugPrint(textImg, r.Text)
ni.textImg = textImg
}
d.paintables = append(d.paintables, ni)
return nil
}
// An animation is a non-interactive element that displays something in a loop // An animation is a non-interactive element that displays something in a loop
func registerAnimation(d *Driver, r *menus.Record) error { func registerAnimation(d *Driver, r *menus.Record) error {
sprite, err := d.menu.Sprite(r.SpriteId[0]) sprite, err := d.menu.Sprite(r.SpriteId[0])
@@ -79,7 +112,7 @@ func registerAnimation(d *Driver, r *menus.Record) error {
ani := &noninteractive{ ani := &noninteractive{
frames: animation(frames), frames: animation(frames),
hoverImpl: hoverImpl{text: r.Desc}, hoverImpl: hoverImpl{text: r.Text},
rect: sprite.Rect, rect: sprite.Rect,
} }
@@ -94,5 +127,11 @@ func (n *noninteractive) bounds() image.Rectangle {
} }
func (n *noninteractive) regions(tick int) []region { func (n *noninteractive) regions(tick int) []region {
return oneRegion(n.bounds().Min, n.frames.image(tick)) out := oneRegion(n.bounds().Min, n.frames.image(tick))
if n.textImg != nil {
out = append(out, oneRegion(n.bounds().Min, n.textImg)...)
}
return out
} }

View File

@@ -2,7 +2,6 @@ package ui
import ( import (
"image" "image"
"log"
"math" "math"
"strconv" "strconv"
@@ -51,7 +50,7 @@ func registerCheckbox(d *Driver, r *menus.Record) error {
baseSpr: sprites[0], // unchecked baseSpr: sprites[0], // unchecked
clickSpr: sprites[2], // checked clickSpr: sprites[2], // checked
frozenSpr: sprites[1], frozenSpr: sprites[1],
hoverImpl: hoverImpl{text: r.Desc}, hoverImpl: hoverImpl{text: r.Text},
}, },
valueImpl: valueImpl{str: "0"}, valueImpl: valueImpl{str: "0"},
} }
@@ -125,8 +124,6 @@ func (s *slider) registerMouseClick() {
} }
s.valueImpl.str = strconv.Itoa(value) s.valueImpl.str = strconv.Itoa(value)
log.Printf("Slider value: %#v", s.valueImpl.str)
log.Printf("%#+v", s.steps)
s.clickImpl.registerMouseClick() s.clickImpl.registerMouseClick()
} }