M7: svårighet, tid, poäng, debuffar, egna kort och sittplatsval
All checks were successful
release / build-release (push) Successful in 1m37s

- sim/stats.go: Stats (fart, törst, fokus, toalett, tur), debuffar som
  multiplikatorer, Profile som följer med mellan banor och räknar ned
- sim.Run: tidsgräns och maxantal missade tåg förlorar banan; poängformel
  (tid mot par, pengar, sällskapets mätare, utforskat, avdrag); debuffar
  tjänas av många steg, lång tid, tom matmätare och seedad otur vid
  kiosken; stats påverkar fart, mätartakter, läsning och hjälptid
- egna kort (individualCards): Member.Card, spärren kräver allas saldo och
  drar från alla, automaten frågar vems kort, biljetter köps åt alla
- sittplatsval (seatSelection): Ticket får ett sittplatssteg med seedade
  önskemål (fönster/gång/sida/bredvid); missnöjda ger mätar- och poängavdrag
- level: maxMissedTrains, individualCards, seatSelection; bana 003 Shibuya
  (svårighet 3, fyra i sällskapet) och 002 med tidsgräns och sittplatser;
  ordboken utökad; generatorn är spec-driven
- game: tid kvar/missade i HUD, debuffar i meny/HUD/resultat, poäng och
  förlustorsaker på resultatet, vyer för personval och vagn, skript-pick,
  flaggan -debuffs; HUD och resultat komprimerade för fyra i sällskapet
- plan (Björn 2026-10-24): flera våningar med trappor som M9a, egna kort
  och sittplatsval som svårighetsinställningar i M7 – i spelbeskrivning,
  roadmap, testplan
- docs/README/wiki med sittplatsbilden

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 10:18:17 +02:00
parent 9fc7e94074
commit b2995d4ca9
44 changed files with 1567 additions and 177 deletions

View File

@@ -48,8 +48,16 @@ type Member struct {
Meters Meters
Urge Urge
UrgeAt float64 // när behovet uppstod
Card int // eget tågkort (yen) när banan kräver egna kort; spelaren använder plånboken
}
// MemberCardYen är vad följarnas egna kort har från start (lite – de får
// fyllas på).
const MemberCardYen = 500
// Rates skalar hur fort mätarna sjunker (debuffar/stats).
type Rates struct{ Thirst, Toilet float64 }
// PartyNames är namnen i den ordning de följer med.
var PartyNames = []string{"Du", "Anna", "Ville", "Sara", "Omar"}
@@ -72,6 +80,7 @@ const (
// Party är sällskapet – lämmeltåget – med mätare, behov och följarnas spår.
type Party struct {
Members []*Member
Rates Rates
trail []level.Point // ledarens rutor, senaste sist
rand *rand.Rand
nextEventAt float64
@@ -83,9 +92,9 @@ type Party struct {
// sällskapet kommer från seed.
func NewParty(n int, start level.Point, seed rng.Seed) *Party {
n = max(1, min(n, len(PartyNames)))
p := &Party{rand: seed.New()}
p := &Party{rand: seed.New(), Rates: Rates{1, 1}}
for i := 0; i < n; i++ {
p.Members = append(p.Members, &Member{Name: PartyNames[i], Meters: Meters{100, 100, 100}})
p.Members = append(p.Members, &Member{Name: PartyNames[i], Meters: Meters{100, 100, 100}, Card: MemberCardYen})
}
p.trail = []level.Point{start}
p.nextEventAt = p.interval() * (0.5 + p.rand.Float64())
@@ -106,8 +115,8 @@ func (p *Party) interval() float64 { return BaseEventSec / float64(p.Size()) }
func (p *Party) Advance(dt, t float64) {
for _, m := range p.Members {
m.Meters.Food = max(0, m.Meters.Food-FoodPerSec*dt)
m.Meters.Drink = max(0, m.Meters.Drink-DrinkPerSec*dt)
m.Meters.Toilet = max(0, m.Meters.Toilet-ToiletPerSec*dt)
m.Meters.Drink = max(0, m.Meters.Drink-DrinkPerSec*p.Rates.Thirst*dt)
m.Meters.Toilet = max(0, m.Meters.Toilet-ToiletPerSec*p.Rates.Toilet*dt)
if m.Urge == Distracted && t-m.UrgeAt >= DistractionSec {
m.Urge = NoUrge
}
@@ -185,6 +194,24 @@ func (p *Party) SpeedFactor() float64 {
return f
}
// Names är medlemmarnas namn i ordning.
func (p *Party) Names() []string {
out := make([]string, len(p.Members))
for i, m := range p.Members {
out[i] = m.Name
}
return out
}
// AvgMeters är medelvärdet av allas lägsta mätare, 0..1 (poängen).
func (p *Party) AvgMeters() float64 {
sum := 0.0
for _, m := range p.Members {
sum += m.Meters.Min()
}
return sum / float64(len(p.Members)) / 100
}
// NeedsToilet är de som har nytta av en toalett just nu.
func (p *Party) NeedsToilet() []*Member {
var out []*Member