M1: rörelse, kollision, kamera, scener, tileset, skärmdumpar, CI
Some checks failed
release / build-release (push) Failing after 27s

- internal/sim: rutbaserad Mover (4 riktningar, riktningsbyte vid rutgräns,
  bildrute-exakt ankomst) och Camera (följer, klampar, centrerar små kartor),
  med tester
- internal/game: scener meny -> bana -> resultat, tangentbord + gamepad,
  tileset och spelaranimation, HUD, skriptläge (-script) för deterministiska
  skärmdumpar och tester
- tools/genart genererar placeholder-grafik (tiles.png, player.png);
  tools/shots tar doc/img/*.png som README och wiki visar
- bana 002-shinjuku (33x21) för kamerascroll; level.List/LoadFile/TrainAt
- .gitea/workflows/release.yaml: vet + test + linux/windows-zippar till
  rullande latest på brasse-linux01-runnern (linux-amd64)
- docs: roadmap M1 klar, tester, plan, README med bilder och styrning

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 19:16:53 +02:00
parent ae50d444a4
commit 4ae7dba291
34 changed files with 1730 additions and 124 deletions

38
internal/sim/camera.go Normal file
View File

@@ -0,0 +1,38 @@
package sim
// Camera är en vy över världen som följer en punkt men aldrig visar utanför
// kartan. Är världen mindre än vyn centreras den i stället.
type Camera struct {
ViewW, ViewH float64
WorldW, WorldH float64
X, Y float64 // vyns övre vänstra hörn i världskoordinater
}
// NewCamera skapar en kamera för en värld av given storlek (pixlar).
func NewCamera(viewW, viewH, worldW, worldH float64) *Camera {
c := &Camera{ViewW: viewW, ViewH: viewH, WorldW: worldW, WorldH: worldH}
c.Follow(worldW/2, worldH/2)
return c
}
// Follow centrerar vyn på (cx, cy) och klampar mot världens kanter.
func (c *Camera) Follow(cx, cy float64) {
c.X = clampAxis(cx-c.ViewW/2, c.ViewW, c.WorldW)
c.Y = clampAxis(cy-c.ViewH/2, c.ViewH, c.WorldH)
}
// Offset returnerar översättningen världs- -> skärmkoordinater.
func (c *Camera) Offset() (ox, oy float64) { return -c.X, -c.Y }
func clampAxis(pos, view, world float64) float64 {
if world <= view {
return -(view - world) / 2 // centrera en liten värld
}
if pos < 0 {
return 0
}
if max := world - view; pos > max {
return max
}
return pos
}