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
*
*
сори за флуд, может кому-то будет полезно.