164 lines
6.3 KiB
C#
164 lines
6.3 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Bogatyri.World;
|
||
using UnityEngine;
|
||
|
||
// Хранит мир и походы игрока. Живёт между сценами (создаётся сам при запуске игры),
|
||
// поэтому войско идёт и возвращается, даже пока игрок в городе.
|
||
//
|
||
// Точка расширения под мультиплеер: сейчас WorldSimulation крутится прямо здесь.
|
||
// Позже StartMarch будет отправлять запрос на сервер, а походы и их итоги -- приходить с сервера.
|
||
// WorldMapController при этом менять не придётся: он работает через события этого класса.
|
||
public class MarchManager : MonoBehaviour
|
||
{
|
||
public const string LocalPlayerId = "local_player";
|
||
|
||
public static MarchManager Instance { get; private set; }
|
||
|
||
public WorldSimulation World { get; private set; }
|
||
|
||
public event Action<MarchData> OnMarchStarted;
|
||
public event Action<MarchData> OnBattleResolved;
|
||
public event Action<MarchData> OnMarchReturned;
|
||
|
||
// Время мира. В мультиплеере -- время сервера, а не телефона
|
||
public static long NowMs => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||
|
||
private readonly List<WorldEvent> events = new List<WorldEvent>();
|
||
|
||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||
static void CreateInstance()
|
||
{
|
||
var go = new GameObject("MarchManager");
|
||
DontDestroyOnLoad(go);
|
||
go.AddComponent<MarchManager>();
|
||
}
|
||
|
||
void Awake()
|
||
{
|
||
Instance = this;
|
||
World = new WorldSimulation(WorldMapLayout.CreateDefault());
|
||
// [объединение] походы хранятся в нашем progress.json (GameProgress), а не в SaveManager
|
||
var saved = GameProgress.Instance.Data.sashaMarches;
|
||
if (saved != null && saved.Count > 0) World.LoadMarches(saved);
|
||
}
|
||
|
||
void SaveToProgress()
|
||
{
|
||
GameProgress.Instance.Data.sashaMarches = GetMarchesForSave();
|
||
GameProgress.Instance.MarkChanged();
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
// Без ResourceManager некуда зачислять вернувшихся солдат -- ждём, пока загрузится город
|
||
if (ResourceManager.Instance == null) return;
|
||
|
||
events.Clear();
|
||
World.Advance(NowMs, events);
|
||
|
||
foreach (var e in events)
|
||
{
|
||
if (e.Type == WorldEventType.BattleResolved) HandleBattleResolved(e.March);
|
||
else if (e.Type == WorldEventType.MarchReturned) HandleMarchReturned(e.March);
|
||
}
|
||
|
||
if (events.Count > 0) SaveToProgress();
|
||
}
|
||
|
||
public bool TryStartMarch(string targetId, int soldiers, out string error)
|
||
{
|
||
if (ResourceManager.Instance == null)
|
||
{
|
||
error = "Нет данных о войске -- запусти игру из города";
|
||
return false;
|
||
}
|
||
// [объединение] солдаты = наши воины в городе (все типы войск)
|
||
if (ArmyBridge.SoldiersAtHome < soldiers)
|
||
{
|
||
error = "Не хватает воинов. Наймите их в Казармах";
|
||
return false;
|
||
}
|
||
|
||
MarchData march = World.StartMarch(LocalPlayerId, WorldMapLayout.PlayerCityId, targetId, soldiers, NowMs, out error);
|
||
if (march == null) return false;
|
||
|
||
// Ушедшие воины недоступны дома, пока не вернутся
|
||
ArmyBridge.Take(march.id, soldiers);
|
||
GameProgress.Instance.Data.marchesSent++;
|
||
GameProgress.Instance.Data.dailyMarches++;
|
||
SaveToProgress();
|
||
|
||
Debug.Log($"[ПОХОД] {soldiers} солдат выступили на {World.GetNode(targetId).displayName}");
|
||
OnMarchStarted?.Invoke(march);
|
||
return true;
|
||
}
|
||
|
||
void HandleBattleResolved(MarchData march)
|
||
{
|
||
MapNode target = World.GetNode(march.targetNodeId);
|
||
Debug.Log($"[ПОХОД] Бой у {target.displayName}: {(march.victory ? "ПОБЕДА" : "поражение")}. " +
|
||
$"{march.soldiers} против {target.garrison}, возвращаются {march.survivors}.");
|
||
OnBattleResolved?.Invoke(march);
|
||
}
|
||
|
||
// Солдаты и добыча зачисляются, только когда войско дошло до дома
|
||
void HandleMarchReturned(MarchData march)
|
||
{
|
||
ResourceManager rm = ResourceManager.Instance;
|
||
ArmyBridge.Return(march.id, march.survivors);
|
||
if (march.victory) GameProgress.Instance.Data.worldWins++;
|
||
|
||
foreach (var reward in march.loot)
|
||
{
|
||
if (Enum.TryParse(reward.resource, out ResourceType type))
|
||
rm.AddResource(type, reward.amount);
|
||
else
|
||
Debug.LogWarning($"[ПОХОД] Неизвестный ресурс награды: {reward.resource}");
|
||
}
|
||
|
||
Debug.Log($"[ПОХОД] Войско вернулось: {march.survivors} солдат, добыча: {FormatLoot(march.loot)}");
|
||
OnMarchReturned?.Invoke(march);
|
||
}
|
||
|
||
// ---------------- СОХРАНЕНИЕ ----------------
|
||
|
||
public List<MarchData> GetMarchesForSave()
|
||
{
|
||
return new List<MarchData>(World.Marches);
|
||
}
|
||
|
||
public void LoadMarches(List<MarchData> saved)
|
||
{
|
||
World.LoadMarches(saved);
|
||
}
|
||
|
||
// ---------------- ТЕКСТ ДЛЯ UI ----------------
|
||
|
||
public static string FormatLoot(List<RewardEntry> loot)
|
||
{
|
||
if (loot == null || loot.Count == 0) return "нет";
|
||
|
||
var parts = new List<string>();
|
||
foreach (var r in loot)
|
||
{
|
||
parts.Add($"+{r.amount} {GetResourceName(r.resource)}");
|
||
}
|
||
return string.Join(", ", parts);
|
||
}
|
||
|
||
static string GetResourceName(string resource)
|
||
{
|
||
switch (resource)
|
||
{
|
||
case "Food": return "еды";
|
||
case "Wood": return "дерева";
|
||
case "Stone": return "камня";
|
||
case "Iron": return "металла";
|
||
case "Gold": return "золота";
|
||
case "Gems": return "кристаллов";
|
||
default: return resource;
|
||
}
|
||
}
|
||
}
|