Есть скрипт для бота, для прохождения квэстов в одной игре ММО игре мною созданный и спроектированный.
Проблема в том, что он мне не нравится, так как я применил анти-паттерн (сам того тогда не осознавая, дело было давненько)
Божественный Объект (
God Object).
Как он работает
Когда я его писал я думал о конечном автомате, который переходит из одного состояния после выполнения очередного шага.
Если шаг скажем 87 выполнится успешно то автомат перейдёт к следующему шагу 88.
Если шаг квеста 87 сфейлится (например на бота нападёт другой игрок и помешает поговорить с NPC)
то шаг повторится и автомат снова перейдет в состояние 87. В каждом состоянии возможна обработка ошибок и команда JumpStep благодаря которой можно перейти из состояния 87 в 65 или любое другое. (для обработки случая смерти в середине квеста)
Проблема: как это всё реализовать в виде кода?
Создавать массив объектов и функций или лепить гигантский case of (aka switch в С++) мне очень не хотелось потому что это все кушает оперативку + когда придется добавлять новый метод придётся редактировать этот array/case
Я решил проблему используя RTTI фишку паскаля благодаря которой можно
вызвать метод объекта по его имени. Получился класс с кучей шагов Step1 Step2 ... Step120.. Внутри автомата храню номер шага и когда надо перейти к следующему шагу просто вызываю
кодом вида: call_method ('Step'+intToStr(Step_Num));
В реальности код выглядит вот так (S - это Step)
procedure TSE.S76;
begin
StepRes:=Go('Шнаин') and (Q([NPC_Shnain,DLG_Ischeznuvshiy_Sakum__v_protsesse_,1,1]) or WaitQS(Qu20,-1));
end;
{----------------------}
const Qu21=10334;
procedure TSE.S77;
begin
StepRes:=Go('Шнаин') and (Q([NPC_Shnain,DLG_Osmotr_kholma_Vetryanyykh_Melynits,1,1]) or WaitQS(Qu21,1));
end;
{----------------------}
procedure TSE.S78;
begin
StepRes:=true;
if PosOutRange('Gludio',5000) then
StepRes:=Escape(TravelSoe);
StepRes:=StepRes and Go('Батис') and (Q([NPC_Batis, DLG_Osmotr_kholma_Vetryanyykh_Melynits__v_protsesse_,1,1]) or WaitQS(Qu21,-1));
if stepRes then
prs['DqusetWeapon']:=QEvents.LastItemAdd;
end;
{----------------------}
Решение хорошо в том плане что при добавлении новых шагов не надо редактировать Всякие кейсы и массивы. Просто пишешь новый метод Step121 и всё.
Но такое решение мне не нравится по многим причинам
1. все методы находятся в оперативной памяти даже когда они не нужны. Да windows хитро устроена в плане выгрузки\подгрузки исполняемых страниц кода с диска по мере надобности но всё таки полагатся на OS в таком вопросе не хотелось бы.
2. применен антипатерн божественый объект в результате весь код в одном объекте а хотелось бы найти хороший паттерн для такого случая который эллегатно решал все мои проблемы не может быть что бы его не было есть же игры типо GTA там же мощные заскриптованые сцены где с NPC все что угодно может пойти не так (типа другу главного героя перегородит путь машина и ему придётся её обходить или еще что-нить непредвиденное) и должна быть обработка таких случаев.