Static Shop class for an in-game store. It exposes purchase checks and buy actions for fish, capacity, habitats, salt tank, meth and M80 items, tracks small player-side inventory (meth/M80 bags) and last UI message, and triggers economy, tank and game-state side effects and saves.
namespace NoChillquarium;
/// <summary>
/// Shop spends: fish, capacity, habitats, salt tank, meth, M80.
/// </summary>
public static class Shop
{
public const int CapacityStep = 5;
public const double BaseCapacityCost = 50;
public const double MethCost = 28;
public const double M80Cost = 18;
public const double SaltTankCost = TankSim.SaltTankCost;
static int _version;
static int _methBags;
static int _m80Bags;
public static int Version => _version;
public static int MethBags => _methBags;
public static int M80Bags => _m80Bags;
public static string LastMessage { get; private set; } = "Click a row.";
public static void SetLastMessage( string msg )
{
LastMessage = msg ?? "";
_version++;
}
public static double CapacityCost =>
BaseCapacityCost + Math.Max( 0, TankSim.Capacity - TankSim.DefaultCapacity ) * 15.0;
public static bool CanBuyFish( FishSpecies species ) =>
species is not null
&& TankSim.IsActive
&& TankSim.FishCount < TankSim.Capacity
&& Economy.Balance >= species.BuyCost;
public static bool CanBuyCapacity() =>
TankSim.IsActive && Economy.Balance >= CapacityCost;
public static bool CanBuySaltTank() =>
TankSim.IsActive && !TankSim.SaltUnlocked && Economy.Balance >= SaltTankCost;
public static HabitatDef NextHabitatOffer => TankSim.NextHabitat;
public static string HabitatUnlockText( HabitatDef def )
{
if ( def is null )
return "";
if ( Economy.PlaytimeSeconds >= def.UnlockPlaytime )
return def.Note;
var left = def.UnlockPlaytime - Economy.PlaytimeSeconds;
var totalMin = (int)(left / 60f);
if ( totalMin >= 60 )
{
var h = totalMin / 60;
var m = totalMin % 60;
return m > 0 ? $"Unlocks in {h}h {m}m" : $"Unlocks in {h}h";
}
var s = (int)(left % 60f);
return $"Unlocks in {totalMin}:{s:00}";
}
public static bool CanBuyHabitat( HabitatDef def ) =>
def is not null
&& TankSim.CanUpgradeTo( def )
&& Economy.Balance >= def.Cost;
public static bool CanBuyMeth() =>
TankSim.IsActive && Economy.Balance >= MethCost;
public static bool CanBuyM80() =>
TankSim.IsActive && Economy.Balance >= M80Cost;
public static bool CanFeedMeth() =>
TankSim.IsActive && _methBags > 0;
public static bool CanArmM80() =>
TankSim.IsActive && _m80Bags > 0;
public static void ResetInventory()
{
_methBags = 0;
_m80Bags = 0;
LastMessage = "Click a row.";
DarkWeb.Clear();
_version++;
}
public static void LoadInventory( int methBags, int m80Bags = 0 )
{
_methBags = Math.Max( 0, methBags );
_m80Bags = Math.Max( 0, m80Bags );
_version++;
}
public static void WriteInventory( SaveData data )
{
if ( data is not null )
{
data.MethBags = _methBags;
data.M80Bags = _m80Bags;
}
}
public static bool TryBuyFish( FishSpecies species )
{
if ( species is null || !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( TankSim.FishCount >= TankSim.Capacity )
{
Fail( "Tank full. Buy capacity." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !Economy.TrySpend( species.BuyCost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( species.BuyCost )}." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !TankSim.Spawn( species ) )
{
Economy.Add( species.BuyCost );
Fail( "Could not spawn fish." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
var warn = species.Water != TankSim.Water && species.Water != WaterType.Any
? " · WRONG WATER ⚠"
: "";
LastMessage = $"Bought {species.Name} · Ð{Economy.FormatDoge( species.BuyCost )}{warn}";
GameAudio.PlayUi( GameAudio.Trade );
SaveGame.TrySave();
_version++;
return true;
}
public static bool TryBuyCapacity()
{
if ( !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
var cost = CapacityCost;
if ( !Economy.TrySpend( cost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( cost )} for +{CapacityStep} slots." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
TankSim.IncreaseCapacity( CapacityStep );
LastMessage = $"+{CapacityStep} capacity on {TankSim.TankName} (now {TankSim.Capacity}).";
GameAudio.PlayUi( GameAudio.Upgrade );
Achievements.OnCapacityUpgraded();
SaveGame.TrySave();
_version++;
return true;
}
public static bool TryBuySaltTank()
{
if ( !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( TankSim.SaltUnlocked )
{
Fail( "You already own a salt tank." );
return false;
}
if ( !Economy.TrySpend( SaltTankCost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( SaltTankCost )} for a salt tank." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
TankSim.TryUnlockSaltTank();
LastMessage = $"Saltwater tank purchased for Ð{Economy.FormatDoge( SaltTankCost )}.";
GameAudio.PlayUi( GameAudio.Upgrade );
Achievements.OnSaltUnlocked();
SaveGame.TrySave();
_version++;
return true;
}
public static bool TryBuyHabitat( HabitatDef def )
{
if ( def is null || !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( !TankSim.CanUpgradeTo( def ) )
{
if ( Economy.PlaytimeSeconds < def.UnlockPlaytime )
Fail( HabitatUnlockText( def ) );
else if ( def.Rank != TankSim.HabitatRank + 1 )
Fail( "Buy the next habitat in order." );
else
Fail( "Can't move into that habitat yet." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !Economy.TrySpend( def.Cost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( def.Cost )} for {def.Name}." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !TankSim.TryUpgradeHabitat( def ) )
{
Economy.Add( def.Cost );
Fail( "Habitat move failed." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
LastMessage = $"Moved into {def.Name} · cap {TankSim.Capacity} · ×{def.IncomeMult:0.##} income.";
GameAudio.PlayUi( GameAudio.Upgrade );
Chaos.Add( 2f + def.Rank * 0.5f );
Karma.Add( 3f + def.Rank * 0.5f ); // better homes = good-boy-ish
Achievements.OnHabitatUpgraded( def );
SaveGame.TrySave();
_version++;
return true;
}
public static bool TryBuyMeth()
{
if ( !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( !Economy.TrySpend( MethCost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( MethCost )} for meth." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
_methBags++;
LastMessage = $"Bought meth ×{_methBags} · Ð{Economy.FormatDoge( MethCost )}";
GameAudio.PlayUi( GameAudio.Trade );
Chaos.Add( 5f );
SaveGame.TrySave();
_version++;
return true;
}
public static bool TryBuyM80()
{
if ( !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( !Economy.TrySpend( M80Cost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( M80Cost )} for an M80." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
_m80Bags++;
LastMessage = $"Bought M80 ×{_m80Bags} · Ð{Economy.FormatDoge( M80Cost )}";
GameAudio.PlayUi( GameAudio.Trade );
Chaos.Add( 3f );
SaveGame.TrySave();
_version++;
return true;
}
/// <summary>Spend M80 stock when detonating (1 per taped fish).</summary>
public static bool TryConsumeM80( int count )
{
if ( count <= 0 )
return true;
if ( _m80Bags < count )
return false;
_m80Bags -= count;
_version++;
return true;
}
public static bool TryFeedMeth()
{
if ( !CanFeedMeth() )
{
Fail( "No meth bags. Buy some in Shady." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
_methBags--;
var n = TankSim.ApplyMethToActiveTank();
GameAudio.PlayUi( n > 0 ? GameAudio.Confirm : GameAudio.Error );
if ( n > 0 )
{
Karma.Add( -6f - n * 0.5f );
var saved = 0;
foreach ( var f in TankSim.Fish )
{
if ( f.OnMeth && f.IsWrongWater( TankSim.Water ) )
saved++;
}
Achievements.OnMethFed( saved );
// Contain the thrash or the glass pays for it.
MethRush.TryStart();
}
SaveGame.TrySave();
_version++;
return true;
}
public static bool CanBuyDecor( DecorDef def ) =>
def is not null
&& TankSim.IsActive
&& TankSim.DecorCount < TankSim.MaxDecor
&& Economy.Balance >= def.Cost;
public static bool TryBuyDecor( DecorDef def )
{
if ( def is null || !TankSim.IsActive )
{
Fail( "No tank open." );
return false;
}
if ( TankSim.DecorCount >= TankSim.MaxDecor )
{
Fail( $"Decor full ({TankSim.MaxDecor} max on this tank)." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !Economy.TrySpend( def.Cost ) )
{
Fail( $"Need Ð{Economy.FormatDoge( def.Cost )} for {def.Name}." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
if ( !TankSim.TryPlaceDecor( def ) )
{
Economy.Add( def.Cost );
Fail( "Could not place decor." );
GameAudio.PlayUi( GameAudio.Error );
return false;
}
LastMessage = $"Placed {def.Name} for Ð{Economy.FormatDoge( def.Cost )}.";
GameAudio.PlayUi( GameAudio.Upgrade );
Karma.Add( def.ChaosOnPlace > 0f ? -1f : 1.5f );
Achievements.OnDecorPlaced();
SaveGame.TrySave();
_version++;
return true;
}
static void Fail( string msg )
{
LastMessage = msg;
_version++;
}
/// <summary>Set shop banner from other systems (WMD etc.).</summary>
public static void FailPublic( string msg ) => Fail( msg );
public static void OkPublic( string msg )
{
LastMessage = msg ?? "";
_version++;
}
}