{ "modVersion": "0.16.0", "title": "IdleSpectator interest scoring model", "updated": "2026-07-15", "note": "Edit weights below — InterestScoringConfig loads this file at mod start. Docs sections are human-facing only (ignored by Unity JsonUtility).", "weights": { "charWeightEventLed": 0.18, "charWeightCharacterLed": 0.55, "visualMultiplier": 12, "noveltyMultiplier": 4, "cutInMargin": 35, "stickyCutInMargin": 50, "rotateSlack": 12, "fillScoreMax": 55, "noticeScoreMin": 70, "hotScoreMin": 110, "dwellFill": 8, "dwellNormal": 15, "dwellHot": 15, "fillRotateSeconds": 8, "minCameraDwell": 15, "maxWatchMoment": 15, "maxWatchStatus": 30, "maxWatchGrief": 45, "maxWatchCombat": 60, "maxWatchEpic": 50, "massFighterThreshold": 4, "massBaseBonus": 28, "massPerExtraFighter": 4, "massExtraCap": 24, "duelMaxFighters": 2, "duelNotablePairBonus": 55, "duelSingleNotableBonus": 15, "duelAnonymousPenalty": -12, "skirmishMinFighters": 3, "notableSkirmishPer": 12, "notableSkirmishCap": 35, "clusterDeathWeight": 10, "clusterFighterWeight": 14, "clusterNotableWeight": 22, "clusterMassBonus": 28, "clusterDuelPenalty": -8, "clusterNotableDuelBonus": 45, "clusterSingleNotableBonus": 12, "preferNotableDuelMargin": 15, "battleCharPerNotable": 18, "metaFavorite": 22, "metaKing": 18, "metaLeader": 14, "metaRenownDivisor": 120, "metaRenownCap": 8, "metaKillsFactor": 0.35, "metaKillsCap": 12, "metaLevelFactor": 0.4, "metaLevelCap": 10, "speciesSingletonBonus": 14, "speciesRareBonus": 8, "speciesRareMaxPop": 5, "notableKills": 40, "notableRenown": 200, "extremeNotableKills": 80, "extremeNotableRenown": 400, "scannerAttackBonus": 40, "scannerHotBonus": 18, "scannerWarmBonus": 8, "scannerSpectacleBonus": 12, "scannerActionKillsFactor": 0.2, "scannerActionKillsCap": 10, "scannerWarriorBonus": 6, "scannerSingletonBonus": 8, "scannerCharKillsFactor": 0.15, "scannerCharKillsCap": 8, "scannerCharRenownDivisor": 120, "scannerCharRenownCap": 8, "scannerColdActionMul": 0.25, "scannerColdCharMul": 0.35, "scannerRankCharWeight": 0.18, "characterLedEventMul": 0.35, "eventLedTarget": 0.7, "mixWindow": 20, "hardCooldownSeconds": 12, "enrichTopK": 8, "metaCacheTtl": 2 }, "sources": [ "IdleSpectator/scoring-model.json", "IdleSpectator/InterestScoringConfig.cs", "IdleSpectator/InterestScoring.cs", "IdleSpectator/WorldActivityScanner.cs", "IdleSpectator/InterestDirector.cs", "IdleSpectator/InterestVariety.cs", "IdleSpectator/Events/Feeds/InterestFeeds.cs" ], "designIntent": [ "Actions matter much more than characters.", "Same action intensity → more important character wins (tie-break).", "Multi-person fights outrank anonymous 1v1s.", "Extremely notable 1v1s (kings/favorites) can beat nameless melees.", "TotalScore is the sole ranking signal; typed rules (combat vs vignette) gate interrupts.", "InterestTier was removed - WorldLog seeds EventStrength; director uses cutInMargin / rotateSlack / dwell bands." ], "pipeline": [ { "id": "feeds", "name": "Feeds → candidates", "detail": "WorldLog, happiness, scanner, battle clusters, harness injects upsert InterestCandidate with EventStrength, CharacterSignificance, ParticipantCount, NotableParticipantCount (no Urgency tier)." }, { "id": "variety", "name": "Variety pick", "detail": "InterestVariety.Pick soft-splits EventLed vs CharacterLed (~70/30), applies novelty penalties, RecalcTotal, EnrichTopK, then probabilistic top-3 by TotalScore." }, { "id": "director", "name": "Director switch", "detail": "InterestDirector.CanSwitchTo gates camera changes via score margins (cutInMargin/rotateSlack), settle grace, and typed combat-vs-vignette cut-ins." }, { "id": "camera", "name": "Camera + UI", "detail": "CameraDirector.Watch + dossier story beats from action verbs (Fighting/War/Grief). Fill/ambient with no sharper beat leaves the reason row empty." } ], "totalScore": { "file": "InterestScoring.RecalcTotal", "formula": "EventStrength + scaleBonus + CharacterSignificance * charWeight + VisualConfidence * visualMultiplier + Novelty * noveltyMultiplier", "charWeight": { "EventLed": "weights.charWeightEventLed", "CharacterLed": "weights.charWeightCharacterLed" }, "scaleBonus": "See weights.mass* / duel* / notableSkirmish*", "directorPolicy": { "cutInMargin": "weights.cutInMargin", "rotateSlack": "weights.rotateSlack", "fillScoreMax": "weights.fillScoreMax", "dwell": "weights.dwellFill / dwellNormal / dwellHot" } }, "workedExamples": [ { "id": "fight_king_vs_peasant", "scenario": "Same evt=70 Action fight; king char=40 notables=1 vs peasant char=5 notables=0", "winner": "FightKing", "harnessAssert": "ap12 interest_score_order" }, { "id": "melee_vs_anon_duel", "scenario": "Same evt=55; 8-fighter anon melee vs 2-fighter anon duel", "winner": "Melee", "harnessAssert": "ap23 interest_score_order" }, { "id": "king_duel_vs_nameless_melee", "scenario": "Same evt=55; king duel notables=2 vs 8-fighter nameless melee", "winner": "DuelKings", "harnessAssert": "ap33 interest_score_order" } ], "tweakKnobs": [ { "id": "charWeightEventLed", "effect": "How much who-you-are matters on event scenes" }, { "id": "massBaseBonus", "effect": "How strongly mass fights beat thin duels" }, { "id": "duelNotablePairBonus", "effect": "How easily king duels outrank nameless melees" }, { "id": "duelAnonymousPenalty", "effect": "Penalty on anonymous 1v1" }, { "id": "clusterFighterWeight", "effect": "Changes battle EventStrength seed" }, { "id": "eventLedTarget", "effect": "Soft mix toward actions vs character vignettes" } ] }