Tape to Tape Wiki All games ☕ Support me

Run Structure (Campaign / Roguelite Mode)#

How a Tape to Tape campaign run is organised: acts, how the world map is generated, what each node type is, how match opponents are chosen and scaled, and what winning or losing a match does to the run.

Related pages: rewards-and-powerups.md (what you get from nodes), npcs-and-events.md (non-match nodes), squads-difficulty-unlocks.md (squads, difficulty tiers, unlocks).

Integer random ranges are written [min, max) because UnityEngine.Random.Range(int,int) excludes the max.


1. Overview#

  • A run has 3 acts. Each act is a separate world map: a small left-to-right graph of 7–8 layers (columns) that ends in a single Boss node.
  • You move from node to node along the map's links. Matches (Challenge, Elite, Boss) are hockey games. Every other node is an NPC event.
  • You win the run by beating the act 3 boss, the Golfers.
  • You lose the run by losing any Elite or Boss match. Losing a Challenge match does not end the run. You just get no reward for it.
  • Data.RogueData (asset rogueData) is an empty marker ScriptableObject with no fields. The run's state lives in State.RunDataModelV2, saved to run_data.es3. Fields include currentAct, currentFloor, currentMap, squadId, difficultyLevel, gameRule (Timer or Max-goal), freeAgents, departedForwards, forwardInjuries, benchQuestData and isRunFailed.

Source: State.CampaignState, State.RunDataModelV2, State.EndOfGame.EndOfGameFlowController::CheckRunFailed/CheckRunWon, State.EndOfGame.EndOfGameContext.


2. Acts and which map each squad uses#

Each squad (State.RunSquadScriptableObject.maps) lists three STS.Map.MapConfig assets, one per act. Each config points to one DAGG.Generation.Template.MapTemplateData and one DAGG.Config.NodeTypeConfig. MapConfig.mapPositionScale is (0.6, 0.45) everywhere.

MapConfig Act Template NodeTypeConfig Used by
Default_Level1Config 1 NEW ACT 1 - Option B NodeTypeConfig most squads
Default_Level2Config 2 6-NEW ACT 3 NodeTypeConfig most squads
Default_Level3Config 3 6-NEW ACT 3 NodeTypeConfig most squads
Level 1 Config GM 1 GM ACT 1 NodeTypeConfig General Manager squad
Level 2/3 Config GM 2, 3 GM ACT 2,3 NodeTypeConfig General Manager squad
GauntletLevel1/2/3Config 1/2/3 Gauntlet_MapTemplate_Act1_4Elite / Act2_4Elite / Act3_3Elite NodeTypeConfig Gauntlet squad
VanillaLevelsConfig Act 1 1 NEW ACT 1 - Option C VanillaNodeTypeConfig Old School squad
VanillaLevelsConfig 2, 3 (act = -1, any) NEW ACT 3 - Option B VanillaNodeTypeConfig Old School squad
Tutorial_Level1Config 1 Act 1 Campaign Tutorial NodeTypeConfig campaign tutorial run

Default acts 2 and 3 use the same template. They differ only in their opponents (section 6) and in the act-dependent rules in section 3.

Source: STS.Map.MapConfig assets, State.RunSquadScriptableObject.maps, STS.Map.TemplateMapGenerator::GetMap.


3. Map generation#

STS.Map.TemplateMapGenerator::GetMap(act, config) takes the config's template and runs DAGG.Generation.TemplateToGraphConverter::Convert. The steps run in this order:

  1. GenerateLayers / LinkNodes. Copies the template's layers, nodes, fixed types, reward counts, elite group and team IDs, custom weight lists and links.
  2. FlipGraph. With 50% probability (Random.value > 0.5), the graph is mirrored vertically. This is cosmetic only.
  3. HandleOldSchoolNodeReplacements (Old School squad only). Every fixed Nurse or General Manager node is replaced by a uniformly random pick from {Blademaster, Conditioning, Fan Number 1} (the static OldSchoolReplacementTypes array).
  4. HandleGeneralManagerNode. See 3.2.
  5. FillRemainingNodeTypesRandomly. Resolves every "random" (NULL) node. See 3.1.
  6. ReplaceSpecialNodesWithTraining (Stats squad only). Every Blademaster and Chaos node becomes a Conditioning node.
  7. NodeAdapter.AdaptNode (graph → map). Each Conditioning node becomes one of {Skater Training, Stat Training, Team Training}, picked at random from TRAINING_NODE_TYPES = [PlayerTraining, StatTraining, TeamTraining].

The squad flags are read in GetMap with CampaignUtil.IsPlayingSquad:

Flag passed to the converter Squad id(s)
hasGeneralManagerNode = false lines (Double Decker), gauntlet, oldschool
isOldSchoolSquad oldschool
replaceSpecialNodesWithTraining statrewards (Stats squad)
isSoloSquad solo

3.1 Filling random nodes#

Rules applied to every node, in layer order:

  • Acts 2 and 3: any node fixed as Challenge in the template becomes a random node. Challenge games therefore only appear in act 1 on normal maps. On Old School maps they can still be rolled from the vanilla weights in any act.
  • For each random node, these types are never rolled: Elite, Boss, Coach, General Manager and NULL.
  • Act 1 only: Nurse is also excluded unless an Elite node comes earlier on the path to this node (IsPastElite checks ancestors recursively).
  • No repeats next to each other: a node cannot get the same type as a parent (incoming link), a child (outgoing link) or any other already-resolved node in the same layer.
  • Solo squad, act 1: at most one Blademaster on the map. If one already exists, or one gets rolled, Blademaster is excluded from later rolls.
  • Weights: the node's own custom weight list from the template is used when present. Otherwise the act's NodeTypeConfig is used. The pick is weighted (DAGG.Utilities.GetRandomNodeType sums the weights), and excluded or zero-weight types cannot be picked.

NodeTypeConfig default weights. These apply to template nodes with an empty weight list, such as the Gauntlet maps:

Node type NodeTypeConfig (normal) VanillaNodeTypeConfig (Old School)
Conditioning (→ one of the 3 trainings) 25 25
Blademaster 19 19
Fan Number 1 16 16
Chaos 12 12
Nurse 10 0
Challenge 0 18
Coach, GM, Elite, Boss 0 0

3.2 General Manager node#

If the squad allows a GM node (hasGeneralManagerNode) and the template has no fixed GM node, the game rolls Random.value ≤ chance. On success, one random still-unresolved node becomes a General Manager:

Act Chance of an extra GM node
1 10%
2 100%
3 20%

If the squad does not allow a GM node (Double Decker, Gauntlet, Old School), any fixed GM node in the template is turned back into a random node.

Source: DAGG.Generation.TemplateToGraphConverter::Convert, FillRemainingNodeTypesRandomly, HandleGeneralManagerNode (constants 0.1 / 1.0 / 0.2), HandleOldSchoolNodeReplacements, ReplaceSpecialNodesWithTraining, IsPastElite, .cctor; GraphToMapConverter.NodeAdapter::AdaptNode; DAGG.Config.NodeTypeConfig (Odin-serialized dictionary).

3.3 Map templates#

Notation: each layer lists its nodes. [T/R] = talent reward count / relic reward count stored on the node (used by match nodes). {…} = custom weights for a random node. C=Challenge, E=Elite, B=Boss, Cond=Conditioning, BM=Blademaster, F1=Fan Number 1, Ch=Chaos, N=Nurse, GM=General Manager, Co=Coach.

Default act 1 – "NEW ACT 1 - Option B" (8 layers)

Layer Nodes
0 Co
1 C [1/1], C [1/1]
2 E [1/0], E [1/0]
3 random
4 2 × random
5 E [0/1]
6 random {Ch 0.5, BM 1, Cond 1, F1 1}, N
7 B [1/1]

Default acts 2 and 3 – "6-NEW ACT 3" (8 layers)

Layer Nodes
0 Co
1 random {BM 3, Cond 3, F1 3}, random {BM 1, Cond 1, F1 1}
2 E [1/0], E [1/0]
3 random
4 2 × random
5 E [0/1]
6 random {Cond 1, F1 1, BM 1, Ch 1}, N
7 B [1/1]

Each default act has 3 Elite nodes. A path passes through 2 of them: one of the two in layer 2, then the single one in layer 5.

General Manager squad. Same shape as "6-NEW ACT 3", except that layer 0 is a GM node instead of Coach, and layer 6 is GM + Nurse. The opening GM offers 6 free-agent choices in act 1 (gmSelectionCount = 6) and 2 in acts 2–3. Every other GM node offers 1. The elites in layers 2 and 5 are all [0/1].

Old School – "NEW ACT 1 - Option C" (act 1, 7 layers): 2 × C → 2 × E [1/0] → random {Ch 10, F1 1, BM 1} → 2 × random {BM, Cond, F1} → E [0/1] → random {Ch 0.5, BM, Cond, F1} + Chaos → Boss. There is no Coach node. "NEW ACT 3 - Option B" (acts 2–3) is "6-NEW ACT 3" with Chaos instead of Nurse in layer 6. Remaining random nodes use the vanilla weights.

Gauntlet maps: Coach → 4 (acts 1–2) or 3 (act 3) Elites with fixed opponents in a single chain, with unweighted random nodes between them and a Nurse before the boss:

Act Fixed elites in path order
1 Calaveras → Greasy Lettuce → Meatballs → Top Cheese
2 Crusaders → Mountaineers → Princess → Prisoners
3 Shooting Stars → Team Canada → Tycoons

Campaign tutorial: Coach → 2 × Challenge → Blademaster / Conditioning → GM → Elite (Calaveras) / Elite (Meatballs) → Chaos → Fan Number 1 / Nurse → Boss.

Source: DAGG.Generation.Template.MapTemplateData assets (layers[].nodes[]: type, nodeTypeWeights, talentRewardCount, relicRewardCount, gmSelectionCount, eliteGroupId, eliteTeamId, outgoing).


4. Node types#

Node Map tooltip (English) What happens
Challenge "1-goal game for a talent reward" Short match against a generated team (section 6.1). The first goal wins in both game rules. Reward: a talent.
Elite "Play an elite team for a relic and a talent" Match against the act's elite teams. Max-goal: first to 4. Timer: 2 periods. The preview says "Talent + Relic", but every shipped Elite node gives one or the other: [1/0] = talent, [0/1] = relic (see rewards-and-powerups).
Boss "Beat the boss to get a special boss relic and a talent" Act finale. Max-goal: first to 5 (6 vs Golfers). Timer: 3 periods. Reward per UI: talent + boss relic. In code the boss relic count could not be traced (see rewards-and-powerups §2.1).
Coach "Choose a strong relic" NPC (see npcs-and-events). Always the first node of an act on default maps.
Blademaster "Gain or upgrade activatable abilities" NPC
Fan Number 1 "A special upgrade for {skater}" NPC
Chaos "Gain a power-up with a drawback" NPC / event
Nurse "Heal your skaters or let the Butcher operate" NPC
General Manager "Recruit a free agent" NPC
Skater Training "Train {0} to boost their attributes" Conditioning variant
Stat Training "Train {0} to boost this attribute on a skater or the whole team" Conditioning variant
Team Training "Train to boost the attributes of your whole team" Conditioning variant

Internally there are two node-type enums. DAGG.Core.NodeType (templates) has Coach = 8 and GeneralManager = 9. STS.Map.NodeType (runtime) has GeneralManager = 8 and Coach = 9. The other values match: Challenge 0, Elite 1, Boss 2, Conditioning 3, Chaos 4, Nurse 5, Blademaster 6, FanNumber1 7, PlayerTraining 12, TeamTraining 13, StatTraining 14.

Source: STS.Map.*Blueprint assets (m_TooltipTitle/Description, m_GoalsMode, m_TimerMode, m_Reward), localization v2ui/world_map/node/*, v2ui/match_preview/*.


5. Match rules#

5.1 Goal targets (Max-goal game rule)#

State.EndConditions.MaxGoal::CampaignGoalTarget(nodeType, opponent, default):

Match Normal Eliminator squad Gauntlet squad
Challenge default (first goal wins) default default + 1 (the +1 is added for every node type; Gauntlet maps have no Challenge nodes)
Elite 4 5 5
Boss 5 8 6
Boss vs Golfers 6 10 7

The Gauntlet bonus (+1) is applied after the other adjustments. The match preview shows the value as "First to {NUMGOALS}".

5.2 Timer game rule#

  • Preview strings: Challenge "1st goal wins", Elite "2 periods", Boss "3 periods".
  • The shared Data.TimerRuleStateData asset holds numOfPeriod = 3, periodLength = 2.5 (minutes, inferred), hasOvertime = false.
  • The squad's m_SupportedGameModes decides whether Timer and/or Max-goal is available. Eliminator and Speedrun only support Max-goal ([1]).

5.3 Mercy rule#

State.EndConditions.Mercy ends a match early when one team leads by mercyDiff goals ("Have a {0} goal lead" on the scoreboard). The margin is set at runtime and was not found in static data. Winning an Elite by mercy unlocks the "Blowout King" achievement.

5.4 Golfers boss (act 3)#

State.GolferBossTierSystem makes the Golfers transform during the match through tiers 0→3 (tier 3 = "Giant", constant GiantAtTier = 3). Tier depends on the score progress versus the goal target and on the current period (ProgressTier, GatedPeriodFloor, inferred). The giant form can perform an uppercut at face-offs: range 4, launch 25 horizontal / 18 vertical, 2.5 s knockout (GolferTransformationSettings).

Source: State.EndConditions.MaxGoal::CampaignGoalTarget, STS.Map.MatchBlueprint::SetGoalTargetParam, Data.TimerRuleStateData, State.EndConditions.Mercy, State.GolferBossTierSystem, State.GolferTransformationSettings.


6. Opponents and scaling per act#

6.1 Challenge opponents (generated)#

Challenge matches use the challengeTeam base team. Skaters come from 7 eligible forwards (Boring Dude, Drake Shields, Harriet Hockett, Ike Frostberg, Liana Rinkley, Perry Puckman, Theo Iceberg) and the goalie is the Spartan goalie. numSkaterHome/Visitor = 5.

Every opponent forward gets each of 4 stats rolled independently (SetChecking, SetSpeed, SetShotPower plus one more setter, inferred to be Accuracy) by ChallengeMapNode::GetOpponentStrength(act, floor):

x = floor + 2 * (3*act - 3)      // = floor + 6*(act-1)
x <= 3        -> Random.Range(0, 20)
4  <= x <= 6  -> Random.Range(30, 50)
7  <= x <= 9  -> Random.Range(60, 80)
10 <= x <= 15 -> Random.Range(70, 90)
x > 15        -> Random.Range(80, 90)

floor is the node's layer on the act map (inferred). On default maps Challenge nodes only exist in act 1, layer 1, so x = 1 and challenge skaters have stats of 0–19. The higher bands only matter on Old School maps, where Challenge can be rolled later and in acts 2–3.

6.2 Elite and Boss opponents#

Data.CampaignOpponentsConfig holds per-act lists of elites and bosses. Most squads use DefaultCampaignOpponents. The Referee squad uses RefereeCampaignOpponents, which is identical except that the Officials are removed from the act 1 bosses.

Selection (STS.Map.MapObject::SetOpponents / SetElite / GetAvailableElites / SetBoss / GetIntroBoss):

  • Elites: a node with a fixed eliteTeamId (Gauntlet, tutorial) always gets that team. Other elite nodes pick at random among elites that are not already on another node in the same layer and not already used on this map (capacity 1 per team). If nothing is left, the full act list is used.
  • Act 1 boss ("intro boss" rule): the Officials (referee team) are always the act 1 boss until you have beaten them once. After that, the Prisoners are always the act 1 boss until you have beaten them once. Once both have been beaten, the boss is picked from the list. "Beaten" is tracked in your profile across runs (HasBeatenTeam).
  • Acts 2 and 3 have one boss each.

Opponent rosters below are from each Data.TeamData (forward list, goalie and team relics). "Avg stats" is the plain mean of the forwards' Shot Power / Speed / Checking / Accuracy. I computed these myself. They are not the game's OVR formula.

Act Role Team Forwards Avg SP / Spd / Chk / Acc Goalie skill Team relics
1 Elite Meatballs (Twin Falls) 7 60 / 48 / 45 / 47 33 –
1 Elite Greasy Lettuce (Toronto) 7 48 / 65 / 26 / 42 18 Odd Fungus
1 Elite Calaveras (Chicago) 7 48 / 37 / 49 / 40 21 –
1 Elite Top Cheese (Midwest) 7 48 / 43 / 32 / 46 21 –
1 Boss Prisoners 7 65 / 56 / 64 / 49 45 Stick Breaking Knockout
1 Boss The Officials (all refs) 7 62 / 35 / 41 / 41 38 Referee Briefcase, False Goal on Post, Briberee, Rigged Faceoff, Zebra Hoof
2 Elite Crusaders 7 71 / 44 / 89 / 50 33 –
2 Elite Mountaineers 7 68 / 46 / 57 / 66 32 –
2 Elite Shooting Stars 7 67 / 78 / 53 / 69 34 –
2 Elite Hockey FC 8 39 / 89 / 22 / 54 45 soccer field / net / wall, No-Stick Soccer
2 Boss Cup Cultists 8 61 / 57 / 58 / 62 57 Relic Copier
3 Elite Princess 7 70 / 63 / 80 / 72 78 Lifeless Branch, Lucky Molar
3 Elite Team Canada 7 68 / 75 / 80 / 70 73 Oven Mitts (plus Canada curses, see npcs-and-events)
3 Elite Tycoons 10 95 / 83 / 86 / 80 82 Tycoons Ice, Annoying Ad, Frog Golden Helmet
3 Boss Golfers 13 (bench 8) 80 / 63 / 84 / 69 95 Golf Fairway, High Flyer, Octopus Attack

All opponents have benchSize = 5 except the Golfers (8). Opponent strength therefore scales per act through hand-made rosters, not through a formula. On top of that, the selected difficulty tier applies CPU stat deltas and AI parameters (Data.DifficultyLevel, see squads-difficulty-unlocks). Difficulty does not change per act.

Source: Data.CampaignOpponentsConfig (DefaultCampaignOpponents, RefereeCampaignOpponents), Data.TeamData, Data.ForwardData, Data.GoaltenderData, STS.Map.ChallengeMapNode::GetOpponentStrength/InitializeOpponentStats, STS.Map.MapObject (elite/boss assignment).


7. Win/loss consequences#

When a match ends, EndOfGameFlowController sets the run flags and builds a pipeline of steps (State.EndOfGame.Steps.*).

Outcome Effect
Win Challenge / Elite / Boss of act 1–2 RewardsStep runs and you pick rewards (counts per node in section 3.3). After the act 1 or act 2 boss, a new act map is generated (OnActCreated).
Lose a Challenge IsChallengeMatchLoss: no reward, the run continues. RewardsStep.ShouldExecute returns false.
Lose an Elite or Boss CheckRunFailed sets IsRunFailed and RunData.isRunFailed. The run ends with the "Defeat!" panel (New Run / See run stats).
Beat the Golfers (act 3 boss) CheckRunWon sets IsRunWin. The run ends with the Golfer outro, stats, unlocks and the bench-quest OnRunWon. Squad completion is recorded per difficulty (ProfileData.ModifySquadData).
Concede Abandons the run ("Abandon Run"). This counts as a conceded run in the stats.

Rewards are only processed when the run is neither won, conceded nor failed, and the match was not a lost Challenge.

Source: State.EndOfGame.EndOfGameFlowController::CheckRunFailed/CheckRunWon/UpdateCampaignContext, EndOfGameContext::get_IsChallengeMatchLoss/get_IsActFinished/get_IsRunFinished, RewardsStep::ShouldExecute, CampaignUpdateStep::Execute, localization worldmap/run_failed_panel/*.

Comments