- CrystalStorage (без Unity) + 5 тестов из раздела 12 - Ступени и цены -- EconomyConfig.crystalExpansions (ТЕСТ), купленная ступень -- в секции economy - Окно Ратуши: «Хранилище синих кристаллов», «Расширить до N»; цена списывается один раз Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
3.9 KiB
C#
64 lines
3.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
// ХРАНИЛИЩЕ СИНИХ КРИСТАЛЛОВ: вместимость и платное расширение
|
|
// (Регламент валют, кристаллов и торговли от 05.11.2026, разд. 8; «Ответ Володе» от 06.11.2026, п. 1).
|
|
// Документ: Docs/Modules/Economy.md, раздел «Синие кристаллы».
|
|
//
|
|
// - Старт -- BaseCapacity (ТЕСТ 50). Ступени (ТЕСТ): Ратуша 3 -- 75, 5 -- 100, 7 -- 150, 9 -- 200.
|
|
// - Ступень НЕ открывается сама с уровнем Ратуши: Ратуша только разрешает её, игрок покупает за золото и
|
|
// обычные ресурсы. Ступени -- по порядку, одна покупка -- одна ступень.
|
|
// - Цена списывается один раз: ступень растёт, только если списание прошло (всё или ничего). Повторное
|
|
// нажатие без следующей ступени или без нужной Ратуши ничего не списывает.
|
|
// - Сохраняется номер купленной ступени (Level), вместимость по нему считается заново -- числа можно менять.
|
|
// Без Unity: те же правила будут на сервере и в тестах. Числа и цены даёт слой приложения (EconomyConfig).
|
|
public sealed class CrystalStorage
|
|
{
|
|
public sealed class Tier
|
|
{
|
|
public int TownHallLevel; // с какого уровня Ратуши можно купить
|
|
public int Capacity; // вместимость после покупки
|
|
public ResourceAmount[] Cost; // цена: золото и обычные ресурсы
|
|
}
|
|
|
|
public readonly int BaseCapacity;
|
|
readonly List<Tier> tiers;
|
|
|
|
public CrystalStorage(int baseCapacity, IEnumerable<Tier> tiers)
|
|
{
|
|
BaseCapacity = Math.Max(0, baseCapacity);
|
|
this.tiers = new List<Tier>();
|
|
if (tiers != null) foreach (var t in tiers) if (t != null) this.tiers.Add(t);
|
|
}
|
|
|
|
// Сколько ступеней куплено (0 -- только стартовая вместимость)
|
|
public int Level { get; private set; }
|
|
|
|
public int TierCount => tiers.Count;
|
|
public int Capacity => Level <= 0 ? BaseCapacity : Math.Max(BaseCapacity, tiers[Level - 1].Capacity);
|
|
|
|
// Следующая ступень; null -- все куплены
|
|
public Tier Next => Level < tiers.Count ? tiers[Level] : null;
|
|
|
|
// Восстановить из сохранения (номер ступени; лишнее обрезается, если ступеней стало меньше)
|
|
public void SetLevel(int level) => Level = Math.Max(0, Math.Min(level, tiers.Count));
|
|
|
|
// Почему сейчас нельзя расширить: null -- можно (если хватит ресурсов -- это проверяет списание)
|
|
public string WhyNot(int townHallLevel)
|
|
{
|
|
var next = Next;
|
|
if (next == null) return "Хранилище расширено до предела";
|
|
if (townHallLevel < next.TownHallLevel) return $"Расширение до {next.Capacity} откроется на Ратуше {next.TownHallLevel} ур.";
|
|
return null;
|
|
}
|
|
|
|
// Купить следующую ступень. trySpend -- списание цены «всё или ничего» (Экономика). true -- ступень куплена
|
|
public bool TryExpand(int townHallLevel, Func<IReadOnlyList<ResourceAmount>, bool> trySpend)
|
|
{
|
|
if (WhyNot(townHallLevel) != null || trySpend == null) return false;
|
|
if (!trySpend(Next.Cost ?? new ResourceAmount[0])) return false;
|
|
Level++;
|
|
return true;
|
|
}
|
|
}
|