313 lines
18 KiB
C#
313 lines
18 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
// ВСЕ ИГРОВЫЕ ТАБЛИЦЫ В ОДНОМ МЕСТЕ: задания, войска, награды, магазин, событие.
|
||
|
|
// Баланс правится здесь -- числа, названия, награды. Логика лежит в GameProgress и панелях.
|
||
|
|
|
||
|
|
[Serializable]
|
||
|
|
public class RewardItem
|
||
|
|
{
|
||
|
|
public ResourceType type;
|
||
|
|
public int amount;
|
||
|
|
public RewardItem() { }
|
||
|
|
public RewardItem(ResourceType type, int amount) { this.type = type; this.amount = amount; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum QuestKind
|
||
|
|
{
|
||
|
|
TownHallLevel, // улучшить Ратушу до уровня N
|
||
|
|
BuildingsBuilt, // построить N зданий
|
||
|
|
Collect, // собрать N ресурса
|
||
|
|
BattlesWon, // выиграть N боёв
|
||
|
|
TroopsTrained, // обучить N воинов
|
||
|
|
HeroLevel, // герой N уровня
|
||
|
|
ArenaFights, // N боёв на арене
|
||
|
|
PlayerLevel, // уровень игрока N
|
||
|
|
Login, // зайти в игру (ежедневное)
|
||
|
|
WorldMarches, // отправить N походов на глобальной карте
|
||
|
|
WorldWins, // победить N раз на глобальной карте
|
||
|
|
BarracksBuilt // построить N казарм
|
||
|
|
}
|
||
|
|
|
||
|
|
public class QuestDef
|
||
|
|
{
|
||
|
|
public string id;
|
||
|
|
public string title;
|
||
|
|
public QuestKind kind;
|
||
|
|
public ResourceType resource;
|
||
|
|
public int target;
|
||
|
|
public int xp;
|
||
|
|
public RewardItem[] rewards;
|
||
|
|
|
||
|
|
public QuestDef(string id, string title, QuestKind kind, int target, int xp, params RewardItem[] rewards)
|
||
|
|
{
|
||
|
|
this.id = id; this.title = title; this.kind = kind; this.target = target; this.xp = xp; this.rewards = rewards;
|
||
|
|
}
|
||
|
|
|
||
|
|
public QuestDef Res(ResourceType r) { resource = r; return this; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public class TroopDef
|
||
|
|
{
|
||
|
|
public string name;
|
||
|
|
public string description;
|
||
|
|
public int power; // сила одного воина
|
||
|
|
public float secondsPerUnit; // время обучения одного
|
||
|
|
public int requiredTownHall;
|
||
|
|
public RewardItem[] cost; // цена одного воина
|
||
|
|
public Color color;
|
||
|
|
|
||
|
|
public TroopDef(string name, string description, int power, float seconds, int th, Color color, params RewardItem[] cost)
|
||
|
|
{
|
||
|
|
this.name = name; this.description = description; this.power = power; secondsPerUnit = seconds;
|
||
|
|
requiredTownHall = th; this.color = color; this.cost = cost;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class ShopItemDef
|
||
|
|
{
|
||
|
|
public string id;
|
||
|
|
public string title;
|
||
|
|
public RewardItem[] gives;
|
||
|
|
public ResourceType priceType;
|
||
|
|
public int price; // 0 = бесплатно
|
||
|
|
public int dailyLimit; // 0 = без лимита
|
||
|
|
public bool comingSoon; // покупка за реальные деньги -- пока недоступна
|
||
|
|
public string priceLabel;
|
||
|
|
|
||
|
|
public ShopItemDef(string id, string title, ResourceType priceType, int price, int dailyLimit, params RewardItem[] gives)
|
||
|
|
{
|
||
|
|
this.id = id; this.title = title; this.priceType = priceType; this.price = price; this.dailyLimit = dailyLimit; this.gives = gives;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static class GameData
|
||
|
|
{
|
||
|
|
// ---------- Названия ресурсов на русском ----------
|
||
|
|
public static string ResName(ResourceType t)
|
||
|
|
{
|
||
|
|
switch (t)
|
||
|
|
{
|
||
|
|
case ResourceType.Population: return "Люди";
|
||
|
|
case ResourceType.Food: return "Еда";
|
||
|
|
case ResourceType.Wood: return "Дерево";
|
||
|
|
case ResourceType.Stone: return "Камень";
|
||
|
|
case ResourceType.Iron: return "Железо";
|
||
|
|
case ResourceType.Gold: return "Золото";
|
||
|
|
case ResourceType.Gems: return "Кристаллы";
|
||
|
|
case ResourceType.Power: return "Сила";
|
||
|
|
}
|
||
|
|
return t.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Color ResColor(ResourceType t)
|
||
|
|
{
|
||
|
|
switch (t)
|
||
|
|
{
|
||
|
|
case ResourceType.Food: return new Color(0.85f, 0.45f, 0.3f);
|
||
|
|
case ResourceType.Wood: return new Color(0.6f, 0.4f, 0.2f);
|
||
|
|
case ResourceType.Stone: return new Color(0.6f, 0.6f, 0.65f);
|
||
|
|
case ResourceType.Iron: return new Color(0.45f, 0.55f, 0.7f);
|
||
|
|
case ResourceType.Gold: return new Color(1f, 0.8f, 0.2f);
|
||
|
|
case ResourceType.Gems: return new Color(0.4f, 0.85f, 1f);
|
||
|
|
case ResourceType.Power: return new Color(1f, 0.4f, 0.3f);
|
||
|
|
}
|
||
|
|
return new Color(0.8f, 0.8f, 0.8f);
|
||
|
|
}
|
||
|
|
|
||
|
|
static RewardItem R(ResourceType t, int a) { return new RewardItem(t, a); }
|
||
|
|
|
||
|
|
// ---------- Опыт игрока ----------
|
||
|
|
public static int XpToNextLevel(int level) { return 100 + (level - 1) * 80; }
|
||
|
|
public const int MaxPlayerLevel = 60;
|
||
|
|
|
||
|
|
// ---------- Основные задания (цепочка) ----------
|
||
|
|
public static readonly List<QuestDef> MainQuests = new List<QuestDef>
|
||
|
|
{
|
||
|
|
new QuestDef("m_collect_wood_1", "Собрать 200 дерева", QuestKind.Collect, 200, 30, R(ResourceType.Gold, 200)).Res(ResourceType.Wood),
|
||
|
|
new QuestDef("m_collect_food_1", "Собрать 200 еды", QuestKind.Collect, 200, 30, R(ResourceType.Gold, 200)).Res(ResourceType.Food),
|
||
|
|
new QuestDef("m_build_1", "Построить первое здание", QuestKind.BuildingsBuilt, 1, 50, R(ResourceType.Wood, 300), R(ResourceType.Stone, 200)),
|
||
|
|
new QuestDef("m_barracks", "Построить Казармы", QuestKind.BarracksBuilt, 1, 50, R(ResourceType.Food, 300), R(ResourceType.Wood, 200)),
|
||
|
|
new QuestDef("m_train_10", "Обучить 10 воинов", QuestKind.TroopsTrained, 10, 50, R(ResourceType.Food, 400)),
|
||
|
|
new QuestDef("m_battle_1", "Выиграть первый бой", QuestKind.BattlesWon, 1, 60, R(ResourceType.Gems, 30)),
|
||
|
|
new QuestDef("m_world_send", "Отправить армию в поход на карте мира", QuestKind.WorldMarches, 1, 60, R(ResourceType.Food, 500)),
|
||
|
|
new QuestDef("m_world_win", "Разграбить поселение на карте мира", QuestKind.WorldWins, 1, 100, R(ResourceType.Gems, 50), R(ResourceType.Gold, 500)),
|
||
|
|
new QuestDef("m_hero_3", "Герой 3 уровня", QuestKind.HeroLevel, 3, 60, R(ResourceType.Gold, 500)),
|
||
|
|
new QuestDef("m_th_2", "Ратуша 2 уровня", QuestKind.TownHallLevel, 2, 100, R(ResourceType.Gems, 50), R(ResourceType.Stone, 500)),
|
||
|
|
new QuestDef("m_build_3", "Построить 3 здания", QuestKind.BuildingsBuilt, 3, 80, R(ResourceType.Wood, 600), R(ResourceType.Iron, 200)),
|
||
|
|
new QuestDef("m_arena_1", "Сразиться на арене", QuestKind.ArenaFights, 1, 60, R(ResourceType.Gems, 20)),
|
||
|
|
new QuestDef("m_collect_stone_1", "Собрать 1000 камня", QuestKind.Collect, 1000, 80, R(ResourceType.Gold, 800)).Res(ResourceType.Stone),
|
||
|
|
new QuestDef("m_train_100", "Обучить 100 воинов", QuestKind.TroopsTrained, 100, 120, R(ResourceType.Food, 1500), R(ResourceType.Gems, 30)),
|
||
|
|
new QuestDef("m_battle_5", "Выиграть 5 боёв", QuestKind.BattlesWon, 5, 150, R(ResourceType.Gems, 60)),
|
||
|
|
new QuestDef("m_player_5", "Достигнуть 5 уровня", QuestKind.PlayerLevel, 5, 100, R(ResourceType.Gems, 100)),
|
||
|
|
new QuestDef("m_th_3", "Ратуша 3 уровня", QuestKind.TownHallLevel, 3, 200, R(ResourceType.Gems, 100), R(ResourceType.Iron, 800)),
|
||
|
|
new QuestDef("m_hero_10", "Герой 10 уровня", QuestKind.HeroLevel, 10, 200, R(ResourceType.Gold, 3000)),
|
||
|
|
new QuestDef("m_battle_15", "Выиграть 15 боёв", QuestKind.BattlesWon, 15, 300, R(ResourceType.Gems, 150)),
|
||
|
|
new QuestDef("m_th_5", "Ратуша 5 уровня", QuestKind.TownHallLevel, 5, 400, R(ResourceType.Gems, 250)),
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Ежедневные задания (сбрасываются каждый день) ----------
|
||
|
|
public static readonly List<QuestDef> DailyQuests = new List<QuestDef>
|
||
|
|
{
|
||
|
|
new QuestDef("d_login", "Зайти в игру", QuestKind.Login, 1, 10, R(ResourceType.Gold, 100)),
|
||
|
|
new QuestDef("d_wood", "Собрать 300 дерева", QuestKind.Collect, 300, 20, R(ResourceType.Food, 200)).Res(ResourceType.Wood),
|
||
|
|
new QuestDef("d_food", "Собрать 300 еды", QuestKind.Collect, 300, 20, R(ResourceType.Wood, 200)).Res(ResourceType.Food),
|
||
|
|
new QuestDef("d_train", "Обучить 20 воинов", QuestKind.TroopsTrained, 20, 25, R(ResourceType.Iron, 150)),
|
||
|
|
new QuestDef("d_battle", "Выиграть 1 бой", QuestKind.BattlesWon, 1, 30, R(ResourceType.Gems, 10)),
|
||
|
|
new QuestDef("d_arena", "2 боя на арене", QuestKind.ArenaFights, 2, 30, R(ResourceType.Gems, 10)),
|
||
|
|
new QuestDef("d_world", "Отправить поход на карте мира", QuestKind.WorldMarches, 1, 25, R(ResourceType.Gold, 200)),
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Войска ----------
|
||
|
|
public static readonly List<TroopDef> Troops = new List<TroopDef>
|
||
|
|
{
|
||
|
|
new TroopDef("Пехота", "Дешёвые и надёжные бойцы ближнего боя.", 4, 3f, 1, new Color(0.55f, 0.6f, 0.7f),
|
||
|
|
R(ResourceType.Food, 20), R(ResourceType.Wood, 10)),
|
||
|
|
new TroopDef("Лучники", "Бьют издалека, хороши против пехоты.", 5, 4f, 1, new Color(0.45f, 0.7f, 0.4f),
|
||
|
|
R(ResourceType.Food, 15), R(ResourceType.Wood, 25)),
|
||
|
|
new TroopDef("Кавалерия", "Быстрые и сильные всадники.", 9, 7f, 2, new Color(0.75f, 0.55f, 0.3f),
|
||
|
|
R(ResourceType.Food, 40), R(ResourceType.Iron, 15)),
|
||
|
|
new TroopDef("Осадные", "Катапульты: огромная сила, долгая сборка.", 16, 12f, 3, new Color(0.7f, 0.35f, 0.35f),
|
||
|
|
R(ResourceType.Wood, 60), R(ResourceType.Stone, 40), R(ResourceType.Iron, 30)),
|
||
|
|
};
|
||
|
|
// Размер очереди обучения зависит от числа казарм: см. Barracks.QueueSize
|
||
|
|
|
||
|
|
// ---------- Герой ----------
|
||
|
|
public static int HeroMaxLevel(int townHallLevel) { return Mathf.Min(50, Mathf.Max(1, townHallLevel) * 5); }
|
||
|
|
public static int HeroAttack(int lvl) { return 10 + (lvl - 1) * 5; }
|
||
|
|
public static int HeroDefense(int lvl) { return 8 + (lvl - 1) * 4; }
|
||
|
|
public static int HeroArmyBonusPercent(int lvl) { return lvl * 2; }
|
||
|
|
public static int HeroPower(int lvl) { return 60 + lvl * 40; }
|
||
|
|
public static RewardItem[] HeroUpgradeCost(int lvl)
|
||
|
|
{
|
||
|
|
float m = Mathf.Pow(1.35f, lvl - 1);
|
||
|
|
return new[] { R(ResourceType.Gold, Mathf.RoundToInt(100 * m)), R(ResourceType.Food, Mathf.RoundToInt(150 * m)) };
|
||
|
|
}
|
||
|
|
public static readonly int[] HeroSkillLevels = { 1, 5, 10, 20, 35 };
|
||
|
|
public static readonly string[] HeroSkillNames =
|
||
|
|
{
|
||
|
|
"Боевой клич: +5% к силе армии",
|
||
|
|
"Железная стена: +10% защиты войск",
|
||
|
|
"Вдохновение: +10% к наградам за бой",
|
||
|
|
"Стратег: потери в бою -20%",
|
||
|
|
"Легенда: +15% к силе армии",
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Кампания (вкладка "Бой") ----------
|
||
|
|
public static readonly string[] EnemyNames =
|
||
|
|
{
|
||
|
|
"Лагерь разбойников", "Волчье логово", "Застава мародёров", "Гоблинский форт", "Тёмная роща",
|
||
|
|
"Пиратская бухта", "Крепость орков", "Проклятые руины", "Логово тролля", "Цитадель некроманта"
|
||
|
|
};
|
||
|
|
public static string StageName(int stage) { return EnemyNames[(stage - 1) % EnemyNames.Length] + (stage > EnemyNames.Length ? " II" : ""); }
|
||
|
|
public static int StageEnemyPower(int stage) { return Mathf.RoundToInt(80f * Mathf.Pow(1.22f, stage - 1)); }
|
||
|
|
public static RewardItem[] StageRewards(int stage)
|
||
|
|
{
|
||
|
|
return new[]
|
||
|
|
{
|
||
|
|
R(ResourceType.Gold, 60 + stage * 25),
|
||
|
|
R(ResourceType.Food, 80 + stage * 30),
|
||
|
|
R(ResourceType.Iron, 20 + stage * 10),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
public static int StageXp(int stage) { return 20 + stage * 6; }
|
||
|
|
public static int StageGems(int stage) { return stage % 5 == 0 ? 25 : 0; } // каждый 5-й этап -- кристаллы
|
||
|
|
|
||
|
|
// ---------- Арена ----------
|
||
|
|
public const int ArenaMaxTickets = 5;
|
||
|
|
public const int ArenaTicketMinutes = 20;
|
||
|
|
public static readonly string[] BotNames =
|
||
|
|
{
|
||
|
|
"Ярослав", "Мирон", "Север", "Ведана", "Ратибор", "Злата", "Горыня", "Любава", "Святогор", "Огнеслав",
|
||
|
|
"Ingvar", "Sigrun", "Bjorn", "Astrid", "Leif", "Ragnar", "Freya", "Ulf", "Helga", "Torvald"
|
||
|
|
};
|
||
|
|
public static string ArenaLeague(int rating)
|
||
|
|
{
|
||
|
|
if (rating < 1100) return "Бронза";
|
||
|
|
if (rating < 1300) return "Серебро";
|
||
|
|
if (rating < 1600) return "Золото";
|
||
|
|
if (rating < 2000) return "Платина";
|
||
|
|
return "Алмаз";
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- Ежедневная награда за вход (7 дней) ----------
|
||
|
|
public static readonly RewardItem[][] LoginRewards =
|
||
|
|
{
|
||
|
|
new[] { R(ResourceType.Gold, 500) },
|
||
|
|
new[] { R(ResourceType.Food, 1000) },
|
||
|
|
new[] { R(ResourceType.Wood, 800), R(ResourceType.Stone, 500) },
|
||
|
|
new[] { R(ResourceType.Gems, 50) },
|
||
|
|
new[] { R(ResourceType.Iron, 600) },
|
||
|
|
new[] { R(ResourceType.Gold, 2000) },
|
||
|
|
new[] { R(ResourceType.Gems, 200) },
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Магазин ----------
|
||
|
|
public static readonly List<ShopItemDef> ShopItems = BuildShop();
|
||
|
|
static List<ShopItemDef> BuildShop()
|
||
|
|
{
|
||
|
|
var list = new List<ShopItemDef>
|
||
|
|
{
|
||
|
|
new ShopItemDef("free_chest", "Бесплатный сундук (раз в день)", ResourceType.Gems, 0, 1),
|
||
|
|
new ShopItemDef("wood_1k", "Пакет дерева", ResourceType.Gems, 50, 5, R(ResourceType.Wood, 1000)),
|
||
|
|
new ShopItemDef("stone_1k", "Пакет камня", ResourceType.Gems, 50, 5, R(ResourceType.Stone, 1000)),
|
||
|
|
new ShopItemDef("food_15", "Пакет еды", ResourceType.Gems, 50, 5, R(ResourceType.Food, 1500)),
|
||
|
|
new ShopItemDef("iron_500", "Пакет железа", ResourceType.Gems, 60, 5, R(ResourceType.Iron, 500)),
|
||
|
|
new ShopItemDef("gold_1k", "Мешок золота", ResourceType.Gems, 80, 3, R(ResourceType.Gold, 1000)),
|
||
|
|
new ShopItemDef("gems_10", "Обмен золота на кристаллы", ResourceType.Gold, 1000, 3, R(ResourceType.Gems, 10)),
|
||
|
|
};
|
||
|
|
var p1 = new ShopItemDef("iap_small", "Горсть кристаллов", ResourceType.Gems, 0, 0, R(ResourceType.Gems, 500));
|
||
|
|
p1.comingSoon = true; p1.priceLabel = "99 руб.";
|
||
|
|
var p2 = new ShopItemDef("iap_big", "Сундук кристаллов", ResourceType.Gems, 0, 0, R(ResourceType.Gems, 3000));
|
||
|
|
p2.comingSoon = true; p2.priceLabel = "499 руб.";
|
||
|
|
list.Add(p1); list.Add(p2);
|
||
|
|
return list;
|
||
|
|
}
|
||
|
|
// Содержимое бесплатного сундука (выбирается случайно)
|
||
|
|
public static readonly RewardItem[] FreeChestPool =
|
||
|
|
{
|
||
|
|
R(ResourceType.Wood, 500), R(ResourceType.Stone, 400), R(ResourceType.Food, 600),
|
||
|
|
R(ResourceType.Iron, 250), R(ResourceType.Gold, 400), R(ResourceType.Gems, 15),
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Еженедельное событие ----------
|
||
|
|
public const string EventTitle = "Сбор войск";
|
||
|
|
public const string EventDescription =
|
||
|
|
"Очки события даются за: победу в бою (+10), разграбление на карте мира (+15), бой на арене (+5), выполненное задание (+20), каждые 5 обученных воинов (+1).";
|
||
|
|
public static readonly int[] EventTiers = { 50, 150, 300, 500, 800 };
|
||
|
|
public static readonly RewardItem[][] EventTierRewards =
|
||
|
|
{
|
||
|
|
new[] { R(ResourceType.Gold, 500) },
|
||
|
|
new[] { R(ResourceType.Food, 1500), R(ResourceType.Wood, 1000) },
|
||
|
|
new[] { R(ResourceType.Gems, 50) },
|
||
|
|
new[] { R(ResourceType.Iron, 1500), R(ResourceType.Gold, 2000) },
|
||
|
|
new[] { R(ResourceType.Gems, 200) },
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------- Текст наград ----------
|
||
|
|
public static string RewardsText(RewardItem[] rewards)
|
||
|
|
{
|
||
|
|
if (rewards == null || rewards.Length == 0) return "";
|
||
|
|
var parts = new List<string>();
|
||
|
|
foreach (var r in rewards) parts.Add(Short(r.amount) + " " + ResName(r.type));
|
||
|
|
return string.Join(", ", parts.ToArray());
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string Short(float value)
|
||
|
|
{
|
||
|
|
if (value >= 1000000f) return (value / 1000000f).ToString("0.#") + "M";
|
||
|
|
if (value >= 10000f) return (value / 1000f).ToString("0.#") + "K";
|
||
|
|
return Mathf.FloorToInt(value).ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string FormatTime(double seconds)
|
||
|
|
{
|
||
|
|
if (seconds < 0) seconds = 0;
|
||
|
|
TimeSpan t = TimeSpan.FromSeconds(Math.Ceiling(seconds));
|
||
|
|
if (t.TotalDays >= 1) return string.Format("{0}д {1}ч", (int)t.TotalDays, t.Hours);
|
||
|
|
if (t.TotalHours >= 1) return string.Format("{0}ч {1:00}м", (int)t.TotalHours, t.Minutes);
|
||
|
|
return string.Format("{0:00}:{1:00}", t.Minutes, t.Seconds);
|
||
|
|
}
|
||
|
|
}
|