DEADSOFTWARE

render: use TAnimationState for monsters
[d2df-sdl.git] / src / game / g_textures.pas
index 1e66c8ee1556ee8634b813ff8d075667b857cb65..aaa91b793fe590d5e3bec09c6f432b148141b148 100644 (file)
@@ -2,8 +2,7 @@
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,7 +20,7 @@ interface
 uses
   SysUtils, Classes,
   {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
-  e_graphics, MAPDEF, ImagingTypes, Imaging, ImagingUtility;
+  g_base, r_graphics, MAPDEF, ImagingTypes, Imaging, ImagingUtility;
 
 type
   TLevelTexture = record
@@ -34,6 +33,50 @@ type
 
   TLevelTextureArray = array of TLevelTexture;
 
+  TAnimationState = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
+  private
+    mAlpha: Byte;
+    mBlending: Boolean;
+    mCounter: Byte; // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
+    mSpeed: Byte; // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
+    mCurrentFrame: Integer; // Òåêóùèé êàäð (íà÷èíàÿ ñ 0)
+    mLoop: Boolean; // Ïåðåõîäèòü íà ïåðâûé êàäð ïîñëå ïîñëåäíåãî?
+    mEnabled: Boolean; // Ðàáîòà ðàçðåøåíà?
+    mPlayed: Boolean; // Ïðîèãðàíà âñÿ õîòÿ áû ðàç?
+    mMinLength: Byte; // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
+    mRevert: Boolean; // Ñìåíà êàäðîâ îáðàòíàÿ?
+
+    mLength: Integer;
+
+  public
+    constructor Create (aloop: Boolean; aspeed: Byte; len: Integer);
+    destructor  Destroy (); override;
+
+    procedure reset ();
+    procedure update ();
+    procedure enable ();
+    procedure disable ();
+    procedure revert (r: Boolean);
+
+    procedure saveState (st: TStream);
+    procedure loadState (st: TStream);
+
+    function totalFrames (): Integer; inline;
+
+  public
+    property played: Boolean read mPlayed;
+    property enabled: Boolean read mEnabled;
+    property isReverse: Boolean read mRevert;
+    property loop: Boolean read mLoop write mLoop;
+    property speed: Byte read mSpeed write mSpeed;
+    property minLength: Byte read mMinLength write mMinLength;
+    property currentFrame: Integer read mCurrentFrame write mCurrentFrame;
+    property currentCounter: Byte read mCounter write mCounter;
+    property counter: Byte read mCounter;
+    property blending: Boolean read mBlending write mBlending;
+    property alpha: Byte read mAlpha write mAlpha;
+  end;
+
   TAnimation = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
   private
     mId: LongWord;
@@ -54,9 +97,6 @@ type
     constructor Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
     destructor  Destroy (); override;
 
-    procedure draw (x, y: Integer; mirror: TMirrorType);
-    procedure drawEx (x, y: Integer; mirror: TMirrorType; rpoint: TDFPoint; angle: SmallInt);
-
     procedure reset ();
     procedure update ();
     procedure enable ();
@@ -83,623 +123,195 @@ type
     property framesId: LongWord read mId;
     property width: Word read mWidth;
     property height: Word read mHeight;
-  end;
-
-
-function g_Texture_CreateWAD (var ID: LongWord; const Resource: AnsiString): Boolean;
-function g_Texture_CreateFile (var ID: LongWord; const FileName: AnsiString): Boolean;
-function g_Texture_CreateWADEx (const textureName, Resource: AnsiString): Boolean;
-function g_Texture_CreateFileEx (const textureName, FileName: AnsiString): Boolean;
-function g_Texture_Get (const textureName: AnsiString; var ID: LongWord): Boolean;
-procedure g_Texture_Delete (const textureName: AnsiString);
-procedure g_Texture_DeleteAll ();
-
-function g_CreateFramesImg (ia: TDynImageDataArray; ID: PDWORD; const Name: AnsiString; BackAnimation: Boolean=false): Boolean;
-
-function g_Frames_CreateWAD (ID: PDWORD; const Name, Resource: AnsiString; mWidth, mHeight, mCount: Word; BackAnimation: Boolean=false): Boolean;
-function g_Frames_CreateFile (ID: PDWORD; const Name, FileName: AnsiString; mWidth, mHeight, mCount: Word; BackAnimation: Boolean=false): Boolean;
-function g_Frames_CreateMemory (ID: PDWORD; const Name: AnsiString; pData: Pointer; dataSize: LongInt;
-                                mWidth, mHeight, mCount: Word; BackAnimation: Boolean=false): Boolean;
-function g_Frames_Dup (const NewName, OldName: AnsiString): Boolean;
-//function g_Frames_CreateRevert(ID: PDWORD; Name: ShortString; Frames: string): Boolean;
-function g_Frames_Get (out ID: LongWord; const FramesName: AnsiString): Boolean;
-function g_Frames_GetTexture (out ID: LongWord; const FramesName: AnsiString; Frame: Word): Boolean;
-function g_Frames_Exists (const FramesName: AnsiString): Boolean;
-procedure g_Frames_DeleteByName (const FramesName: AnsiString);
-procedure g_Frames_DeleteByID (ID: LongWord);
-procedure g_Frames_DeleteAll ();
-
-procedure DumpTextureNames ();
-
-function g_Texture_Light (): Integer;
 
+    property id: LongWord read mId;
+  end;
 
 implementation
 
 uses
-  g_game, e_log, g_basic, g_console, wadreader,
-  g_language, GL, utils, xstreams;
-
-type
-  _TTexture = record
-    name: AnsiString;
-    id: LongWord;
-    width, height: Word;
-    used: Boolean;
-  end;
-
-  TFrames = record
-    texturesID: array of LongWord;
-    name: AnsiString;
-    frameWidth, frameHeight: Word;
-    used: Boolean;
-  end;
-
-var
-  texturesArray: array of _TTexture = nil;
-  framesArray: array of TFrames = nil;
+  g_game, e_log, g_basic, g_console, wadreader, r_animations,
+  g_language, utils, xstreams;
 
 
-const
-  ANIM_SIGNATURE = $4D494E41; // 'ANIM'
 
 
-function allocTextureSlot (): LongWord;
-var
-  f: integer;
+constructor TAnimationState.Create (aloop: Boolean; aspeed: Byte; len: Integer);
 begin
-  for f := 0 to High(texturesArray) do
-  begin
-    if (not texturesArray[f].used) then
-    begin
-      result := f;
-      exit;
-    end;
-  end;
+  assert(len >= 0);
+  mLength := len;
 
-  result := Length(texturesArray);
-  SetLength(texturesArray, result+64);
-  for f := result to High(texturesArray) do
-  begin
-    with texturesArray[f] do
-    begin
-      name := '';
-      id := 0;
-      width := 0;
-      height := 0;
-      used := false;
-    end;
-  end;
+  mMinLength := 0;
+  mLoop := aloop;
+  mSpeed := aspeed;
+  mEnabled := true;
+  mCurrentFrame := 0;
+  mAlpha := 0;
+  mPlayed := false;
 end;
 
-
-function allocFrameSlot (): LongWord;
-var
-  f: integer;
+destructor TAnimationState.Destroy;
 begin
-  for f := 0 to High(framesArray) do
-  begin
-    if (not framesArray[f].used) then
-    begin
-      result := f;
-      exit;
-    end;
-  end;
-
-  result := Length(framesArray);
-  SetLength(framesArray, result+64);
-  for f := result to High(framesArray) do
-  begin
-    with framesArray[f] do
-    begin
-      texturesID := nil;
-      name := '';
-      frameWidth := 0;
-      frameHeight := 0;
-      used := false;
-    end;
-  end;
+  inherited;
 end;
 
-
-// ////////////////////////////////////////////////////////////////////////// //
-function g_Texture_CreateWAD (var ID: LongWord; const Resource: AnsiString): Boolean;
-var
-  WAD: TWADFile;
-  FileName: AnsiString;
-  TextureData: Pointer;
-  ResourceLength: Integer;
+procedure TAnimationState.update;
 begin
-  result := false;
-  FileName := g_ExtractWadName(Resource);
+  if (not mEnabled) then exit;
 
-  WAD := TWADFile.Create;
-  WAD.ReadFile(FileName);
+  mCounter += 1;
 
-  if WAD.GetResource(g_ExtractFilePathName(Resource), TextureData, ResourceLength) then
+  if (mCounter >= mSpeed) then
   begin
-    if e_CreateTextureMem(TextureData, ResourceLength, ID) then
-    begin
-      result := true;
-    end
-    else
+    // Îæèäàíèå ìåæäó êàäðàìè çàêîí÷èëîñü
+    // Îáðàòíûé ïîðÿäîê êàäðîâ?
+    if mRevert then
     begin
-      FreeMem(TextureData);
-    end;
-  end
-  else
-  begin
-    e_WriteLog(Format('Error loading texture %s', [Resource]), TMsgType.Warning);
-    //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
-  end;
-  WAD.Free();
-end;
-
-
-function g_Texture_CreateFile (var ID: LongWord; const FileName: AnsiString): Boolean;
-begin
-  result := true;
-  if not e_CreateTexture(FileName, ID) then
-  begin
-    e_WriteLog(Format('Error loading texture %s', [FileName]), TMsgType.Warning);
-    result := false;
-  end;
-end;
-
-
-function g_Texture_CreateWADEx (const textureName, Resource: AnsiString): Boolean;
-var
-  WAD: TWADFile;
-  FileName: AnsiString;
-  TextureData: Pointer;
-  find_id: LongWord;
-  ResourceLength: Integer;
-begin
-  FileName := g_ExtractWadName(Resource);
+      // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
+      if (mCurrentFrame = 0) then
+      begin
+        if (mLength * mSpeed + mCounter < mMinLength) then exit;
+      end;
 
-  find_id := allocTextureSlot();
+      mCurrentFrame -= 1;
+      mPlayed := (mCurrentFrame < 0);
 
-  WAD := TWADFile.Create;
-  WAD.ReadFile(FileName);
+      // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
+      if mPlayed then
+      begin
+        if mLoop then
+          mCurrentFrame := mLength - 1
+        else
+          mCurrentFrame += 1
+      end;
 
-  if WAD.GetResource(g_ExtractFilePathName(Resource), TextureData, ResourceLength) then
-  begin
-    result := e_CreateTextureMem(TextureData, ResourceLength, texturesArray[find_id].ID);
-    if result then
-    begin
-      e_GetTextureSize(texturesArray[find_id].ID, @texturesArray[find_id].width, @texturesArray[find_id].height);
-      texturesArray[find_id].used := true;
-      texturesArray[find_id].Name := textureName;
+      mCounter := 0;
     end
     else
     begin
-      FreeMem(TextureData);
-    end;
-  end
-  else
-  begin
-    e_WriteLog(Format('Error loading texture %s', [Resource]), TMsgType.Warning);
-    //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
-    result := false;
-  end;
-  WAD.Free();
-end;
-
+      // Ïðÿìîé ïîðÿäîê êàäðîâ
+      // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
+      if (mCurrentFrame = mLength - 1) then
+      begin
+        if (mLength * mSpeed + mCounter < mMinLength) then exit;
+      end;
 
-function g_Texture_CreateFileEx (const textureName, FileName: AnsiString): Boolean;
-var
-  find_id: LongWord;
-begin
-  find_id := allocTextureSlot();
-  result := e_CreateTexture(FileName, texturesArray[find_id].ID);
-  if result then
-  begin
-    texturesArray[find_id].used := true;
-    texturesArray[find_id].Name := textureName;
-    e_GetTextureSize(texturesArray[find_id].ID, @texturesArray[find_id].width, @texturesArray[find_id].height);
-  end
-  else e_WriteLog(Format('Error loading texture %s', [FileName]), TMsgType.Warning);
-end;
+      mCurrentFrame += 1;
+      mPlayed := (mCurrentFrame > mLength - 1);
 
+      // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
+      if mPlayed then
+      begin
+        if mLoop then mCurrentFrame := 0 else mCurrentFrame -= 1;
+      end;
 
-function g_Texture_Get (const textureName: AnsiString; var id: LongWord): Boolean;
-var
-  a: Integer;
-begin
-  result := false;
-  if (Length(texturesArray) = 0) or (Length(textureName) = 0) then exit;
-  for a := 0 to High(texturesArray) do
-  begin
-    if (StrEquCI1251(texturesArray[a].name, textureName)) then
-    begin
-      id := texturesArray[a].id;
-      result := true;
-      break;
+      mCounter := 0;
     end;
   end;
-  //if not Result then g_ConsoleAdd('Texture '+TextureName+' not found');
 end;
 
-
-procedure g_Texture_Delete (const textureName: AnsiString);
-var
-  a: Integer;
+procedure TAnimationState.reset;
 begin
-  if (Length(texturesArray) = 0) or (Length(textureName) = 0) then exit;
-  for a := 0 to High(texturesArray) do
-  begin
-    if (StrEquCI1251(texturesArray[a].name, textureName)) then
-    begin
-      e_DeleteTexture(texturesArray[a].ID);
-      texturesArray[a].used := false;
-      texturesArray[a].name := '';
-      texturesArray[a].id := 0;
-      texturesArray[a].width := 0;
-      texturesArray[a].height := 0;
-    end;
-  end;
+  if mRevert then
+    mCurrentFrame := mLength - 1
+  else
+    mCurrentFrame := 0;
+  mCounter := 0;
+  mPlayed := false
 end;
 
-
-procedure g_Texture_DeleteAll ();
-var
-  a: Integer;
+procedure TAnimationState.disable;
 begin
-  for a := 0 to High(texturesArray) do
-  begin
-    if (texturesArray[a].used) then e_DeleteTexture(texturesArray[a].ID);
-  end;
-  texturesArray := nil;
+  mEnabled := false
 end;
 
-
-function g_Frames_CreateFile (ID: PDWORD; const Name, FileName: AnsiString;
-                              mWidth, mHeight, mCount: Word; BackAnimation: Boolean = false): Boolean;
-var
-  a: Integer;
-  find_id: LongWord;
+procedure TAnimationState.enable;
 begin
-  result := false;
-
-  find_id := allocFrameSlot();
-
-  if (mCount <= 2) then BackAnimation := false;
-
-  if BackAnimation then SetLength(framesArray[find_id].TexturesID, mCount+mCount-2)
-  else SetLength(framesArray[find_id].TexturesID, mCount);
-
-  for a := 0 to mCount-1 do
-  begin
-    if not e_CreateTextureEx(FileName, framesArray[find_id].TexturesID[a], a*mWidth, 0, mWidth, mHeight) then exit;
-  end;
-
-  if BackAnimation then
-  begin
-    for a := 1 to mCount-2 do framesArray[find_id].TexturesID[mCount+mCount-2-a] := framesArray[find_id].TexturesID[a];
-  end;
-
-  framesArray[find_id].used := true;
-  framesArray[find_id].FrameWidth := mWidth;
-  framesArray[find_id].FrameHeight := mHeight;
-  if (Name <> '') then framesArray[find_id].Name := Name else framesArray[find_id].Name := '<noname>';
-
-  if (ID <> nil) then ID^ := find_id;
-
-  result := true;
+  mEnabled := true
 end;
 
-
-function CreateFramesMem (pData: Pointer; dataSize: LongInt; ID: PDWORD; Name: AnsiString;
-                          mWidth, mHeight, mCount: Word; BackAnimation: Boolean = false): Boolean;
-var
-  find_id: LongWord;
-  a: Integer;
+procedure TAnimationState.revert (r: Boolean);
 begin
-  result := false;
-
-  find_id := allocFrameSlot();
-
-  if (mCount <= 2) then BackAnimation := false;
-
-  if BackAnimation then SetLength(framesArray[find_id].TexturesID, mCount+mCount-2)
-  else SetLength(framesArray[find_id].TexturesID, mCount);
-
-  for a := 0 to mCount-1 do
-    if not e_CreateTextureMemEx(pData, dataSize, framesArray[find_id].TexturesID[a], a*mWidth, 0, mWidth, mHeight) then
-    begin
-      //!!!FreeMem(pData);
-      exit;
-    end;
-
-  if BackAnimation then
-  begin
-    for a := 1 to mCount-2 do framesArray[find_id].TexturesID[mCount+mCount-2-a] := framesArray[find_id].TexturesID[a];
-  end;
-
-  framesArray[find_id].used := true;
-  framesArray[find_id].FrameWidth := mWidth;
-  framesArray[find_id].FrameHeight := mHeight;
-  if (Name <> '') then framesArray[find_id].Name := Name else framesArray[find_id].Name := '<noname>';
-
-  if (ID <> nil) then ID^ := find_id;
-
-  result := true;
-end;
-
-
-function g_CreateFramesImg (ia: TDynImageDataArray; ID: PDWORD; const Name: AnsiString; BackAnimation: Boolean = false): Boolean;
-var
-  find_id: LongWord;
-  a, mCount: Integer;
-begin
-  result := false;
-  find_id := allocFrameSlot();
-
-  mCount := Length(ia);
-
-  //e_WriteLog(Format('+++ creating %d frames [%s]', [FCount, Name]), MSG_NOTIFY);
-
-  if (mCount < 1) then exit;
-  if (mCount <= 2) then BackAnimation := false;
-
-  if BackAnimation then SetLength(framesArray[find_id].TexturesID, mCount+mCount-2)
-  else SetLength(framesArray[find_id].TexturesID, mCount);
-
-  //e_WriteLog(Format('+++ creating %d frames, %dx%d', [FCount, ia[0].width, ia[0].height]), MSG_NOTIFY);
-
-  for a := 0 to mCount-1 do
-  begin
-    if not e_CreateTextureImg(ia[a], framesArray[find_id].TexturesID[a]) then exit;
-    //e_WriteLog(Format('+++   frame %d, %dx%d', [a, ia[a].width, ia[a].height]), MSG_NOTIFY);
-  end;
-
-  if BackAnimation then
-  begin
-    for a := 1 to mCount-2 do framesArray[find_id].TexturesID[mCount+mCount-2-a] := framesArray[find_id].TexturesID[a];
-  end;
-
-  framesArray[find_id].used := true;
-  framesArray[find_id].FrameWidth := ia[0].width;
-  framesArray[find_id].FrameHeight := ia[0].height;
-  if (Name <> '') then framesArray[find_id].Name := Name else framesArray[find_id].Name := '<noname>';
-
-  if (ID <> nil) then ID^ := find_id;
-
-  result := true;
-end;
-
-
-function g_Frames_CreateWAD (ID: PDWORD; const Name, Resource: AnsiString;
-                             mWidth, mHeight, mCount: Word; BackAnimation: Boolean=false): Boolean;
-var
-  WAD: TWADFile;
-  FileName: AnsiString;
-  TextureData: Pointer;
-  ResourceLength: Integer;
-begin
-  result := false;
-
-  // models without "advanced" animations asks for "nothing" like this; don't spam log
-  if (Length(Resource) > 0) and ((Resource[Length(Resource)] = '/') or (Resource[Length(Resource)] = '\')) then exit;
-
-  FileName := g_ExtractWadName(Resource);
-
-  WAD := TWADFile.Create();
-  WAD.ReadFile(FileName);
-
-  if not WAD.GetResource(g_ExtractFilePathName(Resource), TextureData, ResourceLength) then
-  begin
-    WAD.Free();
-    e_WriteLog(Format('Error loading texture %s', [Resource]), TMsgType.Warning);
-    //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
-    exit;
-  end;
-
-  if not CreateFramesMem(TextureData, ResourceLength, ID, Name, mWidth, mHeight, mCount, BackAnimation) then
-  begin
-    WAD.Free();
-    exit;
-  end;
-
-  WAD.Free();
-
-  result := true;
+  mRevert := r;
+  reset
 end;
 
-
-function g_Frames_CreateMemory (ID: PDWORD; const Name: AnsiString; pData: Pointer; dataSize: LongInt;
-                                mWidth, mHeight, mCount: Word; BackAnimation: Boolean = false): Boolean;
+function TAnimationState.totalFrames (): Integer; inline;
 begin
-  result := CreateFramesMem(pData, dataSize, ID, Name, mWidth, mHeight, mCount, BackAnimation);
+  result := mLength
 end;
 
-
-{function g_Frames_CreateRevert(ID: PDWORD; Name: ShortString; Frames: string): Boolean;
-var
-  find_id, b: DWORD;
-  a, c: Integer;
-begin
- Result := False;
-
- if not g_Frames_Get(b, Frames) then Exit;
-
- find_id := FindFrame();
-
- FramesArray[find_id].Name := Name;
- FramesArray[find_id].FrameWidth := FramesArray[b].FrameWidth;
- FramesArray[find_id].FrameHeight := FramesArray[b].FrameHeight;
-
- c := High(FramesArray[find_id].TexturesID);
-
- for a := 0 to c do
-  FramesArray[find_id].TexturesID[a] := FramesArray[b].TexturesID[c-a];
-
- Result := True;
-end;}
-
-
-function g_Frames_Dup (const NewName, OldName: AnsiString): Boolean;
-var
-  find_id, b: LongWord;
-  a, c: Integer;
+procedure TAnimationState.saveState (st: TStream);
 begin
-  result := false;
-
-  if not g_Frames_Get(b, OldName) then exit;
-
-  find_id := allocFrameSlot();
-
-  framesArray[find_id].used := true;
-  framesArray[find_id].Name := NewName;
-  framesArray[find_id].FrameWidth := framesArray[b].FrameWidth;
-  framesArray[find_id].FrameHeight := framesArray[b].FrameHeight;
-
-  c := High(framesArray[b].TexturesID);
-  SetLength(framesArray[find_id].TexturesID, c+1);
-
-  for a := 0 to c do framesArray[find_id].TexturesID[a] := framesArray[b].TexturesID[a];
-
-  result := true;
-end;
-
+  if (st = nil) then exit;
 
-procedure g_Frames_DeleteByName (const FramesName: AnsiString);
-var
-  a, b: Integer;
-begin
-  if (Length(framesArray) = 0) then exit;
-  for a := 0 to High(framesArray) do
-  begin
-    if (StrEquCI1251(framesArray[a].Name, FramesName)) then
-    begin
-      if framesArray[a].TexturesID <> nil then
-      begin
-        for b := 0 to High(framesArray[a].TexturesID) do e_DeleteTexture(framesArray[a].TexturesID[b]);
-      end;
-      framesArray[a].used := false;
-      framesArray[a].TexturesID := nil;
-      framesArray[a].Name := '';
-      framesArray[a].FrameWidth := 0;
-      framesArray[a].FrameHeight := 0;
-    end;
-  end;
+  utils.writeSign(st, 'ANIM');
+  utils.writeInt(st, Byte(0)); // version
+  // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
+  utils.writeInt(st, Byte(mCounter));
+  // Òåêóùèé êàäð
+  utils.writeInt(st, LongInt(mCurrentFrame));
+  // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
+  utils.writeBool(st, mPlayed);
+  // Alpha-êàíàë âñåé òåêñòóðû
+  utils.writeInt(st, Byte(mAlpha));
+  // Ðàçìûòèå òåêñòóðû
+  utils.writeInt(st, Byte(mBlending));
+  // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
+  utils.writeInt(st, Byte(mSpeed));
+  // Çàöèêëåíà ëè àíèìàöèÿ
+  utils.writeBool(st, mLoop);
+  // Âêëþ÷åíà ëè
+  utils.writeBool(st, mEnabled);
+  // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
+  utils.writeInt(st, Byte(mMinLength));
+  // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
+  utils.writeBool(st, mRevert);
 end;
 
 
-procedure g_Frames_DeleteByID (ID: LongWord);
-var
-  b: Integer;
+procedure TAnimationState.loadState (st: TStream);
 begin
-  if (Length(framesArray) = 0) then exit;
-  if (framesArray[ID].TexturesID <> nil) then
-  begin
-    for b := 0 to High(framesArray[ID].TexturesID) do e_DeleteTexture(framesArray[ID].TexturesID[b]);
-  end;
-  framesArray[ID].used := false;
-  framesArray[ID].TexturesID := nil;
-  framesArray[ID].Name := '';
-  framesArray[ID].FrameWidth := 0;
-  framesArray[ID].FrameHeight := 0;
-end;
-
+  if (st = nil) then exit;
 
-procedure g_Frames_DeleteAll ();
-var
-  a, b: Integer;
-begin
-  for a := 0 to High(framesArray) do
-  begin
-    if (framesArray[a].used) then
-    begin
-      for b := 0 to High(framesArray[a].TexturesID) do e_DeleteTexture(framesArray[a].TexturesID[b]);
-    end;
-    framesArray[a].used := false;
-    framesArray[a].TexturesID := nil;
-    framesArray[a].Name := '';
-    framesArray[a].FrameWidth := 0;
-    framesArray[a].FrameHeight := 0;
-  end;
-  framesArray := nil;
+  if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
+  // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
+  mCounter := utils.readByte(st);
+  // Òåêóùèé êàäð
+  mCurrentFrame := utils.readLongInt(st);
+  // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
+  mPlayed := utils.readBool(st);
+  // Alpha-êàíàë âñåé òåêñòóðû
+  mAlpha := utils.readByte(st);
+  // Ðàçìûòèå òåêñòóðû
+  mBlending := utils.readBool(st);
+  // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
+  mSpeed := utils.readByte(st);
+  // Çàöèêëåíà ëè àíèìàöèÿ
+  mLoop := utils.readBool(st);
+  // Âêëþ÷åíà ëè
+  mEnabled := utils.readBool(st);
+  // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
+  mMinLength := utils.readByte(st);
+  // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
+  mRevert := utils.readBool(st);
 end;
 
 
-function g_Frames_Get (out ID: LongWord; const FramesName: AnsiString): Boolean;
-var
-  a: Integer;
-begin
-  result := false;
-  if (Length(framesArray) = 0) then exit;
-  for a := 0 to High(framesArray) do
-  begin
-    if (StrEquCI1251(framesArray[a].Name, FramesName)) then
-    begin
-      ID := a;
-      result := true;
-      break;
-    end;
-  end;
-  if not result then g_FatalError(Format(_lc[I_GAME_ERROR_FRAMES], [FramesName]));
-end;
 
 
-function g_Frames_GetTexture (out ID: LongWord; const FramesName: AnsiString; Frame: Word): Boolean;
-var
-  a: Integer;
-begin
-  result := false;
-  if (Length(framesArray) = 0) then exit;
-  for a := 0 to High(framesArray) do
-  begin
-    if (StrEquCI1251(framesArray[a].Name, FramesName)) then
-    begin
-      if (Frame < Length(framesArray[a].TexturesID)) then
-      begin
-        ID := framesArray[a].TexturesID[Frame];
-        result := true;
-        break;
-      end;
-    end;
-  end;
-  if not result then g_FatalError(Format(_lc[I_GAME_ERROR_FRAMES], [FramesName]));
-end;
 
 
-function g_Frames_Exists (const FramesName: AnsiString): Boolean;
-var
-  a: Integer;
+constructor TAnimation.Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
 begin
-  result := false;
-  if (Length(framesArray) = 0) then exit;
-  for a := 0 to High(framesArray) do
+  if (aframesID >= Length(framesArray)) then
   begin
-    if (StrEquCI1251(framesArray[a].Name, FramesName)) then
-    begin
-      result := true;
-      exit;
-    end;
+    //raise Exception.Create('trying to create inexisting frame: something is very wrong here');
+    e_LogWritefln('trying to create inexisting frame %u of %u: something is very wrong here', [aframesID, LongWord(Length(framesArray))], TMsgType.Warning);
+    aframesID := 0;
+    if (Length(framesArray) = 0) then raise Exception.Create('trying to create inexisting frame: something is very wrong here');
   end;
-end;
-
-
-procedure DumpTextureNames ();
-var
-  i: Integer;
-begin
-  e_WriteLog('BEGIN Textures:', TMsgType.Notify);
-  for i := 0 to High(texturesArray) do e_WriteLog('   '+IntToStr(i)+'. '+texturesArray[i].Name, TMsgType.Notify);
-  e_WriteLog('END Textures.', TMsgType.Notify);
-
-  e_WriteLog('BEGIN Frames:', TMsgType.Notify);
-  for i := 0 to High(framesArray) do e_WriteLog('   '+IntToStr(i)+'. '+framesArray[i].Name, TMsgType.Notify);
-  e_WriteLog('END Frames.', TMsgType.Notify);
-end;
-
-
-{ TAnimation }
-
-constructor TAnimation.Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
-begin
-  if (aframesID >= Length(framesArray)) then raise Exception.Create('trying to create inexisting frame: something is very wrong here');
   mId := aframesID;
   mMinLength := 0;
   mLoop := aloop;
@@ -719,14 +331,6 @@ begin
 end;
 
 
-procedure TAnimation.draw (x, y: Integer; mirror: TMirrorType);
-begin
-  if (not mEnabled) then exit;
-  e_DrawAdv(framesArray[mId].TexturesID[mCurrentFrame], x, y, mAlpha, true, mBlending, 0, nil, mirror);
-  //e_DrawQuad(X, Y, X+FramesArray[ID].FrameWidth-1, Y+FramesArray[ID].FrameHeight-1, 0, 255, 0);
-end;
-
-
 procedure TAnimation.update ();
 begin
   if (not mEnabled) then exit;
@@ -792,13 +396,6 @@ procedure TAnimation.disable (); begin mEnabled := false; end;
 procedure TAnimation.enable (); begin mEnabled := true; end;
 
 
-procedure TAnimation.drawEx (x, y: Integer; mirror: TMirrorType; rpoint: TDFPoint; angle: SmallInt);
-begin
-  if (not mEnabled) then exit;
-  e_DrawAdv(framesArray[mId].TexturesID[mCurrentFrame], x, y, mAlpha, true, mBlending, angle, @rpoint, mirror);
-end;
-
-
 function TAnimation.totalFrames (): Integer; inline; begin result := Length(framesArray[mId].TexturesID); end;
 
 
@@ -866,66 +463,4 @@ begin
   mRevert := utils.readBool(st);
 end;
 
-
-// ////////////////////////////////////////////////////////////////////////// //
-var
-  ltexid: GLuint = 0;
-
-function g_Texture_Light (): Integer;
-const
-  Radius: Integer = 128;
-var
-  tex, tpp: PByte;
-  x, y, a: Integer;
-  dist: Double;
-begin
-  if ltexid = 0 then
-  begin
-    GetMem(tex, (Radius*2)*(Radius*2)*4);
-    tpp := tex;
-    for y := 0 to Radius*2-1 do
-    begin
-      for x := 0 to Radius*2-1 do
-      begin
-        dist := 1.0-sqrt((x-Radius)*(x-Radius)+(y-Radius)*(y-Radius))/Radius;
-        if (dist < 0) then
-        begin
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-        end
-        else
-        begin
-          //tc.setPixel(x, y, Color(cast(int)(dist*255), cast(int)(dist*255), cast(int)(dist*255)));
-          if (dist > 0.5) then dist := 0.5;
-          a := round(dist*255);
-          if (a < 0) then a := 0 else if (a > 255) then a := 255;
-          tpp^ := 255; Inc(tpp);
-          tpp^ := 255; Inc(tpp);
-          tpp^ := 255; Inc(tpp);
-          tpp^ := Byte(a); Inc(tpp);
-        end;
-      end;
-    end;
-
-    glGenTextures(1, @ltexid);
-    //if (tid == 0) assert(0, "VGL: can't create screen texture");
-
-    glBindTexture(GL_TEXTURE_2D, ltexid);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-    //GLfloat[4] bclr = 0.0;
-    //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
-
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Radius*2, Radius*2, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
-  end;
-
-  result := ltexid;
-end;
-
-
 end.