- CrystalMine (без Unity) + 5 тестов: ровно 3 за 7 суток, ×12 не действует, 49/50 -> 1, пауза, перезагрузка и часы назад без двойного начисления, офлайн-стройка - Работает с конца стройки, офлайн досчитывается; вне недельного PvE-предела - Вторая шахта -- Ратуша 9 (ТЕСТ); окно здания: следующий кристалл / пауза - DEV «Ресурсы 100 000» -- обычные ресурсы мимо лимита склада Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
5.0 KiB
C#
84 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
|
|
// КРИСТАЛЬНАЯ ШАХТА: выработка синих кристаллов (Регламент редкости синих кристаллов от 05.11.2026, разд. 2.1;
|
|
// Регламент валют от 05.11, разд. 5.2 и 8.1; «Ответ Володе» от 06.11, п. 3). Документ: Docs/Modules/Economy.md.
|
|
//
|
|
// - Каждая работающая шахта -- PerPeriod (ТЕСТ 3) кристаллов за PeriodMs (7 РЕАЛЬНЫХ суток; не игровые часы ×12),
|
|
// равномерно: один кристалл за PeriodMs / PerPeriod. Шахты (до двух) складываются.
|
|
// - Шахта работает с момента, когда достроена (activeFromMs): стройка, закончившаяся офлайн, добывает с её конца.
|
|
// - Хранилище полное -- пауза: прогресс не растёт, скрытого запаса нет. Хранилище заполнилось в ходе расчёта --
|
|
// шахта встаёт сразу после последнего поместившегося кристалла (прогресс 0). Освободилось место -- выработка идёт
|
|
// дальше с оставшегося прогресса, без начисления за время паузы.
|
|
// - Офлайн: досчёт по меткам при входе; уже посчитанное время второй раз не начисляется; часы назад (сброс
|
|
// DEV-перемотки) -- метка не уходит назад, ничего не начисляется.
|
|
// - В недельный PvE-предел не входит (отдельный учёт).
|
|
// Без Unity: те же правила будут на сервере и в тестах.
|
|
public sealed class CrystalMine
|
|
{
|
|
public readonly int PerPeriod;
|
|
public readonly long PeriodMs;
|
|
|
|
// Прогресс к следующему кристаллу (мс работы одной шахты) и до какого момента посчитано
|
|
public long ProgressMs { get; private set; }
|
|
public long LastMs { get; private set; }
|
|
|
|
public CrystalMine(int perPeriod = 3, long periodMs = 7 * CrystalQuota.Day)
|
|
{
|
|
PerPeriod = Math.Max(1, perPeriod);
|
|
PeriodMs = Math.Max(1, periodMs);
|
|
}
|
|
|
|
public long MsPerCrystal => Math.Max(1, PeriodMs / PerPeriod);
|
|
|
|
// Досчитать до nowMs. mineActiveFromMs -- по шахте: с какого момента она работает (long.MinValue -- давно).
|
|
// freeSpace -- сколько кристаллов помещается в хранилище. Возвращает, сколько кристаллов начислить
|
|
public int Advance(long nowMs, IEnumerable<long> mineActiveFromMs, int freeSpace)
|
|
{
|
|
if (LastMs == 0) { LastMs = nowMs; return 0; } // первый расчёт: отсюда и считаем
|
|
if (nowMs <= LastMs) return 0; // часы назад или то же время -- ничего
|
|
long from = LastMs;
|
|
LastMs = nowMs;
|
|
if (freeSpace <= 0) return 0; // пауза: хранилище полное, прогресс не растёт
|
|
|
|
long work = 0;
|
|
if (mineActiveFromMs != null)
|
|
foreach (long active in mineActiveFromMs)
|
|
{
|
|
long start = Math.Max(from, active);
|
|
if (start < nowMs) work += nowMs - start;
|
|
}
|
|
if (work <= 0) return 0;
|
|
|
|
long total = ProgressMs + work;
|
|
long made = total / MsPerCrystal;
|
|
if (made <= freeSpace)
|
|
{
|
|
ProgressMs = total - made * MsPerCrystal;
|
|
return (int)made;
|
|
}
|
|
ProgressMs = 0; // хранилище заполнилось: шахта встала сразу после последнего кристалла, лишнее не копится
|
|
return freeSpace;
|
|
}
|
|
|
|
// Через сколько мс следующий кристалл при count работающих шахтах (0 шахт -- -1)
|
|
public long MsToNext(int count) => count <= 0 ? -1 : Math.Max(0, (MsPerCrystal - ProgressMs + count - 1) / count);
|
|
|
|
public void Load(string line)
|
|
{
|
|
ProgressMs = 0; LastMs = 0;
|
|
if (string.IsNullOrEmpty(line)) return;
|
|
var p = line.Split('|');
|
|
if (p.Length >= 2
|
|
&& long.TryParse(p[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long progress)
|
|
&& long.TryParse(p[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out long last))
|
|
{
|
|
ProgressMs = Math.Max(0, Math.Min(progress, MsPerCrystal - 1));
|
|
LastMs = Math.Max(0, last);
|
|
}
|
|
}
|
|
|
|
public string ToLine() => ProgressMs.ToString(CultureInfo.InvariantCulture) + "|" + LastMs.ToString(CultureInfo.InvariantCulture);
|
|
}
|