package game import ( "fmt" "image" "image/color" "math" "sort" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/vector" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/sim" ) // Minikartan: bara det spelaren sett plus riktmärkena (perronger, start, // spärrar), och en liten avgångslista per spår. Den lägger sig i det hörn // som varken täcker inforutan eller spelaren. var minimapColors = map[level.Tile]color.RGBA{ level.Wall: {58, 68, 102, 255}, level.Floor: {139, 155, 180, 255}, level.Spawn: {99, 199, 77, 255}, level.Platform: {254, 174, 52, 255}, level.WrongPlatform: {254, 174, 52, 255}, level.Gate: {44, 232, 245, 255}, level.TicketMachine: {247, 118, 34, 255}, level.Sign: {90, 140, 230, 255}, level.Toilet: {235, 240, 250, 255}, level.Kiosk: {228, 59, 68, 255}, level.StairsUp: {200, 150, 255, 255}, level.StairsDown: {150, 100, 220, 255}, } // minimapScale väljer pixlar per ruta så att kartan ryms i ungefär en // tredjedel av skärmen. func minimapScale(l *level.Level) int { sc := min(3, (ScreenW/3)/max(1, l.Width()), (ScreenH/3)/max(1, l.Height())) return max(1, sc) } // minimapRect väljer hörn: nedre höger som standard, annars det första hörnet // som inte krockar med inforutan (full bredd vid box) eller spelaren. func minimapRect(w, h int, box image.Rectangle, hasBox bool, player image.Rectangle) image.Rectangle { const m = 4 candidates := []image.Rectangle{ image.Rect(ScreenW-m-w, ScreenH-m-h, ScreenW-m, ScreenH-m), // nedre höger image.Rect(m, ScreenH-m-h, m+w, ScreenH-m), // nedre vänster image.Rect(ScreenW-m-w, 26, ScreenW-m, 26+h), // övre höger (under HUD:en) image.Rect(m, 26, m+w, 26+h), // övre vänster } for _, c := range candidates { if hasBox && c.Overlaps(box) { continue } if c.Overlaps(player.Inset(-8)) { continue } return c } return candidates[0] } func (s *LevelScene) drawMinimap(g *Game, screen *ebiten.Image, box image.Rectangle, hasBox bool) { a := g.Assets r := s.run l := r.Level sc := minimapScale(l) mapW, mapH := l.Width()*sc, l.Height()*sc // Avgångslistan: ett spår per rad. type dep struct { track string at bool secs float64 } var deps []dep for _, tr := range l.Trains { at, secs := tr.Schedule.Status(r.Time) deps = append(deps, dep{tr.Track, at, secs}) } sort.Slice(deps, func(i, j int) bool { return deps[i].track < deps[j].track }) z := r.Player.Tile.Z floorLabel := "" if l.NumFloors() > 1 { floorLabel = l.FloorName(z) } w := max(mapW, 44) + 6 h := mapH + 6 + len(deps)*LineH if floorLabel != "" { h += LineH } px, py := r.Player.WorldPos(TileSize) ox, oy := s.cam.Offset() playerRect := image.Rect(int(px+ox), int(py+oy), int(px+ox)+TileSize, int(py+oy)+TileSize) rc := minimapRect(w, h, box, hasBox, playerRect) vector.DrawFilledRect(screen, float32(rc.Min.X), float32(rc.Min.Y), float32(w), float32(h), colPanel, false) x0, y0 := rc.Min.X+3, rc.Min.Y+3 if floorLabel != "" { a.Draw(screen, floorLabel, x0, y0, colKnown) y0 += LineH } for y := 0; y < l.Height(); y++ { for x := 0; x < l.Width(); x++ { p := level.Point{X: x, Y: y, Z: z} if !r.Fog.Visible(l, p) { continue } t := l.At(p) c := minimapColors[t] if (t == level.Platform || t == level.WrongPlatform) && r.TrainVisible(t) { c = color.RGBA{255, 255, 255, 255} // tåget är inne } vector.DrawFilledRect(screen, float32(x0+x*sc), float32(y0+y*sc), float32(sc), float32(sc), c, false) } } // Sällskapet: följarna i sina toner, spelaren blinkande vit. for i := len(r.Party.Followers()); i >= 1; i-- { from, _ := r.Party.FollowerTiles(i) if from.Z != z { continue } tint := memberTints[i%len(memberTints)] vector.DrawFilledRect(screen, float32(x0+from.X*sc), float32(y0+from.Y*sc), float32(sc), float32(sc), color.RGBA{uint8(255 * tint[0]), uint8(255 * tint[1]), uint8(255 * tint[2]), 255}, false) } if math.Mod(r.Time, 0.6) < 0.4 { pt := r.Player.Tile vector.DrawFilledRect(screen, float32(x0+pt.X*sc), float32(y0+pt.Y*sc), float32(sc), float32(sc), colText, false) } // Avgångar: "2 ●6s" = inne, avgår om 6 s; "1 ○14s" = kommer om 14 s. for i, d := range deps { mark, col := "○", colDim if d.at { mark, col = "●", colGood } a.Draw(screen, fmt.Sprintf("%s %s%.0fs", d.track, mark, math.Ceil(d.secs)), x0, y0+mapH+2+i*LineH, col) } _ = sim.FogRadius }