Defines a FishRarity enum and a static FishRarityInfo helper with labels, short tags, CSS class, income/sell multipliers, pity tracking, weighted rolling logic, shop odds text, parent luck boost, and save/load conversion.
namespace NoChillquarium;
/// <summary>
/// Chillquarium-style rarity. Rolled on spawn/buy; multiplies income + sell.
/// </summary>
public enum FishRarity
{
Common = 0,
Uncommon = 1,
Rare = 2,
PaintedRare = 3,
Legendary = 4,
Mythical = 5
}
/// <summary>Static tables for rarity labels, weights, and income mults.</summary>
public static class FishRarityInfo
{
public static string Label( FishRarity r ) => r switch
{
FishRarity.Common => "Common",
FishRarity.Uncommon => "Uncommon",
FishRarity.Rare => "Rare",
FishRarity.PaintedRare => "Painted Rare",
FishRarity.Legendary => "Legendary",
FishRarity.Mythical => "Mythical",
_ => "Common"
};
/// <summary>Short shop / HUD tag.</summary>
public static string Short( FishRarity r ) => r switch
{
FishRarity.Common => "C",
FishRarity.Uncommon => "U",
FishRarity.Rare => "R",
FishRarity.PaintedRare => "PR",
FishRarity.Legendary => "L",
FishRarity.Mythical => "M",
_ => "C"
};
/// <summary>CSS class suffix: rarity-common, rarity-mythical, …</summary>
public static string CssClass( FishRarity r ) =>
"rarity-" + r.ToString().ToLowerInvariant();
/// <summary>Idle income multiplier — rares are the Chillquarium keepers.</summary>
public static float IncomeMult( FishRarity r ) => r switch
{
FishRarity.Common => 1f,
FishRarity.Uncommon => 1.75f,
FishRarity.Rare => 3.4f,
FishRarity.PaintedRare => 5.5f,
FishRarity.Legendary => 11f,
FishRarity.Mythical => 24f,
_ => 1f
};
/// <summary>Sell price multiplier — flip commons, jackpot rares.</summary>
public static float SellMult( FishRarity r ) => r switch
{
FishRarity.Common => 1f,
FishRarity.Uncommon => 1.55f,
FishRarity.Rare => 2.6f,
FishRarity.PaintedRare => 3.8f,
FishRarity.Legendary => 7f,
FishRarity.Mythical => 14f,
_ => 1f
};
/// <summary>Commons in a row before pity forces Uncommon+ (session-only).</summary>
const int PityAfterCommons = 7;
static int _commonStreak;
static int _lifetimeRareHits;
public static int CommonStreak => _commonStreak;
public static int LifetimeRareHits => _lifetimeRareHits;
public static void ResetPity()
{
_commonStreak = 0;
}
/// <summary>
/// Weighted roll. <paramref name="luck"/> > 1 slightly favors higher tiers
/// (habitat rank / late game), still mostly Commons. Pity soft-guarantees spice.
/// </summary>
public static FishRarity Roll( Random rng, float luck = 1f )
{
rng ??= new Random();
luck = Math.Clamp( luck, 0.5f, 2.8f );
// Base weights. Luck + mild common compression for chase feel.
var wCommon = 52f / luck;
var wUncommon = 24f;
var wRare = 13f * MathF.Sqrt( luck );
var wPainted = 6f * luck;
var wLegend = 3.2f * luck;
var wMyth = 1.5f * luck * luck;
// Pity: after a dry streak, dump common weight into uncommon+.
if ( _commonStreak >= PityAfterCommons )
{
wUncommon += wCommon * 0.55f;
wRare += wCommon * 0.25f;
wCommon *= 0.2f;
}
else if ( _commonStreak >= PityAfterCommons - 2 )
{
wUncommon += wCommon * 0.2f;
wCommon *= 0.8f;
}
var total = wCommon + wUncommon + wRare + wPainted + wLegend + wMyth;
var roll = (float)rng.NextDouble() * total;
FishRarity result;
if ( (roll -= wCommon) <= 0 ) result = FishRarity.Common;
else if ( (roll -= wUncommon) <= 0 ) result = FishRarity.Uncommon;
else if ( (roll -= wRare) <= 0 ) result = FishRarity.Rare;
else if ( (roll -= wPainted) <= 0 ) result = FishRarity.PaintedRare;
else if ( (roll -= wLegend) <= 0 ) result = FishRarity.Legendary;
else result = FishRarity.Mythical;
if ( result == FishRarity.Common )
_commonStreak++;
else
{
_commonStreak = 0;
if ( result >= FishRarity.Rare )
_lifetimeRareHits++;
}
return result;
}
/// <summary>Short shop line: roughly how spicy buys feel right now.</summary>
public static string ShopOddsLine( float luck = 1f )
{
luck = Math.Clamp( luck, 0.5f, 2.8f );
var rarePct = Math.Clamp( 8f + luck * 6f + (_commonStreak >= 5 ? 8f : 0f), 8f, 28f );
var pity = _commonStreak >= PityAfterCommons - 1
? " · pity hot"
: _commonStreak >= 4
? " · warming"
: "";
return "rare~" + (int)rarePct + "%" + pity;
}
/// <summary>Breed/hatch tilt from parent rarities (max parent, slight bump).</summary>
public static float ParentLuckBoost( FishRarity a, FishRarity b )
{
var top = (int)a > (int)b ? a : b;
return 1f + (int)top * 0.12f;
}
public static FishRarity FromSave( int raw )
{
if ( raw < 0 || raw > (int)FishRarity.Mythical )
return FishRarity.Common;
return (FishRarity)raw;
}
}