Allow menu records to be processed hierarchically by the UI driver

Nothing is actually processed in this way yet, but there is a new
assertion forbidding certain types of records from having children.

Because of this new assertion, our menutype tweaks must be moved up a
layer into internal/menus. They fit better there anyway.
This commit is contained in:
2020-03-31 23:29:43 +01:00
parent 7586b90f8a
commit 2ae3611d7f
5 changed files with 61 additions and 34 deletions

View File

@@ -23,7 +23,7 @@ const (
TypeDoorHotspot MenuType = 30 // Like a button I guess? "FONTTYPE is animation speed" TypeDoorHotspot MenuType = 30 // Like a button I guess? "FONTTYPE is animation speed"
TypeDoorHotspot2 MenuType = 31 // Seems like a duplicate of the above? What's different? TypeDoorHotspot2 MenuType = 31 // Seems like a duplicate of the above? What's different?
TypeLineKbd MenuType = 40 TypeLineKbd MenuType = 40
TypeThumb MenuType = 45 TypeThumb MenuType = 45 // A "thumb" appears to be a vertical slider
TypeLineBriefing MenuType = 41 TypeLineBriefing MenuType = 41
TypeInvokeButton MenuType = 50 TypeInvokeButton MenuType = 50
TypeDoorHotspot3 MenuType = 60 // Maybe? Appears in Arrange.mnu TypeDoorHotspot3 MenuType = 60 // Maybe? Appears in Arrange.mnu
@@ -67,6 +67,20 @@ var TextOverrides = map[string]map[string]string{
}, },
} }
// FIXME: The menu is specified as type 2 (button) in these cases, which is
// weird. Make it a menu for now.
var TypeOverrides = map[string]map[string]MenuType{
"levelply": {
"2": TypeMenu,
},
"savegame": {
"2": TypeMenu,
},
"loadgame": {
"2": TypeMenu,
},
}
type Record struct { type Record struct {
Menu *Menu Menu *Menu
Parent *Record Parent *Record
@@ -262,6 +276,13 @@ func setProperty(r *Record, k, v string) {
r.Id = vInt r.Id = vInt
case "MENUTYPE", "SUBMENUTYPE": case "MENUTYPE", "SUBMENUTYPE":
r.Type = MenuType(vInt) r.Type = MenuType(vInt)
// FIXME: Type override. Note that MENUID is specified first, so this works
if overrides, ok := TypeOverrides[r.Menu.Name]; ok {
if newType, ok := overrides[r.Path()]; ok {
r.Type = newType
}
}
case "ACTIVE": case "ACTIVE":
r.Active = (vInt != 0) r.Active = (vInt != 0)
case "SPRITEID": case "SPRITEID":

View File

@@ -8,12 +8,12 @@ import (
) )
func init() { func init() {
registerBuilder(menus.TypeSimpleButton, registerSimpleButton) registerBuilder(menus.TypeSimpleButton, noChildren(registerSimpleButton))
registerBuilder(menus.TypeInvokeButton, registerInvokeButton) registerBuilder(menus.TypeInvokeButton, noChildren(registerInvokeButton))
registerBuilder(menus.TypeMainButton, registerMainButton) registerBuilder(menus.TypeMainButton, noChildren(registerMainButton))
registerBuilder(menus.TypeDoorHotspot, registerDoorHotspot) registerBuilder(menus.TypeDoorHotspot, noChildren(registerDoorHotspot))
registerBuilder(menus.TypeDoorHotspot2, registerDoorHotspot) registerBuilder(menus.TypeDoorHotspot2, noChildren(registerDoorHotspot))
registerBuilder(menus.TypeDoorHotspot3, registerDoorHotspot) registerBuilder(menus.TypeDoorHotspot3, noChildren(registerDoorHotspot))
} }
// A button without hover animation // A button without hover animation
@@ -39,11 +39,6 @@ type mainButton struct {
} }
func registerSimpleButton(d *Driver, r *menus.Record) error { func registerSimpleButton(d *Driver, r *menus.Record) error {
// FIXME: LevelPly.mnu specifies a menu oddly. This tweak gets us past it
if d.Name == "levelply" && r.Id == 2 {
return nil
}
return registerButton(d, r, r.SpriteId[0]) return registerButton(d, r, r.SpriteId[0])
} }

View File

@@ -56,18 +56,26 @@ var (
) )
// Used to add widgets to a driver // Used to add widgets to a driver
type builderFunc func(d *Driver, r *menus.Record) error type builderFunc func(d *Driver, r *menus.Record) (children []*menus.Record, err error)
func registerDebug(reason string, onward builderFunc) builderFunc { func registerDebug(reason string, onward builderFunc) builderFunc {
return func(d *Driver, r *menus.Record) error { return func(d *Driver, r *menus.Record) ([]*menus.Record, error) {
log.Printf("%v: %#+v", reason, r) log.Printf("%v: %#+v", reason, r)
if onward == nil { if onward == nil {
return registerStatic(d, r) return r.Children, nil
} else {
return onward(d, r)
} }
return nil return onward(d, r)
}
}
func noChildren(f func(d *Driver, r *menus.Record) error) builderFunc {
return func(d *Driver, r *menus.Record) ([]*menus.Record, error) {
if len(r.Children) > 0 {
return nil, fmt.Errorf("Children in record %v:%v (%#+v)", r.Menu.Name, r.Path(), r)
}
return nil, f(d, r)
} }
} }
@@ -132,7 +140,7 @@ func (d *Driver) Value(id string, into *string) error {
} }
} }
return fmt.Errorf("Couldn't find valueable widget %q", id) return fmt.Errorf("Couldn't find valueable widget %v:%v", d.menu.Name, id)
} }
func (d *Driver) SetValue(id, value string) error { func (d *Driver) SetValue(id, value string) error {
@@ -143,7 +151,7 @@ func (d *Driver) SetValue(id, value string) error {
} }
} }
return fmt.Errorf("Couldn't find valueable widget %q", id) return fmt.Errorf("Couldn't find valueable widget %v:%v", d.menu.Name, id)
} }
func (d *Driver) ValueBool(id string, into *bool) error { func (d *Driver) ValueBool(id string, into *bool) error {
@@ -173,7 +181,7 @@ func (d *Driver) SetFreeze(id string, value bool) error {
} }
} }
return fmt.Errorf("Couldn't find clickable widget %q", id) return fmt.Errorf("Couldn't find clickable widget %v:%v", d.menu.Name, id)
} }
func (d *Driver) OnClick(id string, f func()) error { func (d *Driver) OnClick(id string, f func()) error {
@@ -184,7 +192,7 @@ func (d *Driver) OnClick(id string, f func()) error {
} }
} }
return fmt.Errorf("Couldn't find clickable widget %q", id) return fmt.Errorf("Couldn't find clickable widget %v:%v", d.menu.Name, id)
} }
// FIXME: HURK. Surely I'm missing something? steps is value:offset // FIXME: HURK. Surely I'm missing something? steps is value:offset
@@ -197,7 +205,7 @@ func (d *Driver) ConfigureSlider(id string, steps map[int]int) error {
} }
} }
return fmt.Errorf("Couldn't find slider %q", id) return fmt.Errorf("Couldn't find slider %v:%v", d.menu.Name, id)
} }
func (d *Driver) ValueInt(id string, into *int) error { func (d *Driver) ValueInt(id string, into *int) error {
@@ -306,6 +314,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)
children := record.Children
handler, ok := widgetBuilders[record.Type] handler, ok := widgetBuilders[record.Type]
if !ok { if !ok {
@@ -313,13 +322,15 @@ func (d *Driver) addRecord(record *menus.Record) error {
} }
if handler != nil { if handler != nil {
if err := handler(d, record); err != nil { var err error
children, err = handler(d, record)
if err != nil {
return err return err
} }
} }
// Recursively add all children of this record // Recursively add all remaining children of this record
for _, record := range record.Children { for _, record := range children {
if err := d.addRecord(record); err != nil { if err := d.addRecord(record); err != nil {
return err return err
} }

View File

@@ -10,11 +10,11 @@ import (
) )
func init() { func init() {
registerBuilder(menus.TypeStatic, registerStatic) registerBuilder(menus.TypeStatic, noChildren(registerStatic))
registerBuilder(menus.TypeHypertext, registerHypertext) registerBuilder(menus.TypeHypertext, noChildren(registerHypertext))
registerBuilder(menus.TypeOverlay, registerOverlay) registerBuilder(menus.TypeOverlay, noChildren(registerOverlay))
registerBuilder(menus.TypeAnimationSample, registerAnimation) registerBuilder(menus.TypeAnimationSample, noChildren(registerAnimation))
registerBuilder(menus.TypeAnimationHover, registerAnimationHover) registerBuilder(menus.TypeAnimationHover, noChildren(registerAnimationHover))
} }
// A non-interactive element is not a widget; it merely displays some pixels and // A non-interactive element is not a widget; it merely displays some pixels and

View File

@@ -10,9 +10,9 @@ import (
) )
func init() { func init() {
registerBuilder(menus.TypeCheckbox, registerCheckbox) registerBuilder(menus.TypeCheckbox, noChildren(registerCheckbox))
registerBuilder(menus.TypeSlider, registerSlider) registerBuilder(menus.TypeSlider, noChildren(registerSlider))
registerBuilder(menus.TypeInventorySelect, registerInventorySelect) registerBuilder(menus.TypeInventorySelect, noChildren(registerInventorySelect))
} }
// A checkbox can be a fancy button // A checkbox can be a fancy button