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>
122 lines
3.5 KiB
Go
122 lines
3.5 KiB
Go
package sim
|
||
|
||
// Stats är spelarens multiplikatorer: 1,0 = normalt. Debuffar (och i M8
|
||
// items/skills) ändrar dem.
|
||
type Stats [5]float64
|
||
|
||
// Statindex.
|
||
const (
|
||
StatSpeed = iota // gångfart
|
||
StatThirst // hur fort dryckesmätaren sjunker (>1 = fortare)
|
||
StatFocus // chans att lära av skyltar, hjälptid (>1 = bättre)
|
||
StatToilet // hur fort toamätaren sjunker (>1 = fortare)
|
||
StatLuck // omskanning, trängsel (>1 = bättre; items i M8)
|
||
)
|
||
|
||
// DefaultStats är allt på 1,0.
|
||
func DefaultStats() Stats { return Stats{1, 1, 1, 1, 1} }
|
||
|
||
// DebuffKind är en typ av debuff (textnyckel: "debuff.<kind>").
|
||
type DebuffKind string
|
||
|
||
// Debuffar. De fyra första uppstår av spelandet (M7), resten delas ut av
|
||
// sidouppdrag (M10) och varar 1–4 banor.
|
||
const (
|
||
SoreFeet DebuffKind = "sore_feet" // många steg
|
||
Tired DebuffKind = "tired" // lång bana
|
||
Hangry DebuffKind = "hangry" // tom matmätare
|
||
FoodPoisoning DebuffKind = "food_poisoning" // otur vid kiosken
|
||
DOMS DebuffKind = "doms" // träningsvärk
|
||
Blisters DebuffKind = "blisters" // skoskav
|
||
HeavyBag DebuffKind = "heavy_bag" // köpt något tungt/stort
|
||
Stuffed DebuffKind = "stuffed" // proppmätt
|
||
Sweaty DebuffKind = "sweaty" // svettig och varm
|
||
)
|
||
|
||
// DebuffEffects är vad varje debuff gör med statsen.
|
||
var DebuffEffects = map[DebuffKind]Stats{
|
||
SoreFeet: {0.85, 1, 1, 1, 1},
|
||
Tired: {0.9, 1, 0.8, 1, 1},
|
||
Hangry: {1, 1.2, 0.7, 1, 1},
|
||
FoodPoisoning: {1, 1, 1, 2.0, 1},
|
||
DOMS: {0.9, 1, 1, 1, 1},
|
||
Blisters: {0.8, 1, 1, 1, 1},
|
||
HeavyBag: {0.9, 1.2, 1, 1, 1},
|
||
Stuffed: {0.9, 1, 1, 1.3, 1},
|
||
Sweaty: {1, 1.5, 0.9, 1, 1},
|
||
}
|
||
|
||
// Utlösare för de spelgenererade debuffarna.
|
||
const (
|
||
SoreFeetSteps = 150
|
||
TiredSec = 300.0
|
||
FoodPoisonChance = 0.05
|
||
FoodPoisonLevels = 2
|
||
)
|
||
|
||
// Debuff är en aktiv debuff med återstående antal banor.
|
||
type Debuff struct {
|
||
Kind DebuffKind `json:"kind"`
|
||
LevelsLeft int `json:"levelsLeft"`
|
||
}
|
||
|
||
// CombinedStats multiplicerar ihop effekterna av alla debuffar.
|
||
func CombinedStats(debuffs []Debuff) Stats {
|
||
st := DefaultStats()
|
||
for _, d := range debuffs {
|
||
if e, ok := DebuffEffects[d.Kind]; ok {
|
||
for i := range st {
|
||
st[i] *= e[i]
|
||
}
|
||
}
|
||
}
|
||
return st
|
||
}
|
||
|
||
// Profile är det som följer spelaren mellan banor och inte hör till
|
||
// kunskap eller plånbok: aktiva debuffar (items och skills i M8).
|
||
type Profile struct {
|
||
Debuffs []Debuff `json:"debuffs"`
|
||
}
|
||
|
||
// Add lägger till en debuff; finns den redan förlängs den till det längsta.
|
||
func (p *Profile) Add(kind DebuffKind, levels int) {
|
||
for i := range p.Debuffs {
|
||
if p.Debuffs[i].Kind == kind {
|
||
p.Debuffs[i].LevelsLeft = max(p.Debuffs[i].LevelsLeft, levels)
|
||
return
|
||
}
|
||
}
|
||
p.Debuffs = append(p.Debuffs, Debuff{Kind: kind, LevelsLeft: levels})
|
||
}
|
||
|
||
// Has rapporterar om debuffen är aktiv.
|
||
func (p *Profile) Has(kind DebuffKind) bool {
|
||
for _, d := range p.Debuffs {
|
||
if d.Kind == kind {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// EndLevel räknar ned alla debuffar en bana och tar bort de som gått ut.
|
||
// Returnerar de som försvann.
|
||
func (p *Profile) EndLevel() []DebuffKind {
|
||
var expired []DebuffKind
|
||
kept := p.Debuffs[:0]
|
||
for _, d := range p.Debuffs {
|
||
d.LevelsLeft--
|
||
if d.LevelsLeft <= 0 {
|
||
expired = append(expired, d.Kind)
|
||
continue
|
||
}
|
||
kept = append(kept, d)
|
||
}
|
||
p.Debuffs = kept
|
||
return expired
|
||
}
|
||
|
||
// Stats är de sammanlagda multiplikatorerna just nu.
|
||
func (p *Profile) Stats() Stats { return CombinedStats(p.Debuffs) }
|