Zemeroth - двухмерная пошаговая игра

Потихоньку разрабатываю двухмерную пошаговую тактику “Земерот” на движке ggez.

Видение проекта:

  • Одиночная “цифровая настолка”;
  • Небольшие карты-арены с неподвижной камерой;
  • 3-6 бойцов под контролем игрока;
  • Шансы попаданий, значительная роль случайностей;
  • Вечная смерть (permadeth) и короткие игровые сессии: кампания ~ 1 час, стратегический режим - 2/3 часа;
  • Акцент на реакционные атаки и прерывания действий;
  • Действиям желательно иметь больше одного эффекта;
  • Намеренно “тупой” и предсказуемый ИИ противников;
  • Угловатая векторная 2д графика, 3-5 статических спрайтов на каждый вид агентов;

Стараюсь как могу уменьшить размах проекта, что бы реально закончить игру в этот раз, а не как обычно :slight_smile: .


Внешний вид версии 0.5 (2019.05.13):


Процесс разработки разделен на два этапа:

  • Первый этап включает в себя: тактическое ядро, дерево улучшений бойцов, уникальные действия классов, короткая кампания на пару часов в виде линейной серии боев с вечной смертью (permadeath) и финальный босс - демон Земерот. Разные виды врагов будут иметь простые, но отличающиеся шаблоны поведения, которые дополняют друг друга и создают сложные тактические задачи.
  • Во втором этапе будет добавлен простой стратегический слой, дающий тактическим боям более разнообразный контекст и рассказывающий предысторию придворного мага, который в тайне заключил сделку с демонами. Важной механикой стратегического режима должно стать сокрытие своих демонических умений и прислужников до момента открытого предательства.

Сейчас (2019.05.20) разработка где-то на середине первого этапа.


Ссылки:

Эта тема задумана как нечто среднее между твитером и блогом. Постараюсь по мере продвижения разработки выкладывать сюда ответами микрообновления + поглядывать за актуальностью информации в шапке.

9 лайков

А ты планируешь делать что-то с интерфейсом?

1 лайк

Что случилось с выпуска 0.4

Историю Земерота до версии 0.4 легко проследить по заметкам в журнале, но с выхода этой версии прошло уже много месяцев - чего существенного произошло?

Häte2d -> ggez

Во первых, я прочувствовал, что свой движок это весело поначалу, но потом мелких проблем становится слишком много. Тем более не классно вкладывать в это силы, видя что параллельно Icefoxen решает примерно те же задачи. Так что Häte убит, а Земерот портирован на ggez.

Häte умел несколько вещей, которые (специально) не умеет минималистичный ggez, так что в процессе перехода выделилось два пакета, дополняющих возможности ggez:

  • ggwp-zgui - очень простой GUI; умеет только в примитивные кнопки и лэйауты и все.

  • ggwp-zscene - простой менеджер сцены и декларативных анимаций.

Про название пакетов: Icefoxen попросил не называть неофициальные пакеты с “ggez-” префиксом. Мне эта идея все еще не кажется такой уж хорошей (кто-то же назовет рано или поздно все равно), но так и быть я использовал “ggwp-” (“good game, well played!”) префикс.

Пакеты дико сырые, так что на crates.io пока не выложены, но хочется верить что кому-нибудь когда-нибудь они пригодятся.

Кстати, не так давно по тыку от diegor8 опубликовал свою макро-обертку над хэшмапами “компонентную систему” на crates.io: https://crates.io/crates/zcomponents.

Где Zemeroth 0.5?

Одним из неупомянутых выше недостатков перехода на ggez является то что его v0.4 (текущая) версия основана на сишном SDL2. Да, это очень надежная библиотека, но сложностей в ржавый процесс разработки она добавляет прям сильно много.

В частности: у master версии Земерота временно нет поддержки андроида и автоматических выпусков релизов на CI, потому что геморно все это делать с сишными библиотеками, лицензиями и т.п.

Эту проблему должен решить ggez 0.5 - он уже будет на winit. Ждем-с. Как он выйдет, сразу перевожу Земерот на него, срезаю 0.5 и пишу новую заметку в журнал.

Еще хочется верить, что ggez к 0.5 или чуть позже научится в вебе работать, но, скорее всего, ждать придется сильно больше (или самому лезть разбираться во всем этом деле, бррр).

Новые спрайты, тени и перспектива

Перерисовал часть картинок, добавил тени и псевдоперспективу (за счет сплющивания карты). Вот парочка макетов:

макет со сценкой

макет с рыцарем и вызываетелем

На макетах выше видно много градиентов. В текущей версии решил отказаться от градиентов и всех округлостей, делая акцент на “типа-низкополигональном” угловатом стиле. На данный момент игра выглядит вот так:

(гм, кстати надо будет переработать “точки”, теперь они прям болтаются в воздухе и иногда вообще не понятно кому принадлежат)

Атлас и исходники картинок

Завел таки репозиторий с исхдниками (svg-атлас + питонячий скрипт экспорта):

При реализации атласа пришлось вставить костыль для регулировки размера экспортируемых спрайтов: в каждой именнованной группе объектов находится обычно невидимый квадрат (прямоугольник для клеток) нужного количества пикселей. Для отладки их даже можно временно делать видимыми, бывает удобно:

Хэши ресурсов

После очередного #310 добавил таки в ресурсы подсчет md5 хэша. Нужный хэш хардкодится прямо в исходник игры, что бы при запуске с другой версией все грохалось с понятным сообщением.

В CI хранилища исходников хэш персчитывается и сверяется с записанным в файл, а в CI самого Земерота проверяется что в исходниках захардкожен самый последний хэш ресурсов.

Да, вот настолько я хочу хранить ресурсы в отдельном репозитории и не люблю git submodules. :-p

Шипы

Добавился новый объект - шипы. С ними все просто - наносят урон при попадании в клетку с ними + каждый ход пребывания там.

демо шипов

(да, это еще до смены спрайтов было сделано)

Обновления ИИ

ИИ хоть и задуман крайне тупым, но кое-что делать таки должен. Исправил несколько проблем.

Научил ИИ метателей бомб и призывателей держать дистанцию. Есть минимальная и максимальная дистанция, демоны стараются держаться в этом диапазоне:

(тестовая сборка с отключенными атаками и единовременными передвижениями)


Изменил как ИИ определяет порядок хода его агентов. Раньше было просто по айдишнику, что часто приводило к ситуациям, когда один рвущийся в бой рукопашник перекрывал путь нескольким другим друзьям за ним:

Теперь эта проблема возникают у отстуающих метателей гранат, но там это заметно намного меньше просто потому что они сильно реже двигаются.


Научил искать путь “в сторону цели”, даже когда пути прямо до цели нет.

В сумме эти изменения ИИ уже делают его похожим на что-то адекватное. Теперь надо будет добавлять больше типов врагов с новыми навыками.

Рост проекта

По звездам Земерот дорос и перерос Зону Контроля:

граф звезд

Мелочь конечно, но все равно приятно.

Вышенаписанное писалось задним числом за нцать месяцев сразу. Дальше уже постараюсь размещать обновления помельче. Парочку выложу прям на днях.

5 лайков

Планирую, но приоритет пока не самый большой.

По хорошему, надо бы ggwp-zgui переработать во что-то более внятное. Добавить больше видов виджетов, табличный лэйаут, добавить фон и какой-то стиль - но это не пятиминутное дело, надо посидеть и подумать как лучше это все запилить.

Еще все хочу заменить большую часть текстовых кнопок на иконки, вот до этого скорей доберусь.

2 лайка

Стримы

Немного пробовал экспериментировать с вещанием процесса разработки. Конечно, очень стремно и непривычно тупить “на камеру” со всеми этими “эээээ, чего-то не собирается. таааак, может вот из-за этого? нееет, не то. а, запятая тут нужна. тааак, что я там делаааал?” :slight_smile: Надо учиться в такой обстановке себя комфортно чувствовать.

Субъективно, самое адекватное что получилось - формат небольшого видео про решение конкретной задачи:

Опыт несколько стрессовый, но это еще один способ бороться с прокрастинацией - довольно сложно залипнуть в совершенно левую статью в реддите, когда записываешь видео - так что я, наверное, буду еще с этим экспериментировать.


Кстати, возможно еще при выпуске Земерота 0.5 к текстовой статье в журнале попробую записать небольшое видео с презентацией чего там нового (в таком духе) на ломаном своем английском.

1 лайк

Окончание боев

К чуть более свежим новостям. Несколько дней назад наконец-то дошли руки реализовать окончание боев:

В деле выглядит вот так (тестовая сборка для быстрого завершения игр :slight_smile: ):

Реализовано относительно прямолинейно:

Тестовая игра

@shmutalov меня утром потыкал что ютуб совсем без дела стоит, так что вот записал небольшую тестовую игру на актуальной версии master ветки:

Видео демонстрирует пачку глупых решений на первых ходах - учитывая неудачное начальное положение бойцов, надо было отступить, перегруппироваться и потом идти в атаку в начале второй фазы призыва.

1 лайк

Бомбы и приостановка неверия

Кусочек из гиттера:

Sherzod Mutalov @shmutalov сент. 11 08:25
у меня тут вопросы к сеттингу появились. Откуда у чуваков бомбы? Если у них есть бомбы, почему они используют пики, мечи, молоты?

Andrey Lesnikov @ozkriff сент. 11 08:31
потому что мне было лень рисовать глиняные сосуды

Sherzod Mutalov @shmutalov сент. 11 08:36
Глиняные сосуды в основе были зажигательными смесями? Да, я уже почитал. в 15 веке все вышеперечисленное было, мечи, молоты, пики, даже бомбы. Похоже, что мой вопрос уже не актуален

Andrey Lesnikov @ozkriff сент. 11 08:38
ты, главное, не спрашивай как алхимик лечит по две единицы здоровья)
тут я тебе исторических прецедентов точно не смогу дать

Alexander Irbis @alexander-irbis сент. 11 22:46
ну, прямо скажем, для нашествия разумных антропо- и не очень морфных созданий, тоже с достоверными историческими прецедентами будет не густо :slight_smile:
Я бы повернул вопрос так, что “в чужую вселенную со своими законами физики - не ходят” :smiley:

Andrey Lesnikov @ozkriff 10:13
Да не, норм вопрос. Приостановка неверия на игры с фантастическим миром и игровыми условностями (вроде очков здоровья) же тоже распространяется - типа да, человек согласен сознательно не париться насчет правдоподобности существования демонов, того как они изображены и того что при ранениях бойцы терябт какие-то кружочки, но это не значит что какая-то неоговоренная нелепость не разрушит адекватность восприятия мира
Если в следующем сезоне Джон Сноу внезапно вытащит из-за спины M60 и начнет расстреливать из него мертвяков - лично я на Мартина или сценаристов очень странно посмотрю, хотя в целом уровень отличия того мира от нашего реального средневековья не так и сильно колыхнется от этого.

В общем, я подумаю над перерисовкой бомб, что бы меньше вопросов вызывали)

Тем временем, в дискорде тоже тыкают что бы UI причесал:

Abovegame: Looks pretty cool and with some ui it will look even better
ozkriff: Yep, UI improvements are on the roadmap, though I want to solidify game logic before investing much time into the UI to avoid doing too much unnecessary work. Do you see any “low-hanging fruit” UI improvements?
let (): () = ();: It’d probably be nice if the text had a background

Окей, фон кнопкам в ggwp-zgui добавить и правда не сложно должно быть. Попробую заняться на днях.

2 лайка

Контроль популяции метателей бомб

После того, как ИИ кидающих бомбы чертей научился держать дистанцию от бойцов игрока, возникла проблема с количеством этих самых метателей на карте.

Рукопашные демоны так и продолжили идти наиболее коротким путем к бойцам игрока на довольно быстрый убой, но вот держащиеся на расстоянии метатели без активных действий со стороны игрока очень быстро разрастались в количестве и ломали всю механику игры напрочь.

Пример дикого перевеса метателей бомб:


В связи с всем этим я изменил навык “Summon” - сделал выбор вызываемого демона саморегулирующимся, а не тупо случайным как было:

Реализация

Для начала, при помощи новой функции

fn count_agents_by_typename(state: &State, player_id: PlayerId) -> HashMap<String, u32>

вытаскиваем из игрового состояния таблицу численности агентов игрока:

{"imp_toxic": 3, "imp_summoner": 2, "imp": 1}

Отфильтровываем из нее тех, кого нет в списке вызова (т.е. самих призывателей в данном случае):

{"imp_toxic": 3, "imp": 1}

Затем добавляем с 0 тех, кто не представлен, но кого можно призвать:

{"imp_toxic": 3, "imp": 1, "imp_bomber": 0}

Находим тип с максимальным количеством и обращаем все числа относительно максимума:

{"imp_toxic": 0, "imp": 2, "imp_bomber": 3}

“Плющим” HashMap в Vec<String> с повторениями:

["imp", "imp", "imp_bomber", "imp_bomber", "imp_bomber"]

И наконец вызываем тот же самый .choose() для получившегося вектора (prototypes_pool):

thread_rng()
    .choose(&prototypes_pool)
    .expect("Can't choose a prototype")

Вуаля, мы получили случайный тип, который скорей всего и нужен в данный момент ИИ. В примере это скорей всего будет метатель бомб.

Теперь даже с увеличенной для теста скоростью призыва сильных перекосов по типам демонов не наблюдается:

P.S. Если у кого есть мысли как упростить всю эту логику - делитесь. :slight_smile:


UPD: И еще в фоне исправил падение, когда на ходе ИИ у него умирал последний боец и игра заканчивалась, но ИИ упорно пытался посылать команду EndTurn (после конца игры никакие команды посылать нельзя, все грохалось на одном из assert’ов).

2 лайка

Точки пропавшей силы

Добавил рисование пропавших очков здоровья, потому что скоро соотношение полного и текущего здоровья должно начать влиять на эффективность бойца.

Из реализации может быть занятна переделка “таблицы точек”: теперь это срез срезов по кортежам из цвета и количества точек, где верхний срез представляет собой строчку:

let rows: &[&[_]] = &[
    &[
        ([0.0, 0.7, 0.0, 1.0], strength.strength.0),
        ([0.3, 0.5, 0.3, 0.5], damage),
    ],
    &[([0.9, 0.1, 0.9, 1.0], agent.jokers.0)],
    &[([1.0, 0.0, 0.0, 1.0], agent.attacks.0)],
    &[([0.0, 0.0, 1.0, 1.0], agent.moves.0)],
];
for &row in rows {
    for &(color, n) in row {
        for _ in 0..n {
            dots.push((color, point));
            point.x -= size;
        }
    }
    point.x = base_x;
    point.y += size;
}

Явное указание что мы хотим срез срезов (: &[&[_]]) нужно, потому что иначе компилятор почему-то пытается вывести внутренние срезы как просто массивы и может ругаться на нестыковку количества блоков в строчках :-\

error[E0308]: mismatched types
   --> src/screen/battle/visualize.rs:189:9
    |
189 |         &[([0.9, 0.1, 0.9, 1.0], agent.jokers.0)],
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size
 of 2 elements, found one with 1 elements
    |
    = note: expected type `&[([{float}; 4], i32); 2]`
               found type `&[([{float}; 4], i32); 1]`

Перехожу к работе над первой итерацией системы брони.

1 лайк

Броня

Первая итерация брони почти готова.
Каждое очко брони снимает единицу урона с каждой прилетающей атаки.
Некоторые виды бойцов могут своими атаками проламывать броню, а огонь и яд игнорируют броню.

PR пока не готов, но между делом запоздало вписался в субботний скриншотник:

2 лайка

Андрей, а вот раньше была древняя игра Fantasy General, в данном ключе Zemeroth это современный ремейк, или есть принципиальные отличия?

Ух ты, обычно спрашивают в духе “это типо как Герои у тебя тут?”, а тут аж Fantasy General. Не банально)

Мои первые эксперименты с пошаговым играми где-то в 11ом году и правда были оочень близки к Fantasy General. Точнее, к Кодексу Войны, который тогда много кто называл перосмыслением Fantasy General и который я пытался клонировать на C+SDL. :slight_smile:

А если конкретно про Земерот, то, с одной стороны, в общей массе игр они и правда не так уж далеки: магическое средневековье, пошаговость, шестигранные клетки, цена хода по местности, очки атаки-защиты-движения и т.п. Но с другой стороны Земерот это чисто тактическая игра на малюсенькой карте (?) про ближний бой конкретных одиночных агентов, а не операционный варгейм. Если искать откуда я что ворую в Земероте, то скорее надо смотреть на игры вроде ENYO, Hoplite, Auro, Into the Breach и т.п. :slight_smile:

Тут еще надо иметь в виду, конечно, что я говорю все это не прямо про текущую версию Земерота, а про ту, что у меня в голове и в прото-диздоках. Например, важными будущими измениями, повышающими динамику происходящего на карте, должны стать добавление:

  • пассивных навыков, дающих возможность врагам двигаться в твой же ход (например, отпрыгивать в безопасность или наоборот внезапно подбегать и атковать при попыте пройти по близким к ним клеткам (привет X-Com));

  • комбинированных действий, одновременно наносящих урон здровью-броне, накладывающих какие-то состояния на бойцов и перемещающих сразу несколько участников в пространстве (например, прыжок через пару клеток на врага, наносящий большой урон непосредственно жертве и расшвыривающий-оглушающий соседних бойцов (привет Диабло)).

Главное, блин, найти в себе силы не сдуться и все это потихоньку реализовать. :slight_smile:

Извиняюсь за долгую реакцию. Все надеялся ответить одновременно с выкладыванием новостей про разработку Земерота, но не слишком активная неделя вышла.

2 лайка

Прогрессирующий призыв и ослабевший яд

Неделя выдалась и правда не такая продуктивная и особо хвастаться нечем, но кое чего я все-таки умудрился сделать:

  • PR с базовой системой брони таки закончен и влит.

  • Изменена логика призыва демонов. Раньше каждый раз призывалось три демона, а теперь каждый призыв будет создавать на карте на демона больше, начиная с двух и до шести - еще один метод борьбы с затягиванием партий и попытками играть чисто от защиты.

    Реализовано через добавление компонента summoner (есть только у демонов-призывателей), который хранит уровень прогрессии числом. Была еще мысль обойтись без компонента и отталкиваться от номера хода, но как-то это показлось слишком костыльным.

  • Само по себе отравление больше не может убить бойца - оно всегда останавливается на последней единице силы, т.е. играет только ослабляющую роль. Понерфленным ядовитым демонам в будущем будет выдана альтернативная атака.

  • “На днях” таки случилось и у кнопок появился полупрозрачный фон, призванный сделать текст читаемым даже поверх контрастных объектов на карте:

    image

Теперь работаю над рассчетом шансов попаданий и простеньким режимом “компании”.

2 лайка

предлагаю на 1 милишника призывать 1 метателя

надо больше стримов разработки!
я буду смотреть на ютубе)
только анансы сделай.:sunglasses:

запили GOAP
:sunglasses::sunglasses::sunglasses::sunglasses::sunglasses::sunglasses::sunglasses:

1 лайк

И тут надо бы пояснить, почему ты считаешь что текущий вариант с “балансирующим” призывом не работает)

Вариант “всегда вызывать пару из рукопашника и метателя” мне не нравится тем слишком близок к старому поведению, когда просто три случайных демона призывались:

Эта же проблема с взрывом популяции метателей вернется, разве нет?

Постараюсь в обозримом будущем собраться с духом. Если/когда надумаю, в игровой гиттер закину объявление.

Думаю что это не серьезное предложение, но на всякий отвечу что это прямо противоположно одной и из зиначальных целей проекта) Да и просто на порядок сложнее в реализации, а работа и так еле ползет.

UPD: на всякий поясню, что план изначально включал в себя только врагов, поведение которых очевидно и легко предсказывается. Это важно для возможности планировать игру в условиях сильного влияния случайностей на вероятность успеха атаки. В данный момент типов врагов мало, как и шаблонов их поведения, но постепенно и того, и другого должно стать больше и тактическая глубина происходящего будет вытекать из взаимосдействия простых, но разных ИИ (см Emergent gameplay и т.п.).

игра на английском, ничегоне понятно

В принципе, можно отложить задачи по непосредственно добавлению-проработе игровых механик и заняться локализацией, но я не уверен что это своевременно и прям много кому нужно. Вот опросик небольшой:

  • да, сейчас самое время запилить локализацию
  • нет, этим лучше заняться на поздних этапах, когда проект устаканится
0 проголосовавших

У гитхаба (из-за конкуренции с гитлабом) еще где-то в 2017ом году появились “проекты” - v0.5 · GitHub. Нахожу их довольно удобной штукой, особенно для небольшого личного проекта.

Спасибо, не знал что такая штука есть в гитхабе.

Текущий вариант сложно просчитать, а вот если на одного призывателя будет 1 метатель, то будет норм.
И вообще, когда много мобов на поле - все запутанно как-то становиться.

Нет, я не предлагаю пару.
Вызывать милишников отдельно, независимо от метателей.
А фиолетовый моб это же призыватель?
Ограничить ему вызов метателей до 1 шт. Так будет легче предсказать ход противника.
Считаю этот момент важным, т.к. упрощение правил и геймплея снизит входной порог, тем самым увеличит количество лемингов что будут играть в это на работе :grin:

Черт, две недели с последнего поста тут уже прошло, а никто не пингует что я отписываться забыл)
Постараюсь выкатить обновление вечером.

@Gexon Может и норм мысль, я подумаю над этим. В целом, план по дальнейшему развитию призывателей был в том что когда появится больше видов демонов, они будут оценены какой-то абстрактной стоимостью и призыватель будет по простому алгоритму “покупать” более крутых демонов с каждым призывом (да и призывателей должно стать больше одного вида). Поэтому, не уверен что хочу прям жестко завязываться на конкретные типы демонов сейчас. Надо думать.

UPD: вот заодно, что б не потерялась, цитата из игрового гиттера: