All checks were successful
release / build-release (push) Successful in 1m34s
- level: Point har Z; banor kan ha floors (lika stora rutnät med namn), gamla grid-banor läses som en våning; trappor U/D på samma x,y en våning upp/ner valideras parvis; Neighbors ger grannar över våningar så att Path, lösbarhet, spärregel och Unreachable fungerar; skyltar har z - sim: att kliva på en trappa flyttar spelaren till andra änden (FloorChanges); dimman är per våning och avslöjar trappans andra ände; tåg hörs/syns bara på egen våning - game: bara spelarens våning ritas (följare på andra våningar döljs), HUD och minikarta visar våningens namn, trappgrafik i genart och färg på minikartan - bana 004 Tokyo (svårighet 3, tre i sällskapet): start under mark på B1, trappa upp till 1F med perronger; generatorn gör våningar med trappor som flaskhalsar; ordboken utökad - testbotar och tools/shots förstår trappor; två våningsbilder - docs/README/wiki uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
84 lines
2.8 KiB
Go
84 lines
2.8 KiB
Go
package sim
|
||
|
||
import "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
||
|
||
// Mover är något som rör sig ruta för ruta i labyrinten (spelaren, senare
|
||
// följarna). Rörelsen är mjukt interpolerad mellan rutor men beslut om
|
||
// riktning fattas bara vid rutgränser, som i klassiska labyrintspel.
|
||
type Mover struct {
|
||
Tile level.Point // rutan vi står på / lämnar
|
||
Target level.Point // rutan vi är på väg till (== Tile när vi står stilla)
|
||
Progress float64 // 0..1 längs Tile -> Target
|
||
Facing Dir // senaste riktning (för sprite), aldrig None
|
||
Speed float64 // rutor per sekund
|
||
}
|
||
|
||
// NewMover skapar en stillastående Mover på en ruta.
|
||
func NewMover(start level.Point, speed float64) *Mover {
|
||
return &Mover{Tile: start, Target: start, Facing: Down, Speed: speed}
|
||
}
|
||
|
||
// Moving rapporterar om vi är mellan två rutor.
|
||
func (m *Mover) Moving() bool { return m.Target != m.Tile }
|
||
|
||
// maxTilesPerStep skyddar mot oändliga loopar vid absurt stora dt.
|
||
const maxTilesPerStep = 16
|
||
|
||
// Step för rörelsen framåt dt sekunder med önskad riktning want (None = ingen
|
||
// input). canEnter avgör om en ruta får beträdas. Returnerar antalet rutor
|
||
// som nåddes under steget (normalt 0 eller 1).
|
||
//
|
||
// Regler: står vi stilla och får input startar vi mot grannrutan om den är
|
||
// passerbar, annars vänder vi oss bara åt det hållet. Är vi på väg fortsätter
|
||
// vi till rutgränsen; väl där fortsätter vi direkt i önskad riktning om den är
|
||
// fri (så att hållen tangent ger jämn fart), annars stannar vi.
|
||
func (m *Mover) Step(dt float64, want Dir, canEnter func(level.Point) bool) int {
|
||
arrived := 0
|
||
remaining := m.Speed * dt // i rutor
|
||
for i := 0; i < maxTilesPerStep; i++ {
|
||
if remaining <= eps {
|
||
return arrived
|
||
}
|
||
if !m.Moving() {
|
||
if want == None {
|
||
return arrived
|
||
}
|
||
m.Facing = want
|
||
dx, dy := want.Delta()
|
||
next := level.Point{X: m.Tile.X + dx, Y: m.Tile.Y + dy, Z: m.Tile.Z}
|
||
if !canEnter(next) {
|
||
return arrived
|
||
}
|
||
m.Target = next
|
||
m.Progress = 0
|
||
}
|
||
need := 1 - m.Progress
|
||
if remaining+eps < need {
|
||
m.Progress += remaining
|
||
return arrived
|
||
}
|
||
// Framme vid rutan. eps gör att 10 × 0.1 räknas som exakt 1 ruta trots
|
||
// flyttalsavrundning, så att rörelsen blir deterministisk per bildruta.
|
||
remaining = max(0, remaining-need)
|
||
m.Tile = m.Target
|
||
m.Progress = 0
|
||
arrived++
|
||
}
|
||
return arrived
|
||
}
|
||
|
||
// eps är toleransen för flyttalsavrundning i rutor.
|
||
const eps = 1e-9
|
||
|
||
// WorldPos returnerar den interpolerade positionen i pixlar (övre vänstra
|
||
// hörnet av figuren) givet rutstorleken.
|
||
func (m *Mover) WorldPos(tileSize float64) (x, y float64) {
|
||
x = float64(m.Tile.X) * tileSize
|
||
y = float64(m.Tile.Y) * tileSize
|
||
if m.Moving() {
|
||
x += float64(m.Target.X-m.Tile.X) * tileSize * m.Progress
|
||
y += float64(m.Target.Y-m.Tile.Y) * tileSize * m.Progress
|
||
}
|
||
return x, y
|
||
}
|