package sim import "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level" // FogRadius är hur många steg från spelaren som avslöjas (gångavstånd, så // väggar skymmer – man ser längs gången, inte genom väggen). const FogRadius = 3 // Fog håller reda på vilka rutor spelaren har sett i den här omgången. // Minikartan ritar bara sedda rutor plus riktmärken som alltid syns. type Fog struct { w, h int seen []bool Radius int count int } // NewFog skapar en tom dimma för banan. func NewFog(l *level.Level, radius int) *Fog { return &Fog{w: l.Width(), h: l.Height(), seen: make([]bool, l.Width()*l.Height()), Radius: radius} } func (f *Fog) idx(p level.Point) (int, bool) { if p.X < 0 || p.Y < 0 || p.X >= f.w || p.Y >= f.h { return 0, false } return p.Y*f.w + p.X, true } // Seen rapporterar om rutan har setts. func (f *Fog) Seen(p level.Point) bool { i, ok := f.idx(p) return ok && f.seen[i] } func (f *Fog) mark(p level.Point) { if i, ok := f.idx(p); ok && !f.seen[i] { f.seen[i] = true f.count++ } } // Reveal avslöjar allt inom Radius steg från from via passerbara rutor, // samt väggarna som gränsar till dem (så att gångarnas form syns). func (f *Fog) Reveal(l *level.Level, from level.Point) { dist := map[level.Point]int{from: 0} queue := []level.Point{from} for len(queue) > 0 { p := queue[0] queue = queue[1:] f.mark(p) for dx := -1; dx <= 1; dx++ { for dy := -1; dy <= 1; dy++ { n := level.Point{X: p.X + dx, Y: p.Y + dy} if !l.At(n.X, n.Y).Passable() { f.mark(n) // gränsande vägg } } } if dist[p] >= f.Radius { continue } for _, d := range [...]level.Point{{X: 1}, {X: -1}, {Y: 1}, {Y: -1}} { n := level.Point{X: p.X + d.X, Y: p.Y + d.Y} if _, ok := dist[n]; ok || !l.At(n.X, n.Y).Passable() { continue } dist[n] = dist[p] + 1 queue = append(queue, n) } } } // Landmark är rutor som alltid syns på minikartan: perrongerna (avgångarna), // starten och spärrarna – det man orienterar sig efter. func Landmark(l *level.Level, p level.Point) bool { switch l.At(p.X, p.Y) { case level.Platform, level.WrongPlatform, level.Spawn, level.Gate: return true } return false } // Visible rapporterar om rutan ska ritas på minikartan. func (f *Fog) Visible(l *level.Level, p level.Point) bool { return f.Seen(p) || Landmark(l, p) } // Count är antalet sedda rutor (inkl. väggar). func (f *Fog) Count() int { return f.count } // Explored är andelen passerbara rutor som setts, 0..1. func (f *Fog) Explored(l *level.Level) float64 { total, seen := 0, 0 for y := 0; y < l.Height(); y++ { for x := 0; x < l.Width(); x++ { if !l.At(x, y).Passable() { continue } total++ if f.Seen(level.Point{X: x, Y: y}) { seen++ } } } if total == 0 { return 0 } return float64(seen) / float64(total) }