package main import ( "flag" "log" "os" "path/filepath" "github.com/faiface/pixel" "github.com/faiface/pixel/pixelgl" "golang.org/x/image/colornames" "ur.gs/ordoor/internal/conv" "ur.gs/ordoor/internal/data" "ur.gs/ordoor/internal/fonts" "ur.gs/ordoor/internal/menus" "ur.gs/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 batch *pixel.Batch fonts []*fonts.Font fontObjs []*conv.Object fontBatch *pixel.Batch } type state struct { env *env step int // Redraw the window if these change winPos pixel.Vec winBounds pixel.Rect } func loadObjects(names ...string) ([]*conv.Object, *pixel.Batch) { var raw []*data.Object for _, name := range names { objFile := filepath.Join(filepath.Dir(*menuFile), name) obj, err := data.LoadObject(objFile) if err != nil { log.Fatalf("Failed to load %s: %v", name, err) } obj.Name = name raw = append(raw, obj) } objects, spritesheet := conv.ConvertObjects(raw) batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet) return objects, batch } 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) } var loadedFonts []*fonts.Font for _, name := range menu.FontNames { font, err := fonts.LoadFont(filepath.Join(*gamePath, "Fonts", name+".fnt")) if err != nil { log.Fatalf("Failed to load font %v: %v", name, err) } loadedFonts = append(loadedFonts, font) } menuObjs, menuBatch := loadObjects(menu.ObjectFiles...) env := &env{ menu: menu, objects: menuObjs, batch: menuBatch, fonts: loadedFonts, // TODO: load the objects and start displaying text } // The main thread now belongs to pixelgl pixelgl.Run(env.run) } func (e *env) run() { win, err := ui.NewWindow("View Menu: " + *menuFile) if err != nil { log.Fatal("Couldn't create window: %v", err) } pWin := win.PixelWindow state := &state{env: e} // For now, just try to display the various objects // left + right to change object, up + down to change frame win.Run(func() { oldState := *state state = state.runStep(pWin) if oldState != *state || oldState.step == 0 { state.present(pWin) } state.step += 1 }) } func (s *state) runStep(pWin *pixelgl.Window) *state { newState := *s newState.winPos = pWin.GetPos() newState.winBounds = pWin.Bounds() newState.handleKeys(pWin) return &newState } const ( origX = 640.0 origY = 480.0 ) func (s *state) present(pWin *pixelgl.Window) { pWin.Clear(colornames.Black) s.env.batch.Clear() // 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 := pWin.Bounds().Max scaleFactor := pixel.Vec{winSize.X / origX, winSize.Y / origY} cam := pixel.IM cam = cam.ScaledXY(pixel.ZV, pixel.Vec{1.0, -1.0}) // invert the Y axis cam = cam.Moved(pixel.Vec{origX / 2, origY / 2}) cam = cam.ScaledXY(pixel.ZV, scaleFactor) s.env.batch.SetMatrix(cam) for _, record := range s.env.menu.Records { s.drawRecord(record, s.env.batch) } s.env.batch.Draw(pWin) } func (s *state) drawRecord(record *menus.Record, target pixel.Target) { // Draw this record if it's valid to do so. FIXME: lots to learn if record.SpriteId >= 0 { x := float64(record.X) y := float64(record.Y) // FIXME: some are set at -1, -1. No idea why if x < 0.0 { x = 0.0 } if y < 0.0 { y = 0.0 } log.Printf( "Drawing id=%v type=%v spriteid=%v x=%v y=%v desc=%q parent=%p", record.Id, record.Type, record.SpriteId, record.X, record.Y, record.Desc, record.Parent, ) // FIXME: Need to handle multiple objects obj := s.env.objects[0] sprite := obj.Sprites[record.SpriteId] sprite.Spr.Draw(target, pixel.IM.Moved(pixel.V(x, y))) } // Draw all children of this record for _, child := range record.Children { s.drawRecord(child, target) } } func (s *state) handleKeys(pWin *pixelgl.Window) { /* if pWin.JustPressed(pixelgl.KeyLeft) { if s.objIdx > 0 { s.objIdx -= 1 s.spriteIdx = 0 } } if pWin.JustPressed(pixelgl.KeyRight) { if s.objIdx < s.env.set.Count()-1 { s.objIdx += 1 s.spriteIdx = 0 } } if pWin.JustPressed(pixelgl.KeyDown) { if s.spriteIdx > 0 { s.spriteIdx -= 1 } } if pWin.JustPressed(pixelgl.KeyUp) { if s.spriteIdx < len(s.curObject().Sprites)-1 { s.spriteIdx += 1 } } // Zoom in and out with the mouse wheel s.zoom *= math.Pow(1.2, pWin.MouseScroll().Y) */ }