327 lines
17 KiB
C#
327 lines
17 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
// ТАБЛИЦА БАЛАНСА ЭКОНОМИКИ. Все числа экономики -- здесь, а не в коде модулей.
|
|
//
|
|
// Ассет: Assets/Resources/EconomyConfig.asset (выдели его в окне Project и меняй числа в Inspector,
|
|
// перекомпиляция не нужна). Создать заново: правый клик в Project -> Create -> Bogatyri -> Economy Config,
|
|
// имя файла -- EconomyConfig, папка -- Resources.
|
|
//
|
|
// Модули конфиг не видят: App/GameModules переводит его в правила модулей (например, правила Населения).
|
|
// Числа -- из дизайн-документа. Помеченные "не утверждено" -- временные, их решает команда.
|
|
[CreateAssetMenu(fileName = "EconomyConfig", menuName = "Bogatyri/Economy Config")]
|
|
public class EconomyConfig : ScriptableObject
|
|
{
|
|
[Serializable]
|
|
public class PopulationSettings
|
|
{
|
|
[Tooltip("Стартовый город T1: всего людей")]
|
|
public int startTotal = 300;
|
|
[Tooltip("Из них рабочих")]
|
|
public int startWorkers = 120;
|
|
[Tooltip("Из них воинов (выдаются в Армию пехотой при новой игре)")]
|
|
public int startSoldiers = 30;
|
|
|
|
[Tooltip("Еды в час на одного жителя")]
|
|
public float foodPerCivilianPerHour = 0.1f;
|
|
[Tooltip("Еды в час на одного воина")]
|
|
public float foodPerSoldierPerHour = 0.2f;
|
|
|
|
[Tooltip("Прирост людей в час, если есть еда (документ: 2–3)")]
|
|
public float growthPerHour = 2.5f;
|
|
|
|
[Tooltip("Предел населения без Домов на 1 уровне Ратуши (решение команды от 19.10: 300 при 300 -- прирост стоит)")]
|
|
public int housingBase = 300;
|
|
[Tooltip("Прибавка к пределу за каждый следующий уровень Ратуши. Не утверждено")]
|
|
public int housingPerTownHallLevel = 50;
|
|
[Tooltip("Прибавка к пределу за каждый уровень Дома (здание \"Население\"). Не утверждено")]
|
|
public int housingPerHouseLevel = 50;
|
|
|
|
[Tooltip("Офлайн-прогресс считается не больше чем за столько часов. Не утверждено")]
|
|
public int maxOfflineHours = 48;
|
|
|
|
[Tooltip("Гражданский резерв по уровню Ратуши: [0] -- уровень 1, [1] -- уровень 2... Столько свободных жителей нельзя забрать в войско. Регламент 31.10, ТЕСТ: 1-2 -- 10, 3-5 -- 20, 6-10 -- 30")]
|
|
public int[] civilianReserveByTownHallLevel = { 10, 10, 20, 20, 20, 30, 30, 30, 30, 30 };
|
|
}
|
|
|
|
[Serializable]
|
|
public class ProductionSettings // этап 3: производство через рабочих
|
|
{
|
|
[Tooltip("Лесоруб T1: дерева в час")]
|
|
public float woodcutterPerHour = 2f;
|
|
[Tooltip("Фермер T1: еды в час")]
|
|
public float farmerPerHour = 2f;
|
|
[Tooltip("Каменотёс T1: камня в час")]
|
|
public float stonecutterPerHour = 1.5f;
|
|
[Tooltip("Шахтёр T1: металла в час")]
|
|
public float minerIronPerHour = 1f;
|
|
[Tooltip("Шахтёр T1: золота в час")]
|
|
public float minerGoldPerHour = 0.4f;
|
|
[Tooltip("Мест для рабочих в здании T1 (Лесопилка и т.п.)")]
|
|
public int workerSlotsT1 = 30;
|
|
[Tooltip("+ мест за каждый уровень здания выше первого. Не утверждено")]
|
|
public int workerSlotsPerLevel = 10;
|
|
[Tooltip("+ к норме за каждый уровень здания выше первого (0.1 = +10%). Не утверждено")]
|
|
public float efficiencyPerLevel = 0.1f;
|
|
[Tooltip("Голод: множитель добычи всех ресурсов, кроме еды (0.5 -- вдвое меньше). Еда добывается без штрафа. Регламент 31.10, ТЕСТ")]
|
|
[Range(0f, 1f)] public float famineNonFoodMultiplier = 0.5f;
|
|
}
|
|
|
|
[Serializable]
|
|
public class StorageSettings // этап 1: лимиты хранения
|
|
{
|
|
[Tooltip("Лимит без Складов: дерево, камень, металл, золото. Не утверждено")]
|
|
public float baseGoods = 1000f;
|
|
[Tooltip("Лимит еды без Амбаров. Не утверждено")]
|
|
public float baseFood = 1500f;
|
|
[Tooltip("+ к лимиту дерева, камня, металла, золота за каждый уровень Склада. Не утверждено")]
|
|
public float perWarehouseLevel = 1000f;
|
|
[Tooltip("+ к лимиту еды за каждый уровень Амбара. Не утверждено")]
|
|
public float perGranaryLevel = 1500f;
|
|
[Tooltip("Лимит синего кристалла (решение команды от 19.10: небольшой, не бесконечный). Число не утверждено")]
|
|
public float crystalCap = 50f;
|
|
|
|
[Tooltip("Цена Склада: дерево / камень. Не утверждено")]
|
|
public float warehouseWood = 200f;
|
|
public float warehouseStone = 100f;
|
|
[Tooltip("Цена Амбара: дерево / камень. Не утверждено")]
|
|
public float granaryWood = 150f;
|
|
public float granaryStone = 50f;
|
|
}
|
|
|
|
[Serializable]
|
|
public class UpgradeSettings // улучшение зданий (рабочие места, Дома, Склады, Амбары)
|
|
{
|
|
[Tooltip("Цена улучшения с 1 на 2 уровень: дерево / камень. Не утверждено")]
|
|
public float baseWood = 150f;
|
|
public float baseStone = 100f;
|
|
[Tooltip("Во сколько раз дорожает каждый следующий уровень. Не утверждено")]
|
|
public float multiplierPerLevel = 1.6f;
|
|
public int maxLevel = 10;
|
|
}
|
|
|
|
[Serializable]
|
|
public class DepositSettings // этап 4: качество месторождения (пока не используется)
|
|
{
|
|
public float poor = 0.6f;
|
|
public float normal = 1f;
|
|
public float rich = 1.5f;
|
|
}
|
|
|
|
[Serializable]
|
|
public class SoldierSettings // этап 5: цена солдата
|
|
{
|
|
[Tooltip("Сколько жителей стоит один воин (берутся из жителей при начале обучения)")]
|
|
public int people = 1;
|
|
public float food = 15f;
|
|
public float iron = 5f;
|
|
public float gold = 5f;
|
|
[Tooltip("Время подготовки, секунд. Не утверждено")]
|
|
public int trainSeconds = 60;
|
|
[Tooltip("Уровень Казарм, с которого открывается тип войск (порядок как в GameData.Troops: пехота, лучники, кавалерия, осадные). " +
|
|
"Нужен и уровень Ратуши. Считаются самые прокачанные Казармы. ТЕСТ, не утверждено")]
|
|
public int[] requiredBarracksLevel = { 1, 2, 3, 5 };
|
|
}
|
|
|
|
[Serializable]
|
|
public class ConstructionSettings // время стройки и улучшения, строители (01.11, ТЕСТ)
|
|
{
|
|
[Tooltip("Сколько строек и улучшений идёт одновременно (строители). ТЕСТ")]
|
|
public int builders = 2;
|
|
[Tooltip("Постройка нового здания, секунд. ТЕСТ")]
|
|
public int buildSeconds = 60;
|
|
[Tooltip("Улучшение до уровня 2, 3, 4... (секунд): [0] -- до 2 ур., [1] -- до 3 ур. ... ТЕСТ")]
|
|
public int[] upgradeSecondsToLevel = { 60, 120, 300, 600, 1200, 1800, 3600, 5400, 7200 };
|
|
[Tooltip("Ратуша строится дольше остальных: множитель времени улучшения. ТЕСТ")]
|
|
public float townHallTimeMultiplier = 2f;
|
|
[Tooltip("Ускорение: секунд стройки за 1 кристалл. ТЕСТ")]
|
|
public int secondsPerGem = 30;
|
|
// Сноса зданий нет (решение 04.11): только бесплатный перенос
|
|
}
|
|
|
|
[Serializable]
|
|
public class BuildingLimit
|
|
{
|
|
public string buildingName;
|
|
[Tooltip("Сколько таких зданий можно иметь на Ратуше 1, 2, 3 ... уровня. 0 -- нельзя")]
|
|
public int[] maxByTownHall;
|
|
|
|
public BuildingLimit() { }
|
|
public BuildingLimit(string name, params int[] max) { buildingName = name; maxByTownHall = max; }
|
|
}
|
|
|
|
[Serializable]
|
|
public class ProtectionSettings // защита ресурсов от грабежа (01.11, ТЕСТ)
|
|
{
|
|
[Tooltip("Защищено без Складов: дерево, камень, металл, золото. ТЕСТ")]
|
|
public float baseGoods = 100f;
|
|
[Tooltip("+ к защите дерева, камня, металла, золота за каждый уровень Склада. ТЕСТ")]
|
|
public float perWarehouseLevel = 200f;
|
|
[Tooltip("Защищено без Амбаров: еда. ТЕСТ")]
|
|
public float baseFood = 100f;
|
|
[Tooltip("+ к защите еды за каждый уровень Амбара. ТЕСТ")]
|
|
public float perGranaryLevel = 300f;
|
|
[Tooltip("Кристаллы: без технологии «Грабёж» нападающий не уносит ничего (решение 04.11). С ней -- не больше этой доли запаса. ТЕСТ, точные ограничения определят отдельно")]
|
|
[Range(0f, 1f)] public float plunderGemsShare = 0.1f;
|
|
}
|
|
|
|
[Serializable]
|
|
public class StartResources
|
|
{
|
|
[Tooltip("Ресурсы новой игры. Не утверждено")]
|
|
public float wood = 500f;
|
|
public float food = 500f;
|
|
public float stone = 200f;
|
|
public float iron = 100f;
|
|
public float gold = 100f;
|
|
public float gems = 0f;
|
|
}
|
|
|
|
[Header("Население (этап 2)")]
|
|
public PopulationSettings population = new PopulationSettings();
|
|
|
|
[Header("Стартовые ресурсы новой игры")]
|
|
public StartResources startResources = new StartResources();
|
|
|
|
[Header("Склады и лимиты (этап 1)")]
|
|
public StorageSettings storage = new StorageSettings();
|
|
|
|
[Header("Производство T1 (этап 3)")]
|
|
public ProductionSettings production = new ProductionSettings();
|
|
|
|
[Header("Улучшение зданий")]
|
|
public UpgradeSettings upgrade = new UpgradeSettings();
|
|
|
|
[Header("Месторождения (этап 4)")]
|
|
public DepositSettings deposits = new DepositSettings();
|
|
|
|
[Header("Солдат T1 (этап 5)")]
|
|
public SoldierSettings soldier = new SoldierSettings();
|
|
|
|
[Header("Стройка: время, строители, снос (ТЕСТ)")]
|
|
public ConstructionSettings construction = new ConstructionSettings();
|
|
|
|
[Header("Лимит зданий по уровню Ратуши (ТЕСТ). Здания не из списка -- без лимита")]
|
|
public BuildingLimit[] buildingLimits =
|
|
{
|
|
// Ратуша: 1 2 3 4 5 6 7 8 9 10
|
|
new BuildingLimit("Лес", 2, 3, 3, 4, 4, 5, 5, 6, 6, 7),
|
|
new BuildingLimit("Еда", 2, 3, 3, 4, 4, 5, 5, 6, 6, 7),
|
|
new BuildingLimit("Камень", 2, 2, 3, 3, 4, 4, 5, 5, 6, 6),
|
|
new BuildingLimit("Металл", 0, 0, 1, 1, 2, 2, 3, 3, 4, 4),
|
|
new BuildingLimit("Золото", 0, 1, 1, 2, 2, 3, 3, 3, 4, 4),
|
|
new BuildingLimit("Население", 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
|
|
new BuildingLimit("Склад", 1, 1, 2, 2, 2, 3, 3, 3, 4, 4),
|
|
new BuildingLimit("Амбар", 1, 1, 2, 2, 2, 3, 3, 3, 4, 4),
|
|
new BuildingLimit("Казармы", 1, 1, 2, 2, 3, 3, 3, 3, 3, 3),
|
|
new BuildingLimit("Госпиталь", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
|
new BuildingLimit("Терем богатыря", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
|
new BuildingLimit("Кристалл", 0, 0, 0, 0, 0, 1, 1, 1, 1, 1),
|
|
};
|
|
|
|
[Header("Защита ресурсов от грабежа (ТЕСТ)")]
|
|
public ProtectionSettings protection = new ProtectionSettings();
|
|
|
|
// ---------------- Загрузка ----------------
|
|
|
|
static EconomyConfig cached;
|
|
|
|
// Конфиг из Assets/Resources/EconomyConfig.asset. Нет ассета -- числа по умолчанию (из этого файла) и предупреждение.
|
|
public static EconomyConfig Load()
|
|
{
|
|
if (cached != null) return cached;
|
|
cached = Resources.Load<EconomyConfig>("EconomyConfig");
|
|
if (cached == null)
|
|
{
|
|
Debug.LogWarning("[ЭКОНОМИКА] Нет Assets/Resources/EconomyConfig.asset -- берутся числа по умолчанию из EconomyConfig.cs.");
|
|
cached = CreateInstance<EconomyConfig>();
|
|
}
|
|
return cached;
|
|
}
|
|
|
|
// Лимит склада для ресурса по уровням Складов и Амбаров. PositiveInfinity -- без лимита (люди, сила)
|
|
public float GetCapacity(ResourceType type, int warehouseLevels, int granaryLevels)
|
|
{
|
|
var s = storage;
|
|
switch (type)
|
|
{
|
|
case ResourceType.Wood:
|
|
case ResourceType.Stone:
|
|
case ResourceType.Iron:
|
|
case ResourceType.Gold: return s.baseGoods + s.perWarehouseLevel * warehouseLevels;
|
|
case ResourceType.Food: return s.baseFood + s.perGranaryLevel * granaryLevels;
|
|
case ResourceType.Gems: return s.crystalCap;
|
|
default: return float.PositiveInfinity;
|
|
}
|
|
}
|
|
|
|
// Сколько зданий с таким именем можно иметь на этом уровне Ратуши. -1 -- без лимита (здания нет в списке)
|
|
public int BuildingLimitFor(string buildingName, int townHallLevel)
|
|
{
|
|
if (buildingLimits == null || string.IsNullOrEmpty(buildingName)) return -1;
|
|
foreach (var l in buildingLimits)
|
|
{
|
|
if (l == null || l.buildingName != buildingName || l.maxByTownHall == null || l.maxByTownHall.Length == 0) continue;
|
|
return Mathf.Max(0, l.maxByTownHall[Mathf.Clamp(townHallLevel, 1, l.maxByTownHall.Length) - 1]);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Время стройки (targetLevel = 1) или улучшения до targetLevel, мс. Ратуша -- с множителем
|
|
public long ConstructionMs(bool townHall, int targetLevel)
|
|
{
|
|
var c = construction;
|
|
double seconds;
|
|
if (targetLevel <= 1) seconds = c.buildSeconds;
|
|
else
|
|
{
|
|
var t = c.upgradeSecondsToLevel;
|
|
seconds = t == null || t.Length == 0 ? c.buildSeconds : t[Mathf.Clamp(targetLevel - 2, 0, t.Length - 1)];
|
|
}
|
|
if (townHall) seconds *= Mathf.Max(0.01f, c.townHallTimeMultiplier);
|
|
return (long)(Math.Max(0, seconds) * 1000.0);
|
|
}
|
|
|
|
// Сколько ресурса не отнять при грабеже: часть запаса, которую прячут Склады и Амбары
|
|
public float GetProtected(ResourceType type, int warehouseLevels, int granaryLevels)
|
|
{
|
|
var p = protection;
|
|
switch (type)
|
|
{
|
|
case ResourceType.Wood:
|
|
case ResourceType.Stone:
|
|
case ResourceType.Iron:
|
|
case ResourceType.Gold: return p.baseGoods + p.perWarehouseLevel * warehouseLevels;
|
|
case ResourceType.Food: return p.baseFood + p.perGranaryLevel * granaryLevels;
|
|
case ResourceType.Gems: return float.PositiveInfinity; // без «Грабежа» кристаллы не грабятся (с ним -- ResourceProtection)
|
|
default: return 0f;
|
|
}
|
|
}
|
|
|
|
// Цена улучшения здания с уровня level на level + 1
|
|
public ResourceAmount[] UpgradeCost(int level)
|
|
{
|
|
float m = Mathf.Pow(upgrade.multiplierPerLevel, Mathf.Max(0, level - 1));
|
|
return new[]
|
|
{
|
|
new ResourceAmount(ResourceType.Wood, Mathf.Round(upgrade.baseWood * m)),
|
|
new ResourceAmount(ResourceType.Stone, Mathf.Round(upgrade.baseStone * m)),
|
|
};
|
|
}
|
|
|
|
public float GetStartResource(ResourceType type)
|
|
{
|
|
var s = startResources;
|
|
switch (type)
|
|
{
|
|
case ResourceType.Wood: return s.wood;
|
|
case ResourceType.Food: return s.food;
|
|
case ResourceType.Stone: return s.stone;
|
|
case ResourceType.Iron: return s.iron;
|
|
case ResourceType.Gold: return s.gold;
|
|
case ResourceType.Gems: return s.gems;
|
|
default: return 0f;
|
|
}
|
|
}
|
|
}
|