CFA wrote:
Заодно посмотрел что за group в ConvertIntegerToFlags
0 = tfLand возвращает флаги тайлов карты
1 = tfStatic возвращает флаги для тайлов статики
Некоторые значения этих флагов совпадают, некоторые различаются
Если честно, мне осталось непонятна мысль, о том, что флаги чем-то различаются. Давно, давно, когда не было нормальной документации, о флагах я узнал отсюда 
http://uo.stratics.com/heptazane/fileformats.shtml, и прощупал используя InsedeUO 
http://uo.stratics.com/heptazane/insideuo/. Нигде упоминания о разных наборах флагов не нашел. Можно говорить лишь о том, что какие-то из флагов тайлов статики, обычно не встречаются на тайлах земли.
Исходя из написанного на Stratics, можно сделать такую штуку:
Code: Select all
const tfBackground  = $00000001;  // Фон  
const tfWeapon      = $00000002;  // Оружие
const tfTransparent = $00000004;  // Прозрачный
const tfTranslucent = $00000008;  // Полупразрачный
const tfWall        = $00000010;  // Стена
const tfDamaging    = $00000020;  // Может нанести урон
const tfImpassable  = $00000040;  // Непроходимый  
const tfWet         = $00000080;  // Мокрый (обычно вода либо ее источник)
const tfUnknown1    = $00000100;  
const tfSurface     = $00000200;  // Поверхность
const tfBridge      = $00000400;  // Мост
const tfGeneric     = $00000800;
const tfWindow      = $00001000;  // Окно в стене
const tfNoShoot     = $00002000;  // Не простреливается
const tfArticleA    = $00004000;  // Существительное с артиклем A
const tfArticleAn   = $00008000;  // Существительное с артиклем An
const tfInternal    = $00010000;  
const tfFoiliage    = $00020000;  // Листва деревьев
const tfPartialHue  = $00040000;  // Частично окрашен
const tfUnknown2    = $00080000;  
const tfMap         = $00100000;  
const tfContainer   = $00200000;  // Контейнер
const tfWearable    = $00400000;  // Можно одеть - т.е. одежда, броня, оружие и т.п.
const tfLightSource = $00800000;  // Источник света
const tfAnimation   = $01000000;  // Анимированный
const tfNoDiagonal  = $02000000;  // Нельзя пройти по диагонали
const tfMirrored    = $04000000;  // Зеркальный
const tfArmor       = $08000000;  // Броня
const tfRoof        = $10000000;  // Крыша здания
const tfDoor        = $20000000;  // Дверь
const tfStairBack   = $40000000;  // Лестница вниз  
const tfStairRight  = $80000000;  // Лестница наверх
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Возвращает истину, если набор флагов Flag содержится в наборе Flags
function InFlags(const Flag, Flags: LongWord): Boolean;
begin
  Result := (Flag = (Flag and Flags));
end;
Константы, приведенные выше представляют собой семейство чисел с побитовым сдвигом на одну позицию. Первая константа в бинарном виде "00000001", вторая "00000010", третья "00000100"  и т.д.
Все это позволяет работать с флагами, например, вот так:
Code: Select all
//////////////////////////////////////////////////////////////////////
// получить набор флагов объекта
function GetArtFlags(Item: Cardinal) : LongWord;
begin
  Result := GetStaticTileData(GetType(Item)).Flags;
end;
//////////////////////////////////////////////////////////////////////
// Проверить, является ли объект "дверью"
function IsDoor(ObjectId: Cardinal): Boolean;
begin
  Result := InFlags(tfDoor+tfImpassable, GetArtFlags(ObjectId));
end;
Для более удобного отображения содержимого флагов или для тестов можно сделать такую функцию:
Code: Select all
//////////////////////////////////////////////////////////////////////
// Строковая расшифровка набора флагов Flags.
function FlagsToStr(Flags: LongWord): String;
begin
  if Flags = -1 then Result := '[All flags]'
  else begin
    Result := '[';
    if InFlags(tfBackground, Flags) then Result := Result+'Background,';
    if InFlags(tfWeapon, Flags) then Result := Result+'Weapon,';
    if InFlags(tfTransparent, Flags) then Result := Result+'Transparent,';
    if InFlags(tfTranslucent, Flags) then Result := Result+'Translucent,';
    if InFlags(tfWall, Flags) then Result := Result+'Wall,';
    if InFlags(tfDamaging, Flags) then Result := Result+'Damaging,';
    if InFlags(tfImpassable, Flags) then Result := Result+'Impassable,';
    if InFlags(tfWet, Flags) then Result := Result+'Wet,';
    if InFlags(tfUnknown1, Flags) then Result := Result+'Unknown1,';
    if InFlags(tfSurface, Flags) then Result := Result+'Surface,';
    if InFlags(tfBridge, Flags) then Result := Result+'Bridge,';
    if InFlags(tfGeneric, Flags) then Result := Result+'Generic,';
    if InFlags(tfWindow, Flags) then Result := Result+'Window,';
    if InFlags(tfNoShoot, Flags) then Result := Result+'NoShoot,';
    if InFlags(tfArticleA, Flags) then Result := Result+'ArticleA,';
    if InFlags(tfArticleAn, Flags) then Result := Result+'ArticleAn,';
    if InFlags(tfInternal, Flags) then Result := Result+'Internal,';
    if InFlags(tfFoiliage, Flags) then Result := Result+'Foiliage,';
    if InFlags(tfPartialHue, Flags) then Result := Result+'PartialHue,';
    if InFlags(tfUnknown2, Flags) then Result := Result+'Unknown2,';
    if InFlags(tfMap, Flags) then Result := Result+'Map,';
    if InFlags(tfContainer, Flags) then Result := Result+'Container,';
    if InFlags(tfWearable, Flags) then Result := Result+'Wearable,';
    if InFlags(tfLightSource, Flags) then Result := Result+'LightSource,';
    if InFlags(tfAnimation, Flags) then Result := Result+'Animation,';
    if InFlags(tfNoDiagonal, Flags) then Result := Result+'NoDiagonal,';
    if InFlags(tfMirrored, Flags) then Result := Result+'Mirrored,';
    if InFlags(tfArmor, Flags) then Result := Result+'Armor,';
    if InFlags(tfRoof, Flags) then Result := Result+'Roof,';
    if InFlags(tfDoor, Flags) then Result := Result+'Door,';
    if InFlags(tfStairBack, Flags) then Result := Result+'StairBack,';
    if InFlags(tfStairRight, Flags) then Result := Result+'StairRight,';
    if Result[Length(Result)]=',' then Result[Length(Result)] := ']' else Result := Result+']';
  end;
end;
Вообще описанный прием практически совпадает с конструкцией из манулала по RC1 
http://stealth.od.ua/forum/viewtopic.php?f=6&t=1220
Code: Select all
TTileDataFlags = (tsfBackground, tsfWeapon, tsfTransparent, tsfTranslucent, tsfWall, tsfDamaging, tsfImpassable, tsfWet, tsfUnknown, tsfSurface, tsfBridge, tsfGeneric, tsfWindow, tsfNoShoot, tsfPrefixA, tsfPrefixAn, tsfInternal, tsfFoliage, tsfPartialHue, tsfUnknown1, tsfMap, tsfContainer, tsfWearable, tsfLightSource, tsfAnimated, tsfNoDiagonal, tsfUnknown2, tsfArmor, tsfRoof, tsfDoor, tsfStairBack, tsfStairRight, 
// флаги ниже по сути совпадают с флагами выше
tlfTranslucent, tlfWall, tlfDamaging, tlfImpassable, tlfWet, tlfSurface, tlfBridge, tlfPrefixA, tlfPrefixAn, tlfInternal, tlfMap, tlfUnknown3); 
TTileDataFlagSet = set of TTileDataFlags;
set - это условно говоря "длинное целое", обычно 32 или 64 бита, в котором каждый бит, аналогично означает наличие или отсутствие элемента в множестве. Вот причина того, что в Паскале нельзя в множестве хранить больше определенного числа элементов.  Операции над множеством перегружены Паскалем, это арифметические действия имеющие смысл: объединение, пересечение, дополнение и др. - суть есть побитовые операции. Хорошей иллюстрацией работы с множеством может быть такой пример:
Code: Select all
Program setsample;
type Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
type Days = Set of Day;
//////////////////////////////////////////////////////////////////////
//  Операция "проверка вхождения"
function InDays(const d1,d2: Days): Boolean;
begin
  Result := (d2 = (d1+d2));
end;
//////////////////////////////////////////////////////////////////////
//  Операция "объединение"
function UntDays(const d1,d2: Days): Days;
begin
  Result := d1+d2;
end;
//////////////////////////////////////////////////////////////////////
//  Операция "пересечение"
function IscDays(const d1,d2: Days): Days;
begin
  Result := d1*d2;
end;
//////////////////////////////////////////////////////////////////////
//  Операция "левое дополнение"
function LExclDays(const d1,d2: Days): Days;
begin
  Result := (d1-d2);
end; 
//////////////////////////////////////////////////////////////////////
//  Операция "полное дополнение"
function ExclDays(const d1,d2: Days): Days;
begin
  Result := (d1-d2)+(d2-d1);
end; 
   
begin 
  if InDays([Wed], IscDays(UntDays([Mon,Wed],[Thu]),[Tue,Wed]))  then AddToSystemJournal('ok');
end;
Преимущества set очевидны - компактно, быстро обрабатывается, наглядно. Но ничем от обычных побитовых операций над целым, вроде and, or, xor не отличается. И собственно мне осталось непонятно зачем преобразователю ConvertIntegerToFlags использовать еще один параметр. Кстати, в единственном известном примере использования функции этого параметра нет. У меня, почему-то при любом значении параметра Group получается пустое множество. Видимо я с чем-то не разобрался, если не затруднит, запостите, пожалуйста работающую версию IsDoor с помощью ConvertIntegerToFlags.
Если вернуться к тайлам, то мне, при их изучении очень помогла следующая самописная процедурка:
Code: Select all
procedure InfoXY(X,Y: Word);
var
  MapCell: TMapCell;
  LandTileData: TLandTileData;
  StaticCell: TStaticCell;
  StaticItem: TStaticItem;
  StaticTileData: TStaticTileData;
  i: Integer;
  FL: TStringList;
  Id: Cardinal;
  Z: ShortInt;
begin
  FillNewWindow('********************* Info X='+IntToStr(X)+', Y='+IntToStr(Y)+' *********************');
  MapCell := GetMapCell(X,Y,WorldNum);
  LandTileData := GetLandTileData(MapCell.Tile);
  FillNewWindow('*  Land: Name="'+ConvertCharArray2String(LandTileData.Name)+'", Tile=$'+IntToHex(MapCell.Tile,4)+', Flags='+FlagsToStr(LandTileData.Flags)+', Z='+IntToStr(MapCell.Z));
  FillNewWindow('*  Statics:');
  StaticCell := ReadStaticsXY(X,Y,WorldNum);
  if StaticCell.StaticCount > 0 then begin
    for i := 0 to StaticCell.StaticCount-1 do begin
      StaticItem := StaticCell.Statics[i];
      StaticTileData := GetStaticTileData(StaticItem.Tile);
      FillNewWindow('*       Name="'+ConvertCharArray2String(StaticTileData.Name)+'", Tile=$'+IntToHex(StaticItem.Tile,4)+', Flags='+FlagsToStr(StaticTileData.Flags)+', Z='+IntToStr(StaticItem.Z));
    end;
  end; 
  FillNewWindow('*  Objects:');
  if FindAtCoord(X,Y) > 0 then begin
    FL := TStringList.Create;
    GetFindedList(FL);
    for i := 0 to FL.Count-1 do begin
      Id := StrToInt('$'+FL.Strings[i]);
      FillNewWindow('*       Name="'+GetArtName(Id)+'", Id=$'+IntToHex(Id,8)+', Type=$'+IntToHex(GetType(Id),4)+', Color=$'+IntToHex(GetColor(Id),4)+', Flags='+FlagsToStr(GetArtFlags(Id))+', Z='+IntToStr(GetZ(Id)));
    end;
    FL.Free;
  end;
  if IsWorldCellPassable(GetX(Self), GetY(Self), GetZ(Self), X,Y,Z, WorldNum) then  FillNewWindow('*  Passable point') else FillNewWindow('*  Impassable point') ;
  FillNewWindow('*');  
end;
Которую, начиная с 5-го Стелса можно обрамить таким образом:
Code: Select all
procedure info;
var t: TTargetInfo;
begin
  ClientRequestTileTarget;
  WaitForClientTargetResponse(15000);
  t := ClientTargetResponse;
  InfoXY(t.X, t.Y);  
end;
И забиндить на клавишу.
Тогда вы сможете тыкать на землю и видеть как устроены тайлы в нужной точке, получая в дополнительном окне сообщения такого типа:
********************* Info X=2852, Y=3536 *********************
*  Land: Name="grass", Tile=$0003, Flags=[Mirrored], Z=0
*  Statics:
*  Objects:
*       Name="a wooden door", Id=$4050DE1E, Type=$06A5, Color=$0000, Flags=[Wall,Impassable,NoShoot,ArticleA,Door], Z=7
*  Impassable point
*
********************* Info X=2842, Y=3542 *********************
*  Land: Name="grass", Tile=$00C0, Flags=[], Z=0
*  Statics:
*       Name="leaves", Tile=$0D8C, Flags=[Foiliage], Z=0
*  Objects:
*  Passable point
*
********************* Info X=2840, Y=3538 *********************
*  Land: Name="grass", Tile=$0003, Flags=[Mirrored], Z=0
*  Statics:
*       Name="leaves", Tile=$0D52, Flags=[Foiliage], Z=0
*  Objects:
*  Passable point
*
*
 
 сори за флуд, может кому-то будет полезно.