2018-12-30 23:23:08 +00:00
|
|
|
package menus
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-04-19 20:57:45 +01:00
|
|
|
"image"
|
2020-03-30 00:15:19 +01:00
|
|
|
"image/color"
|
2018-12-30 23:23:08 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2020-03-30 00:15:19 +01:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/data"
|
2019-12-31 01:55:58 +00:00
|
|
|
"code.ur.gs/lupine/ordoor/internal/util/asciiscan"
|
2018-12-30 23:23:08 +00:00
|
|
|
)
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
// MenuType tells us what sort of Group we have
|
2020-03-22 22:12:59 +00:00
|
|
|
type MenuType int
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
// SubMenuType tells us what sort of Record we have
|
|
|
|
type SubMenuType int
|
|
|
|
|
2020-03-21 18:50:26 +00:00
|
|
|
const (
|
2020-04-14 03:14:49 +01:00
|
|
|
TypeStatic MenuType = 0
|
|
|
|
TypeMenu MenuType = 1
|
|
|
|
TypeDragMenu MenuType = 2 // Only seen in Configure_Vehicle_{Chaos,Ultra}
|
|
|
|
TypeRadioMenu MenuType = 3 // ???
|
|
|
|
TypeMainBackground MenuType = 45 // ???
|
|
|
|
TypeDialogue MenuType = 300
|
|
|
|
|
|
|
|
SubTypeSimpleButton SubMenuType = 3
|
|
|
|
SubTypeDoorHotspot1 SubMenuType = 30 // Like a button I guess? "FONTTYPE is animation speed"
|
|
|
|
SubTypeDoorHotspot2 SubMenuType = 31 // Seems like a duplicate of the above? What's different?
|
|
|
|
SubTypeLineKbd SubMenuType = 40
|
|
|
|
SubTypeLineBriefing SubMenuType = 41
|
|
|
|
SubTypeThumb SubMenuType = 45 // A "thumb" appears to be a vertical slider
|
|
|
|
SubTypeInvokeButton SubMenuType = 50
|
|
|
|
SubTypeDoorHotspot3 SubMenuType = 60 // Maybe? Appears in Arrange.mnu
|
|
|
|
SubTypeOverlay SubMenuType = 61
|
|
|
|
SubTypeHypertext SubMenuType = 70
|
|
|
|
SubTypeCheckbox SubMenuType = 91
|
|
|
|
SubTypeEditBox SubMenuType = 100
|
|
|
|
SubTypeInventorySelect SubMenuType = 110
|
|
|
|
SubTypeRadioButton SubMenuType = 120
|
|
|
|
SubTypeDropdownButton SubMenuType = 200
|
|
|
|
SubTypeComboBoxItem SubMenuType = 205
|
|
|
|
SubTypeAnimationSample SubMenuType = 220
|
|
|
|
SubTypeAnimationHover SubMenuType = 221 // FONTTYPE is animation speed. Only animate when hovered
|
|
|
|
SubTypeMainButton SubMenuType = 228
|
|
|
|
SubTypeSlider SubMenuType = 232
|
|
|
|
SubTypeStatusBar SubMenuType = 233
|
|
|
|
|
|
|
|
SubTypeListBoxUp SubMenuType = 400 // FIXME: these have multiple items in SUBMENUTYPE
|
|
|
|
SubTypeListBoxDown SubMenuType = 405
|
2020-03-21 18:50:26 +00:00
|
|
|
)
|
|
|
|
|
2020-03-26 23:35:34 +00:00
|
|
|
// FIXME: certain elements - especially overlays - don't have a DESC specified
|
|
|
|
// in the .mnu file, but display text specified with a number in i18n. The only
|
|
|
|
// conclusion I can draw is that they're hardcoded in the binary and set from
|
|
|
|
// outside. So, do that here.
|
2020-04-01 01:38:42 +01:00
|
|
|
var DescOverrides = map[string]int{
|
|
|
|
"main:2.6": 50992,
|
|
|
|
"newgame:2.5": 50993,
|
|
|
|
"keyboard:3.3": 50995,
|
|
|
|
"levelply:2.6": 50996,
|
2020-03-26 23:35:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Same idea with text overrides, only these aren't mentioned in the .dta
|
|
|
|
// file at all!
|
2020-04-01 01:38:42 +01:00
|
|
|
var TextOverrides = map[string]string{
|
|
|
|
"main:2.7": "0.1-ordoor",
|
2020-03-26 23:35:34 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
var TypeOverrides = map[string]SubMenuType{
|
|
|
|
// FIXME: These are put down as simple buttons, but it's a *lot* easier to
|
|
|
|
// understand them as list box buttons.
|
|
|
|
"configure_ultequip:7.5": SubTypeListBoxUp,
|
|
|
|
"configure_ultequip:7.6": SubTypeListBoxDown,
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Menu struct {
|
|
|
|
Name string
|
|
|
|
// TODO: load these
|
|
|
|
ObjectFiles []string
|
|
|
|
FontNames []string
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
// These are properties set in the menu header. We don't know what they're
|
|
|
|
// all for.
|
2020-03-30 00:15:19 +01:00
|
|
|
BackgroundColor color.Color
|
|
|
|
HypertextColor color.Color
|
2020-04-13 21:03:54 +01:00
|
|
|
FontType int
|
2018-12-30 23:23:08 +00:00
|
|
|
|
|
|
|
// The actual menu records. There are multiple top-level items. Submenus are
|
|
|
|
// only ever nested one deep.
|
2020-04-14 03:14:49 +01:00
|
|
|
Groups []*Group
|
|
|
|
}
|
|
|
|
|
|
|
|
// Group represents an element with a MENUTYPE. It is part of a Menu and may
|
|
|
|
// have children.
|
|
|
|
type Group struct {
|
|
|
|
Menu *Menu
|
2018-12-30 23:23:08 +00:00
|
|
|
Records []*Record
|
2020-04-14 03:14:49 +01:00
|
|
|
|
|
|
|
Properties
|
|
|
|
Type MenuType
|
|
|
|
}
|
|
|
|
|
|
|
|
type Record struct {
|
|
|
|
Menu *Menu
|
|
|
|
Group *Group
|
|
|
|
|
|
|
|
Properties
|
|
|
|
Type SubMenuType
|
|
|
|
}
|
|
|
|
|
|
|
|
type Properties struct {
|
|
|
|
Locator string // Not strictly a property. Set for tracking.
|
|
|
|
|
|
|
|
ID int
|
|
|
|
ObjectIdx int // Can be specified in MENUID, defaults to 0
|
|
|
|
|
|
|
|
Accelerator int
|
|
|
|
Active bool
|
|
|
|
Desc string
|
|
|
|
DrawType int
|
|
|
|
FontType int
|
|
|
|
Moveable bool
|
|
|
|
Share int
|
|
|
|
SoundType int
|
|
|
|
SpriteId []int
|
|
|
|
X int
|
|
|
|
Y int
|
|
|
|
|
|
|
|
// From i18n
|
|
|
|
Text string
|
|
|
|
Help string
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 20:57:45 +01:00
|
|
|
func (p *Properties) Point() image.Point {
|
|
|
|
if p.X > 0 || p.Y > 0 {
|
|
|
|
return image.Pt(p.X, p.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
return image.Point{}
|
|
|
|
}
|
|
|
|
|
2018-12-30 23:23:08 +00:00
|
|
|
func LoadMenu(filename string) (*Menu, error) {
|
|
|
|
name := filepath.Base(filename)
|
2020-04-13 21:03:54 +01:00
|
|
|
name = strings.TrimSuffix(name, filepath.Ext(name))
|
2020-03-26 22:09:26 +00:00
|
|
|
name = strings.ToLower(name)
|
2018-12-30 23:23:08 +00:00
|
|
|
|
|
|
|
scanner, err := asciiscan.New(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-01-02 04:38:03 +00:00
|
|
|
defer scanner.Close()
|
|
|
|
|
2018-12-30 23:23:08 +00:00
|
|
|
out := &Menu{
|
2020-04-13 21:03:54 +01:00
|
|
|
Name: name,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadObjects(out, scanner); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadProperties(out, scanner); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadFonts(out, scanner); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadRecords(filepath.Dir(filename), out, scanner); err != nil {
|
|
|
|
return nil, err
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadObjects(menu *Menu, scanner *asciiscan.Scanner) error {
|
|
|
|
strs, err := scanner.ConsumeStringList()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.ObjectFiles = strs
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadProperties(menu *Menu, scanner *asciiscan.Scanner) error {
|
2018-12-30 23:23:08 +00:00
|
|
|
for {
|
2020-04-13 21:03:54 +01:00
|
|
|
ok, err := scanner.PeekProperty()
|
|
|
|
|
2018-12-30 23:23:08 +00:00
|
|
|
if err != nil {
|
2020-04-13 21:03:54 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
break
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
k, v, err := scanner.ConsumeProperty()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
vInt, err := strconv.Atoi(v) // All properties have been int
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-15 21:11:01 +01:00
|
|
|
// DeBrief.mnu misspells these
|
|
|
|
parts := strings.SplitN(strings.ToUpper(k), " ", 3)
|
|
|
|
if len(parts) > 2 {
|
|
|
|
k = strings.Join(parts[0:2], " ")
|
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
switch strings.ToUpper(k) {
|
2020-04-15 21:11:01 +01:00
|
|
|
case "BACKGROUND COLOR":
|
2020-04-13 21:03:54 +01:00
|
|
|
menu.BackgroundColor = data.ColorPalette[vInt]
|
2020-04-15 21:11:01 +01:00
|
|
|
case "HYPERTEXT COLOR":
|
2020-04-13 21:03:54 +01:00
|
|
|
menu.HypertextColor = data.ColorPalette[vInt]
|
2020-04-15 21:11:01 +01:00
|
|
|
case "FONT TYPE":
|
2020-04-13 21:03:54 +01:00
|
|
|
menu.FontType = vInt
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unhandled menu property in %v: %q=%q", menu.Name, k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadFonts(menu *Menu, scanner *asciiscan.Scanner) error {
|
|
|
|
// FIXME: Can we just ignore NULL, or does the index matter?
|
|
|
|
strs, err := scanner.ConsumeStringList("NULL")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.FontNames = strs
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadRecords(baseDir string, menu *Menu, scanner *asciiscan.Scanner) error {
|
2020-04-14 03:14:49 +01:00
|
|
|
// We build things up line by line in these variables
|
|
|
|
var group *Group
|
|
|
|
var record *Record
|
|
|
|
var properties *Properties
|
2020-04-13 21:03:54 +01:00
|
|
|
|
|
|
|
for {
|
|
|
|
str, err := scanner.ConsumeString()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(str, "$") {
|
|
|
|
subScanner, err := asciiscan.New(filepath.Join(baseDir, str[1:]))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
2020-04-13 21:03:54 +01:00
|
|
|
|
|
|
|
err = loadRecords(baseDir, menu, subScanner)
|
|
|
|
subScanner.Close() // Don't keep this around for all of loadRecords
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Processing child %q: %v", str, err)
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
if str == "*" {
|
2020-04-13 21:03:54 +01:00
|
|
|
if record != nil {
|
2020-04-14 03:14:49 +01:00
|
|
|
group.Records = append(group.Records, record)
|
|
|
|
record = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if group != nil {
|
|
|
|
menu.Groups = append(menu.Groups, group)
|
|
|
|
group = nil
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
2020-04-13 21:03:54 +01:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
continue // New group
|
|
|
|
}
|
|
|
|
|
|
|
|
if str == "~" {
|
|
|
|
break // THE END
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
k, v := asciiscan.ConsumeProperty(str)
|
2020-04-14 03:14:49 +01:00
|
|
|
switch strings.ToUpper(k) {
|
2020-04-13 21:03:54 +01:00
|
|
|
case "MENUID":
|
2020-04-14 03:14:49 +01:00
|
|
|
if group != nil {
|
|
|
|
menu.Groups = append(menu.Groups, group)
|
|
|
|
}
|
|
|
|
|
|
|
|
group = newGroup(menu, v)
|
|
|
|
properties = &group.Properties
|
2020-04-13 21:03:54 +01:00
|
|
|
case "SUBMENUID":
|
2020-04-14 03:14:49 +01:00
|
|
|
if record != nil {
|
|
|
|
group.Records = append(group.Records, record)
|
|
|
|
}
|
|
|
|
|
|
|
|
record = newRecord(group, v)
|
|
|
|
properties = &record.Properties
|
|
|
|
case "MENUTYPE":
|
|
|
|
group.setMenuType(v)
|
|
|
|
case "SUBMENUTYPE":
|
|
|
|
record.setSubMenuType(v)
|
|
|
|
default:
|
|
|
|
if err := properties.setProperty(k, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-13 21:03:54 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-30 00:15:19 +01:00
|
|
|
|
2020-04-13 21:03:54 +01:00
|
|
|
return nil
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func LoadMenus(dir string) (map[string]*Menu, error) {
|
|
|
|
fis, err := ioutil.ReadDir(dir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make(map[string]*Menu, len(fis))
|
|
|
|
|
|
|
|
for _, fi := range fis {
|
|
|
|
relname := fi.Name()
|
|
|
|
basename := filepath.Base(relname)
|
|
|
|
extname := filepath.Ext(relname)
|
|
|
|
|
|
|
|
// Skip anything that isn't a .mnu file
|
|
|
|
if !strings.EqualFold(extname, ".mnu") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
built, err := LoadMenu(filepath.Join(dir, relname))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%s: %v", filepath.Join(dir, relname), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
out[basename] = built
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func listOfInts(s string) []int {
|
|
|
|
vSplit := strings.Split(s, ",")
|
|
|
|
vSplitInt := make([]int, len(vSplit))
|
|
|
|
|
|
|
|
for i, subV := range vSplit {
|
|
|
|
vSplitInt[i], _ = strconv.Atoi(subV)
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
return vSplitInt
|
|
|
|
}
|
|
|
|
|
|
|
|
func newGroup(menu *Menu, idStr string) *Group {
|
|
|
|
out := &Group{Menu: menu}
|
|
|
|
|
|
|
|
// ObjectIdx can be specified in the MENUID. Only seen for .mni files
|
|
|
|
ints := listOfInts(idStr)
|
|
|
|
out.ID = ints[0]
|
|
|
|
if len(ints) > 1 {
|
|
|
|
out.ObjectIdx = ints[1]
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
out.Locator = fmt.Sprintf("%v:%v", menu.Name, out.ID)
|
|
|
|
|
2018-12-30 23:23:08 +00:00
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func newRecord(group *Group, idStr string) *Record {
|
|
|
|
out := &Record{Group: group}
|
2018-12-30 23:23:08 +00:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
out.ID, _ = strconv.Atoi(idStr) // FIXME: we're ignoring conversion errors here
|
|
|
|
out.ObjectIdx = group.ObjectIdx // FIXME: we shouldn't *copy* this
|
|
|
|
|
|
|
|
out.Locator = fmt.Sprintf("%v.%v", group.Locator, out.ID)
|
|
|
|
|
|
|
|
return out
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (g *Group) setMenuType(s string) {
|
|
|
|
v, _ := strconv.Atoi(s) // FIXME: conversion errors
|
|
|
|
g.Type = MenuType(v)
|
|
|
|
}
|
2019-10-09 00:41:41 +01:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (r *Record) setSubMenuType(s string) {
|
|
|
|
// FIXME: Type overrides shouldn't be necessary!
|
|
|
|
if override, ok := TypeOverrides[r.Locator]; ok {
|
|
|
|
r.Type = override
|
|
|
|
return
|
2019-10-09 00:41:41 +01:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
// FIXME: what are the other types here? Related to list boxes?
|
|
|
|
ints := listOfInts(s)
|
|
|
|
r.Type = SubMenuType(ints[0])
|
|
|
|
}
|
2020-03-31 23:29:43 +01:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (p *Properties) setProperty(k, v string) error {
|
|
|
|
ints := listOfInts(v)
|
|
|
|
vInt := ints[0]
|
|
|
|
asBool := (vInt != 0)
|
|
|
|
|
|
|
|
switch strings.ToUpper(k) {
|
|
|
|
case "ACCELERATOR":
|
|
|
|
p.Accelerator = vInt
|
2018-12-30 23:23:08 +00:00
|
|
|
case "ACTIVE":
|
2020-04-14 03:14:49 +01:00
|
|
|
p.Active = asBool
|
|
|
|
case "DESC":
|
|
|
|
p.Desc = v // Usually int, occasionally string
|
|
|
|
case "DRAW TYPE":
|
|
|
|
p.DrawType = vInt
|
|
|
|
case "FONTTYPE":
|
|
|
|
p.FontType = vInt
|
|
|
|
case "MOVEABLE":
|
|
|
|
p.Moveable = asBool
|
|
|
|
case "SOUNDTYPE":
|
|
|
|
p.SoundType = vInt
|
2018-12-30 23:23:08 +00:00
|
|
|
case "SPRITEID":
|
2020-04-14 03:14:49 +01:00
|
|
|
p.SpriteId = ints
|
2018-12-30 23:23:08 +00:00
|
|
|
case "X-CORD":
|
2020-04-14 03:14:49 +01:00
|
|
|
p.X = vInt
|
2018-12-30 23:23:08 +00:00
|
|
|
case "Y-CORD":
|
2020-04-14 03:14:49 +01:00
|
|
|
p.Y = vInt
|
2020-03-21 18:50:26 +00:00
|
|
|
case "SHARE":
|
2020-04-14 03:14:49 +01:00
|
|
|
p.Share = vInt
|
|
|
|
|
2018-12-30 23:23:08 +00:00
|
|
|
default:
|
2020-04-14 03:14:49 +01:00
|
|
|
return fmt.Errorf("Unknown property for %v: %v=%v", p.Locator, k, v)
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
2020-04-14 03:14:49 +01:00
|
|
|
|
|
|
|
return nil
|
2018-12-30 23:23:08 +00:00
|
|
|
}
|
2019-01-02 06:16:15 +00:00
|
|
|
|
|
|
|
type Replacer interface {
|
2020-03-26 22:09:26 +00:00
|
|
|
ReplaceText(int, *string)
|
|
|
|
ReplaceHelp(int, *string)
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Record) Internationalize(replacer Replacer) {
|
2020-04-14 03:14:49 +01:00
|
|
|
if override, ok := TextOverrides[r.Locator]; ok {
|
2020-04-01 01:38:42 +01:00
|
|
|
r.Text = override
|
2020-04-14 03:14:49 +01:00
|
|
|
return
|
2020-03-26 23:35:34 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
if override, ok := DescOverrides[r.Locator]; ok {
|
|
|
|
r.Desc = strconv.Itoa(override)
|
2020-03-26 22:09:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
id, err := strconv.Atoi(r.Desc)
|
2019-01-02 06:16:15 +00:00
|
|
|
if err == nil {
|
2020-03-26 22:09:26 +00:00
|
|
|
replacer.ReplaceText(id, &r.Text)
|
|
|
|
replacer.ReplaceHelp(id, &r.Help)
|
2020-04-14 03:14:49 +01:00
|
|
|
} else {
|
|
|
|
r.Text = r.Desc // Sometimes it's a string like "EQUIPMENT"
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Menu) Internationalize(replacer Replacer) {
|
2020-04-14 03:14:49 +01:00
|
|
|
for _, group := range m.Groups {
|
|
|
|
for _, record := range group.Records {
|
|
|
|
record.Internationalize(replacer)
|
|
|
|
}
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-23 00:33:29 +00:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (g *Group) Props() *Properties {
|
|
|
|
return &g.Properties
|
|
|
|
}
|
2020-03-23 00:33:29 +00:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (r *Record) Props() *Properties {
|
|
|
|
return &r.Properties
|
2020-03-23 00:33:29 +00:00
|
|
|
}
|
2020-04-01 01:38:42 +01:00
|
|
|
|
2020-04-14 03:14:49 +01:00
|
|
|
func (p *Properties) BaseSpriteID() int {
|
|
|
|
base := p.Share
|
|
|
|
|
|
|
|
// SpriteId takes precedence if present
|
|
|
|
if len(p.SpriteId) > 0 && p.SpriteId[0] >= 0 {
|
|
|
|
base = p.SpriteId[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return base
|
2020-04-01 01:38:42 +01:00
|
|
|
}
|