Code: Select all
Program SparringUP;
{
Новая версия спарринга двух воинов. Делает:
1. автоопределение партнера по спаррингу (имя выводится в системный
журнал при запуске скрипта для визуального контроля)
2. автонахождение мешка под ногами с армами, оружием, бинтами
3. автонахождение корыта с водой для мытья бинтов рядом с чаром
4. автоопределение направления, в котором стоит партнер по спаррингу
и, соответственно, вычисление точки для отхода (противоположное
направление, расстояние в 1 клетку)
5. автоопределение видов арма и на какие слои одевать, автоматическое
одевание арма, автоматическая проверка арма,
одетого на чара и одевание из мешка по мере разрушения.
6. автоматическое взятие в руки оружия, типы оружия занесены в массив
(по скиллам, порядок прокачки скиллов задается в скрипте)
7. Автоматическое переключение на прокачку следующего боевого после того,
как текущий вырастет до заданной величины (по умолчанию - 100.0)
8. Автоматический подхил чара во время сражения, переключение режимов
war/peace через глобальные переменные, отход чаров назад в случае
критического уменьшения здоровья, команды на начало/останов боя через
глобальные переменные, автодобор бинтов и мытье их в корыте.
9. В случае отсутствия какого-либо ресурса скрипт приостанавливает свое действие,
отведя чара назад на один шаг (для безопасности), выдает в системный журнал
соответствующее сообщение и ожидает пока вы не добавите в его мешок
необходимые ресурсы.
10. Контроль за реконнектом, при необходимости переоткрытие мешка с ресурсами.
Настройка скрипта: проверьте только типы, все остальное скрипт сделает
сам. Не забудьте правильно заполнить массивы в функции IniMyArm - вам надо
изменять только типы на соответствующих слоях. Слои перечислены все, если
на каком-то слое ничего не надевается - там стоит $0000. Второй массив содержит
названия скиллов, макс размеры, типы оружия (скрипт сам будет перезаписывать нужную
строку в первом массиве по мере необходимости). Скиллы идут в том порядке, в каком
вы хотите их контролировать и качать.
Оружие в соответствии со скиллами находится в отдельном массиве. Двуручное
оружие не предусмотрено, все оружие надевается в правую руку.
Вам нет необходимости делать копии этого скрипта для каждого чара!
Один скрипт на всех чаров.
v.2.02b (c) Edred
1.02 - вводим щиты, брать их
1.03 - автопоиск партнера по спаррингу в радиусе 1 клетки. Ищет как
мужиков, так и баб. Вывод в системный журнал имени найденного
противника для визуальной проверки игрока. Автоодевание армора:
chain туники, ног, шеи. Естественно, при необходимости добор из
сундука.
1.04 - Вводим подхил чаров в этот скрипт с мытьем бинтов. Стелс, как
оказалось, очень не любит параллельно запущенных скриптов.
Подхил идет во время боя и после останова до полного здоровья.
Мойка для бинтов ищется вокруг чаров по типу.
1.05 - Мелкая правка. Все равно не всегда останавливаются чары!
2.00 - Новый алгоритм работы, смотри описание в начале скрипта
2.01 - дополнительный контроль. Теперь чары контролируют дополнительно
здоровье противника, если оно меньше 30% - отход на безопасные позиции.
2.02 - Во время лечения чара в безопасной позиции теперь проверяется армор-класс
чара. Если он меньше заданной величины - при проверке арма и оружия по слоям
проверяется армслором износ всех вещей. Если он меньше опять же заданной
(индивидуально для каждого типа, в массиве по слоям) величины - вещь снимается
и сбрасывается в спецмешок для складывания битых армов (один мешок на обоих чаров),
а чар одевает новую вещь на этот слой.
}
{$Include 'all.inc'}
const
EnemyType1 = $0190; // типы чаров разных полов
EnemyType2 = $0191;
MyWait = 500; // пауза на перемещение и одевание
SundukType = $0E76; // Тип контейнера с армами, оружием, бинтами
WaterType = $B42; // тип тазика с водой
Delta = 60; // Разница в здоровье с максимумом для останова
Bint=$0E21; // Бинты
BlBint=$0E20; // Кровавые бинты
HowMinBandages = 20; // Если меньше - вымыть и, если надо, добрать
HowGetBandages = 100; // и взять вот столько
MinArmorClass = 50; // Если меньше - будет проверяться битость армов
ArmorTrashType = $0E79; // тип мешка, куда скидывать битые армы. Тип должен
// отличаться от типа мешков с ресурсами! Мешок
// должен лежать в доступности обоим чарам!
type ArmLayers = Record
typlayer : Byte; // Слой
typarm : Cardinal; // Тип вещи
minarm : integer; // минимальный допустимый уровень битости
end;
type DefineSkill = Record
NamSkill : String;
MaxValue : Double;
TypWeapon : Cardinal;
end;
var
MyArm : array [0..19] of ArmLayers;
MySkill : array [0..2] of DefineSkill;
EnemyWarrior, CurBag, ArmorTrash, WaterID : Cardinal;
SafeX, SafeY, WarX, WarY, k : Integer;
LastContOpen : TDateTime;
MyFlagWar,EnemyFlagWar, CurSkill : String;
OldSkill : Double;
procedure IniMyArm;
begin
// Армы и оружие по слоям
MyArm[0].typlayer := RhandLayer;
MyArm[0].typarm := $13E3;
MyArm[0].minarm := 1;
MyArm[1].typlayer := LhandLayer;
MyArm[1].typarm := $1B76;
MyArm[1].minarm := 10;
MyArm[2].typlayer := ShoesLayer;
MyArm[2].typarm := $0000;
MyArm[2].minarm := 20;
MyArm[3].typlayer := PantsLayer;
MyArm[3].typarm := $1411;
MyArm[3].minarm := 20;
MyArm[4].typlayer := ShirtLayer;
MyArm[4].typarm := $0000;
MyArm[4].minarm := 20;
MyArm[5].typlayer := HatLayer;
MyArm[5].typarm := $1412;
MyArm[5].minarm := 20;
MyArm[6].typlayer := GlovesLayer;
MyArm[6].typarm := $1414;
MyArm[6].minarm := 20;
MyArm[7].typlayer := RingLayer;
MyArm[7].typarm := $0000;
MyArm[7].minarm := 20;
MyArm[8].typlayer := NeckLayer;
MyArm[8].typarm := $1413;
MyArm[8].minarm := 20;
MyArm[9].typlayer := WaistLayer;
MyArm[9].typarm := $0000;
MyArm[9].minarm := 20;
MyArm[10].typlayer := TorsoLayer;
MyArm[10].typarm := $1415;
MyArm[10].minarm := 20;
MyArm[11].typlayer := BraceLayer;
MyArm[11].typarm := $0000;
MyArm[11].minarm := 20;
MyArm[12].typlayer := TorsoHLayer;
MyArm[12].typarm := $0000;
MyArm[12].minarm := 20;
MyArm[13].typlayer := EarLayer;
MyArm[13].typarm := $0000;
MyArm[13].minarm := 20;
MyArm[14].typlayer := BraceLayer;
MyArm[14].typarm := $0000;
MyArm[14].minarm := 20;
MyArm[15].typlayer := ArmsLayer;
MyArm[15].typarm := $1410;
MyArm[15].minarm := 20;
MyArm[16].typlayer := CloakLayer;
MyArm[16].typarm := $0000;
MyArm[16].minarm := 20;
MyArm[17].typlayer := RobeLayer;
MyArm[17].typarm := $0000;
MyArm[17].minarm := 20;
MyArm[18].typlayer := EggsLayer;
MyArm[18].typarm := $0000;
MyArm[18].minarm := 20;
MyArm[19].typlayer := LegsLayer;
MyArm[19].typarm := $0000;
MyArm[19].minarm := 20;
// Скиллы, макс величины, оружие (для перезаписи в пред. массиве
MySkill[0].NamSkill := 'Mace Fighting';
MySkill[0].MaxValue := 100.0;
MySkill[0].TypWeapon := $13E3; // Smith's Hammer
MySkill[1].NamSkill := 'Fencing';
MySkill[1].MaxValue := 100.0;
MySkill[1].TypWeapon := $0F51; // Dagger
MySkill[2].NamSkill := 'Swordsmanship';
MySkill[2].MaxValue := 100.0;
MySkill[2].TypWeapon := $13F6; // Butcher's Knife
end;
function AutoFindEnemy : Cardinal;
// функция поиска чаров рядом для определения партнера по спаррингу
// Возвращает сериал партнера по спаррингу.
begin
Finddistance := 1;
Ignore(self);
if FindType(EnemyType1, Ground) = 0 then FindType(EnemyType2, Ground);
if finditem = 0 then
begin
// не нашли врагов :(
AddToSystemJournal('Error: Партнер по спаррингу не найден!');
Result := $00000000;
Exit;
end
else
begin
// Ага! Вот он, гадина!
AddToSystemJournal('Enemy found: ' + GetName(finditem));
Result := finditem;
end;
end;
procedure OpenContainer(f : boolean);
// если f - true - просто открываем наш мешок и мешок для битого
// если f - false - проверяем, был ли реконнект чара после последнего
// открывания контейнера, если был - переоткрываем контейнер и записываем
// новое значение в переменную LastContOpen, если не был - ничего не делаем
begin
waitconnection(3000);
if (f = true) or (LastContOpen < ConnectedTime) then
begin
UseObject(CurBag);
wait(MyWait * 2);
checksave;
UseObject(ArmorTrash);
wait(MyWait * 2);
checksave;
LastContOpen := Now;
addtosystemjournal('Контейнеры открыт в ' + DateTimeToStr(Now));
end;
end;
function testCurArm(tlayer : byte; tmin : integer) : boolean;
{ Тестируем вещь на указанном слое армслором.
ловим в журнале ответ, парсим его, вычленяем величину. Если она меньше
переданного значения - возвращаем true, иначе - false
Вот образцы строк:
Defense [56].This item is in full repair.
Defense [8].This item is badly damaged.
Defense [0].This item is about to fall apart.It looks quite fragile.
Defense [48].This item is a bit worn.
В журнале будет ловиться строка содержащая 'Defense'. Из нее вычленяться
число в квадратных скобках. Если на вашем шарде армслор выдает другие строки,
вам придется переписать эту функцию.
}
var tmpid : Cardinal;
tmptime : TDateTime;
r, p : Integer;
tmpstr : String;
begin
Result :=false;
tmpid := ObjAtLayer(tlayer);
if TargetPresent then CancelTarget;
tmptime := Now;
UseSkill('Arms Lore');
WaitForTarget(5000);
If TargetPresent then TargetToObject(tmpid);
r := 0;
repeat
wait(100);
r := r + 1;
p := InJournalBetweenTimes('Defense', tmptime, Now);
until (p <> -1) or (r > 300);
if p <> -1 then
begin
tmpstr := Journal(p);
r := Pos('[',tmpstr);
p := Pos(']',tmpstr);
if StrToInt(Copy(tmpstr,r+1,p-r-1)) < tmin then Result := True;
end else Addtosystemjournal('Warning: Армслор не работает. Проверьте правильность тестирования');
end;
procedure CheckArm;
// проверка в цикле есть ли на соответствующих слоях нужные типы, если
// чего-то нет - возьмем это из мешка и оденем на этот слой.
VAR i : integer;
tmpid : cardinal;
testarm : boolean;
begin
if Armor < MinArmorClass then testarm := true else testarm := false;
for i := 0 to 19 do
begin
if MyArm[i].typarm = $0000 then continue;
if GetType(ObjAtLayer(MyArm[i].typlayer)) = MyArm[i].typarm then
begin
if not testarm then continue;
// требуется проверка текущей части арма на битость
if i = 0 then continue;
if not testCurArm(MyArm[i].typlayer,MyArm[i].minarm) then continue;
end;
// На соответствующем слое либо ничего не надето, либо одето что-то другое
if (ObjAtLayer(MyArm[i].typlayer) <> 0) then
begin
tmpid := ObjAtLayer(MyArm[i].typlayer);
while not UnEquip(MyArm[i].typlayer) do wait(500);
// тут надо сделать сброс снятой вещи в пак для битых армов
OpenContainer(false);
MoveItem(tmpid,1,ArmorTrash,0,0,0);
wait(MyWait);
checksave;
end;
// теперь на слое точно ничего не надето
waitconnection(3000);
OpenContainer(false);
if Findtype(MyArm[i].typarm,CurBag) = 0 then
begin
AddToSystemJournal('Не могу найти в мешке что надеть на слой ' + inttostr(MyArm[i].typlayer));
AddToSystemJournal('Добавьте в мешок вещь типа $' + inttohex(MyArm[i].typarm,4));
repeat
wait(MyWait * 4);
waitconnection(3000);
Findtype(MyArm[i].typarm,CurBag);
until finditem <> 0;
end;
tmpid := finditem;
while Count(MyArm[i].typarm) = 0 do
begin
waitconnection(3000);
OpenContainer(false);
Grab(tmpid,1);
wait(MyWait);
checksave;
end;
while ObjAtLayer(MyArm[i].typlayer) <> tmpid do
begin
waitconnection(3000);
Equip(MyArm[i].typlayer,tmpid);
wait(MyWait);
checksave;
end;
end;
end;
function FindEnvironment : Boolean;
// Находит под ногами у чара или сзади него мешок с добром (радиус = 1 клетка от чара)
// Находит корыто с водой для мытья бинтов (радиус = 2 клетки от чара)
// Находит мешок для битых армов (радиус = 3 клетки от чара)
// Возвращает false если что-то не найдено.
begin
// мешок с добром
Result := False;
WaitConnection(3000);
Finddistance := 1;
if FindType(SundukType, Ground) = 0 then
begin
AddToSystemJournal('Error: Сундук или мешок не найден!');
exit;
end;
CurBag := finditem;
// мешок для битых армов
Finddistance := 3;
if FindType(ArmorTrashType, Ground) = 0 then
begin
AddToSystemJournal('Error: Сундук или мешок для битых армов не найден!');
exit;
end;
ArmorTrash := finditem;
LastContOpen := Now;
OpenContainer(true);
// корыто с водой
Finddistance := 2;
WaterID := FindType(WaterType, Ground);
if WaterID = 0 then
begin
AddToSystemJournal('Error: Корыто с водой не найдено!');
exit;
end;
Result := True;
end;
function CreateSafePoints : boolean;
// Определяет координаты, где стоит чар и где стоит враг. Вычисляет направление
// и определяет координаты точки отхода. Записывает их в SafeX и SafeY.
// Текущие координаты - в WarX, WarY.
var tmpx, tmpy : cardinal;
begin
Result := false;
WarX := GetX(self);
WarY := GetY(self);
tmpx := GetX(EnemyWarrior);
tmpy := GetY(EnemyWarrior);
if (WarX <> tmpx) AND (WarY <> tmpY) then
begin
AddToSystemJournal('Error: Неправильная установка чаров!');
AddToSystemJournal('Чары должны стоять по одной из осей,');
AddToSystemJournal('Поставьте чаров либо по направлению 1-9 (на цифровой клавиатуре),');
AddToSystemJournal('либо по направлению 3-7.');
exit;
end;
if WarY = tmpy then
begin
SafeY := WarY;
if WarX < tmpx then SafeX := WarX - 1;
if WarX > tmpx then SafeX := WarX + 1;
if WarX = tmpx then
begin
AddToSystemJournal('Error: Чары стоят в одной точке!');
exit;
end;
end;
if WarX = tmpx then
begin
SafeX := WarX;
if WarY < tmpy then SafeY := WarY - 1;
if WarY > tmpy then SafeY := WarY + 1;
end;
Result := true;
end;
procedure CheckBandages;
// моет грязные бинты (если есть), добирает из мешка чистые (если мало)
VAR tmpid : cardinal;
i : integer;
begin
WaitConnection(3000);
if Count(Bint) >= HowMinBandages then exit;
// проверим грязные и помоем
findtype(BlBint,backpack);
while (FindCount > 0) do
begin
WaitConnection(3000);
checksave;
useobject(FindItem);
waitfortarget(5000);
if TargetPresent then TargetToObject(WaterID);
wait(MyWait * 2);
findtype(BlBint,backpack);
end;
WaitConnection(3000);
if Count(Bint) >= HowMinBandages then exit;
// бинты вымыты и их все равно меньше минимума
OpenContainer(false);
if FindType(Bint, CurBag) = 0 then
begin
AddToSystemjournal('Warning: Кончились бинты, подложите в мешок');
repeat
wait(MyWait * 4);
waitconnection(3000);
Findtype(Bint, CurBag);
until finditem <> 0;
end;
i := HowGetBandages;
tmpid := finditem;
if GetQuantity(tmpid) < HowGetBandages then i := GetQuantity(tmpid);
OpenContainer(false);
Grab(tmpid, i);
wait(MyWait * 2);
checksave;
end;
procedure FullHealChar;
// лечение чара до максимума
var stime : TDateTime;
i : integer;
begin
while (HP < MaxHP) do
begin
CheckBandages;
if TargetPresent then CancelTarget;
stime := Now;
if UseType(Bint,$FFFF) = 0 then break;
WaitForTarget(5000);
If TargetPresent then TargetToObject(Self);
i := 0;
repeat
wait(100);
i := i + 1;
until (InJournalBetweenTimes('You put the bloody bandage|failed.', stime, Now)<>-1) or (i > 300);
waitconnection(3000);
end;
end;
procedure GotoXY(x,y,prec : integer; runflag : boolean);
// x,y - координаты, в которые идем
// prec - точность подхода
// runflag - бежать или идти
// v. 1.04 (с) Edred, скрипт портирован с инжекта
// оригинальная версия Yoko с доработкой AGRS и Edred.
var ld, ldc, dx, dy, mx, my : Integer;
begin
ld := 0; ldc := 0;
while true do
begin
dx := GetX(self) - x; if dx < 0 then dx := 0 - dx;
dy := GetY(self) - y; if dy < 0 then dy := 0 - dy;
if dy > dx then dx := dy;
if dx <= prec then exit;
mx := GetX(self); my := GetY(self);
dx := mx - x; if dx < 0 then dx := 0 - dx;
dy := my - y; if dy < 0 then dy := 0 - dy;
if dy > dx then dx := dy;
if dx <= prec then exit;
if ld = dx then begin
ldc := ldc + 1;
if ldc > 100 then begin addtosystemjournal( 'GotoXY: Cannot reach location!' ); exit; end;
end
else ld := dx;
waitconnection(5000);
if mx = x then begin
if my = y then exit;
// North
if my > y then begin Raw_Move(0,runflag); continue; end;
// South
Raw_Move(4,runflag); continue;
end;
if mx < x then begin
// Northeast
if my > y then begin Raw_Move(1,runflag); continue; end;
// East
if my = y then begin Raw_Move(2,runflag); continue; end;
// Southeast
Raw_Move(3,runflag); continue;
end;
// Southwest
if my < y then begin Raw_Move(5,runflag); continue; end;
// West
if my = y then begin Raw_Move(6,runflag); continue; end;
// Nortwest
Raw_Move(7,runflag); continue;
end;
end;
function SkillControl : boolean;
// Переключает какой скилл качать и перезаписывает в оружие.
// Возвращает false если необходим выход из скрипта (все вкачано).
var i : integer;
begin
waitconnection(3000);
Result := true;
for i := 0 to 2 do
begin
if GetSkillValue(MySkill[i].NamSkill) < MySkill[i].MaxValue then break;
end;
if GetSkillValue(MySkill[i].NamSkill) < MySkill[i].MaxValue then
begin
MyArm[0].typarm := MySkill[i].TypWeapon;
CurSkill := MySkill[i].NamSkill;
end
else
begin
// Все три скилла по 100.0
Result := false;
exit;
end;
end;
function EnemyHPControl : Double;
begin
Result := 100 * GetHP(EnemyWarrior) / GetMaxHP(EnemyWarrior);
end;
Begin
waitconnection(3000);
SetGlobal('stealth',MyFlagWar,'0');
EnemyWarrior := AutoFindEnemy;
if EnemyWarrior = $00000000 then exit;
if not FindEnvironment then exit;
if not CreateSafePoints then exit;
IniMyArm;
if not SkillControl then exit;
OldSkill := GetSkillValue(CurSkill);
FullHealChar;
CheckArm;
CheckBandages;
MyFlagWar := GetName(self) + 'War';
EnemyFlagWar := GetName(EnemyWarrior) + 'War';
SetGlobal('stealth',MyFlagWar,'1');
waitconnection(3000);
repeat
wait(100);
until (Life < MaxLife) OR (GetGlobal('stealth',EnemyFlagWar) = '1');
// оба чара готовы к началу спарринга
while not Dead do
begin
if not WarMode then SetWarMode(true);
attack(EnemyWarrior);
k := 0;
repeat
wait(100);
k := k + 1;
if (k = 50) and (count(Bint) > 0) and (Life < MaxLife) then
// подхил чара во время боя
begin
waitconnection(3000);
if TargetPresent then CancelTarget;
if UseType(Bint,$FFFF) <> 0 then
begin
WaitForTarget(5000);
If TargetPresent then TargetToObject(Self);
k := 0;
end;
end;
waitconnection(3000);
until (Life <= (MaxLife - Delta)) OR (GetGlobal('stealth',EnemyFlagWar) = '0') OR (EnemyHPControl < 30.0);
SetGlobal('stealth',MyFlagWar,'0');
waitconnection(3000);
GotoXY(SafeX,SafeY,0,false);
SetWarMode(false);
FullHealChar;
if GetSkillValue(CurSkill) > OldSkill then
begin
AddToSystemJournal('Скилл ' + CurSkill + ' вырос. Теперь он равен ' + FloatToStr(GetSkillValue(CurSkill)));
OldSkill := GetSkillValue(CurSkill);
if not SkillControl then exit;
end;
CheckArm;
waitconnection(3000);
GotoXY(WarX,WarY,0,false);
SetGlobal('stealth',MyFlagWar,'1');
waitconnection(3000);
repeat
wait(100);
until (Life < MaxLife) OR (GetGlobal('stealth',EnemyFlagWar) = '1');
end;
End.