Skip to content

Fix: energy icons in custom keyword tooltips outside of a run - #344

Open
sethmcleod wants to merge 1 commit into
Alchyr:developfrom
sethmcleod:fix/keyword-tip-energy-prefix
Open

Fix: energy icons in custom keyword tooltips outside of a run#344
sethmcleod wants to merge 1 commit into
Alchyr:developfrom
sethmcleod:fix/keyword-tip-energy-prefix

Conversation

@sethmcleod

@sethmcleod sethmcleod commented Jul 16, 2026

Copy link
Copy Markdown

Fixes #337.

Problem

Custom keyword tooltips containing energy icons render the correct icon during a run, but fall back to the colorless icon in the card library opened from the main menu, logging:

[WARN] No energy prefix found for EnergyIconsFormatter! Using colorless as a fallback.

Cause

CustomTooltips.DynamicKeywordTips passes an empty prefix:

description.Add("energyPrefix", "");

An empty value makes the game's EnergyIconsFormatter fall back to the character being played:

if (string.IsNullOrEmpty(text) || text == "colorless")
    text = RunManager.Instance.GetLocalCharacterEnergyIconPrefix();

GetLocalCharacterEnergyIconPrefix() reads LocalContext.GetMe(State)?.Character.CardPool and returns null when there is no RunState — which is the case in the card library.

The game itself never relies on that fallback: CardModel, PowerModel, PotionModel, RelicModel and EnchantmentModel all populate the variable with EnergyIconHelper.GetPrefix(this), which resolves from the model's own pool and therefore works outside a run. HoverTipFactory.FromKeyword only receives the keyword, and KeywordInfo carried no pool, so BaseLib had nothing to resolve from.

Change

Let a keyword declare the pool its energy icon comes from:

[CustomEnum]
[KeywordProperties(AutoKeywordPosition.None, Pool = typeof(MyCardPool))]
public static CardKeyword MyKeyword;

KeywordPropertiesAttribute.Pool is stored on KeywordInfo.PoolType at registration and used to resolve the prefix via EnergyIconHelper.GetPrefix, matching how the game does it for cards and powers.

Opt-in and backwards compatible: keywords that don't declare a pool still pass "" and behave exactly as before.

Testing

Tested with a custom character mod (custom CustomCardPoolModel with TextEnergyIconPath), hovering keyword tooltips in the card library from the main menu with no run active. Resolved tooltip text:

Keyword Before After
No Pool declared colorless_energy_icon.png colorless_energy_icon.png (unchanged)
Pool = typeof(AlchemistCardPool) colorless_energy_icon.png res://Alchemist/images/charui/text_energy.png
Screenshot 2026-07-16 at 12 28 33 PM

The No energy prefix found warning no longer fires for keywords that declare a pool. Icons in-run are unaffected.

Alternative considered: inferring the pool

This PR asks each keyword to name its pool. The alternative is to infer it: BaseLib already knows the keyword's declaring type and its mod prefix (t.GetPrefix(), used to build the keyword ID), so it could look for a custom card pool registered under that same prefix and use it when exactly one exists, falling back to "" otherwise.

The upside is real — most character mods have exactly one card pool, so existing mods (including the one in #337) would be fixed with no code change at all.

The trade-offs:

  • It picks a pool the author never named, which is wrong for a keyword that belongs to a colorless or shared pool.
  • It's ambiguous for mods with more than one pool, so an explicit Pool would still be needed as an override.
  • That makes inference strictly additive to this PR rather than an alternative to it.

I went with the explicit version because it's predictable and easy to build on, but I'm happy to add inference on top if you'd rather existing mods heal without changes — Pool would stay as the override.

Rich keyword tooltips passed an empty energyPrefix, which makes
EnergyIconsFormatter fall back to RunManager.GetLocalCharacterEnergyIconPrefix().
That returns null when there is no RunState, so custom energy icons rendered
correctly during a run but fell back to the colorless icon in the card library
opened from the main menu (and logged "No energy prefix found for
EnergyIconsFormatter!").

The game resolves this from the model rather than the run: CardModel, PowerModel,
PotionModel, RelicModel and EnchantmentModel all populate energyPrefix with
EnergyIconHelper.GetPrefix(this), which falls back to the model's own pool and so
works outside a run. HoverTipFactory.FromKeyword only receives the keyword, so
let the keyword declare its pool:

    [KeywordProperties(AutoKeywordPosition.None, Pool = typeof(MyCardPool))]
    public static CardKeyword MyKeyword;

Keywords that don't declare a pool keep the previous behaviour.

Fixes Alchyr#337
@Alchyr

Alchyr commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Hmmm definitely better than current but the ideal fix would probably be for the tooltip energy icon to be based on the model it's attached to
maybe will leave this for a bit and look into how reasonable that would be to set up

@sethmcleod

sethmcleod commented Jul 17, 2026

Copy link
Copy Markdown
Author

Thanks for considering! I agree that would be the ideal fix and I'd be happy to take a stab at that if you'd like. Not high priority though since there's a workaround.

@pikcube

pikcube commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

I wrote a hyper niche hook ages ago that let you modify a card's hover tips dynamically by postfixing the get method. A patch like that would give you the ability to check the model the tip is attached to and perform any logic you wanted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants