Показать сообщение отдельно
Старый 07.06.2008, 09:26   #408
Новичок
 
Регистрация: 05.05.2008
Сообщений: 19
Сказал Спасибо: 33
Имеет 10 спасибок в 4 сообщенях
MHz пока неопределено
По умолчанию Cleric party healer-buffer

Несмотря на то, что в этой ветке уже многократно выкладывались всяческие хилеры-баферы, я посчитал своим долгом "вернуть в копилку OpenSource" то что взял оттуда

Итак, Пати Лекарь-Бафер Клерик 35 лвл.
Работа скрипта "как есть" гарантируется при выученных скилах соответствующих этому классу на 35 уровне и выше. Скрипт проверялся только на Интерлюде в пати с живым хозяином и локомотивом от nezabudkin (в катах без бега).

delphi Код:
{   Cleric party healer-buffer from MHz   v0.4.2   Благодарности:     Изначально делался из бота     mks "SE party healer" [url]http://coderx.ru/showpost.php?p=2252&postcount=176[/url]   Идея каста последовательности скилов     Grinch "Бешенный бафер" [url]http://coderx.ru/showpost.php?p=7169&postcount=513[/url]   Окно лога     VORON [url]http://coderx.ru/showpost.php?p=2648&postcount=217[/url]   Бег прицепом и разные фишки     nezabudkin "БОТ - Воин помошник от Alexus" [url]http://coderx.ru/showpost.php?p=6348&postcount=1[/url]       Инструкция по использованию     Вписываем в скрипт имена клера и командира, дополнительно можно подкорректировать время перебафа и   пороговые значения HP, свободно можно уменьшить константу LogicTimerInterval до 500, что сделает клера более "активным".   Запускаем скрипт.   Если клер еще не в пати, зовем его в пати (вообще-то должен работать и оффпати но я так его не проверял).   В окне командира появляются надписи "Now in party NNN members", кидаем на сопартийцев какой-нибудь полезный баф   типа ВВ пока кол-во NNN не будет соответствовать реальному кол-ву членов в пати.   Теперь бот готов к работе. Бот управляется социальными действиям со стороны командира. В ответ на социальные действия   бот переключает свои режимы   1.   Режим хиляния. Если включен, то клер мониторит здоровье сопартийцев и при падении здоровья до 1-й границы хиляет       Heal, при падении здоровья ниже 2-й границы хиляет подряд Battle Heal + Heal   2.  Режим бега. При включении клер "даблкликает" по командиру. Больше по командиру не долбит, поэтом в принципе может       отцепиться, если это произошло, снова выключите-включите режим бега. При выключении режима бега клер встает на том       месте где стоял и никуда больше не бегает.   3.  Режим перебафа. При включении клер перебафывает всю пати в соответствии со своими "настройками" вообще имеет 3 вида       бафов "самобаф", баф бойца и баф "прицепа" (более детально см. листинг). Далее клер запоминает когда кого бафал и       перебафывает по мере необходимости. Режим перебафа менее приоритетен чем хил, т.е. если есть выбор хилять или бафать,       то клер будет хилять. Клер помнит когда кого бафал последний раз, так что можете свободно включать-выключать этот режим       дабы клер не "сорвался" с места и не побежал вас перебафывать потому что посчитал что пора.   4.   Принудительный перебаф всей пати. } const   BotName = 'Cleric';            // Имя бота   MasterName = 'Master';        // Имя командира   AssistantName = 'Assistant'// Необязательный ассистент     RebuffTime = 1000*60*18;      // Время перебафывания в миллисекундах   HP1=1000;                     // Порог HP для Heal   HP2=300;                      // Порог HP для Battle Heal + Heal   tt_MillisecondsInDay = 86400000;   LogicTimerInterval = 1000;   Chaos=100;   SkillDelay = 10;   DEBUG = False;     //****************************************************************************************************   // Инфомационная таблица по скилам     SkillsSize        = 32;   // Максимальная длина последовательности кастуемых скилов   cst_BattleHeal    = 1015;   cst_Heal          = 1011;   cst_SelfHeal      = 1216;   cst_WindWalk      = 1204;   cst_Shield        = 1040;   cst_Might         = 1068;   cst_Focus         = 1077;   cst_Acumen        = 1085;   cst_HolyWeapon    = 1043;   cst_MentalShield  = 1035;   cst_Concentration = 1078;   cst_ResistFire    = 1191;   cst_Regeneration  = 1044;   cst_BerserkerSpirit = 1062;         // Смещения инфы о касте в пакте 48 MagicSkillUse   cstc_DoerIdOffset =     2;   cstc_SkillIdOffset =    10;   cstc_SkillTimeOffset =  18;   cstc_ReuseDelayOffset = 22; var   frm: Tform;   memo: Tmemo;     //****************************************************************************************************   // Таблица каста   Skills: array [1..SkillsSize] of Integer; // Собственно буфер   SkillsLastIndex: Integer;                 // Используется записей   SkillsCurIndex: Integer;                  // Текущая запись   EndCastTimer: TTimer;                     // Таймер, разматывающий таблицу каста   ImCasting: Boolean;                       // Признак что я нахожусь в состоянии каста     CurRebuffedChar: Integer;                 // Индекс перебафываемого чара   TimeBeginRebuff: Integer;                 // Время начала перебафа тек. чара   OldRunKey: Boolean;                       // Старое состояние флага бега, чтобы при перецеливани при перебафе не сбивался прицел   AlreadyDoubleClicked: Boolean;            // Флаг что уже дабликнули по мастеру     //****************************************************************************************************   // Эти переменные возможно используются в других модулях   BotId: Integer;           // Id бота   BotIdx: Integer;          // Индекс бота в массиве пати   BotTargetId: Integer;     // Id цели бота   MyX, MyY, MyZ: Integer;   // Координаты бота     MasterId: Integer;        // Id командира       //****************************************************************************************************   // От se_healer     PartyCount:   Integer;                  // Текущее кол-во обслуживаемых персов     CharName:     array[1..9] of string;   CharID:       array[1..9] of integer;   CurHP:        array[1..9] of integer;   MaxHP:        array[1..9] of integer;   CurMP:        array[1..9] of integer;   MaxMP:        array[1..9] of integer;   //Dev:        array[1..9] of extended;  // Пока не используется это % здоровья   LastBuffTime: array[1..9] of integer;   //xe, ye: extended;   e,t,x,y,z: integer;     HealKey, RunKey, BuffKey: boolean;      // Флажки режимов: Лечения, Бега и Перебафа   LogicTimer: TTimer;                     // Таймер, организующий "разум" бота //**************************************************************************************************** // "Разум" бота // Подготовить посл скилов для бойцового бафа procedure FighterBuffSequence; begin   Skills[1] := cst_Shield;   Skills[2] := cst_Might;   Skills[3] := cst_WindWalk;   Skills[4] := cst_Focus;   Skills[5] := cst_HolyWeapon;   Skills[6] := cst_MentalShield;   Skills[7] := cst_ResistFire;   Skills[8] := cst_Regeneration;   Skills[9] := cst_BerserkerSpirit;   SkillsLastIndex := 9; end; // Подготовить посл скилов для мага procedure WizardBuffSequence; begin   Skills[1] := cst_Acumen;   Skills[2] := cst_Shield;   Skills[3] := cst_WindWalk;   Skills[4] := cst_MentalShield;   Skills[5] := cst_Concentration;   Skills[6] := cst_ResistFire;   Skills[7] := cst_Regeneration;   SkillsLastIndex := 7; end; // Подготовить посл скилов для "прицепа" сидящего в коридоре procedure SitterBuffSequence; begin   Skills[1] := cst_Shield;   Skills[2] := cst_WindWalk;   SkillsLastIndex := 2; end;   // Выбираем как будем лечить и лечим procedure Heal(idx: Integer); begin   if (CurHP[idx] <= HP2) then begin     Skills[1] := cst_BattleHeal;     Skills[2] := cst_Heal;     SkillsLastIndex := 2;     CastSequence;   end else begin     Cast(cst_Heal);   end; end; // Выбираем как будем бафать и бафаем procedure Rebuff(idx: Integer); begin   // Здесь выбор варианта бафов по имени перса   if CharName[idx] = BotName then     WizardBuffSequence   else if (CharName[idx] = MasterName) or (CharName[idx] = AssistantName) then     FighterBuffSequence   else     SitterBuffSequence;     CurRebuffedChar := idx;   TimeBeginRebuff := GetTime;     CastSequence; end; // Поиск нуждающегося в лечении (возвращает его индекс) function WhoNeedHPIndex: Integer; var   i: Integer;   curMinHP, curNeeded: Integer; begin   curMinHP := 100000;   curNeeded := -1;   for i:=1 to PartyCount do begin     if (CurHP[i] > 0) and (CurHP[i] <= HP1) and (CurHP[i] < MaxHP[i]) then begin       if CurHP[i] < curMinHP then begin         curNeeded := i;         curMinHP := CurHP[i];       end;     end;   end;     Result := curNeeded; end; // Поиск нуждающегося в перебафе (возвращает его индекс) function WhoNeedBuffIndex: Integer; var   i: Integer;   curTime: Integer;   curDeltaTime: Integer;   curMinTime: Integer;   curNeeded: Integer; begin   curNeeded := -1;   curTime := GetTime;   curMinTime := 2147483647;     for i:=1 to PartyCount do begin     if CurHP[i] > 0 then begin       curDeltaTime := curTime - LastBuffTime[i];       if curDeltaTime > RebuffTime then begin         if curDeltaTime < curMinTime then begin           curNeeded := i;           curMinTime := curDeltaTime;         end;       end;     end;   end;     Result := curNeeded; end; // Выработка "общей" линии поведения бота (обработчик таймера логики) procedure OnLogicTimer(Sender: TObject); var   selectedForHeal: Integer;   selectedForBuff: Integer; begin    if PartyCount <= 0 then exit; // Если еще ничего не знаем ни о ком, ничего не делаем       for t:=1 to PartyCount do begin   // Id командира и бота     if CharName[t]=MasterName then MasterId:=CharID[t];     if CharName[t]=BotName then begin       BotID:=CharID[t];       BotIdx:=t;     end;   end;   if MyHP = 0 then exit; // Если я мертв - то все пофигу     // Если я что-то кастую, то докастую и будем выбирать что делать дальше   if ImCasting then begin     AddLog('************ Im casting ****************');     exit;   end;     // Не прерываем последовательность перебафа!   if CurRebuffedChar > 0 then begin     AddLog('************ Im rebuffing char ' + CharName[CurRebuffedChar] + '****************');     exit;   end;     // Если включен режим лечения, то это приоритетная задача, сначала займемся ей   if HealKey then begin     selectedForHeal := WhoNeedHPIndex;     if selectedForHeal > 0 then begin       if CharID[selectedForHeal] <> BotTargetId then  // Проверяем прицел если надо переставляем его         Action(CharID[selectedForHeal]);              // но не контролируем результат,       Heal(selectedForHeal);                          // и сразу ХИЛ!       exit;   // Щас бой, не до бафов, оббафаю потом     end;   end;     // Если включен режим перебафа и время пришло, то запускаем перебаф   if BuffKey then begin     selectedForBuff := WhoNeedBuffIndex;     if selectedForBuff > 0 then begin   // Нашелся перс для перебафа       AddLog('Im going to rebuff ' + CharName[selectedForBuff]);             OldRunKey := RunKey;       RunKey := False;             if BotTargetId = CharID[selectedForBuff] then // Не бафаем кого попало! Убеждаемся что выбрана правильная         Rebuff(selectedForBuff)                     // цель       else         Action(CharID[selectedForBuff]);          end;   end;       end;     //**************************************************************************************************** // Отладка и тестирование procedure PrintLastBuffTime; var    i: Integer; begin   AddLog('');   PrintFlag('BuffKey', BuffKey);   PrintFlag('ImCasting', ImCasting);   AddLog('СurRebufedChar='+IntToStr(CurRebuffedChar));   AddLog('Time='+IntToStr(GetTime));   for i:=1 to PartyCount do begin     AddLog('Member='+CharName[i]+' Time='+IntToStr(LastBuffTime[i]));   end; end; procedure PrintFlag(caption: String; flag: Boolean); begin   if flag then      AddLog(caption + '=True')   else      AddLog(caption + '=False'); end; //**************************************************************************************************** // OnEndCastTimer procedure OnEndCastTimer(Sender: TObject); begin   AddLog('End casting');     // Выключаем таймер потому что не знаем сколько продлится следующий каст. Таймер включает обработчик MagicSkillUse   EndCastTimer.Enabled := False;   ImCasting := False;   Inc(SkillsCurIndex);     if SkillsCurIndex <= SkillsLastIndex then begin // Если еще есть касты в последовательности, то отправляем их дальше     Cast(Skills[SkillsCurIndex]);       end else begin    // Если последовательность кончилась, то проверяем, если это был режим перебафа,   // то фиксируем его окончание и окончательно фиксируем время когда начинали бафать перса     if CurRebuffedChar > 0 then begin       LastBuffTime[CurRebuffedChar] := TimeBeginRebuff;             TimeBeginRebuff := 0;       CurRebuffedChar := 0;             RunKey := OldRunKey;  // Перебаф окончен, если надо, можно бегать       AlreadyDoubleClicked := False;     end;   end; end; // Обработчик получения инфы о текущем касте procedure OnMagicSkillUse; var   skillId, skillTime, reuseDelay: Integer;   curTime: Integer;   skillIndex: Integer; begin   if (pck[1] <> #$48) or (ReadD(cstc_DoerIdOffset) <> BotID) then  // Блокируем затупления     exit;     ImCasting := True;       skillId :=    ReadD(cstc_SkillIdOffset);   skillTime :=  ReadD(cstc_SkillTimeOffset);   reuseDelay :=  ReadD(cstc_ReuseDelayOffset);     AddLog('');   AddLog('MagicSkillUse');   AddLog('Id=' + IntToStr(skillId));   AddLog('Time=' + IntToStr(skillTime));   AddLog('Delay=' + IntToStr(reuseDelay));     EndCastTimer.Interval := skillTime + SkillDelay;   EndCastTimer.Enabled := True; end; procedure OnActionFail; begin   AddLog('ActionFail'); end; function MyHP: Integer; begin    if BotIdx <= 0 then     Result := 0   else     Result := CurHP[BotIdx]; end; // Кастуй последовательность скилов описаную в Skills массиве procedure CastSequence; begin   SkillsCurIndex := 1;   Cast(Skills[1]); end; procedure Init; //Вызывается при включении скрипта var   i: Integer; begin   if DEBUG then begin     frm := TForm.Create(nil);     frm.Caption := 'Chat';     frm.BorderStyle := bsSizeable;     frm.Position := poScreencenter;     frm.Width:=600;     frm.Height:=700;     //frm.FormStyle:=FsStayOnTop;     frm.Show;     memo :=TMemo.create(nil);     memo.parent:=frm;     memo.align:=alClient;   end;     AddLog('Init');   BotId := 0;   BotIdx := 0;     // Очистка содержимого таблиц   BotTargetId := 0;   // Если сначала если бот дважды кликнет по кому-то то нестрашно   PartyCount := 0;   for i:=1 to 9 do begin     CharName[i] := '';     CharID[i] := 0;     CurHP[i] := 0;     MaxHP[i] := 0;     CurMP[i] := 0;     MaxMP[i] := 0;     LastBuffTime[i] := 0;      end;   ClearLastBuffTime;   BuffKey := False;   HealKey := False;   RunKey := False;     AlreadyDoubleClicked := False;     ImCasting := False;   CurRebuffedChar := 0;   TimeBeginRebuff := 0;      EndCastTimer := TTimer.Create(nil);   EndCastTimer.OnTimer := @OnEndCastTimer;   EndCastTimer.Enabled := False;     LogicTimer:=TTimer.Create(nil);   LogicTimer.OnTimer:=@OnLogicTimer;   LogicTimer.Interval:=LogicTimerInterval;   LogicTimer.Enabled:=True; end; procedure ClearLastBuffTime; var   i: Integer; begin   for i:=1 to 9 do begin     LastBuffTime[i] := 0;      end; end; // Взять цель в таргет или мочить ее пухой procedure Action(targetId: integer); begin   AddLog('Action on ID=' + IntToStr(targetId));   buf:=#$04;   WriteD(targetId);   WriteD(MyX);   WriteD(MyY);   WriteD(MyZ);   WriteC(0);     SendToServerEx(BotName); end; procedure Cast(skillId: integer); begin   // Формируем пакет на каст и отправляем его   buf:=hstr('2F 00 00 00 00 00 00 00 00 00');   WriteD(skillId, 2);   SendToServerEx(BotName);   //ImCasting := True; end; function GetTime: Integer; begin   Result := Round(Time*tt_MillisecondsInDay); end; procedure AddLog(msg: string); begin   if DEBUG then     memo.Lines.Add(IntToStr(GetTime) + '; ' + msg); end; procedure Say(msg:string); begin   buf:=hstr('4A 00 00 00 00');   WriteD(2);   WriteS(BotName);   WriteS(msg);   SendToClientEx(MasterName); end; procedure Free; //Вызывается при выключении скрипта begin   if DEBUG then begin     memo.Free;     frm.Free;   end;     EndCastTimer.Free;   LogicTimer.free;     PartyCount:=0;   HealKey:=false;   RunKey:=false;   BuffKey:=False; end; procedure OnPartySmallWindowUpdate; var   i, f: Integer;   exist: Boolean; begin   exist := False;     i:=6;   for f:=1 to (PartyCount+1) do begin     exist := CharID[f]=ReadD(2);           if exist=true then begin       CharName[f]:=ReadS(i);       i:=i+8;       CurHP[f]:=ReadD(i);       MaxHP[f]:=ReadD(i);       CurMP[f]:=ReadD(i);       MaxMP[f]:=ReadD(i);       //Dev[f]:=CurHP[f]/MaxHP[f];  // Пока не используется это % здоровья       break;     end;   end;   if exist=false then begin     PartyCount:=PartyCount+1;     Say('Now in party '+IntToStr(PartyCount) + ' members');         f:=PartyCount;     CharID[f]:=ReadD(2);     CharName[f]:=ReadS(i);     i:=i+8;     CurHP[f]:=ReadD(i);     MaxHP[f]:=ReadD(i);     CurMP[f]:=ReadD(i);     MaxMP[f]:=ReadD(i);     //Dev[f]:=CurHP[f]/MaxHP[f];  // Пока не используется это % здоровья   end; end; procedure FollowMaster; begin   if AlreadyDoubleClicked then   // Не тычем по 100 раз в хозяина     exit;       if BotTargetId = MasterId then     Action(MasterId)   else begin     Action(MasterId); Action(MasterId);   end;     AlreadyDoubleClicked := True; end; procedure OnValidatePosition; begin   MyX:=ReadD(2);   MyY:=ReadD(6);   MyZ:=ReadD(10); end; //**************************************************************************************************** // Обработка пакетов скриптом begin   if pck='' then exit;     // Управление составом пати   if FromServer and (pck[1]=#$52) and ((ConnectName=MasterName) or (ConnectName=BotName)) then begin     OnPartySmallWindowUpdate;     exit;   end;   if FromClient and (ConnectName=MasterName) and (pck[1]=#$01) then begin     if RunKey and (not ImCasting) then  // Прицепляемся в хвост только если включен режим, и ничего не кастуем       FollowMaster;     // Остаемся в скрипте   end;   // Обновляем свои координаты   if FromClient and (ConnectName=BotName) and (pck[1]=#$48) then begin     OnValidatePosition;   end;       if FromServer and (ConnectName = BotName) then begin     case pck[1] of     #$04: BotId:=ReadD(18);    // StatusUpdate     #$48: begin     // MagicSkillUse             if ReadD(cstc_SkillTimeOffset) = 0 then               exit;             if ReadD(cstc_DoerIdOffset) = BotId then begin               OnMagicSkillUse;               exit;             end;           end;     #$25: OnActionFail;     #$A6: begin    //TargetSelected             BotTargetId := ReadD(2);             AddLog('Selected target ID=' + IntToStr(BotTargetId));           end;      #$2A: begin    //TargetUnselected             BotTargetId := 0;             AddLog('UnSelected target');           end;     end;   end;     // Управление ботом со стороны командира через соц. действия   if FromClient and (ConnectName=MasterName) and (pck[1] = #$1B) then begin     case pck[2] of     #$02: begin             pck := '';             HealKey:=not(HealKey);             Case HealKey of               true: Say('Heal mode ON');               false: Say('Heal mode OFF');             end;           end;     #$03: begin             pck := '';             RunKey:=not(RunKey);             if RunKey then begin               Say('Run mode ON');               AlreadyDoubleClicked := False;             end else begin               Say('Run mode OFF');               Action(BotId);        // Прицеливаемся на себя чтобы сбить "привязку хвостом к командиру"               Action(BotId);             end;           end;     #$04: begin             pck := '';             BuffKey:=not(BuffKey);             if BuffKey then begin               Say('Buff mode ON');             end else begin               Say('Buff mode OFF');             end;           end;     #$05: begin             pck := '';             ClearLastBuffTime;             Say('Buffs times table cleared');           end;     end;      end; end.

Для тех, кто "асилил".
Основная сложность в написании скриптов для баферов это то, что у разных расс разные бафы. Что касается лекарей, то основные споры идут вокруг "принятия решения о том кого хилять". В вышеприведенном скрипте все эти "спорные" моменты выделены в отдельные функции, таким образом "заточка" под конкретные нужды не представляется проблематичной.
В боте расширена и "зарефакторена" идея каста последовательности скилов из бота Grinch, так что бот кастует практически со скоростью макроса.
Бот очень "трепетно" следит за своим таргетом. Возможно я чересчур уделил этому внимание и "перегрузил" код, если вы считаете это излишним, то можете выкинуть эту часть кода и значительно упростить логику.
Бот в отличие от других "не долбит" по командиру в ответ на каждое его перемещение. Я посчитал что это более "правильно" и менее палевно. Опять же вернуть код как в "помощнике от nezabudkin не составит труда.

Последний раз редактировалось MHz, 07.06.2008 в 10:27. Причина: подправил косяк с RunKey
MHz вне форума   Ответить с цитированием
За это сообщение MHz нажился 5 спасибками от: