2026-10-12 23:54:03 +03:00
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 ;
// ---------- Основные задания (цепочка) ----------
2026-11-05 18:29:31 +01:00
// Синие кристаллы (регламент 05.11, разд. 3): простое задание -- 1, сложное -- 2 (было 20-250). В ежедневных
// кристаллов нет: кристальное задание -- не чаще раза в 7-14 дней. Числа ТЕСТ
2026-10-12 23:54:03 +03:00
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 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_battle_1" , "Выиграть первый бой" , QuestKind . BattlesWon , 1 , 60 , R ( ResourceType . Gems , 1 )),
2026-10-12 23:54:03 +03:00
new QuestDef ( "m_world_send" , "Отправить армию в поход на карте мира" , QuestKind . WorldMarches , 1 , 60 , R ( ResourceType . Food , 500 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_world_win" , "Разграбить поселение на карте мира" , QuestKind . WorldWins , 1 , 100 , R ( ResourceType . Gems , 2 ), R ( ResourceType . Gold , 500 )),
2026-10-12 23:54:03 +03:00
new QuestDef ( "m_hero_3" , "Герой 3 уровня" , QuestKind . HeroLevel , 3 , 60 , R ( ResourceType . Gold , 500 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_th_2" , "Ратуша 2 уровня" , QuestKind . TownHallLevel , 2 , 100 , R ( ResourceType . Gems , 1 ), R ( ResourceType . Stone , 500 )),
2026-10-12 23:54:03 +03:00
new QuestDef ( "m_build_3" , "Построить 3 здания" , QuestKind . BuildingsBuilt , 3 , 80 , R ( ResourceType . Wood , 600 ), R ( ResourceType . Iron , 200 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_arena_1" , "Сразиться на арене" , QuestKind . ArenaFights , 1 , 60 , R ( ResourceType . Gems , 1 )),
2026-10-12 23:54:03 +03:00
new QuestDef ( "m_collect_stone_1" , "Собрать 1000 камня" , QuestKind . Collect , 1000 , 80 , R ( ResourceType . Gold , 800 )). Res ( ResourceType . Stone ),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_train_100" , "Обучить 100 воинов" , QuestKind . TroopsTrained , 100 , 120 , R ( ResourceType . Food , 1500 ), R ( ResourceType . Gems , 1 )),
new QuestDef ( "m_battle_5" , "Выиграть 5 боёв" , QuestKind . BattlesWon , 5 , 150 , R ( ResourceType . Gems , 1 )),
new QuestDef ( "m_player_5" , "Достигнуть 5 уровня" , QuestKind . PlayerLevel , 5 , 100 , R ( ResourceType . Gems , 1 )),
new QuestDef ( "m_th_3" , "Ратуша 3 уровня" , QuestKind . TownHallLevel , 3 , 200 , R ( ResourceType . Gems , 2 ), R ( ResourceType . Iron , 800 )),
2026-10-12 23:54:03 +03:00
new QuestDef ( "m_hero_10" , "Герой 10 уровня" , QuestKind . HeroLevel , 10 , 200 , R ( ResourceType . Gold , 3000 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "m_battle_15" , "Выиграть 15 боёв" , QuestKind . BattlesWon , 15 , 300 , R ( ResourceType . Gems , 2 )),
new QuestDef ( "m_th_5" , "Ратуша 5 уровня" , QuestKind . TownHallLevel , 5 , 400 , R ( ResourceType . Gems , 2 )),
2026-10-12 23:54:03 +03:00
};
// ---------- Ежедневные задания (сбрасываются каждый день) ----------
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 )),
2026-11-05 18:29:31 +01:00
new QuestDef ( "d_battle" , "Выиграть 1 бой" , QuestKind . BattlesWon , 1 , 30 , R ( ResourceType . Gold , 200 )),
new QuestDef ( "d_arena" , "2 боя на арене" , QuestKind . ArenaFights , 2 , 30 , R ( ResourceType . Gold , 200 )),
2026-10-12 23:54:03 +03:00
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 );
}
}