Tape to Tape Wiki All games ☕ Tip on Ko-fi

Rewards & Power-up Generation#

How the roguelite campaign decides which talents, abilities and relics you are offered, how many you get, how upgrades and rerolls work, and which systems in the code are dormant. The individual power-ups are listed in ../talents.md, ../abilities.md and ../relics.md. Map and node structure is in run-structure.md, and the NPC events (Blademaster, Fan #1, Chaos, ...) are in npcs-and-events.md.


1. Overview#

After a won match, the end-of-game flow (EndOfGameFlowController::Initialize) asks RewardCounter how many talent rewards and relic rewards the match is worth. RewardsStep then plays them in this order:

  1. every talent reward (LaunchTalentReward): one screen each, pick 1 of 3 talents and the skater who gets it
  2. every relic reward (LaunchRelicReward): one screen each, pick 1 of 3 relics
  3. any forced skater talent rewards (LaunchForcedSkaterTalentReward, from the Butt Reward talent)

A lost Challenge match clears all pending rewards. Any reward screen can be skipped ("Skip Reward" confirmation, tracked as Rewards Skipped).

Source: State.EndOfGame.Steps.RewardsStep::HandlePotentialRewards / ProcessAllRewards, Rogue.Rewards.RewardCounter, Events.RogueEvents (LaunchSingleReward, LaunchMultipleRewards, RewardSkipped).


2. How many rewards per match#

RewardCounter.SetTalentRewardCount and SetRelicRewardCount run when the match ends. They use the current node's NodeType (CampaignState.currentNode.nodeType) and, for Elite nodes, the per-node counts stored in the map template (MapLayerNodeType.talentRewardCount / relicRewardCount).

2.1 Base counts#

Node Talent rewards Relic rewards
Challenge (0) 1 0
Elite (1) node's talentRewardCount (1 if the node isn't an EliteBlueprint) node's relicRewardCount (1 if the node isn't an EliteBlueprint)
Boss (2) 1 0 from the counter (see the note below)
Any other node 0 0

Elite nodes are either talent nodes or relic nodes. In every shipped map template an Elite node has either talentRewardCount = 1, relicRewardCount = 0 or 0 / 1. A single Elite match gives one or the other, which matches the glossary ("Offers a random Talent or Relic as a reward"). The match-preview string "Reward: Talent + Relic" is generic.

Template Elite (1 T / 0 R) Elite (0 T / 1 R) Boss (T / R in data)
NEW ACT 1 Option B / C, ACT 3 Option B, 6-NEW ACT 3 2 1 1 / 1
Gauntlet Act 1 / Act 2 (4 Elite) 2 2 1 / 1
Gauntlet Act 3 (3 Elite) 2 1 1 / 1
GM ACT 1, GM ACT 2,3 0 3 1 / 1
Act 1 Campaign Tutorial 2 0 1 / 1

Boss relic note. The UI ("Reward: Talent + Boss Relic") and the glossary say the boss gives a Boss Relic. LaunchRelicReward does switch to the Boss Relic pool when the node is a BossBlueprint (section 4). But in this build SetRelicRewardCount only sets a base relic count for NodeType == Elite. For a Boss node it gives 0, plus the Extra Reward bonus below. Nothing else writes the counter. The template's boss relicRewardCount = 1 is only read for EliteBlueprint nodes. How the boss relic count reaches 1 in normal play could not be traced. Treat the boss relic as given by the UI, not verified in code.

2.2 Modifiers#

Condition Effect
Talented Squad (norelics) Elite: 2 talents, 0 relics. Boss: 2 talents. ("Elite and Boss games reward twice as many talents, but no relics.")
General Manager Squad (generalmanager) 0 talents and 1 relic after every rewarded match, whatever the node type.
Extra Reward relic (Rogue.DoubleReward, Boss relic, remainingUses = 2) Only on Elite/Boss matches: +1 relic reward, and one use is spent. Level 2 also gives +1 talent reward (this check only needs Level > 1 and spends no use).
No Reward curse (Rogue.Relics.FinalSquadRelics.NoRewardCurse) While in act 1: 0 talents and 0 relics. In later acts the talent count is left unset (inferred: probably also no talents), and relics are counted as normal.
Butt Reward talent (ButtRewardTalent, "+1 talent reward") If the match grants at least one talent reward, each forward with this talent is queued for an extra forced talent reward that only they can receive.
Lost Challenge All rewards cleared.

Source: Rogue.Rewards.RewardCounter::SetTalentRewardCount / SetRelicRewardCount / GetDoubleRewardCount / SetForcedSkaterTalentRewards. Generic methods were resolved from the metadata MethodSpec table (TeamData.HaveRelic<NoRewardCurse>, HaveRelicCached<DoubleReward>, SkaterData.HaveTalentCached<ButtRewardTalent>). Data comes from the DAGG.Generation.Template.MapTemplateData, STS.Map.*Blueprint and Rogue.DoubleReward assets.


3. Talent rewards#

3.1 Standard talent reward screen#

RewardsStep.LaunchTalentReward calls

RogueUtils.GetRandomTalentRewards(team, numberOfTalents = 3, category, repo, exclude = [], minSkaterChoices = 1, preferredSkaterChoices = 3, mustBeInPool = false, onlyActiveSkaters = false)

Parameter Value
Talents offered 3
Category UsedInCampaignPool (256). The Stats Squad (statrewards) uses OnlyStats (65536) instead, so every reward is an attribute boost.
Skaters offered up to 3 (at least 1). You pick a talent and which offered skater gets it.

Skater selection (GetSkaterSelectionData, RemoveUnselectableSkaters, SetNumSelectableRewardsPerSkater, SelectSkaters):

  • Skaters carrying the Coach's Favorite talent (AlwaysRewarded, "Always appears in end of game rewards") get priority AlwaysPick. Skaters with Healthy Scratch (NeverRewarded) get NeverPick and are never offered (mapping inferred from the PICK_ME_TALENT_TYPE / NEVER_ME_TALENT_TYPE names).
  • A skater is only eligible for an offered talent if they have not already fully upgraded it (SkaterSelectionData.CanGetTalentReward → !HasFullyUpgradedTalent).
  • Skaters who can take all the offered talents are preferred (inferred from HasFullTalentChoices / prioritizeSkatersWithFullTalentChoices).

Challenge matches don't roll here. Each Challenge node rolls its reward in advance on the map (TalentNodeReward or RelicNodeReward, shown as "Reward: {powerUpName}"). After a won Challenge, LaunchTalentReward hands that exact power-up out as a single reward (RogueEvents.LaunchSingleReward → RogueUtils.ConvertSingleReward). You still choose which skater gets a talent.

Source: State.EndOfGame.Steps.RewardsStep::LaunchTalentReward, Rogue.RogueUtils::GetRandomTalentRewards and helpers, STS.NodeRewards.TalentNodeReward::GetRewardCategory.

3.2 Drawing talents from the repository#

TalentRepository.GetRandomTalents(category, excluded, isClone, numberOfTalents, mustBeInPool, matchAnyCategory = true, forwardData):

  1. Candidates: every talent in talents (319 entries) whose category shares any bit with the requested mask (GetTalentsMatchingAnyCategory, (category & mask) != 0). With matchAnyCategory = false all bits are required.
  2. Level 1 only: candidates must have Level == 1. Level 2 versions are never drawn directly.
  3. Exclusions: the ids of the passed list plus CampaignUtil.GetExcludedTalentRewards (section 6) are removed (AddExcludedIds).
  4. Weighted draw without replacement of numberOfTalents items (WeightedRandomSelector.GetRandomSelection, cumulative-weight roll). Weight rules are in section 5.
  5. Upgrade roll: each drawn card may be swapped for its Level 2 version (section 7.1).

The ability and relic repositories follow the same steps (AbilityRepository.GetRandomAbilities, RelicRepository.GetRandomRelics, default numberOfX = 3).

Source: Rogue.Powerups.Repository.TalentRepository::GetRandomTalents / GetTalentsMatchingAnyCategory / AddExcludedIds / TryUpgradeTalent, Tape2Tape.WeightedRandomSelector.


4. Relic rewards#

RewardsStep.LaunchRelicReward calls

RogueUtils.GetRandomRelicRewards(team, numChoices = 3, category, repo, exclude, isTemporary = false, mustBeInPool = false, matchAnyCategory = true, cloneRelics = true)

Match node Relic category requested Choices
BossBlueprint BossRelics (4096) 3
anything else (Elite, GM squad, ...) UsedInCampaignPool (1024) 3

Before the draw, GetRandomRelicRewards adds every relic the team owns at full upgrade (HasFullyUpgradedRelic) to the exclusion list. The repository draw then follows the steps in 3.2, with these relic-specific details:

  • RelicRepository.GetRandomRelics checks candidates against relics (294 entries) by category bit, keeps Level == 1 only, and with mustBeInPool also requires the UsedInCampaignPool bit.
  • CampaignUtil.GetExcludedRelicRewards also removes every relic already in TeamData.relics. A relic you own is never offered again by a normal relic reward, so upgrades come only from the upgrade roll and upgrade effects (section 7).

Pool sizes in the shipped RelicRepository (resources.assets). The category sub-lists in the asset are editor caches. Filtering uses each relic's own category flags.

Relic set (by category bit) Level 1 count Level 2 count
UsedInCampaignPool (normal relic rewards) 106 1
BossRelics (boss rewards) 33 11

Level 2 relics usually have category = 0 (for example Weighted Die (Level 2)), so they are reachable only as upgrades.

Source: State.EndOfGame.Steps.RewardsStep::LaunchRelicReward, Rogue.RogueUtils::GetRandomRelicRewards, Rogue.Relics.Repository.RelicRepository::GetRandomRelics (lambdas b__25_2 = category & 1024, b__25_3 = level == 1), GetRelicsMatchingAnyCategory, Tape2Tape.CampaignUtil::GetExcludedRelicRewards.

4.1 eliteRewardRelicCategory#

RunSquadScriptableObject.eliteRewardRelicCategory (offset 0xB8) is set to 2048 on these squads: All Angus, Defense, Eliminator, Gauntlet, SNES, Speedrun, Stats, Modern (tchel) and Violence. All others have 0.

  • 2048 is not a named RelicCategory value. It is the missing bit between UsedInCampaignPool = 1024 and BossRelics = 4096, probably a removed enum member (inferred).
  • No relic in the game has bit 2048 in its category.
  • No code in this build reads the field. There is no getter, and a scan of every method that fetches the current squad (GetCurrentSquad / GetSquad) found no load of offset 0xB8 from it. EliteReward.GetRelicCategory is compiled to the same body as RelicNodeReward.GetRelicCategory and returns a constant UsedInCampaignPool (1024). BossReward.GetRelicCategory returns BossRelics (4096).

Conclusion: the field is a leftover. If it were used it would select an empty pool. In practice, Elite relic rewards always come from the normal campaign pool.

Source: State.RunSquadScriptableObject assets, STS.NodeRewards.EliteReward / BossReward / RelicNodeReward::GetRelicCategory.


5. Reward weights and eligibility#

5.1 Base weight#

Every candidate's draw weight comes from GetRewardWeight(runData[, forwardData]), which is the same for PowerUp (talents and abilities) and Relic:

weight = OnGetRewardWeight(runData)        // virtual, default 1.0
if CampaignUtil.IsUndiscoveredUnlock(id):  // unlockable you have never received
    weight *= 2

IsUndiscoveredUnlock(id) is true when the id has unlock data (UnlockSystem.HasUnlockData) and is not yet in the profile's discovered set. Newly unlocked power-ups appear twice as often until you receive one.

The talent draw adds one squad rule. On the Slapshot Squad, talents with the SlapshotEffect category (bit 0x1000000) get ×1.2 weight. The ability draw adds no extra rule.

Source: Rogue.PowerUp::GetRewardWeight, Rogue.Relic::GetRewardWeight, Tape2Tape.CampaignUtil::IsUndiscoveredUnlock, TalentRepository/<>c__DisplayClass35_0::<GetWeightedRandomTalents>g__GetWeight|0, AbilityRepository::<GetWeightedRandomAbilities>g__GetWeight|25_0, RelicRepository::<GetWeightedRandomRelics>g__GetWeight|26_0.

5.2 Rarity does nothing#

PowerUpRarity (Common 0, Uncommon 1, Rare 2, Epic 3) exists on both PowerUp and Relic, but:

  • No reward code reads it. The only users of RarityHelper are dev-browser UIs (Tape2Tape.Dev.UI.*BrowserUI / *CardUI).
  • In the data, every talent and ability (386 assets, Level 1 and Level 2) is Common. Every campaign relic is Common except one pool relic marked Rare.

There are no rarity weights.

5.3 Synergy-gated weights (OnGetRewardWeight overrides)#

About 35 power-ups override OnGetRewardWeight, almost all as payoff gating. A payoff only appears, or appears more often, when the team already has an enabler of the same theme. Data.TeamData.HasPowerupTypes(relicCategory, talentCategory) checks the team's relics and talents for those category bits.

Theme (relic cat. / talent cat. checked) Typical result Examples
Fart (131072 / 262144) 1.0 if the team has fart power-ups, else 0 (never offered) Fart OVR, Fart Knockout, Fart Immunity, Bean Diet
Enrage (262144 / 524288) 1.0 if present, else 0 Enrage Bonus, Enraged OVR, Enraged Injury, Prolonged Rage
Critical (524288 / 2097152) 1.0 if present, else 0.5 (0.2 for Crit Shot Knockout) Critical relics, Crit Shot Frog / Double Goal / Enrage, Defense Crit, Both Team Crit
Referee (2097152) 1.1 if present, else 0 Second Referee
Golden Helmet (HaveRelicInCategory 8388608) 1.0 if present, else 0 Golden Helmet Second Scorer
Other fixed or conditional values 0.75 / 0.5 / 1.5 ... The Original Puck 0.75, Onepunch 0.5, Lose Helmet 1.0 or 1.5
Chaos stat-lock talents 1.0 or 0 depending on whether a valid target exists Mold, Liquidation (GetEligibleTalents), Bedrock, Railgun

Per-item values belong in the catalog pages.

5.4 Hard eligibility (IsEligibleReward)#

The base implementation returns true. CampaignUtil.GetExcluded*Rewards drops every candidate whose IsEligibleReward(runData) is false, and every candidate that is not unlocked (UnlockSystem.IsUnlocked). Overrides found:

Power-up Eligible when
Zebra Hoof (relic) CurrentAct > 1
Steady Eddie (talent) CurrentAct <= 2
Organ Donor (BenchwarmerStrip), Ginger Fury (AngusAscension) team / squad composition checks (details not traced)

Source: overrides of OnGetRewardWeight / IsEligibleReward in Rogue.*, Rogue.Powerups.Chaos.*.


6. Pool exclusions (squads and game rule)#

CampaignUtil.GetExcludedRelicRewards, GetExcludedTalentRewards and GetExcludedAbilityRewards build the removal list used in every draw:

Source Relics Talents Abilities
Squad excluded*Category (rogue mode only) all relics matching excludedRelicCategory all talents matching excludedTalentCategory abilities matching excludedTalentCategory
Game rule Timer MaxGoal relics (32768) MaxGoal talents (8192) MaxGoal (8192)
Game rule Max-goal Timer relics (16384) Timer talents (4096) Timer (4096)
Not eligible / not unlocked yes yes yes
Already owned all owned relics – (handled per skater, section 3.1) –
Squad explicit list excludedRelics excludedTalents excludedAbilities

Squads with non-empty exclusions:

Squad Category exclusions Explicit exclusions (summary)
Eliminator relics: Referee Briefcase (both levels)
Old School relics: OnlyStats; talents: Injury Participation Trophy, all bench-bonus relics, Short Stick; Short Contract, Benchwarmer, Double/Butt Bench talents
Double Decker (lines) – same bench relics and talents as Old School
Bum Squad – Double Bench Bonus
General Manager – Short Contract
Referee Squad – Golf Club; Fury Stick, Triple Twig, Twig Tax, Goalie Throw Stick; abilities Throwing Stick, Stick Thief
Slapshot Squad – Critical One Timer relic
SNES – ability Dash
Solo – Participation Trophy, Short Contract
Speedy – talent Armor
Modern (tchel) – Gain Random Ability relic; Random Skill talent; Polymorphic ability

Source: Tape2Tape.CampaignUtil::GetExcludedRelicRewards / GetExcludedTalentRewards / GetExcludedAbilityRewards, State.RunSquadScriptableObject (excludedRelicCategory, excludedRelics, excludedTalentCategory, excludedTalents, excludedAbilities).


7. Upgrades and duplicates#

All power-ups have at most two levels (PowerUp.level is [Range(1,2)]). There are 103 Level 2 talent/ability assets, for example "Armor (Level 2)", plus the Level 2 relics.

7.1 Random upgraded offers#

After the weighted draw, every offered card goes through TryUpgradeX(card, upgradeChance). If a next-level version exists and is not isHidden, and Random.value <= upgradeChance, the card is replaced by its Level 2 version.

Current act (RunData.CurrentAct, 1-based) Upgrade chance per offered card
1 2.5%
2 7.5%
3 15%

The same constants are used by the talent, ability and relic repositories.

Source: RelicRepository::GetRandomRelics (constants 0.025 / 0.075 / 0.15 selected on _currentAct == 2 / 3), TryUpgradeRelic, TalentRepository::GetRandomTalents / TryUpgradeTalent, AbilityRepository::GetRandomAbilities / TryUpgradeAbility. CurrentAct starts at 1 (RunManager::CreateMapFromScratch).

7.2 Duplicates#

  • Talents: the talent pool does not exclude owned talents. Only a skater who already has the fully upgraded version is barred from that card. Giving a skater a talent they already own at Level 1 replaces it with the upgraded version. The reward menu marks such a card as an upgrade (SkaterDetailsUI.SetPowerUpAsUpgrade), and ApplyCurrentSelection replaces the talent at the slot GetTalentIndex(checkUpgradesAndDowngrades) returns ("Invalid talent upgrade." error path) (flow inferred from method names and strings).
  • Relics: owned relics are excluded from normal relic draws (section 4). The relic menu can still show an "upgrade" state (RelicRewardMenu.InstantiateRelics → HasRelic → UpgradeableUI.SetUpgrade) for single relic rewards from other sources (inferred).

7.3 Other upgrade sources#

The Blademaster upgrade mode, Fan #1 upgrade choices, The Improverizer (RandomUpgrade, 1 or 2 random upgrades after each game), Ginger Fury (AngusAscension), Mold, and Bench Bonus UpgradeRandomTalent. All of them use Rogue.Upgrades.UpgradeHelper.GetUpgradeablePowerups. Details are in npcs-and-events.md and the catalogs.


8. Rerolls#

  • The number of rerolls on a reward screen is CalculateAvailableRerolls(team): the rerollCount of the team's Weighted Die relic (Rogue.Reroll), otherwise 0.
Relic rerollCount Text
Weighted Die 1 "+1 reroll for each reward at the end of a game"
Weighted Die (Level 2) 2 "+2 rerolls for each reward at the end of a game"
  • The count is per reward screen. Each talent or relic screen gets the full count again.
  • A reroll redraws the whole set with RogueUtils.GetRerolledTalentRewards(team, previousChoices, ...) or GetRerolledRelicRewards(team, previousChoices, ...). The previous cards are passed so they are left out of the new set (inferred from the parameter). The usual weights and upgrade roll apply.
  • Weighted Die is in the normal relic pool (category = 1024).

Source: Tape2Tape.Hockey.UI.RogueRewardsHandler::CalculateAvailableRerolls (TeamData.HaveRelicCached<Reroll> / GetRelic<Reroll>().rerollCount), SkaterPowerupRewardMenu::RerollChoicesNow, RelicRewardMenu::RerollChoicesNow, Rogue.Reroll assets.


9. Abilities#

  • One ability per skater. SkaterData has a single ability field. The glossary says "A skater can have a single active power-up called an ability".
  • Abilities are not part of normal post-match rewards. They come from the Blademaster node ("teaches a skater a new ability or upgrades power-ups"). Its new-ability mode is chosen when:
  • you are not playing the Modern (tchel) squad,
  • and at least one skater is eligible for a new ability,
  • and either nothing is upgradeable, or it is act 1, or Random.Range(0,1) <= 0.5.
  • Other ability sources are relics/talents such as Random Abilities and Random Skill.
  • Draws use AbilityRepository (56 abilities, the UsedInCampaign pool) with the same level-1, weight and upgrade-chance rules as talents.

Source: Data.SkaterData.ability, Rogue.NPC.BlademasterNPC.BlademasterEvent::ShouldOfferNewAbility, STS.NodeRewards.BlademasterNodeReward::GenerateReward, Rogue.Powerups.Repository.AbilityRepository::GetRandomAbilities.


10. Chaos power-ups#

Chaos power-ups are talents with the Chaos category (16384, 32 talents in chaosTalents, Rogue.Powerups.Chaos.*). They are a buff with a built-in drawback. They come from the Chaos node event (ChaosEvent3Choice, asset chaos3Choice):

  • One random eligible forward is chosen for the event (CampaignUtil.GetEventEligibleForwards, never a Healthy Scratch skater). The map node shows that skater's face (ChaosNodeReward).
  • ITalentRepository.GetRandomTalents(category = Chaos, excluded = that skater's current talents, numberOfTalents = m_NumChoices = 2) gives 2 chaos talents, and you can decline.
  • The normal weights apply. Some chaos talents gate themselves through OnGetRewardWeight, for example Mold and Liquidation need valid target talents.

Source: Rogue.NPC.ChaosEvents.ChaosEvent3Choice::Initialize / SelectForward, STS.NodeRewards.ChaosNodeReward::GenerateReward, asset chaos3Choice (m_NumChoices = 2).


11. Boss relics#

Boss relics are relics with the BossRelics category bit (4096): 33 at Level 1 and 11 at Level 2 in the shipped repository. They are drawn only when the reward comes from a BossBlueprint match: 3 choices, normal weights, act-based upgrade chance (15% in act 3). For example, Extra Reward is a Boss relic (see the counter caveat in section 2.1). The list of boss relics is in ../relics.md and npcs-and-events.md.


12. Dormant and legacy systems#

Stickers (Rogue.Stickers.*): implemented but never called#

Reward has a stickers list, and StickerManager.GenerateStickersForRewards → StickerRewardGenerator → StickerFactory.Create would roll stickers for a reward. Nothing calls GenerateStickersForRewards, AddSkaterSticker or AddTeamSticker, and there are no sticker localization strings. The system is unused in this build. For reference, the shipped StickerConfig roll is one Random.Range(0,1) per reward:

Roll r Result
r < 0.01 (rareRoll) "Rare sticker"
r < 0.04 (rare + uncommon) "Two stickers"
r < 0.14 (rare + uncommon + common) "One sticker"
otherwise none

Each sticker would give +statValue = 5 to a shuffled stat (Sticker.IsRare = stat type 4) (thresholds inferred from the cumulative compares in StickerFactory.Create).

Shop (Rogue.Shop.*): legacy#

ShopData, ShopItem (price), AbilityShopItem, EliteShopItem and StatsHeroCalculator exist. They are referenced only by the old Blademaster.HerosAttributesItem UI. No ShopData asset ships. There is no in-run currency or shop. The "shop_*" achievements refer to unlocks.

Unused weight flag#

Relic.debugAppearMoreOften exists but is not read by GetRewardWeight.


13. Quick reference#

Question Answer
Talent choices per reward 3 talents × up to 3 skaters
Relic choices per reward 3
Chaos event choices 2 chaos talents for one fixed skater
Rarity weighting none (everything is Common)
New-unlock bonus ×2 weight until first received
Upgraded-card chance 2.5% / 7.5% / 15% in acts 1 / 2 / 3
Rerolls 0, +1 Weighted Die, +2 Weighted Die L2, per screen
Owned relics re-offered? No
Owned talents re-offered? Yes. Picking one for its owner upgrades it, and fully upgraded owners are excluded.
Abilities per skater 1