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:
@@ -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 {
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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)
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user