2026-10-19 19:07:45 +02:00
|
|
|
using System;
|
2026-10-19 18:39:28 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
// Контракты модулей: всё, что один модуль может попросить у другого.
|
|
|
|
|
// Реализация живёт в своём модуле, здесь -- только "что умеет".
|
|
|
|
|
// Добавляя метод сюда, обнови документ модуля в Docs/Modules.
|
|
|
|
|
|
|
|
|
|
// ---------------- Экономика: ресурсы и производство ----------------
|
|
|
|
|
public interface IEconomy
|
|
|
|
|
{
|
|
|
|
|
float Get(ResourceType type);
|
|
|
|
|
bool Has(ResourceType type, float amount);
|
|
|
|
|
void Add(ResourceType type, float amount);
|
|
|
|
|
bool TrySpend(ResourceType type, float amount);
|
|
|
|
|
|
|
|
|
|
bool CanAfford(IReadOnlyList<ResourceAmount> cost);
|
|
|
|
|
// Списывает всё или ничего
|
|
|
|
|
bool TrySpend(IReadOnlyList<ResourceAmount> cost);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------- Поселение: здания и развитие города ----------------
|
|
|
|
|
public interface ISettlement
|
|
|
|
|
{
|
|
|
|
|
int TownHallLevel { get; }
|
|
|
|
|
int BuildingCount { get; }
|
|
|
|
|
bool IsCityLoaded { get; } // открыта ли сейчас сцена города
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Здание с уровнем (добывающие постройки и т.п.) -- Поселение сохраняет уровень, не зная, чьё это здание
|
|
|
|
|
public interface ILeveledBuilding
|
|
|
|
|
{
|
|
|
|
|
int Level { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------- Армия: войска ----------------
|
|
|
|
|
public interface IArmy
|
|
|
|
|
{
|
|
|
|
|
int SoldiersAtHome { get; }
|
|
|
|
|
// Забрать воинов из города в поход (запоминает состав похода)
|
|
|
|
|
bool TakeForMarch(int marchId, int count);
|
|
|
|
|
// Вернуть выживших из похода по тем же типам войск
|
|
|
|
|
void ReturnFromMarch(int marchId, int survivors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------- Глобальный мир: карта и перемещения ----------------
|
|
|
|
|
public interface IWorld
|
|
|
|
|
{
|
|
|
|
|
int ActiveMarches { get; }
|
|
|
|
|
bool TryStartMarch(string targetId, int soldiers, out string error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------- Боевая система: урон, потери, исход ----------------
|
|
|
|
|
public struct BattleRequest
|
|
|
|
|
{
|
|
|
|
|
public int Attackers;
|
|
|
|
|
public int Defenders;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct BattleOutcome
|
|
|
|
|
{
|
|
|
|
|
public bool Victory; // победил атакующий
|
|
|
|
|
public int AttackerLosses;
|
|
|
|
|
public int DefenderLosses;
|
|
|
|
|
public int Survivors; // сколько атакующих осталось
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IBattleSystem
|
|
|
|
|
{
|
|
|
|
|
BattleOutcome Resolve(BattleRequest request);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-19 19:07:45 +02:00
|
|
|
// ---------------- Чат: общий мир и Круг ----------------
|
|
|
|
|
public enum ChatChannel
|
|
|
|
|
{
|
|
|
|
|
World, // общий чат всего мира
|
|
|
|
|
Circle // чат своего Круга
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class ChatMessage
|
|
|
|
|
{
|
|
|
|
|
public ChatChannel channel;
|
|
|
|
|
public string senderId;
|
|
|
|
|
public string senderName;
|
|
|
|
|
public string text;
|
|
|
|
|
public long timeMs; // Unix-время в миллисекундах
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IChat
|
|
|
|
|
{
|
|
|
|
|
int MaxMessageLength { get; }
|
|
|
|
|
IReadOnlyList<ChatMessage> GetMessages(ChatChannel channel);
|
|
|
|
|
bool CanWrite(ChatChannel channel); // в канал "Круг" -- только состоя в Круге
|
|
|
|
|
// Отправить от имени локального игрока. false -- пустое сообщение или канал "Круг" без Круга
|
|
|
|
|
bool Send(ChatChannel channel, string text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------- Круг: объединение игроков ----------------
|
|
|
|
|
public enum CircleRole
|
|
|
|
|
{
|
|
|
|
|
Leader, // глава
|
|
|
|
|
Member // участник
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class CircleMember
|
|
|
|
|
{
|
|
|
|
|
public string playerId;
|
|
|
|
|
public string name;
|
|
|
|
|
public CircleRole role;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class CircleInfo
|
|
|
|
|
{
|
|
|
|
|
public string id;
|
|
|
|
|
public string name;
|
|
|
|
|
public List<CircleMember> members = new List<CircleMember>();
|
|
|
|
|
|
|
|
|
|
public string LeaderName
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
foreach (var m in members) if (m.role == CircleRole.Leader) return m.name;
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface ICircle
|
|
|
|
|
{
|
|
|
|
|
int MaxMembers { get; }
|
|
|
|
|
CircleInfo Current { get; } // Круг игрока; null -- игрок не в Круге
|
|
|
|
|
bool IsInCircle { get; }
|
|
|
|
|
bool IsLeader { get; }
|
|
|
|
|
IReadOnlyList<CircleInfo> GetCircles(); // все Круги, в которые можно вступить
|
|
|
|
|
|
|
|
|
|
bool TryCreate(string name, out string error); // создатель становится главой
|
|
|
|
|
bool TryJoin(string circleId, out string error);
|
|
|
|
|
void Leave(); // глава один в Круге -- Круг распускается
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-19 18:39:28 +02:00
|
|
|
// ---------------- Сохранения: игровое состояние ----------------
|
|
|
|
|
public interface ISaveSystem
|
|
|
|
|
{
|
|
|
|
|
bool HasSave { get; }
|
|
|
|
|
void SaveNow();
|
|
|
|
|
void DeleteSave();
|
|
|
|
|
// Новая игра: все модули возвращаются к стартовому состоянию, файл перезаписывается
|
|
|
|
|
void ResetAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Участник сохранения: каждый модуль сам решает, что и как хранит, в своей секции файла.
|
|
|
|
|
public interface ISaveParticipant
|
|
|
|
|
{
|
|
|
|
|
string SaveKey { get; } // имя секции в файле: "economy", "settlement", "world"...
|
|
|
|
|
string Capture(); // JSON секции; null -- оставить то, что уже было в файле
|
|
|
|
|
void Restore(string json);
|
|
|
|
|
void ResetState(); // вернуться к состоянию новой игры (сброс сохранений)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Список участников. Модули регистрируются сами, модуль Сохранений обходит их при записи и загрузке.
|
|
|
|
|
public static class SaveRegistry
|
|
|
|
|
{
|
|
|
|
|
private static readonly List<ISaveParticipant> participants = new List<ISaveParticipant>();
|
|
|
|
|
|
|
|
|
|
public static IReadOnlyList<ISaveParticipant> All => participants;
|
|
|
|
|
|
|
|
|
|
public static void Register(ISaveParticipant participant)
|
|
|
|
|
{
|
|
|
|
|
participants.RemoveAll(p => p.SaveKey == participant.SaveKey);
|
|
|
|
|
participants.Add(participant);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Unregister(ISaveParticipant participant)
|
|
|
|
|
{
|
|
|
|
|
participants.Remove(participant);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Clear()
|
|
|
|
|
{
|
|
|
|
|
participants.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|