You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Странствующий торговец (моб 1901, держатель магазина BandageShop из lib/cfg/economics/shops.xml) расставляется по миру целиком из кода — town_shop_keepers(), src/gameplay/economics/shop_ext.cpp:554, вызывается один раз на буте из db.cpp:1525:
if (GetZoneRooms(world[ch->in_room]->zone_rn, &rnum_start, &rnum_end)) {
CharData *mob = ReadMobile(1901, kVirtual);
if (mob) {
PlaceCharToRoom(mob, number(rnum_start, rnum_end));
}
}
Функция идёт по всем рентёрам и в каждую зону с рентой сажает одного торговца в случайную комнату диапазона зоны. В данных этого моба нет нигде: резета моба 1901 нет ни в одной зоне, внум зашит ровно в одном месте во всём движке — в строке выше. shops.xml задаёт только ассортимент, но не место.
Следствие — #3770: торговец оказался в комнате 9052 «На дубе» (зона 90), у которой нет ни одного выхода, лезут туда командным триггером. Он там появился при буте и уйти не может.
В #3771 выбор комнаты ограничен фильтром (есть выход, не служебка %100 == 99, не смерть-комната, не kNoEntryMob, не комната богов/дом/клан/арена). Это заплатка на существующем механизме: критерий по-прежнему живёт в .cpp.
Что предлагается
Вынести автозаселение в конфиг: моб, область действия, количество на зону и критерий пригодной комнаты — данные, а не константы в коде. Движок остаётся исполнителем предиката, но перестаёт быть источником политики.
По сути это не новая сущность: town_shop_keepers() и так представляет собой один захардкоженный резет. Отличий от штатного механизма четыре, и все — минусы:
исполняется однократно на буте, а не на каждом резете зоны: убили торговца или он ушёл — до перезагрузки в городе продавца бинтов нет;
внум зашит в код;
критерий комнаты зашит в код;
идёт мимо штатных счётчиков резета.
Прецедент такого конфига в проекте уже есть — lib/cfg/specials.xml, где мобы привязываются к спецпроцедурам списком, без строчки в коде.
Масштаб: под kRent в specials.xml 75 мобов, это примерно 70 зон, то есть столько же торговцев.
Подводные камни
1. Счётчики резета. Команда M (db.cpp:2913) имеет вид 'M' <flag> <MobVnum> <max_in_world> <RoomVnum> <max_in_room> и при исполнении проверяет mob_index[rnum].total_online < max_in_world и, если задан, CountMobsInRoom(...) < max_in_room. Один внум на все города это выдерживает, если генератор сам выставит max_in_world = число городов и max_in_room = 1. Отдельные мобы-клоны на каждый город не нужны.
2. Бродячесть. У моба 1901 нет ни kSentinel, ни kStayZone — он уходит из города и может уйти из зоны совсем. При резетном подходе, пока он жив где-то в лесу, max_in_world занят, и в опустевшем городе новый не появится. Лечится флагом kStayZone в прототипе — одна правка в зоне 19, а не в семидесяти. Нужно решить, хотим ли мы вообще, чтобы он бродил между зонами.
3. Сгенерированные резеты и OLC.zone_table[].cmd — сырой C-массив (zone.h:53), а zedit_save_to_disk() (zedit.cpp:434) пишет зону в файл именно из него. Если генератор допишет команды в этот массив, они утекут в файл зоны при первом же сохранении зоны билдером. Нужен признак «команда сгенерирована движком, не сохранять» — и это, похоже, единственная настоящая работа во всей затее. Альтернатива — не трогать cmd вовсе и держать сгенерированные заселения отдельным списком, который исполняется на резете зоны рядом со штатными командами.
4. Формат предиката. Соблазн сделать язык выражений; вероятно, избыточно. Хватит перечислений: требуется хотя бы один выход, чёрный список флагов комнаты, исключение vnum % 100 == 99, возможно белый список секторов. Плюс возможность прибить место списком конкретных комнат для отдельной зоны — тогда билдер решает точно, а дефолт остаётся «случайная подходящая».
5. Совместимость. Пока конфига нет или он пуст — поведение не должно меняться. И надо решить, что делать, если в зоне не нашлось ни одной подходящей комнаты: сейчас в #3771 зона пропускается с записью в SYSLOG.
6. Зоны-подземелья и служебки. Текущая функция отсекает зоны с внумом ≤ 39 и комнату рентёра %100 == 99; при переезде в конфиг эти правила должны быть выражены явно, иначе потеряются.
Набросок реализации
Пометка: это черновик по итогам разбора #3770, его надо пересмотреть критически — особенно пункты 2 и 3, там возможны решения лучше.
Новый конфиг (например, lib/cfg/mechanics/autopopulate.xml со схемой рядом): записи вида «моб — область действия — сколько на зону — критерий комнаты — необязательный список фиксированных комнат по зонам».
Загрузчик конфига при буте, рядом с остальными lib/cfg.
Область действия «зоны, где есть моб со спецпроцедурой kRent» — то же условие, что сейчас в town_shop_keepers(), но выраженное в конфиге, а не в коде.
Исполнение на резете зоны, а не только на буте, чтобы торговец восстанавливался. Механизм — либо генерируемые команды резета с признаком «не сохранять», либо отдельный список заселений, исполняемый на резете (см. подводный камень 3).
Что сейчас
Странствующий торговец (моб 1901, держатель магазина
BandageShopизlib/cfg/economics/shops.xml) расставляется по миру целиком из кода —town_shop_keepers(),src/gameplay/economics/shop_ext.cpp:554, вызывается один раз на буте изdb.cpp:1525:Функция идёт по всем рентёрам и в каждую зону с рентой сажает одного торговца в случайную комнату диапазона зоны. В данных этого моба нет нигде: резета моба 1901 нет ни в одной зоне, внум зашит ровно в одном месте во всём движке — в строке выше.
shops.xmlзадаёт только ассортимент, но не место.Следствие — #3770: торговец оказался в комнате 9052 «На дубе» (зона 90), у которой нет ни одного выхода, лезут туда командным триггером. Он там появился при буте и уйти не может.
В #3771 выбор комнаты ограничен фильтром (есть выход, не служебка
%100 == 99, не смерть-комната, неkNoEntryMob, не комната богов/дом/клан/арена). Это заплатка на существующем механизме: критерий по-прежнему живёт в.cpp.Что предлагается
Вынести автозаселение в конфиг: моб, область действия, количество на зону и критерий пригодной комнаты — данные, а не константы в коде. Движок остаётся исполнителем предиката, но перестаёт быть источником политики.
По сути это не новая сущность:
town_shop_keepers()и так представляет собой один захардкоженный резет. Отличий от штатного механизма четыре, и все — минусы:Прецедент такого конфига в проекте уже есть —
lib/cfg/specials.xml, где мобы привязываются к спецпроцедурам списком, без строчки в коде.Масштаб: под
kRentвspecials.xml75 мобов, это примерно 70 зон, то есть столько же торговцев.Подводные камни
1. Счётчики резета. Команда
M(db.cpp:2913) имеет вид'M' <flag> <MobVnum> <max_in_world> <RoomVnum> <max_in_room>и при исполнении проверяетmob_index[rnum].total_online < max_in_worldи, если задан,CountMobsInRoom(...) < max_in_room. Один внум на все города это выдерживает, если генератор сам выставитmax_in_world= число городов иmax_in_room= 1. Отдельные мобы-клоны на каждый город не нужны.2. Бродячесть. У моба 1901 нет ни
kSentinel, ниkStayZone— он уходит из города и может уйти из зоны совсем. При резетном подходе, пока он жив где-то в лесу,max_in_worldзанят, и в опустевшем городе новый не появится. Лечится флагомkStayZoneв прототипе — одна правка в зоне 19, а не в семидесяти. Нужно решить, хотим ли мы вообще, чтобы он бродил между зонами.3. Сгенерированные резеты и OLC.
zone_table[].cmd— сырой C-массив (zone.h:53), аzedit_save_to_disk()(zedit.cpp:434) пишет зону в файл именно из него. Если генератор допишет команды в этот массив, они утекут в файл зоны при первом же сохранении зоны билдером. Нужен признак «команда сгенерирована движком, не сохранять» — и это, похоже, единственная настоящая работа во всей затее. Альтернатива — не трогатьcmdвовсе и держать сгенерированные заселения отдельным списком, который исполняется на резете зоны рядом со штатными командами.4. Формат предиката. Соблазн сделать язык выражений; вероятно, избыточно. Хватит перечислений: требуется хотя бы один выход, чёрный список флагов комнаты, исключение
vnum % 100 == 99, возможно белый список секторов. Плюс возможность прибить место списком конкретных комнат для отдельной зоны — тогда билдер решает точно, а дефолт остаётся «случайная подходящая».5. Совместимость. Пока конфига нет или он пуст — поведение не должно меняться. И надо решить, что делать, если в зоне не нашлось ни одной подходящей комнаты: сейчас в #3771 зона пропускается с записью в SYSLOG.
6. Зоны-подземелья и служебки. Текущая функция отсекает зоны с внумом ≤ 39 и комнату рентёра
%100 == 99; при переезде в конфиг эти правила должны быть выражены явно, иначе потеряются.Набросок реализации
Пометка: это черновик по итогам разбора #3770, его надо пересмотреть критически — особенно пункты 2 и 3, там возможны решения лучше.
lib/cfg/mechanics/autopopulate.xmlсо схемой рядом): записи вида «моб — область действия — сколько на зону — критерий комнаты — необязательный список фиксированных комнат по зонам».lib/cfg.kRent» — то же условие, что сейчас вtown_shop_keepers(), но выраженное в конфиге, а не в коде.town_shop_keepers()и захардкоженный внум 1901 удаляются.kStayZoneмобу 1901 — если решим, что торговец не должен уходить из города.Связано: #3770, #3771.