package game import ( "fmt" "image" "image/color" "strings" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/vector" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/lang" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/minigame" ) // Minispelen ritas som en panel över banan. Logiken bor i internal/minigame. func (s *LevelScene) drawMinigame(g *Game, screen *ebiten.Image) { a := g.Assets r := s.run panel := image.Rect(12, 28, ScreenW-12, ScreenH-10) vector.DrawFilledRect(screen, float32(panel.Min.X), float32(panel.Min.Y), float32(panel.Dx()), float32(panel.Dy()), colPanel, false) vector.DrawFilledRect(screen, float32(panel.Min.X), float32(panel.Min.Y), float32(panel.Dx()), 3, colKnown, false) switch m := r.Mini.(type) { case *minigame.TopUp: s.drawTopUp(a, screen, m, panel) case *minigame.Ticket: s.drawTicket(a, screen, m, panel) case *minigame.Crowd: s.drawCrowd(a, screen, m, panel) case *minigame.Ride: s.drawRide(a, screen, m, panel) case *minigame.CarPick: s.drawCarPick(a, screen, m, panel) } } // drawCarPick: vagnarna i rad, rosa för kvinnovagn, figurer för trängsel, // kömarkeringar under, och en fas där folk kliver av först. func (s *LevelScene) drawCarPick(a *Assets, screen *ebiten.Image, m *minigame.CarPick, p image.Rectangle) { x, y := p.Min.X+8, p.Min.Y+8 a.DrawBig(screen, "乗車", x, y, colKnown) a.Draw(screen, a.T("mini.car.title"), x+36, y+2, colText) y += 24 if m.Alighting > 0 { a.Draw(screen, a.T("mini.car.alighting"), x, y, colWarn) w := float32(p.Dx() - 16) vector.DrawFilledRect(screen, float32(x), float32(y+LineH+2), w, 4, colBar, false) vector.DrawFilledRect(screen, float32(x), float32(y+LineH+2), w*float32(1-m.Alighting/minigame.AlightSec), 4, colKnown, false) } else { a.Draw(screen, a.T("mini.car.pick"), x, y, colDim) } y += LineH + 12 carW := min(64, (p.Dx()-16)/max(1, len(m.Cars))-4) for i, c := range m.Cars { cx := x + i*(carW+4) body := color.RGBA{230, 234, 240, 255} if c.WomenOnly { body = color.RGBA{255, 160, 205, 255} } vector.DrawFilledRect(screen, float32(cx), float32(y), float32(carW), 30, body, false) vector.DrawFilledRect(screen, float32(cx), float32(y+30), float32(carW), 3, colBad, false) if i == m.Cursor && m.Alighting <= 0 { vector.DrawFilledRect(screen, float32(cx), float32(y-4), float32(carW), 2, colKnown, false) } a.Draw(screen, fmt.Sprintf("%d%s", c.Index, a.T("hud.car_suffix_ja")), cx+3, y+2, colPanel) // figurer = trängsel for k := 0; k <= c.Crowd; k++ { px := cx + 4 + k*7 vector.DrawFilledRect(screen, float32(px), float32(y+16), 4, 10, color.RGBA{60, 70, 100, 255}, false) vector.DrawFilledRect(screen, float32(px), float32(y+13), 4, 3, color.RGBA{240, 200, 160, 255}, false) } if c.WomenOnly { a.Draw(screen, "女性専用", cx+2, y+34, color.RGBA{255, 150, 200, 255}) } else { a.Draw(screen, a.T("car.crowd."+minigame.CrowdNames[c.Crowd]), cx+2, y+34, colDim) } // kömarkering under vagnen vector.DrawFilledRect(screen, float32(cx+carW/2-6), float32(y+48), 12, 2, colWarn, false) vector.DrawFilledRect(screen, float32(cx+carW/2-1), float32(y+50), 2, 4, colWarn, false) } if s.run.Party.HasMen() { a.Draw(screen, a.T("mini.car.men"), x, p.Max.Y-28, colWarn) } a.Draw(screen, a.T("mini.car.hint"), x, p.Max.Y-16, colDim) } // jp ritar japansk text sedd genom spelarens kunskap (kända ord i gult). func (s *LevelScene) jp(a *Assets, screen *ebiten.Image, text string, x, y int) { rd := lang.Render(a.Dict, s.run.Know, text, UILang) a.drawLines(screen, []line{shownLine(rd.Original, colText, false)}, x, y) } func (s *LevelScene) drawTopUp(a *Assets, screen *ebiten.Image, m *minigame.TopUp, p image.Rectangle) { x, y := p.Min.X+8, p.Min.Y+8 a.DrawBig(screen, "チャージ", x, y, colKnown) a.Draw(screen, a.T("mini.topup.title"), x+60, y+2, colText) y += 20 switch m.Stage { case minigame.TopUpPickMember: a.Draw(screen, a.T("mini.topup.member"), x, y, colDim) y += LineH + 4 for i, name := range m.Members { col := colText if i == m.Cursor { vector.DrawFilledRect(screen, float32(x-2), float32(y+i*LineH-1), 200, LineH, colBar, false) col = colKnown } bal := s.run.Wallet.Card if i > 0 { bal = s.run.Party.Members[i].Card } a.Draw(screen, fmt.Sprintf("%-8s %5d¥", name, bal), x, y+i*LineH, col) } case minigame.TopUpPickButton: a.Draw(screen, a.T("mini.topup.pick"), x, y, colDim) y += LineH + 4 for i, b := range m.Buttons { bx, by := x+(i%2)*140, y+(i/2)*26 col := colBar if i == m.Cursor { col = color.RGBA{90, 105, 136, 255} } vector.DrawFilledRect(screen, float32(bx), float32(by), 132, 22, col, false) s.jp(a, screen, b, bx+6, by+5) } if m.Penalty > 0 { a.Draw(screen, "ピッ! "+a.T("mini.topup.wrong"), x, y+56, colBad) } case minigame.TopUpPickAmount: a.Draw(screen, a.T("mini.topup.amount"), x, y, colDim) y += LineH + 4 for i, amt := range m.Amounts { col := colText if i == m.Cursor { vector.DrawFilledRect(screen, float32(x-2), float32(y+i*LineH-1), 120, LineH, colBar, false) col = colKnown } a.Draw(screen, fmt.Sprintf("%d円 (%d¥)", amt, amt), x, y+i*LineH, col) } case minigame.TopUpFeed: a.Draw(screen, fmt.Sprintf(a.T("mini.topup.feed"), m.Amount), x, y, colText) y += LineH + 8 vector.DrawFilledRect(screen, float32(x), float32(y), float32(p.Dx()-16), 8, colBar, false) vector.DrawFilledRect(screen, float32(x), float32(y), float32(float64(p.Dx()-16)*m.Fill), 8, colGood, false) } a.Draw(screen, a.T("mini.cancel"), x, p.Max.Y-16, colDim) } func (s *LevelScene) drawTicket(a *Assets, screen *ebiten.Image, m *minigame.Ticket, p image.Rectangle) { x, y := p.Min.X+8, p.Min.Y+8 a.DrawBig(screen, "きっぷ", x, y, colKnown) a.Draw(screen, a.T("mini.ticket.title"), x+48, y+2, colText) y += 20 if m.Stage == minigame.TicketPickSeat { s.drawSeats(a, screen, m, p, x, y) return } a.Draw(screen, fmt.Sprintf(a.T("mini.ticket.hint"), s.run.Level.Destination.Get("romaji"), m.Cash), x, y, colDim) y += LineH + 4 for i, o := range m.Options { ry := y + i*LineH col := colText if i == m.Cursor { vector.DrawFilledRect(screen, float32(x-2), float32(ry-1), float32(p.Dx()-12), LineH, colBar, false) } if o.Fare > m.Cash { col = colDim } s.jp(a, screen, o.Dest, x, ry) fare := fmt.Sprintf("%d円", o.Fare) a.Draw(screen, fare, p.Max.X-10-a.Width(fare), ry, col) } a.Draw(screen, a.T("mini.cancel"), x, p.Max.Y-16, colDim) } func (s *LevelScene) drawCrowd(a *Assets, screen *ebiten.Image, m *minigame.Crowd, p image.Rectangle) { x, y := p.Min.X+8, p.Min.Y+8 a.DrawBig(screen, "満員", x, y, colBad) a.Draw(screen, a.T("mini.crowd.title"), x+36, y+2, colText) y += 24 a.Draw(screen, fmt.Sprintf(a.T("mini.crowd.hint"), m.MaxTries-m.Attempts), x, y, colDim) y += LineH + 10 w := float32(p.Dx() - 16) vector.DrawFilledRect(screen, float32(x), float32(y), w, 14, colBar, false) vector.DrawFilledRect(screen, float32(x)+w*float32(m.ZoneLo), float32(y), w*float32(m.ZoneHi-m.ZoneLo), 14, colGood, false) vector.DrawFilledRect(screen, float32(x)+w*float32(m.Pos)-1, float32(y)-3, 3, 20, colText, false) } func (s *LevelScene) drawRide(a *Assets, screen *ebiten.Image, m *minigame.Ride, p image.Rectangle) { x, y := p.Min.X+8, p.Min.Y+8 a.DrawBig(screen, "次は", x, y, colDim) a.Draw(screen, a.T("mini.ride.title"), x+36, y+2, colText) y += 22 cur := m.Current() a.DrawBig(screen, cur, x, y, colText) rd := lang.Render(a.Dict, s.run.Know, cur, UILang) a.drawLines(screen, []line{shownLine(rd.Partial, colText, false)}, x+a.Width(cur)*12/10+12, y+2) lvl := s.run.Level if lang.ShowEnglish(lvl.ID, m.Index, 77, lvl.Difficulty) { if e := a.Dict.Lookup(cur); e != nil { a.Draw(screen, e.Romaji, x, y+16, colEnglish) } } y += 34 // Linjen: en prick per station, den aktuella större. for i := range m.Stops { cx := x + 6 + i*22 col := colDim if i == m.Index { col = colText } if i < m.Index { col = colBar } vector.DrawFilledRect(screen, float32(cx), float32(y+3), 14, 2, colBar, false) vector.DrawFilledRect(screen, float32(cx-3), float32(y), 6, 8, col, false) } y += 14 vector.DrawFilledRect(screen, float32(x), float32(y), float32(p.Dx()-16), 3, colBar, false) vector.DrawFilledRect(screen, float32(x), float32(y), float32(float64(p.Dx()-16)*(1-m.Timer/m.StopSec)), 3, colKnown, false) hint := a.T("mini.ride.hint") if dest := lvl.Destination.Get("romaji"); dest != "" { hint = strings.Replace(hint, "%s", dest, 1) } if m.NeedsLetPass() { a.Draw(screen, a.T("mini.ride.let_pass"), x, p.Max.Y-30, colBad) } else if m.Crowd > 0 { a.Draw(screen, fmt.Sprintf("%s: %s", a.T("mini.ride.crowd"), a.T("car.crowd."+minigame.CrowdNames[m.Crowd])), x, p.Max.Y-30, colDim) } a.Draw(screen, hint, x, p.Max.Y-16, colGood) } // prefText beskriver ett önskemål. func prefText(a *Assets, m *minigame.Ticket, p minigame.SeatPref) string { switch p.Kind { case minigame.PrefWindow: return a.T("seat.window") case minigame.PrefAisle: return a.T("seat.aisle") case minigame.PrefLeft: return a.T("seat.left") case minigame.PrefRight: return a.T("seat.right") default: return fmt.Sprintf(a.T("seat.with"), m.Members[p.With]) } } // drawSeats ritar vagnen: tre rader, fönster V | gång | fönster H, och // sällskapets önskemål till höger. func (s *LevelScene) drawSeats(a *Assets, screen *ebiten.Image, m *minigame.Ticket, p image.Rectangle, x, y int) { a.Draw(screen, fmt.Sprintf(a.T("mini.seat.hint"), m.Members[m.Current]), x, y, colDim) y += LineH + 4 const sw, sh, gap = 22, 18, 10 a.Draw(screen, a.T("seat.front"), x+2, y-2, colDim) y += 10 for row := 0; row < minigame.SeatRows; row++ { for col := 0; col < minigame.SeatCols; col++ { sx := x + col*(sw+2) if col >= 2 { sx += gap } sy := y + row*(sh+3) seat := minigame.Seat{Row: row, Col: col} bg := colBar if m.SeatTaken(seat) { bg = color.RGBA{90, 105, 136, 255} } vector.DrawFilledRect(screen, float32(sx), float32(sy), sw, sh, bg, false) if seat == m.Seat { vector.DrawFilledRect(screen, float32(sx), float32(sy), sw, 2, colKnown, false) vector.DrawFilledRect(screen, float32(sx), float32(sy+sh-2), sw, 2, colKnown, false) } for i, as := range m.Assigned { if as == seat { a.Draw(screen, string([]rune(m.Members[i])[:1]), sx+8, sy+3, colText) } } } } // Önskemål. px := x + 4*(sw+2) + gap + 14 a.Draw(screen, a.T("mini.seat.prefs"), px, y-10, colDim) for i, pr := range m.Prefs { col := colText if pr.Member == m.Current { col = colKnown } a.Draw(screen, m.Members[pr.Member]+": "+prefText(a, m, pr), px, y+i*LineH, col) } a.Draw(screen, a.T("mini.cancel"), x, p.Max.Y-16, colDim) }