
В 90-х и 2000-х студия Impressions Games выпускала отличные исторические градостроительные симуляторы. Я играл во все игры этой серии от незабываемого Caesar 3, который вообще был первой моей компьютерной игрой на отцовском компуктере, до Императора про древний Китай. Но египетский Pharaoh и греческий Zeus запомнились намного четче, но вот почему я сказать не берусь.
Единственное серьёзное отличие между Фараоном и Зевсом в нетехническом плане — это были графические ресурсы, внутри же там полностью сменилась вся команда и со слов старожилов сменился движок, но серия уже была известна и многочисленные отличия по возможности скрыли, перенеся практически без изменений разные механики из Фараона в Зевс, да похоже перестарались, и многим сейчас игра покажется скорее дополнением, а не полноценной номерной частью серии.
И всё же, эти относительно незначительные различия в механиках в сумме делают Зевса более качественной игрой, но качественной в плане QoL - Quality of Live, т.е. мы вам дорогие игроки отсыпем изменений, но большая часть из них просто сделает игру проще, не меняя в общем ничего. Это немного печалит, потому что от новой части ждешь возможности построить другой красивый город, а не тот, что ты уже 50 миссий подряд построил в пустыне.
Но различия есть, и многим игрокам, кто начал знакоство с серией по творению учеников гениального Саймона Бредбури (мэтр ушел из студии после Цезаря делать Stronghold) с Зевса - считают её лучшей в серии. Хотя те, кто начинал с Фараона считают лучшей в серии именно эту часть, как я например, про любителей римлян уже и не говорю, у них там своя тусовка и свои боги. Еще придется немного затронуть ремейк Pharaoh: A New Era (коли уж он вышел и хотел покуситься на лавры предков), и сравнение будет, к сожалению, не в его пользу, так что не обижайтейсь.
Первое различие в старом Фараоне - это распределение рабочих, и именно его в переиздании Pharaoh: A New Era изменили так, чтобы оно работало как в Zeus, но это была одна из тех мелких фишечек оригинального Фараона, которая делала достоверным развитие города. Не не очень красиво, когда твои рабочие по волшебсту добегают до самых уголоков карты и работают там. В Pharaoh, когда ты строишь здание, которому нужны рабочие (а это почти любое здание), появляется специальный «рекрутёр», который начинает идти по дороге в поисках ближайших жилых домов, откуда можно забрать работников, причем он идет не точно к домам, он идет "просто так", а на перекрестках поворачивает в "случайную" сторону. Случайность тут неслучайная, а генерится при старте уровня для каждого тайла, потому что компы были тогда не очень мощные и делать настоящую случайность было дорого, вот так её и зашили в каждый тайл карты.


Как это сделано в коде игры (у меня он включается опцией)
void building::common_spawn_labor_seeker(int min_houses) { if (g_city.population.current <= 0) { return; } // тут проверка что включен глобальный пул if (!!game_features::gameplay_change_global_labour) { // и просто проверяется что любые дома присоединены к дороге houses_covered = std::min(300, distance_from_entry ? 2 * min_houses : 0); return; } // а иначе используем логику с проверкой наличия домов рядом // со зданием if (houses_covered > min_houses) { return; } if (has_figure(BUILDING_SLOT_LABOR_SEEKER)) { // no figure slot available! return; } create_roaming_figure(FIGURE_LABOR_SEEKER, FIGURE_ACTION_125_ROAMING, BUILDING_SLOT_LABOR_SEEKER); }
А в Зевсе жилые дома просто добавляют рабочих в глобальный пул, из которого остальные здания автоматически их черпают — никаких рекрутёров, само собой нет, как отпадает и необходимость строить жильё рядом с рабочими зонами. Это значительно упрощает создание отдалённых производственных «форпостов» — например, можно построить серебряные шахты прямо у месторождения или торговые доки где-нибудь у подходящего изгиба реки.
Всё, что было нужно для этого в Pharaoh — это пара хижин первого уровня. Можно снабдить их водой и едой, если удобно, тогда туда заселится больше людей, что даст прирост населения (а это почти всегда было плюс), но делать это не обязательно. Главное — просто обеспечить наличие жилья в пределах досягаемости от рабочего места.
Такая система распределения рабочих в Pharaoh снижает, но не устраняет полностью, важность заботы о благополучии жителей цифрового города и даже при богатом и заваленом товарами городе там, скорее всего, будут существовать трущобы — временные поселения в неудобных или отдалённых местах, где нет смысла строить всю инфраструктуру. В Zeus же любые дома ниже максимального уровня — это пустая трата места.
Можно разместить там больше людей, а значит, увеличить рабочую силу и налоговую базу, если просто поставить рядом дополнительный склад с оливковым маслом и шерстью для апгрейда домов.
Кстати в Фараоне "бедные" дома не платят налоги, а в Зевсе платят все
int figure_tax_collector::provide_service() { int max_tax_rate = 0; int houses_serviced = figure_provide_service(tile(), &base, [&] (building *b, figure*) { // rtti без dynamic_cast, проверяем что здание это дом, а не // например библиотека auto house = b->dcast_house(); if (!house) { return; } if (house->house_population() > 0) { // настройки сколько платит дом задаются через конфиг // в Зевсе дома платят просто x * уровень дома // упрощение не пошло игре на пользу, ибо очень снизило сложность int tax_multiplier = model_get_house(house->house_level()).tax_multiplier; if (tax_multiplier > max_tax_rate) { max_tax_rate = tax_multiplier; } if (house->house_level() < HOUSE_ORDINARY_COTTAGE) { runtime_data().poor_taxed++; } else if (house->house_level() < HOUSE_COMMON_MANOR) { runtime_data().middle_taxed++; } else { runtime_data().reach_taxed++; } // еще один игродевовский паттерн для // для хранения разных данных без аллокаций // в одном блоке памяти auto &housed = house->runtime_data(); housed.tax_collector_id = home()->id; housed.tax_coverage = 50; } }); base.min_max_seen = max_tax_rate; return houses_serviced; }
В Pharaoh, если тебе нужно построить золотую шахту посреди пустыни в надежде получать с неё золото, то чаще проще просто забить на неё и развернуть производству гончарных изделий и пива, которые дадут больше прибыли через торговлю. Понимание этого нюанса приходит не сразу, а миссии где-то с десятой и приходится только удивляться, это баг или продуманная фича, лично я склоняюсь к первому варианту.
Zeus определённо делает игру более приятной и легкой в управлении, но подход Pharaoh придаёт симуляции реалистичность, несовершенность, а попытки сделать всем хорошо ломают баланс и добавляют вызовов. Не скажу что мне не нравится глобальный пул рабочих, но если игра отбирает у игрока такие мелкие радости, которые можно обнаружить посреди прохождения кампании, то дизайнер где-то профилонил свои прямые обязанности. Тем не менее, подход Zeus явно выигрывает, если мы играем кампанию именно как сюжет, а не как песочницу, как это сделана в Фараоне. Кстати Pharaoh: ANA взял понемногу от обоих подходов, не поняв однако нюансов, докинули сверху миниквестов из мира мобильных игр, и в итоге получилась вообще каша.

Второе важное различие между играми - миссии, а точнее стиль подачи и ожидания от игрока. В Zeus кампания разбита на серии из нескольких «приключений» и в каждом игрок строит основной город и основывает несколько колоний. Каждая колония получает по отдельной миссии и развивается гдето до 1000–2000 жителей — это менее половины от размера главного города, но строится с определенной целью, т.е. это действительно миссия, а не отдельный город.
Иногда ограничения таких колоний реализованы достаточно жёстко — через отключённые механики, а иногда через нехватку места или ресурсов, но почти никогда игра не заставляет тратить ресурсы на строительство полной военной инфраструктуры в колонии. И бывает достаточно поставить один-два храма разным богам - в Зевсе это элемент игры выдвинули на первый план, исторически понятно: Греция - родина слонов, то есть богов, но с точки зрения игровых механик - просто поленились нормально забалансить миссии. Главный город в этом случае предоставляет свободный доступ к ключевым товарам, что позволяет построить почти автономную инфраструктуру, экспортируюя излишки в колонии, что создаёт постоянную "видимость" нагрузки на экономику и торговлю.
В Pharaoh же игра поделена на четыре периода: учебный период, а затем Древнее, Среднее и Новое царства. В каждом периоде несколько эпизодов и каждый требует строить новый город с нуля, часть миссий похожи на колонии из Zeus — с простыми задачами и ограниченными картами, другие напоминают «основной» город — с высокими требованиями, но и с богатой картой, тут не очень понятно это в Зевсе развили эту идею до колоний или просто стилистика миссий сама вела к такому ходу событий. Главное, и пожалуй самое печальное отличие игрового дизайна миссий в том, что игра не сохраняет основной город между эпизодами, и каждый уровень — это полный рестарт, независимо от успехов на предыдущем. Для игр начала нулевых это было священной коровой, и даже ребята под руководством Саймона не решили это ломать, а вот ребята из ремейка сломать не побоялись и таки сломали, только это не комплимент: теперь у нас просто набор миссий не связаных ни сюжетом ни историей.
В Зевсе финальный эпизод первого приключения заканчивается задачей победить группу предавших союзников в решающей битве, требующей развитой военной инфраструктуры, и тут игра заставляет полноценно использовать механику армии, к чему игрок был не готов, потому что боевки достаточно мало. Но делается это с тем же самым городом, который развивался в течение всего эпизода — не с нуля, а сразу начиная с развитого производства.
В Pharaoh аналогичная по сложности миссия начнётся на новой карте с пустыми берегами Нила, и снова придется раскладывать блоки базового жилья, строить фермы, водопроводы и склады, но на мой взгляд это не является каким-то минусом, потому что игра за предыдущие миссии дает перепробовать кучу вариантов, чтобы подобрать удобный лично тебе. Этот первый час каждого уровня проходит быстро, прежде чем можно будет перейти к более интересным и целенаправленным задачам, а начинает он надоедать аккурат к 40 миссии, т.е. самому концу игры,
Что было серьезно переработано, но переработано тут не очень уместное слово, скорее капитально сломано - так это система жилых домов. В Зевсе жильё делится на два типа: обычное и элитное. Обычные дома всегда занимают площадь 2×2 клетки, а элитные — 4×4 и в принципе это всё, знай себе добавляй рядом нужные постройки для уровня, а иногда игра багует и просто поднимает или снижает уровень дома без видимых причин, и это не исправили даже в патчах, т.е. вы вообще могли залить всю карту домами первого уровня и пройти миссию не испытывая нехватки рабочих.
В Pharaoh же жильё начинается с домика 1×1, и увеличивается в размерах по мере предоставления новых услуг. Переход от обычного жилья к элитному происходит при апгрейде с 2×2 до 3×3. Всего в Pharaoh 20 уровней развития жилья, в то время как в Zeus — 11 (или 12, если учитывать опустевшие элитные дома, потерявшие необходимый уровень обслуживания). И заливка карты домиками тут не только не помогает, но и начинает вредить, в какой-то момент срабатывает отрицательный коэффициент миграции, и люди начинают покидать город. Вот эту логику фактически убрали из Зевса, оставив только периодический приход новых поселенцев, и новый Фараон взял это за основу.
Вот тут немного про апдейт миграции
void city_migration_t::update_status() { auto& params = g_migration_params; const auto &sentiment = g_city.sentiment; auto it = std::find_if(g_migration_sentiment_influence.begin(), g_migration_sentiment_influence.end(), [&] (const auto& t) { return sentiment.value > t.s; }); // Определяем влияние настроения населения на миграцию // Устанавливаем процент миграции на основе настроения населения // Если найден подходящий порог, используем соответствующий процент влияния, иначе 0 percentage_by_sentiment = (it != g_migration_sentiment_influence.end()) ? it->i : 0; percentage = percentage_by_sentiment; immigration_amount_per_batch = 0; emigration_amount_per_batch = 0; // Проверка лимита населения // Если достигнут лимит населения, останавливаем иммиграцию // иногда миссия ставит свой лимит if (population_cap > 0 && g_city.population.current >= population_cap) { percentage = 0; migration_cap = true; return; } // war scares immigrants away if (g_city.figures_total_invading_enemies() > 3 && percentage > 0) { percentage = 0; invading_cap = true; return; } // Обработка иммиграции (положительный процент) if (percentage > 0) { // immigration if (emigration_duration) { emigration_duration--; } else { // Рассчитываем количество иммигрантов за цикл на основе процента immigration_amount_per_batch = calc_adjust_with_percentage<int>(params.max_newcomers_per_update, percentage); immigration_duration = 2; } } // Обработка эмиграции (отрицательный процент) else if (percentage < 0) { // emigration if (immigration_duration) { immigration_duration--; } else if (g_city.population.current > 100) { emigration_amount_per_batch = calc_adjust_with_percentage<int>(params.max_leftovers_per_update, -percentage); emigration_duration = 2; } } }


Требования к улучшению жилья в Pharaoh растут схожим образом с Zeus: требуется всё больше товаров и развлечений, но Pharaoh добавляет ещё больше уровней за счёт религиозных и бытовых служб. Чтобы добиться максимального уровня жилья в Pharaoh, игроку нужно обеспечить доступ к местам поклонения разным богам, а также множеству специфических служб, таких как стоматолог, школа писцов, морг, врач, суд и библиотека. А уместить их все рядом задача из разряда построить все пути в голове и поставить домики, там где они пересекаются. Кроме того, на высоких уровнях требуется наличие нескольких видов пищи одновременно.
Как в Фараоне дома потребляют еду
resource_list building_house::consume_food() { if (!hsize()) { return {}; } // разделяемые данные, которые для любых зданий лежат рядом // с общими данными, но уникальна для каждого типа здания auto &d = runtime_data(); int num_types = model().food_types; uint16_t amount_per_type = calc_adjust_with_percentage<short>(d.population, 35); if (num_types > 1) { amount_per_type /= num_types; } d.num_foods = 0; resource_list food_types_eaten; if (scenario_property_kingdom_supplies_grain()) { d.foods[0] = amount_per_type; food_types_eaten[RESOURCE_GRAIN] += amount_per_type; d.num_foods = 1; return food_types_eaten; } if (num_types <= 0) { return {}; } // дом сьест сначала дешевую еду, а потом дорогую // В Зевсе дом ест по частям всю доступную еду, упрощение // но частично сломало механику роста домов for (int t = INVENTORY_MIN_FOOD; t < INVENTORY_MAX_FOOD && d.num_foods < num_types; t++) { const uint16_t exist_amount = std::min(d.foods[t], amount_per_type); d.foods[t] -= exist_amount; d.num_foods += (exist_amount > 0) ? 1 : 0; e_resource food_res = g_city.allowed_foods(t); food_types_eaten.push_back({ food_res, amount_per_type }); } return food_types_eaten; }
И как потребляются ресурсы
resource_list building_house::consume_resources() { if (!hsize()) { return {}; } resource_list good_types_consumed; auto &d = runtime_data(); const model_house& model = model_get_house(house_level()); auto consume_resource = [&] (int inventory, uint16_t amount) { if (amount <= 0) { return; } amount = std::min(amount, d.inventory[inventory]); d.inventory[inventory] -= amount; good_types_consumed.push_back({ (e_resource)inventory, amount }); }; // И вот такое потребление всех типов ресурсов сделано в Зевсе // есть ресурс - он используется, опять же упрощение // но сильно меняет баланс товаров в городе consume_resource(INVENTORY_GOOD1, model.pottery); consume_resource(INVENTORY_GOOD2, model.jewelry); consume_resource(INVENTORY_GOOD3, model.linen); consume_resource(INVENTORY_GOOD4, model.beer); return good_types_consumed; }
Все это приводит к тому, что для развития одного блока жилья до максимального уровня в Pharaoh необходимо построить огромное количество обслуживающих зданий. И здесь проявляется одна из самых раздражающих проблем, которую условно можно назвать «египетской тупостью» — неумной логикой обхода. Товары и услуги распространяются с помощью ходоков, которых порождают обслуживающие здания: здание врача порождает персонажа, который идёт по дороге и обслуживает все дома, мимо которых проходит. Проблема в том, что на развилках эти ходоки выбирают направление случайно, насколько "случайное" я уже написал. То есть врач, вместо того чтобы пойти налево и обойти весь жилой блок, может пойти направо, пройти мимо складов, храмов и фонтанов, и вернуться обратно в здание, ни разу не попав в нужный район. Такой вот врач, и если хотя бы несколько «провалят» свою роль из-за такого случайного выбора, дома могут начать деградировать.
Что приводило к довольно забавным ситуациям, когда тебя игра фактически вынуждает строить закольцованные районы: дома располагаются по внутренней стороне дороги, а обслуживающие здания — по внешней, гарантирует проход ходоков, независимо от того, повернут ли они налево или направо при появлении.


К идее постройки кольцевых блоков игрок приходит уже где-то на миссии десятой, но сам факт "египетской тупости" раздражает, а причины я описал выше. Слабое железо приводило к простым эвристикам, которые не всегда работали хорошо. Я очень удивился реализации этой системы Pharaoh, когда разбирал как она реализована, тем более с тем, что она работает в паре со сложными требованиями к жилью, множеством ресурсов и обслуживающих зданий. Думаю вот сейчас у нас железо достаточное мощное и можно спокойно сделать нормальную эвристику направления, а не случайное направление, но это для следующих обновлений, а пока сохранена "историческая тупость".
Как это было сделано в коде игры
// большая часть данных в игре статическая // динамических аллокаций практически нет, это нормально для игр тех лет static grid_xx random_xx = {0, FS_UINT8}; void map_random_clear() { map_grid_clear(random_xx); } void map_random_init() { int grid_offset = 0; // в этом цикле раздаем случайные повороты на тайлах карты // при старте уровня, "египетская тупость" for (int y = 0; y < GRID_LENGTH; y++) { for (int x = 0; x < GRID_LENGTH; x++, grid_offset++) { random_generate_next(); map_grid_set(random_xx, grid_offset, (uint8_t)random_short()); } } } int map_random_get(int grid_offset) { return map_grid_get(random_xx, grid_offset); } io_buffer* iob_random_grid = new io_buffer([](io_buffer* iob, size_t version) { iob->bind(BIND_SIGNATURE_GRID, &random_xx); });

На скриншоте показано куда с большой вероятностью будут "случайно" поворачивать жители, но этого инструмента визуализации у игроков не было. Эта "случайность" где-то к середине игры начинает реально мешать, Pharaoh следует "условной" исторической прогрессии и новый здания открываются постепенно архаика, Старое, Среднее и Новое царства, и по мере развития технологий в каждом сценарии получаешь доступ к чему-то новому. И каждый новый уровень приносит ещё одно здание, которое нужно как-то впихнуть в твой блок, что иногда приводит к полной пересборке устоявшейся схемы района.
В Zeus же порядок кампании основан на сложности, а не на исторической последовательности, игра тоже открывает постройки не сразу, но к середине первого приключения ты уже получаешь доступ почти ко всем зданиям, что даёт возможность выработать стиль один раз и использовать его в течение всей игры, но это упрощение делает в целом игру несколько скучной после середины. В Pharaoh, напротив, каждый новый сценарий — это новая игра со своими особенностями, особенно в поздних периодах, когда инфраструктура становится сложнее, за что я так и люблю её.
В ремейке Pharaoh: ANA все эти решения убрали, и добавили мобильных механик: теперь довольные боги могут магическим образом ускорить строительство монумента на определённый процент, не хватает только микротранзакций и монеток над домами, которые нужно собирать. То есть, если ты регулярно устраиваешь фестивали и поддерживаешь религиозную инфраструктуру, то это влияет на скорость постройки пирамиды не меньше, а иногда и больше, чем добавление новых кирпичных мастерских и, как вы могли догадаться, делает развитие города несбалансированным без реальной встройки в геймплей. Потому что любые "божественные" вмешательства - это факторы, которые очень трудно балансить из-за непредсказуемости, ситуация примерно как со случайными поворотами.
Я помню миссию на строительство пирамид Гизы, тогда в 2001 она заняла что-то около 100 игровых лет, или неделю вечеров по несколько часов. У меня была на тот момент очень несбалансированная экономика в городе (правда своим подростковым умом я этого не видел), зато вижу сейчас, когда есть на руках восстановленные исходники и можно посмотреть и подкрутить разные ручки, но тогда это казалось супер трудной миссией. Это была миссия времён Старого царства, и она не позиционировалась как финальное испытание на мастерство в логистике, хотя по факту ею являлась, и по отзывам на форумах так и остается одной из самых трудных мирных миссий в игре.

Лучше ли Zeus чем Pharaoh?
Это разные игры, сделаны разными командами и разными дизайнерами, пусть и в похожей упаковке, и играть в них надо по разному. В Zeus есть цепочка из 5–8 эпизодов, связанных одним городом, который развивается постепенно. В Pharaoh же каждая из 50 миссий это отдельная головоломка, которая начинается с чистого листа. Упрощённые жилые блоки Зевса помогали справляться с «египетской тупостью», а вот в Фараоне волей-неволей приходилось становиться мастером планировки.


P.S. Что мне действительно нравится, так это то, что получилось, пусть даже в урезанном виде, восстановить функционал этой легендарной, без преувеличения, игры. Если вам нравится эстетика Древнего Египта и очень старый код - приходите в Akhenaten (можно поиграть теперь и в браузере (https://dalerank.github.io/ все еще нужны ресурсы OG), за что отдельное спасибо Roman Turchin). А я продолжаю восстанавливать систему врагов и боевку, и поверьте, там есть чему поучиться у старых игр (https://github.com/dalerank/Akhenaten)

P.P.S Немного рекламы моего курса по программированию на Stepik
Примерно с полгода назад, я опубликовал на Хабре цикл статей про игровую разработку (начинать можно отсюда https://habr.com/ru/articles/873016/), которая была хорошо принята сообществом. Мои знакомые и некоторые Хабровчане просили выложить эту информацию в более удобном и концентрированном виде, в виде курса по С++ или одной большой статьи. Решил сделать пробный шар в виде небольшого курса по программированию на С++ без аллокаций, он действительно небольшой - всего 45 уроков и захватил пару статей из цикла, но если вам понравится можно попробовать сделать еще один по интересным темам (Нескучное программирование. С++ без аллокаций памяти). Курс платный, дабы отсеять охотников на халявные сертификаты и любителей пошуметь в коментах. Тем кто меня читает - промокод (HABR50), если нужна скидка больше или бесплатный инвайт напишите в личку.
