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


#23

Шанс попасть, зоны расстановки и упрощение конфигов

Итак, вот запоздалое обновление за последние две недели.

  • Наконец-то реализован рассчет шансов попадания (взамен вечного 50% как было до этого).

    Пока остановился на таком подходе, вроде он не сильно сложный и довольно гибкий:

    • у бойцов появляются параметры “точность атаки”, “сила атаки”, “уклонение”;
    • эффективность_атаки = точность + сила - уклонение;
    • кидается куб с 10 гранями;
    • из эффективность_атаки вычитаем результат броска;
    • если получается <= 0 - значит это полный промах;
    • если получилось больше “силы атаки”, то срезаем до нее;
    • если посередине - значит такой урон и уходит на вражескую броню (типа, удар пришелся по касательной, но таки был ощутим);
    • значение брони просто вычитается из этого урона для подсчета итогового урона цели.

    В эту формулу интуитивно вставляются всякие коэффициенты, типа “если атакующий ранен, то вычесть силу его ран из эффективности атаки” (тоже реализовано).

    Пока я два недостатка описанной выше схемы знаю:

    1. Сходу в ней не показать оружие, у которого нет градации урона. Хз что это именно за оружие должно быть и нужно ли оно мне (вряд ли), но штуки вида “или попал и нанес 4 урона, или не попал совсем” непредставимы без дополнительных костылей.
    2. Отравляющий демон наносит 0 урона при атках - т.е. его шанс попасть ниже остальных демонов. Тут вбил костыль в виде повышения его точности атаки.

    Вживую выглядит так сейчас:

    Из визуала:

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

    Какие изменния случились с балансом:

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

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

    А насчет зон, добавлено перечисление pub enum Line { Any, Front, Middle, Back }, позволяющее указывать в сценарии где мы какие виды агентов хотим видить. Теперь демоны-вызываетли всегда сощдаются в дальнем конце карты за жвым щитом, т.е. застрахованы от быстрой расправы на первом ходу.

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

    (Вот еще до кучи пример начальной расстановки на огромной карте).

  • Кстати об огромных картах, я тут из интереса провел небольшой стресс-тест с реальным боем на огромной карте:

    ИИ думает секунд по 10, а анимации его хода минуты две занимают. Играть в это, конечно, не реально, но хорошо что игра хоть как-то функционирует.

  • Убрал дублирование информации из конфигов.

      Strength((
    -     base_strength: 4,
          strength: 4,
      )),
    

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

Сейчас играюсь с настройкой винды в тревисе и пытаюсь закончить с базовым режимом компании.

(Извиняюсь, многовато получилось текста. Надо почаще писать.)


#24

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

UPD: Просили описание проекта родить (не техническое), смог вот такое:

Описание: минималистичная фэнтезийная пошаговая тактика, механика которой вдохновлена Into the Breach, Banner Saga, Hoplite и Auro.

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

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

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


#25

@ozkriff Молодец! :+1:
А может быть нам еще чисто игровые растосходки наладить в Индиспейсе? Раз в месяц или хотя бы раз в два месяца.


#26

Думаешь, имеет смысл? Ржавая игровая экосистема пока в таком состоянии, что только для готовых бороться с неудобствами годится. В целом, практичный игродельческий народ смеется даже когда говоришь на godot’е проект писать, а до такого уровня Аметисту (он же у нас самый зрелый и серьезный из движков все еще?) еще просто куча человеколет предстоит. Да и прям проектов-то игровых в питере на расте не то что бы много. Я боюсь что мы (пока?) из своих субботних сходок не выросли особо, что бы сильно уж народ зазывать.


#27

раз в год самое то)))


#28

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


#29

Базовый режим кампании

Нашел в себе силы влить PR c базовой кампанией.

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

Файл описания демо-кампании выглядит примерно так:

initial_agents: ["swordsman", "alchemist"],
nodes: [
    (
        scenario: (
            map_radius: (4),
            rocky_tiles_count: 8,
            objects: [
                (owner: Some((1)), typename: "imp", line: Front, count: 3),
                (owner: Some((1)), typename: "imp_bomber", line: Middle, count: 2),
            ],
        ),
        award: (
            recruits: ["hammerman", "alchemist"],
        ),
    ),
    (
        scenario: (
            rocky_tiles_count: 10,
            objects: [
                (owner: None, typename: "boulder", line: Any, count: 3),
                (owner: None, typename: "spike_trap", line: Any, count: 3),
                (owner: Some((1)), typename: "imp", line: Front, count: 4),
                (owner: Some((1)), typename: "imp_toxic", line: Middle, count: 2),
                (owner: Some((1)), typename: "imp_bomber", line: Back, count: 1),
                (owner: Some((1)), typename: "imp_summoner", line: Back, count: 2),
            ],
        ),
        award: (
            recruits: ["swordsman", "spearman", "hammerman"],
        ),
    ),
]

^ Вначале идет список бойцов, с которым мы начинаем самый первый бой, потом перечисляется список сценариев боев, где задаются свойства карты, списки объектов (врагов и просто булыжников всяких) и варианты награды за победу.

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

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

#[test]
fn short_happy_path() {
    let mut state = State::from_plan(campaign_plan_short());
    assert!(state.aviable_recruits().is_empty());
    assert_eq!(state.mode(), Mode::ReadyForBattle);
    let battle_result = BattleResult {
        winner_id: PlayerId(0),
        survivor_types: initial_agents(),
    };
    state.report_battle_results(&battle_result).unwrap();
    assert_eq!(state.mode(), Mode::Won);
}

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

Game Planet

Сходил на этих выходных на выставку Game Planet, никогда на такие штуки не ходил. И не зря, видимо - мероприятие в целом сильно не про меня оказалось. Куча школьников, фортнайта и странных косплееров. Убежал оттуда через пару часов, но хоть позалипал в секции с инди-стендами, посмотрел всякое про настолки и авторские комиксы. Сделал вывод: можно смело идти шоукейсить поделку - ничего дико страшного, в худшем случае просто все проигнорят. :slight_smile:

Твиторы

И да, если кому интересно, я тут психанул и завел две твитерных учетки дополнительных:

  • @ozkriff_ru - для всего, что будет интересно только русскоговорящим;
  • @rust_gamedev - для ретвитов всего подряд про ржавый игрострой.

#30

Пыль

Запилил простенькую пыль при прыжках-бросках всяких:

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

Следующее на очереди: квадратные брызги крови при попаданиях.


UPD: еще внезапно решил заглянуть в /r/gamedev на субботний скриншотник, давно там уже ничего не писал.


#31

Исчезающие брызги крови и улучшенные анимации атак

Продолжаю лениться выкладывать сюда частые обновления.

  • Добавил разлетающихся брызг крови и следов от оружия при атаках для оживления визуального ряда.

    Количество капель крови пропорционально нанесенному урону.

    Добавил бойцам параметр WeaponType. Пока есть четыре вида: smash, slash, pierce и claw и они чисто визуальные - для выбора подходящей текстурки. Некоторые спрайты атаки под углами смотрятся странно (копейщик, я на тебя смотрю), надо будет потом дополнительные варианты добавить и зеркалировать все это хозяйство по ситуации.

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

    Может потом еще для чего-то декоративного этот же механизм использую.

  • Первел все пакеты в репозитории на Rust 2018:

    • везде прописал edition = "2018"
    • обновил все импорты на новые пути (привет, вездесущий crate::);
    • удалил все extern crate;
    • заменил все #[macro_use] на use конкретных макросов;
    • перешел на стабильный rustfmt;
    • подчистил некоторые ставшие лишними явные ВЖ в паре мест;
    • выкинул все mod.rs.

    О переходе не жалею, в целом все неплохо, но некоторые вспомогательные штуки (например, cargo-outdated) пока со скрипом на Rust 2018 смотрят еще.

  • Выкинул прямую зависимость от serde_derive;

  • Отключил кэш тревиса, потому что Levans’ workshop: “Beware the rust cache on Travis”. А то замерил, что кэш после сборки иногда минуты по три собирается, а толку от него и правда не так что бы прям много.

  • Починил вертикальную позицию декоративной травы;

  • Обновил картинки в ридми. Изначально задумка была в том, что в ридми показываются картинки только с последней выпущенной версии, а не из мастера. Но 0.5 я выпущу только после перехода на ggez 0.5, который все маячит на горизонте, но не появляется. А картинки от 0.4 совсем уж сильно устарели, больше полугода уже прошло таки.


#32

Затянувшийся переход на GGEZ 0.5, good-web-game/WASM и itch

Разработка за этот месяц продвинулась так себе.

Статус треклятого перехода на GGEZ 0.5

В начале года вышел ggez 0.5.0-rc.0 - первый релиз-кандидат 0.5 версии, который без сишного SDL2 и вообще весь классный и долгожданный. Я решил, что время настало, пора начинать портировать Земерота, потому как API там сильно переколбасили и лучше на ранних стадиях, до выхода настоящего стабильного 0.5 поправить обо что я там споткнусь.

Так и случилось, я неприятно споткнулся (пока что) о три момента:

  • Самое для меня неприятное: Text больше не реализуется простой картинкой, которую раньше можно было через into_inner() получить и не думать ни о чем.

    Какая абстракция мне нужна? Что бы был метод отрисовки этого дела + какой-то способ узнать размер примтива, как минимум что бы кнопки в UI расставить с правильными отступами.

    Первым пришедшим в голову решением было просто сделать перечисление с двумя вариантами (Text и Image) и прокинуть там эти две несчастных функции. Я это сделал и оно даже вполне работало. Но возникает проблема использования этого enum’а между разными пакетами. У меня же не только один пакет Zemeroth, у меня еще отдельные и независимые zgui и zscene пакеты, оба из которых так же должны уметь одинаково работать с текстом и картинками (zgui - показывать кнопки и с текстом, и с икноками, zscene - рисовать на сцене как всяких бойцов/объекты, так и двигать всплывающий текст). Так что пришлось бы делать из этого перечисления отдельный пакет, который потом везде подключать - уже сильно менее приятное решение, которое потом боком может выйти.


    Окей, какой тогда еще есть вариант? Взять родной ggez’овский типаж Drawable и научить его всему, что мне нужно. Перетер это дело с Ледолисом в дискорде, получил одобрение и пошел ковырять.

    Для начала убрал Into<DrawParams> из сигнатуры метода типажа, что бы его можно было использовать для динамического полиморфизма: #556: “The Drawable trait isn’t object safe” -> PR #559: “Remove the generic argument from Drawable::draw”.

    А потом на несколько вечеров провалился в попытки родить API получше для dimensions метода: #557: “Add a ‘dimensions’ method to the ‘Drawable’ trait”. Там пришлось докинуть Rect’у дополнительных методов для его вращения, вставить костылей для повершинного подсчета размеров Mesh’а и еще всякого наковырять. Плодом усилий стал среднего размера PR #567 “Drawable::dimensions()”, который Ледолис начисто две недели игнорирует без какой-либо обратной связи. :’-( (UPD: уже влито)

    Кроме игнора, эту беду я в целом считаю решенной, думаю, Ледолис в итоге найдет время отсмотреть и принять PR.

  • Другая беда с текстом - разная высота - пока не решена, надо будет еще в коде покопаться. Там суть в том, что у строк без символов с “хвостами” высота считается по базовой линии почему-то. Учитывая, что я все элементы UI масштабирую просто относительно высоты экрана, это приводик к неприятному эффекту (ggez v0.4 vs v0.5):

    Строки с хвостами выглядят как раньше, а вот штуки типа “moves: 1/1” в полтора раза больше нужного.

  • Ну и на десерт есть еще небольшая проблема с доступом к файловой системе без создания графического контекста. Тут, видимо, надо будет кидать PR, делающий некоторые pub(crate) методы просто pub'ами.

Так что вот, пока переход на ggez 0.5 буксует. Код WIP порта живет в wip_i409_ggez_0_5_alpha_0 ветке.

good-web-game и веб версия Земерота

Через какое-то время, после публикации ggez v0.5.0-rc.0, Ледолис еще выложил огромный пост “The State Of GGEZ 2019”. Среди прочего, он там говорит:

  • с веб версией много проблем и хз когда она будет;
  • он скоро перестанет уделять ggez много времени, потому что задолбался и хочет позаниматься другими проектами;
  • на последок он хочет выпустить ggez 0.6, в котором будет осуществлен переход на gfx-hal.

Если ggez 0.5 выйдет, то от десктопной версии мне особо ничего и не надо, вроде, а вот веб версию я давно и сильно хотел. Потому что небольшую 2д игру от ноунейма без возможности запуска в браузере в 2019м ооочень мало кто не поленится скачивать.

И тут, внезапно, Федя @not-fl3 замутил WASM/WebGL движок, частично апи-совместимый с GGEZ, так что у Земерота появилась полноценная веб версия!

https://github.com/not-fl3/good-web-game

На самом деле он не прям из воздуха его замутил, конечно: он давно экспериментирует с 2д протипами в вебе:

…и “просто” поверх своего движка накрутил GGEZ слой.

Работает это дело через замену обычного GGEZ’а в Cargo.toml проекта на good-web-game + там еще немного код инициализации надо поправить: за подробностями можно посмотреть пример из репы.

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

Кстати, в процессе синхронизации API Федя наткнулся на еще один косяк в черновике 0.5 GGEZ’а: #568: “There’re still some non-mint types in the API”.

Спасибо тебе, Федя, еще раз! :slight_smile:

itch.io и обратная связь

Выложил эту играбельную веб версию на itch.io:

и закинул клич в твитер с реддитом:

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

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

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


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

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


UPD: Кстати, гитхаб репа за 400 звезд перевалила уже (420 в момент написания) :slight_smile:

image


#33

Продолжение возни с GGEZ (2019.02.12 - 2019.02.18)

  • Земерот попал в rustwasm рассылку: Feb 13, 2019 This Week in Rust and WebAssembly 10

  • PR в GGEZ про добавление Drawable::dimensions принят.

  • Покопался с Федей в #569: “Text::dimensions height behavior changed”.

    Меня немного загрузило, что может я не в ту сторону с текстом копаю, и не должен хотеть, что бы у него всегда одна высота была в dimensions, но поговорил с Icefoxen’ом в дискорде и он меня успокоил:

    icefox: ggez should handle this shit for you
    icefox: all yo ushould have to do is say “give me text where each line is X pixels tall”
    icefox: and so you can do for i in 0…lines.len() { draw(lines[i], Point2::new(0, i*x)); }
    icefox: and it doesn’t look like ass
    icefox: that’s the goal from ggez’s perspective

    В итоге в задаче висит патч от меня с Федей, который, вроде, чинит проблему, по крайней мере для моего кода. Жду реакции Ледолиса, готов по отмашке сделать PR.

  • На вот такое дело еще нарвался: #583: “(0.5 feedback) Text::dimensions should return Rect, not (u32, u32)” - может и мелочь, но выглядит криво.

  • В процессе всего выше копался еще и в https://docs.rs/glyph_brush - должен сказать, что хоть библиотека и толковая, но как же там много всяких типов и вспомогательных структур на каждый чих. Прям копаешься во всем этом и очень хочется на голый rusttype откатиться.

  • Icefox выпиливает остатки nalgebra типов из API (заменя все на mint), приходится чинить код, обмызвая все пачкой .into();

  • Добавил в zgui виджет “Spacer”, потому что использование Label с пустым текстом (приводящее к созданию текстуры 0 на 0) это такое себе дело;

  • PR #426 “GGEZ: v0.4 -> 0.5.0-rc.0” - перетащил Земерот на git зависимость GGEZ’а (свой форк). И там еще пачка мелочевки осталась техдолга. Часть заблочена правками в самом GGEZ’е, а до штук вроде “свой тип ошибок” просто надо добраться отдельно.

    Так что вот, теперь мастер Земерота использует (временно) гитовый GGEZ.
    На этой неделе надеюсь втащить веб версию в мастер и вернуться наконец-то к полезной деятельность по улучшению интерфейса и функционала игры.


#34

Ошибки и рефакторинги (2019.02.18 - 2019.02.24)


#35

Я устанавливал какое-то время назад Земерот “на поиграть”, и понял, что это такой фановый чисто прогерский проект, потому что если почитать тебя, кажется, вау!, и каждому кодеру знакома эта эйфория от фанового проекта: какие-то идеи кипят, тут фичу новую добавил, там рефакторинг на ходу затеял — всё кипит, всё меняется, работа прёт, ты щастлив. Но со стороны юзера мне чего-то не хватило, хотя я вообще не геймер ни разу. Может лора, может дружелюбности: что делать?, куда бежать?, где враги? etc.
Игроку ведь не очень нужно, чтобы все мутации были засунуты в один модуль или надписи имели одинаковую высоту. Это мое мнение, не критика, просто здравый взгляд со стороны.


#36

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

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

image

Так что я пытаюсь приоритизировать задачи по критичности. Очень короткая версия краткосрочной дорожной карты проекта выглядит как-то так:

  1. выплатить минимальный техдолг (наладить тестирование логики, дорефакторить мутации состояния, влить веб версию в мастер);
  2. выплатить минимальный “пользовательский долг” (гуй, руководство, переработка механик случайностей);
  3. заняться реализацией прям нового пользовательского функционала;

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


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

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


Ты так от слова “критика” открещиваешься, будто что-то плохое) Не, я люблю, когда конструктивно тыкают - значит есть какое-то дело до поделки. :slight_smile:


#37

Потому что не считаю свои замечания критикой, критика это либо конкретика, либо заурядный хэйт. А я обращаю внимание на абстрактные, метафизические вопросы, на то, что в игру прежде всего должно быть приятно играть, если попросту говорить, это не тот тип ПО, где функционал решает всё.
Вот ты говоришь, что устал. Это нормально, когда упираешься в оверинжиниринг — беду всех прогеров и их пет-проектов. Потому что хочется добиться совершенства, а не получается, не получается. Потому что совершенство обычно конечный итог длительной эволюции, а кто способен вывезти долгую эволюцию проекта?)
Я бы сосредоточился на веб-версии, эта вещь может дать проекту жизнь. И обрезай фичи, не распыляйся, иначе не вывезешь. Простынешь там, выпадешь на время головой из проекта и не вернешься. Вполне реальный сценарий, сколько таких я реализовывал на своем веку.


#38

Вот тут не соглашусь. Несмотря на то, что Земерот “сыроват”, таки очень много сил вложено в его визуальную составляющую и он смотрится мало сказать “приятно” - он офигенен. Единственное слабое место в визуальной составляющей - это отсутствие GUI для управления. Все эти менюшки действий и их статусов, которые достаточно тяжело парсить глазами с непривычки. И разнобой со шрифтами их окончательно добивал. В общем, забота о визуальной части - это важно.

Ну а то, что видение разработчика не совпадает с видением игрока… Ну, блин, да - Земерот отличается тем, что разработчик пустил всех желающих “в мастерскую” и подробно всё объясняет по пути. В этом имеется своя прелесть и своя доля восторга, наблюдать как из практически “ничего” рождается и растёт что-то волшебное. Но это точно не про то, что “вжух и у нас готовый играбельный продукт, (заранее кладём на верстак, чтобы желающие посмотреть на разработку изнутри, сразу же и играли в готовое)” :slight_smile:


#39

Я мимокрокодил, можешь ещё попробовать quick_error. Он даёт макросы для тривиальных реализаций description, display, from, into, с возможностью перегрузки. При этом это обычные enum, всё очень примитивно.