package main import ( "flag" "fmt" "image" "log" "os" "path/filepath" "github.com/hajimehoshi/ebiten" "code.ur.gs/lupine/ordoor/internal/conv" "code.ur.gs/lupine/ordoor/internal/data" "code.ur.gs/lupine/ordoor/internal/fonts" "code.ur.gs/lupine/ordoor/internal/menus" "code.ur.gs/lupine/ordoor/internal/ui" ) var ( gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation") menuFile = flag.String("menu", "", "Path to a .mnu file, e.g. ./orig/Menu/MainGame.mnu") ) type env struct { menu *menus.Menu objects []*conv.Object fonts []*conv.Font fontObjs []*conv.Object step int state state lastState state } type state struct { // Redraw the window if these change winBounds image.Rectangle } func loadObjects(names ...string) ([]*conv.Object, error) { objs := make([]*conv.Object, 0, len(names)) for _, name := range names { objFile := filepath.Join(filepath.Dir(*menuFile), name) rawObj, err := data.LoadObject(objFile) if err != nil { log.Fatalf("Failed to load %s: %v", name, err) } obj, err := conv.ConvertObject(rawObj, name) if err != nil { return nil, err } objs = append(objs, obj) } return objs, nil } func loadFonts(names ...string) ([]*conv.Font, error) { var out []*conv.Font for _, name := range names { fnt, err := fonts.LoadFont(filepath.Join(*gamePath, "Fonts", name+".fnt")) if err != nil { return nil, fmt.Errorf("%v: %v", name, err) } out = append(out, conv.ConvertFont(fnt)) } return out, nil } func main() { flag.Parse() if *gamePath == "" || *menuFile == "" { flag.Usage() os.Exit(1) } menu, err := menus.LoadMenu(*menuFile) if err != nil { log.Fatalf("Couldn't load menu file %s: %v", *menuFile, err) } if i18n, err := data.LoadI18n(filepath.Join(*gamePath, "Data", data.I18nFile)); err != nil { log.Printf("Failed to load i18n data, skipping internationalization: %v", err) } else { menu.Internationalize(i18n) } loadedFonts, err := loadFonts(menu.FontNames...) if err != nil { log.Fatalf("Failed to load font: %v", err) } menuObjs, err := loadObjects(menu.ObjectFiles...) if err != nil { log.Fatalf("Failed to load objects: %v", err) } state := state{} env := &env{ menu: menu, objects: menuObjs, fonts: loadedFonts, state: state, lastState: state, } win, err := ui.NewWindow("View Menu: " + *menuFile) if err != nil { log.Fatal("Couldn't create window: %v", err) } if err := win.Run(env.Update, env.Draw); err != nil { log.Fatal(err) } } func (e *env) Update() error { // No behaviour yet e.step += 1 e.lastState = e.state return nil } const ( origX = 640.0 origY = 480.0 ) func (e *env) Draw(screen *ebiten.Image) error { // The menus expect to be drawn to a 640x480 screen. We need to scale and // project that so it fills the window appropriately. This is a combination // of translate + zoom winSize := screen.Bounds().Max scaleX := float64(winSize.X) / float64(origX) scaleY := float64(winSize.Y) / float64(origY) cam := ebiten.GeoM{} cam.Scale(scaleX, scaleY) for _, record := range e.menu.Records { if err := e.drawRecord(record, screen, cam); err != nil { return err } } return nil } func (e *env) drawRecord(record *menus.Record, screen *ebiten.Image, offset ebiten.GeoM) error { origOffset := offset // Draw this record if it's valid to do so. FIXME: lots to learn if len(record.SpriteId) >= 0 { spriteId := record.SpriteId[0] x := float64(record.X) y := float64(record.Y) // Maybe: we either give spriteid, or y,x,spriteId ? Unsure, doesn't seem // to be needed for now if len(record.SpriteId) == 3 { // x = x + float64(record.SpriteId[1]) // y = y + float64(record.SpriteId[0]) spriteId = record.SpriteId[2] } // FIXME: some here are set at -1. Presume that means don't draw. if spriteId < 0 { goto out } // FIXME: some are set at -1, -1. No idea why. Origin? if x < 0.0 { x = 0.0 } if y < 0.0 { y = 0.0 } // FIXME: Need to handle multiple objects obj := e.objects[0] sprite := obj.Sprites[spriteId] x = x + float64(sprite.XOffset) y = y + float64(sprite.YOffset) // Account for scaling x, y = offset.Apply(x, y) log.Printf( "Drawing id=%v type=%v spriteid=%v x=%v(+%v) y=%v(%+v) desc=%q parent=%p", record.Id, record.Type, spriteId, record.X, record.Y, sprite.XOffset, sprite.YOffset, record.Desc, record.Parent, ) offset.Translate(x, y) screen.DrawImage(sprite.Image, &ebiten.DrawImageOptions{GeoM: offset}) // FIXME: we probably shouldn't draw everything? // FIXME: handle multiple fonts if len(e.fonts) > 0 && record.Desc != "" { e.fonts[0].Output(screen, origOffset, record.Desc) } } out: // Draw all children of this record for _, child := range record.Children { if err := e.drawRecord(child, screen, offset); err != nil { return err } } return nil }