DEADSOFTWARE

render: move player model texture loading into render
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Sun, 16 Jan 2022 16:19:14 +0000 (19:19 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Fri, 9 Jun 2023 07:51:14 +0000 (10:51 +0300)
src/game/Doom2DF.lpr
src/game/g_game.pas
src/game/g_menu.pas
src/game/g_netmsg.pas
src/game/g_player.pas
src/game/g_playermodel.pas
src/game/opengl/r_playermodel.pas
src/game/opengl/r_render.pas

index 2b4fe158d53daa2ced428c06cd0449bad647e51d..12b9efd61a501b2e6fef77e7a7cfb39ef2717608 100644 (file)
@@ -988,6 +988,7 @@ end;
     {$IFDEF ENABLE_HOLMES}
       InitHolmes;
     {$ENDIF}
+    g_PlayerModel_LoadAll;
     r_Render_Load;
     g_Game_Init;
     {$IFNDEF HEADLESS}
index 365c0444c34fcb21c0517f25ee98a8008b22ca30..3423b33e4cd3ad2d5d2dc4ce4409db992cc5e4b6 100644 (file)
@@ -1460,12 +1460,6 @@ begin
 end;
 
 procedure g_Game_Init();
-var
-  SR: TSearchRec;
-  knownFiles: array of AnsiString = nil;
-  found: Boolean;
-  wext, s: AnsiString;
-  f: Integer;
 begin
   gExit := 0;
   gMapToDelete := '';
@@ -1487,43 +1481,7 @@ begin
     g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
     g_Game_SetLoadingText('', 0, False);
 
-    g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
-    // load models from all possible wad types, in all known directories
-    // this does a loosy job (linear search, ooph!), but meh
-    for wext in wadExtensions do
-    begin
-      for f := High(ModelDirs) downto Low(ModelDirs) do
-      begin
-        if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
-        begin
-          repeat
-            found := false;
-            for s in knownFiles do
-            begin
-              if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
-              begin
-                found := true;
-                break;
-              end;
-            end;
-            if not found then
-            begin
-              SetLength(knownFiles, length(knownFiles)+1);
-              knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
-            end;
-          until (FindNext(SR) <> 0);
-        end;
-        FindClose(SR);
-      end;
-    end;
-
-    if (length(knownFiles) = 0) then raise Exception.Create('no player models found!');
-
-    if (length(knownFiles) = 1) then e_LogWriteln('1 player model found.', TMsgType.Notify) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify);
-    for s in knownFiles do
-    begin
-      if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
-    end;
+//    g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
 
     gGameOn := false;
     gPauseMain := false;
index 34c529889e962bda2933788330910fbca2f91a30..f81733b599449751196d0a1c5081663a0e02f259 100644 (file)
@@ -331,7 +331,7 @@ begin
                                   TEAM_RED, TEAM_BLUE);
   with TGUIModelView(g_GUI_GetWindow('OptionsPlayersP1Menu').GetControl('mvP1Model')) do
   begin
-    gPlayer1Settings.Model := Model.Name;
+    gPlayer1Settings.Model := Model.GetName();
     gPlayer1Settings.Color := Model.Color;
   end;
 
@@ -341,7 +341,7 @@ begin
                                   TEAM_RED, TEAM_BLUE);
   with TGUIModelView(g_GUI_GetWindow('OptionsPlayersP2Menu').GetControl('mvP2Model')) do
   begin
-    gPlayer2Settings.Model := Model.Name;
+    gPlayer2Settings.Model := Model.GetName();
     gPlayer2Settings.Color := Model.Color;
   end;
 
@@ -1490,8 +1490,7 @@ begin
   with TGUIModelView(g_ActiveWindow.GetControl('mv'+s+'Model')) do
   begin
     NextAnim();
-    Model.GetCurrentAnimation.Loop := True;
-    Model.GetCurrentAnimationMask.Loop := True;
+    Model.AnimState.Loop := True;
   end;
 end;
 
index a2e2bc944d5ec6651f93b41a55b749e431255a5d..fb32fa2b4f40361aa7b52809167cdb2c83c3487a 100644 (file)
@@ -766,7 +766,7 @@ begin
 
   if (g_Force_Model_Get() <> 0) then
     TmpModel := g_Forced_Model_GetName();
-  if TmpModel <> Pl.Model.Name then
+  if TmpModel <> Pl.Model.GetName() then
     Pl.SetModel(TmpModel);
 
   if (TmpWeapSwitch <> Pl.WeapSwitchMode) then
@@ -1406,7 +1406,7 @@ begin
   NetOut.Write(PID);
   NetOut.Write(Pl.Name);
   if Mdl = '' then
-    NetOut.Write(Pl.Model.Name)
+    NetOut.Write(Pl.Model.GetName())
   else
     NetOut.Write(Mdl);
   NetOut.Write(Pl.FColor.R);
@@ -2697,7 +2697,7 @@ begin
 
   if (g_Force_Model_Get() <> 0) then
     TmpModel := g_Forced_Model_GetName();
-  if TmpModel <> Pl.Model.Name then
+  if TmpModel <> Pl.Model.GetName() then
     Pl.SetModel(TmpModel);
 end;
 
index acd49d048b697a658d07a6e02cf64f1b88789a4c..968c9742f14136c8c01ef3f8cdf4eb5e2de64357 100644 (file)
@@ -1530,7 +1530,7 @@ begin
         if not ok then
           find_id := Random(Length(gCorpses));
 
-        gCorpses[find_id] := TCorpse.Create(FObj.X, FObj.Y, FModel.Name, FHealth < -20);
+        gCorpses[find_id] := TCorpse.Create(FObj.X, FObj.Y, FModel.GetName(), FHealth < -20);
         gCorpses[find_id].FColor := FModel.Color;
         gCorpses[find_id].FObj.Vel := FObj.Vel;
         gCorpses[find_id].FObj.Accel := FObj.Accel;
@@ -1541,7 +1541,7 @@ begin
     else
       g_Player_CreateGibs(FObj.X + PLAYER_RECT_CX,
                           FObj.Y + PLAYER_RECT_CY,
-                          FModel.Name, FModel.Color);
+                          FModel.GetName(), FModel.Color);
   end;
 end;
 
@@ -3047,23 +3047,27 @@ begin
 end;
 
 procedure TPlayer.MakeBloodSimple(Count: Word);
+  var Blood: TModelBlood;
 begin
+  Blood := SELF.FModel.GetBlood();
   g_GFX_Blood(FObj.X+PLAYER_RECT.X+(PLAYER_RECT.Width div 2)+8,
               FObj.Y+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2),
               Count div 2, 3, -1, 16, (PLAYER_RECT.Height*2 div 3),
-              FModel.Blood.R, FModel.Blood.G, FModel.Blood.B, FModel.Blood.Kind);
+              Blood.R, Blood.G, Blood.B, Blood.Kind);
   g_GFX_Blood(FObj.X+PLAYER_RECT.X+(PLAYER_RECT.Width div 2)-8,
               FObj.Y+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2),
               Count div 2, -3, -1, 16, (PLAYER_RECT.Height*2) div 3,
-              FModel.Blood.R, FModel.Blood.G, FModel.Blood.B, FModel.Blood.Kind);
+              Blood.R, Blood.G, Blood.B, Blood.Kind);
 end;
 
 procedure TPlayer.MakeBloodVector(Count: Word; VelX, VelY: Integer);
+  var Blood: TModelBlood;
 begin
+  Blood := SELF.FModel.GetBlood();
   g_GFX_Blood(FObj.X+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
               FObj.Y+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2),
               Count, VelX, VelY, 16, (PLAYER_RECT.Height*2) div 3,
-              FModel.Blood.R, FModel.Blood.G, FModel.Blood.B, FModel.Blood.Kind);
+              Blood.R, Blood.G, Blood.B, Blood.Kind);
 end;
 
 procedure TPlayer.ProcessWeaponAction(Action: Byte);
@@ -4742,11 +4746,10 @@ begin
   if (FActionAnim = A_PAIN) and (FModel.Animation <> A_PAIN) then
   begin
     FModel.ChangeAnimation(FActionAnim, FActionForce);
-    FModel.GetCurrentAnimation.MinLength := i;
-    FModel.GetCurrentAnimationMask.MinLength := i;
+    FModel.AnimState.MinLength := i;
   end else FModel.ChangeAnimation(FActionAnim, FActionForce and (FModel.Animation <> A_STAND));
 
-  if (FModel.GetCurrentAnimation.Played or ((not FActionChanged) and (FModel.Animation = A_WALK)))
+  if (FModel.AnimState.Played or ((not FActionChanged) and (FModel.Animation = A_WALK)))
   then SetAction(A_STAND, True);
 
   if not ((FModel.Animation = A_WALK) and (Abs(FObj.Vel.X) < 4) and not FModel.Fire) then FModel.Update;
@@ -5448,7 +5451,7 @@ begin
   // Время до повторного респауна, смены оружия, исользования, захвата флага
   for i := T_RESPAWN to T_FLAGCAP do utils.writeInt(st, LongWord(FTime[i]));
   // Название модели
-  utils.writeStr(st, FModel.Name);
+  utils.writeStr(st, FModel.GetName());
   // Цвет модели
   utils.writeInt(st, Byte(FColor.R));
   utils.writeInt(st, Byte(FColor.G));
index af56f51bbce5cabce54bb8cee797cdbb57e3ce99..8ffb589787109f0a7ccf680e619b87c4dea97ea4 100644 (file)
@@ -65,6 +65,21 @@ const
   FLAG_BASEPOINT: TDFPoint = (X:16; Y:43);
 
 type
+  TWeaponPoints = Array [WP_FIRST + 1..WP_LAST, A_STAND..A_LAST, TDirection.D_LEFT..TDirection.D_RIGHT] of Array of TDFPoint;
+
+  TModelMatrix = Array [TDirection.D_LEFT..TDirection.D_RIGHT, A_STAND..A_LAST] of TAnimationState;
+
+  TModelTextures = Array [TDirection.D_LEFT..TDirection.D_RIGHT, A_STAND..A_LAST] of record
+    Resource: String;
+    Mask:     String;
+    Frames:   Integer;
+    Back:     Boolean;
+  end;
+
+  TModelBlood = record
+    R, G, B, Kind: Byte;
+  end;
+
   TModelInfo = record
     Name:        String;
     Author:      String;
@@ -72,10 +87,6 @@ type
     HaveWeapon:  Boolean;
   end;
 
-  TModelBlood = record
-    R, G, B, Kind: Byte;
-  end;
-
   TModelSound = record
     ID:    DWORD;
     Level: Byte;
@@ -90,38 +101,28 @@ type
 
   TModelSoundArray = Array of TModelSound;
   TGibsArray = Array of TGibSprite;
-  TWeaponPoints = Array [WP_FIRST + 1..WP_LAST] of
-                  Array [A_STAND..A_LAST] of
-                  Array [TDirection.D_LEFT..TDirection.D_RIGHT] of Array of TDFPoint;
-  TModelMatrix = Array [TDirection.D_LEFT..TDirection.D_RIGHT] of Array [A_STAND..A_LAST] of TAnimation;
 
   TPlayerModel = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
   private
-    FName:             String;
     FDirection:        TDirection;
     FColor:            TRGB;
-    FBlood:            TModelBlood;
     FCurrentAnimation: Byte;
-    FAnim:             TModelMatrix;
-    FMaskAnim:         TModelMatrix;
-    FWeaponPoints:     TWeaponPoints;
+    FAnimState:        TAnimationState;
     FPainSounds:       TModelSoundArray;
     FDieSounds:        TModelSoundArray;
     FSlopSound:        Byte;
     FCurrentWeapon:    Byte;
-    FDrawWeapon:       Boolean;
     FFlag:             Byte;
     FFlagPoint:        TDFPoint;
     FFlagAngle:        SmallInt;
-    FFlagAnim:         TAnimation;
+    FFlagAnim:         TAnimation; // !!! TAnimationState
     FFire:             Boolean;
     FFireCounter:      Byte;
+    FID:               Integer;
 
   public
     destructor  Destroy(); override;
     procedure   ChangeAnimation(Animation: Byte; Force: Boolean = False);
-    function    GetCurrentAnimation: TAnimation;
-    function    GetCurrentAnimationMask: TAnimation;
     procedure   SetColor(Red, Green, Blue: Byte);
     procedure   SetWeapon(Weapon: Byte);
     procedure   SetFlag(Flag: Byte);
@@ -129,40 +130,66 @@ type
     function    PlaySound(SoundType, Level: Byte; X, Y: Integer): Boolean;
     procedure   Update();
 
+    function GetBlood (): TModelBlood;
+    function GetName (): String;
+
   published
     property    Fire: Boolean read FFire;
     property    Direction: TDirection read FDirection write FDirection;
     property    Animation: Byte read FCurrentAnimation;
     property    Weapon: Byte read FCurrentWeapon;
-    property    Name: String read FName;
 
   public
     property    Color: TRGB read FColor write FColor;
-    property    Blood: TModelBlood read FBlood;
 
-    property    Anim: TModelMatrix read FAnim;
-    property    MaskAnim: TModelMatrix read FMaskAnim;
+    property    AnimState: TAnimationState read FAnimState;
     property    CurrentAnimation: Byte read FCurrentAnimation;
 
     property    CurrentWeapon: Byte read FCurrentWeapon;
-    property    DrawWeapon: Boolean read FDrawWeapon;
-    property    WeaponPoints: TWeaponPoints read FWeaponPoints;
 
     property    Flag: Byte read FFlag;
     property    FlagAnim: TAnimation read FFlagAnim;
     property    FlagAngle: SmallInt read FFlagAngle;
     property    FlagPoint: TDFPoint read FFlagPoint;
+
+    property    ID: Integer read FID;
   end;
 
+procedure g_PlayerModel_LoadAll;
 procedure g_PlayerModel_FreeData();
 function  g_PlayerModel_Load(FileName: String): Boolean;
 function  g_PlayerModel_GetNames(): SSArray;
 function  g_PlayerModel_GetInfo(ModelName: String): TModelInfo;
 function  g_PlayerModel_GetBlood(ModelName: String): TModelBlood;
 function  g_PlayerModel_Get(ModelName: String): TPlayerModel;
-function  g_PlayerModel_GetAnim(ModelName: String; Anim: Byte; var _Anim, _Mask: TAnimation): Boolean;
+function  g_PlayerModel_GetAnim(ModelName: String; AnimTyp: Byte; var _Anim, _Mask: TAnimation): Boolean;
 function  g_PlayerModel_GetGibs(ModelName: String; var Gibs: TGibsArray): Boolean;
 
+(* --- private data --- *)
+
+  type
+    TPlayerModelInfo = record
+      Info:         TModelInfo;
+      ModelSpeed:   Array [A_STAND..A_PAIN] of Byte;
+      FlagPoint:    TDFPoint;
+      FlagAngle:    SmallInt;
+      WeaponPoints: TWeaponPoints;
+      Gibs:         TGibsArray; // !!! move to render
+      PainSounds:   TModelSoundArray;
+      DieSounds:    TModelSoundArray;
+      SlopSound:    Byte;
+      Blood:        TModelBlood;
+      // =======================
+      FileName:    String;
+      Anim:        TModelTextures;
+      GibsCount:   Integer;
+      GibsResource:String;
+      GibsMask:    String;
+      GibsOnce:    Integer;
+    end;
+
+  var
+    PlayerModelsArray: Array of TPlayerModelInfo;
 
 implementation
 
@@ -170,20 +197,6 @@ uses
   g_sound, g_console, SysUtils, g_player, CONFIG, r_textures, r_animations,
   e_sound, g_options, g_map, Math, e_log, wadreader;
 
-type
-  TPlayerModelInfo = record
-    Info:         TModelInfo;
-    ModelSpeed:   Array [A_STAND..A_PAIN] of Byte;
-    FlagPoint:    TDFPoint;
-    FlagAngle:    SmallInt;
-    WeaponPoints: TWeaponPoints;
-    Gibs:         TGibsArray;
-    PainSounds:   TModelSoundArray;
-    DieSounds:    TModelSoundArray;
-    SlopSound:    Byte;
-    Blood:        TModelBlood;
-  end;
-
 const
   FLAG_DEFPOINT:  TDFPoint = (X:32; Y:16);
   FLAG_DEFANGLE = -20;
@@ -203,9 +216,6 @@ const
   WeapNames: Array [WP_FIRST + 1..WP_LAST] of String =
              ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm');
 
-var
-  PlayerModelsArray: Array of TPlayerModelInfo;
-
 function GetPoint(var str: String; var point: TDFPoint): Boolean;
 var
   a, x, y: Integer;
@@ -275,49 +285,29 @@ begin
   Result := True;
 end;
 
-procedure ExtAnimFromBaseAnim(MName: String; AIdx: Integer);
-const
-  CopyAnim: array [A_LASTBASE+1..A_LASTEXT] of Integer = (
-    A_WALK, A_WALK, A_WALK, A_WALK, A_WALK,
-    A_STAND, A_WALK, A_ATTACK, A_WALK, A_SEEUP, A_SEEDOWN,
-    A_ATTACKUP, A_ATTACKDOWN
-  );
-var
-  OIdx, W, I: Integer;
-  D: TDirection;
-  AName, OName: String;
-begin
-  // HACK: shitty workaround to duplicate base animations
-  //       in place of extended, replace with something better later
-
-  Assert((AIdx > A_LASTBASE) and (AIdx <= A_LASTEXT));
-  OIdx := CopyAnim[AIdx];
-
-  AName := MName + '_RIGHTANIM' + IntToStr(AIdx);
-  OName := MName + '_RIGHTANIM' + IntToStr(OIdx);
-  Assert(g_Frames_Dup(AName, OName));
-  Assert(g_Frames_Dup(AName + '_MASK', OName + '_MASK'));
-  AName := MName + '_LEFTANIM' + IntToStr(AIdx);
-  OName := MName + '_LEFTANIM' + IntToStr(OIdx);
-  if g_Frames_Exists(AName) then
-  begin
-    g_Frames_Dup(AName, OName);
-    g_Frames_Dup(AName + '_MASK', OName + '_MASK');
-  end;
-
-  with PlayerModelsArray[High(PlayerModelsArray)] do
+  procedure g_PlayerMode_ExtendPoints (id: Integer; AIdx: Integer);
+    const
+      CopyAnim: array [A_LASTBASE+1..A_LASTEXT] of Integer = (
+        A_WALK, A_WALK, A_WALK, A_WALK, A_WALK,
+        A_STAND, A_WALK, A_ATTACK, A_WALK, A_SEEUP, A_SEEDOWN,
+        A_ATTACKUP, A_ATTACKDOWN
+      );
+    var W, I, OIdx: Integer; D: TDirection;
   begin
-    for W := WP_FIRST + 1 to WP_LAST do
+    OIdx := CopyAnim[AIdx];
+    with PlayerModelsArray[id] do
     begin
-      for D := TDirection.D_LEFT to TDirection.D_RIGHT do
+      for W := WP_FIRST + 1 to WP_LAST do
       begin
-        SetLength(WeaponPoints[W, AIdx, D], Length(WeaponPoints[W, OIdx, D]));
-        for I := 0 to High(WeaponPoints[W, AIdx, D]) do
-          WeaponPoints[W, AIdx, D, I] := WeaponPoints[W, OIdx, D, I]
+        for D := TDirection.D_LEFT to TDirection.D_RIGHT do
+        begin
+          SetLength(WeaponPoints[W, AIdx, D], Length(WeaponPoints[W, OIdx, D]));
+          for I := 0 to High(WeaponPoints[W, AIdx, D]) do
+            WeaponPoints[W, AIdx, D, I] := WeaponPoints[W, OIdx, D, I]
+        end;
       end;
     end;
   end;
-end;
 
 function g_PlayerModel_CalcGibSize (pData: Pointer; dataSize, x, y, w, h: Integer): TRectWH;
   var i, j: Integer; done: Boolean; img: TImageData;
@@ -386,9 +376,9 @@ var
   config: TConfig;
   pData, pData2: Pointer;
   WAD: TWADFile;
-  s, aname: string;
+  s: string;
   prefix: string;
-  ok, chk: Boolean;
+  ok, chk, chk2: Boolean;
 begin
   e_WriteLog(Format('Loading player model "%s"...', [FileName]), TMsgType.Notify);
 
@@ -432,6 +422,7 @@ begin
     Description := config.ReadStr('Model', 'description', '');
   end;
 
+  PlayerModelsArray[ID].FileName := FileName;
   with PlayerModelsArray[ID] do
   begin
     Blood.R := MAX(0, MIN(255, config.ReadInt('Blood', 'R', 150)));
@@ -448,27 +439,25 @@ begin
 
   for b := A_STAND to A_LAST do
   begin
-    aname := s+'_RIGHTANIM'+IntToStr(b);
-    //e_LogWritefln('### MODEL FILE: [%s]', [prefix+config.ReadStr(AnimNames[b], 'resource', '')]);
-    if not (g_Frames_CreateWAD(nil, aname,
-                               prefix+config.ReadStr(AnimNames[b], 'resource', ''),
-                               64, 64, config.ReadInt(AnimNames[b], 'frames', 1),
-                               config.ReadBool(AnimNames[b], 'backanim', False)) and
-            g_Frames_CreateWAD(nil, aname+'_MASK',
-                               prefix+config.ReadStr(AnimNames[b], 'mask', ''),
-                               64, 64, config.ReadInt(AnimNames[b], 'frames', 1),
-                               config.ReadBool(AnimNames[b], 'backanim', False))) then
+    with PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b] do
     begin
-      if b <= A_LASTBASE then
-      begin
-        config.Free();
-        WAD.Free();
-        Exit;
-      end
-      else
+      Resource := config.ReadStr(AnimNames[b], 'resource', '');
+      Mask := config.ReadStr(AnimNames[b], 'mask', '');
+      Frames := config.ReadInt(AnimNames[b], 'frames', 1);
+      Back := config.ReadBool(AnimNames[b], 'backanim', False);
+      if (Resource = '') or (Mask = '') then
       begin
-        ExtAnimFromBaseAnim(s, b);
-        continue;
+        if b <= A_LASTBASE then
+        begin
+          config.Free();
+          WAD.Free();
+          Exit
+        end
+        else
+        begin
+          g_PlayerMode_ExtendPoints(ID, b);
+          continue
+        end
       end;
     end;
 
@@ -476,25 +465,16 @@ begin
       for bb := A_STAND to A_LAST do
         for cc := TDirection.D_LEFT to TDirection.D_RIGHT do
         begin
-          f := config.ReadInt(AnimNames[bb], 'frames', 1);
-          if config.ReadBool(AnimNames[bb], 'backanim', False) then
-            if f > 2 then f := 2*f-2;
+          f := PlayerModelsArray[ID].Anim[cc, bb].Frames;
+          if PlayerModelsArray[ID].Anim[cc, bb].Back and (f > 2) then
+            f := 2 * f - 2;
           SetLength(PlayerModelsArray[ID].WeaponPoints[aa, bb, cc], f);
         end;
 
-    if (config.ReadStr(AnimNames[b], 'resource2', '') <> '') and
-       (config.ReadStr(AnimNames[b], 'mask2', '') <> '') then
+    with PlayerModelsArray[ID].Anim[TDirection.D_LEFT, b] do
     begin
-      aname := s+'_LEFTANIM'+IntToStr(b);
-      g_Frames_CreateWAD(nil, aname,
-                         prefix+config.ReadStr(AnimNames[b], 'resource2', ''),
-                         64, 64, config.ReadInt(AnimNames[b], 'frames', 1),
-                         config.ReadBool(AnimNames[b], 'backanim', False));
-
-      g_Frames_CreateWAD(nil, aname+'_MASK',
-                         prefix+config.ReadStr(AnimNames[b], 'mask2', ''),
-                         64, 64, config.ReadInt(AnimNames[b], 'frames', 1),
-                         config.ReadBool(AnimNames[b], 'backanim', False));
+      Frames := PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b].Frames;
+      Back := PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b].Back;
     end;
 
     PlayerModelsArray[ID].ModelSpeed[b] := Max(1, config.ReadInt(AnimNames[b], 'waitcount', 1) div 3);
@@ -530,11 +510,15 @@ begin
 
     SlopSound := Min(Max(config.ReadInt('Sound', 'slop', 0), 0), 2);
 
-    SetLength(Gibs, ReadInt('Gibs', 'count', 0));
+    GibsCount := config.ReadInt('Gibs', 'count', 0);
+    GibsResource := config.ReadStr('Gibs', 'resource', 'GIBS');
+    GibsMask := config.ReadStr('Gibs', 'mask', 'GIBSMASK');
+    GibsOnce := config.ReadInt('Gibs', 'once', -1);
 
+    SetLength(Gibs, GibsCount); // !!! remove load
     if (Gibs <> nil) and
-       (WAD.GetResource('TEXTURES/'+config.ReadStr('Gibs', 'resource', 'GIBS'), pData, lenpd)) and
-       (WAD.GetResource('TEXTURES/'+config.ReadStr('Gibs', 'mask', 'GIBSMASK'), pData2, lenpd2)) then
+       (WAD.GetResource('TEXTURES/' + GibsResource, pData, lenpd)) and
+       (WAD.GetResource('TEXTURES/' + GibsMask, pData2, lenpd2)) then
     begin
       for a := 0 to High(Gibs) do
         if e_CreateTextureMemEx(pData, lenpd, Gibs[a].ID, a*32, 0, 32, 32) and
@@ -544,7 +528,7 @@ begin
           Gibs[a].Rect := g_PlayerModel_CalcGibSize(pData, lenpd, a*32, 0, 32, 32);
           with Gibs[a].Rect do
             if Height > 3 then Height := Height-1-Random(2);
-          Gibs[a].OnlyOne := config.ReadInt('Gibs', 'once', -1) = a+1;
+          Gibs[a].OnlyOne := GibsOnce = a + 1;
         end;
 
       FreeMem(pData);
@@ -556,17 +540,27 @@ begin
       for bb := A_STAND to A_LAST do
         if not (bb in [A_DIE1, A_DIE2, A_PAIN]) then
         begin
-          chk := GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[aa]+'_points', ''), aa, bb, TDirection.D_RIGHT,
-                               config.ReadInt(AnimNames[bb], 'frames', 0),
-                               config.ReadBool(AnimNames[bb], 'backanim', False),
-                               WeaponPoints);
+          chk := GetWeapPoints(
+            config.ReadStr(AnimNames[bb], WeapNames[aa] + '_points', ''),
+            aa,
+            bb,
+            TDirection.D_RIGHT,
+            Anim[TDirection.D_RIGHT, bb].Frames,
+            Anim[TDirection.D_RIGHT, bb].Back,
+            WeaponPoints
+          );
           if ok and (not chk) and (aa = WEAPON_FLAMETHROWER) then
           begin
             // workaround for flamethrower
-            chk := GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[WEAPON_PLASMA]+'_points', ''), aa, bb, TDirection.D_RIGHT,
-                                 config.ReadInt(AnimNames[bb], 'frames', 0),
-                                 config.ReadBool(AnimNames[bb], 'backanim', False),
-                                 WeaponPoints);
+            chk := GetWeapPoints(
+              config.ReadStr(AnimNames[bb], WeapNames[WEAPON_PLASMA] + '_points', ''),
+              aa,
+              bb,
+              TDirection.D_RIGHT,
+              Anim[TDirection.D_RIGHT, bb].Frames,
+              Anim[TDirection.D_RIGHT, bb].Back,
+              WeaponPoints
+            );
             if chk then
             for f := 0 to High(WeaponPoints[aa, bb, TDirection.D_RIGHT]) do
             begin
@@ -609,17 +603,26 @@ begin
               end;
             end;
           end;
+
           ok := ok and (chk or (bb > A_LASTBASE));
 
-          if not GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[aa]+'2_points', ''), aa, bb, TDirection.D_LEFT,
-                               config.ReadInt(AnimNames[bb], 'frames', 0),
-                               config.ReadBool(AnimNames[bb], 'backanim', False),
-                               WeaponPoints) then
+          chk2 := GetWeapPoints(
+            config.ReadStr(AnimNames[bb], WeapNames[aa] + '2_points', ''),
+            aa,
+            bb,
+            TDirection.D_LEFT,
+            Anim[TDirection.D_LEFT, bb].Frames,
+            Anim[TDirection.D_LEFT, bb].Back,
+            WeaponPoints
+          );
+          if not chk2 then
+          begin
             for f := 0 to High(WeaponPoints[aa, bb, TDirection.D_RIGHT]) do
             begin
               WeaponPoints[aa, bb, TDirection.D_LEFT, f].X := -WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X;
               WeaponPoints[aa, bb, TDirection.D_LEFT, f].Y := WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y;
             end;
+          end;
 
           if not ok then Break;
         end;
@@ -628,7 +631,8 @@ begin
     Info.HaveWeapon := ok;
 
     s := config.ReadStr('Model', 'flag_point', '');
-    if not GetPoint(s, FlagPoint) then FlagPoint := FLAG_DEFPOINT;
+    if not GetPoint(s, FlagPoint) then
+      FlagPoint := FLAG_DEFPOINT;
 
     FlagAngle := config.ReadInt('Model', 'flag_angle', FLAG_DEFANGLE);
   end;
@@ -640,65 +644,37 @@ begin
 end;
 
 function g_PlayerModel_Get(ModelName: String): TPlayerModel;
-var
-  a: Integer;
-  b: Byte;
-  ID, ID2: DWORD;
+  var a: Integer;
 begin
   Result := nil;
 
   if PlayerModelsArray = nil then Exit;
 
   for a := 0 to High(PlayerModelsArray) do
+  begin
     if AnsiLowerCase(PlayerModelsArray[a].Info.Name) = AnsiLowerCase(ModelName) then
     begin
       Result := TPlayerModel.Create;
 
       with PlayerModelsArray[a] do
       begin
-        Result.FName := Info.Name;
-        Result.FBlood := Blood;
-
-        for b := A_STAND to A_LAST do
-        begin
-          if not (g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(b)) and
-                  g_Frames_Get(ID2, Info.Name+'_RIGHTANIM'+IntToStr(b)+'_MASK')) then
-          begin
-            Result.Free();
-            Result := nil;
-            Exit;
-          end;
-
-          Result.FAnim[TDirection.D_RIGHT][b] := TAnimation.Create(ID, b in [A_STAND, A_WALK], ModelSpeed[b]);
-
-          Result.FMaskAnim[TDirection.D_RIGHT][b] := TAnimation.Create(ID2, b in [A_STAND, A_WALK], ModelSpeed[b]);
-
-          if g_Frames_Exists(Info.Name+'_LEFTANIM'+IntToStr(b)) and
-             g_Frames_Exists(Info.Name+'_LEFTANIM'+IntToStr(b)+'_MASK') then
-          if g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(b)) and
-             g_Frames_Get(ID2, Info.Name+'_LEFTANIM'+IntToStr(b)+'_MASK') then
-          begin
-            Result.FAnim[TDirection.D_LEFT][b] := TAnimation.Create(ID, b in [A_STAND, A_WALK], ModelSpeed[b]);
-
-            Result.FMaskAnim[TDirection.D_LEFT][b] := TAnimation.Create(ID2, b in [A_STAND, A_WALK], ModelSpeed[b]);
-          end;
-        end;
-
         Result.FPainSounds := PainSounds;
         Result.FDieSounds := DieSounds;
         Result.FSlopSound := SlopSound;
-        Result.FDrawWeapon := Info.HaveWeapon;
-        Result.FWeaponPoints := WeaponPoints;
 
         Result.FFlagPoint := FlagPoint;
         Result.FFlagAngle := FlagAngle;
+        Result.FID := a;
+
+        Result.ChangeAnimation(A_STAND, True);
 
         Break;
       end;
+    end;
   end;
 end;
 
-function g_PlayerModel_GetAnim(ModelName: string; Anim: Byte; var _Anim, _Mask: TAnimation): Boolean;
+function g_PlayerModel_GetAnim(ModelName: string; AnimTyp: Byte; var _Anim, _Mask: TAnimation): Boolean;
 var
   a: Integer;
   c: Boolean;
@@ -711,19 +687,19 @@ begin
     if PlayerModelsArray[a].Info.Name = ModelName then
       with PlayerModelsArray[a] do
       begin
-        if Anim in [A_STAND, A_WALK] then c := True else c := False;
+        if AnimTyp in [A_STAND, A_WALK] then c := True else c := False;
 
-        if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(Anim)) then
-          if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(Anim)) then Exit;
+        if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(AnimTyp)) then
+          if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(AnimTyp)) then Exit;
 
-        _Anim := TAnimation.Create(ID, c, ModelSpeed[Anim]);
-        _Anim.Speed := ModelSpeed[Anim];
+        _Anim := TAnimation.Create(ID, c, ModelSpeed[AnimTyp]);
+        _Anim.Speed := ModelSpeed[AnimTyp];
 
-        if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(Anim)+'_MASK') then
-          if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(Anim)+'_MASK') then Exit;
+        if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(AnimTyp)+'_MASK') then
+          if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(AnimTyp)+'_MASK') then Exit;
 
-        _Mask := TAnimation.Create(ID, c, ModelSpeed[Anim]);
-        _Mask.Speed := ModelSpeed[Anim];
+        _Mask := TAnimation.Create(ID, c, ModelSpeed[AnimTyp]);
+        _Mask.Speed := ModelSpeed[AnimTyp];
 
         Break;
       end;
@@ -819,97 +795,48 @@ begin
 end;
 
 procedure g_PlayerModel_FreeData();
-var
-  i: DWORD;
-  a, b: Integer;
+  var i, b: Integer;
 begin
   e_WriteLog('Releasing models...', TMsgType.Notify);
 
   if PlayerModelsArray = nil then Exit;
 
   for i := 0 to High(PlayerModelsArray) do
+  begin
     with PlayerModelsArray[i] do
     begin
-      for a := A_STAND to A_LAST do
-      begin
-        g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a));
-        g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a)+'_MASK');
-        g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a));
-        g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a)+'_MASK');
-      end;
-
       if PainSounds <> nil then
         for b := 0 to High(PainSounds) do
           e_DeleteSound(PainSounds[b].ID);
-
       if DieSounds <> nil then
         for b := 0 to High(DieSounds) do
           e_DeleteSound(DieSounds[b].ID);
-
-      if Gibs <> nil then
-        for b := 0 to High(Gibs) do
-        begin
-          e_DeleteTexture(Gibs[b].ID);
-          e_DeleteTexture(Gibs[b].MaskID);
-        end;
     end;
-
+  end;
   PlayerModelsArray := nil;
 end;
 
 { TPlayerModel }
 
-procedure TPlayerModel.ChangeAnimation(Animation: Byte; Force: Boolean = False);
-begin
-  if not Force then if FCurrentAnimation = Animation then Exit;
-
-  FCurrentAnimation := Animation;
-
-  if (FDirection = TDirection.D_LEFT) and
-     (FAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) and
-     (FMaskAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) then
+  procedure TPlayerModel.ChangeAnimation (Animation: Byte; Force: Boolean = False);
+    var once: Boolean; speed, count: Integer;
   begin
-    FAnim[TDirection.D_LEFT][FCurrentAnimation].Reset;
-    FMaskAnim[TDirection.D_LEFT][FCurrentAnimation].Reset;
-  end
-  else
-  begin
-    FAnim[TDirection.D_RIGHT][FCurrentAnimation].Reset;
-    FMaskAnim[TDirection.D_RIGHT][FCurrentAnimation].Reset;
+    if not Force then
+      if FCurrentAnimation = Animation then
+        Exit;
+    FCurrentAnimation := Animation;
+    once := FCurrentAnimation in [A_STAND, A_WALK];
+    speed := PlayerModelsArray[FID].ModelSpeed[FCurrentAnimation];
+    count := PlayerModelsArray[FID].Anim[FDirection, FCurrentAnimation].Frames;
+    FAnimState := TAnimationState.Create(once, speed, count);
   end;
-end;
 
 destructor TPlayerModel.Destroy();
-var
-  a: Byte;
 begin
-  for a := A_STAND to A_LAST do
-  begin
-    FAnim[TDirection.D_LEFT][a].Free();
-    FMaskAnim[TDirection.D_LEFT][a].Free();
-    FAnim[TDirection.D_RIGHT][a].Free();
-    FMaskAnim[TDirection.D_RIGHT][a].Free();
-  end;
-
+  FAnimState.Free;
   inherited;
 end;
 
-function TPlayerModel.GetCurrentAnimation: TAnimation;
-begin
-  if (FDirection = TDirection.D_LEFT) and (FAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) then
-    Result := FAnim[TDirection.D_LEFT][FCurrentAnimation]
-  else
-    Result := FAnim[TDirection.D_RIGHT][FCurrentAnimation];
-end;
-
-function TPlayerModel.GetCurrentAnimationMask: TAnimation;
-begin
-  if (FDirection = TDirection.D_LEFT) and (FMaskAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) then
-    Result := FMaskAnim[TDirection.D_LEFT][FCurrentAnimation]
-  else
-    Result := FMaskAnim[TDirection.D_RIGHT][FCurrentAnimation];
-end;
-
 function TPlayerModel.PlaySound(SoundType, Level: Byte; X, Y: Integer): Boolean;
 var
   TempArray: array of DWORD;
@@ -973,14 +900,15 @@ end;
 procedure TPlayerModel.SetFire(Fire: Boolean);
 begin
   FFire := Fire;
-
-  if FFire then FFireCounter := FAnim[TDirection.D_RIGHT, A_ATTACK].Speed*FAnim[TDirection.D_RIGHT, A_ATTACK].TotalFrames
-  else FFireCounter := 0;
+  if FFire then
+    FFireCounter := PlayerModelsArray[FID].ModelSpeed[A_ATTACK] * PlayerModelsArray[FID].Anim[TDirection.D_RIGHT, A_ATTACK].Frames
+  else
+    FFireCounter := 0
 end;
 
 procedure TPlayerModel.SetFlag(Flag: Byte);
 var
-  id: DWORD;
+  tid: DWORD;
 begin
   FFlag := Flag;
 
@@ -988,30 +916,86 @@ begin
   FFlagAnim := nil;
 
   case Flag of
-    FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
-    FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
+    FLAG_RED: g_Frames_Get(tid, 'FRAMES_FLAG_RED');
+    FLAG_BLUE: g_Frames_Get(tid, 'FRAMES_FLAG_BLUE');
     else Exit;
   end;
 
-  FFlagAnim := TAnimation.Create(id, True, 8);
+  FFlagAnim := TAnimation.Create(tid, True, 8);
 end;
 
-procedure TPlayerModel.SetWeapon(Weapon: Byte);
-begin
-  FCurrentWeapon := Weapon;
-end;
+  procedure TPlayerModel.SetWeapon(Weapon: Byte);
+  begin
+    FCurrentWeapon := Weapon;
+  end;
 
-procedure TPlayerModel.Update();
-begin
-  if (FDirection = TDirection.D_LEFT) and (FAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) then
-    FAnim[TDirection.D_LEFT][FCurrentAnimation].Update else FAnim[TDirection.D_RIGHT][FCurrentAnimation].Update;
+  function TPlayerModel.GetBlood (): TModelBlood;
+  begin
+    Result := PlayerModelsArray[FID].Blood
+  end;
 
-  if (FDirection = TDirection.D_LEFT) and (FMaskAnim[TDirection.D_LEFT][FCurrentAnimation] <> nil) then
-    FMaskAnim[TDirection.D_LEFT][FCurrentAnimation].Update else FMaskAnim[TDirection.D_RIGHT][FCurrentAnimation].Update;
+  function TPlayerModel.GetName (): String;
+  begin
+    Result := PlayerModelsArray[FID].Info.Name
+  end;
 
-  if FFlagAnim <> nil then FFlagAnim.Update;
+  procedure TPlayerModel.Update;
+  begin
+    if FAnimState <> nil then
+      FAnimState.Update;
+    if FFlagAnim <> nil then
+      FFlagAnim.Update;
+    if FFireCounter > 0 then
+      Dec(FFireCounter)
+    else
+      FFire := False
+  end;
 
-  if FFireCounter > 0 then Dec(FFireCounter) else FFire := False;
-end;
+  procedure g_PlayerModel_LoadAll;
+    var
+      SR: TSearchRec;
+      knownFiles: array of AnsiString = nil;
+      found: Boolean;
+      wext, s: AnsiString;
+      f: Integer;
+  begin
+    // load models from all possible wad types, in all known directories
+    // this does a loosy job (linear search, ooph!), but meh
+    for wext in wadExtensions do
+    begin
+      for f := High(ModelDirs) downto Low(ModelDirs) do
+      begin
+        if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
+        begin
+          repeat
+            found := false;
+            for s in knownFiles do
+            begin
+              if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
+              begin
+                found := true;
+                break;
+              end;
+            end;
+            if not found then
+            begin
+              SetLength(knownFiles, length(knownFiles)+1);
+              knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
+            end;
+          until (FindNext(SR) <> 0);
+        end;
+        FindClose(SR);
+      end;
+    end;
+    if (length(knownFiles) = 0) then
+      raise Exception.Create('no player models found!');
+    if (length(knownFiles) = 1) then
+      e_LogWriteln('1 player model found.', TMsgType.Notify)
+    else
+      e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify);
+    for s in knownFiles do
+      if not g_PlayerModel_Load(s) then
+        e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
+  end;
 
 end.
index c15a713800114833cd4fe50d89af4b2705410d05..1602f23e0d2b346d9c4f10e77665ca249486657d 100644 (file)
@@ -19,15 +19,16 @@ interface
 
   uses g_playermodel; // TPlayerModel
 
-  procedure r_PlayerModel_Initialize;
-  procedure r_PlayerModel_Finalize;
+  procedure r_PlayerModel_Load;
+  procedure r_PlayerModel_Free;
   procedure r_PlayerModel_Draw (pm: TPlayerModel; X, Y: Integer; Alpha: Byte = 0);
 
 implementation
 
   uses
     SysUtils, Classes, Math,
-    MAPDEF,
+    MAPDEF, utils,
+    ImagingTypes, Imaging, ImagingUtility,
     r_graphics, g_options, r_animations, r_textures,
     g_base, g_basic, g_map, g_weapons
   ;
@@ -35,27 +36,128 @@ implementation
   const
     WeapNames: Array [WP_FIRST + 1..WP_LAST] of String = ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm');
 
+  type
+    TDirIdx = TDirection.D_LEFT..TDirection.D_RIGHT;
+    TAnimIdx = A_STAND..A_LAST;
+
   var
     WeaponID: Array [WP_FIRST + 1..WP_LAST, W_POS_NORMAL..W_POS_DOWN, W_ACT_NORMAL..W_ACT_FIRE] of DWORD;
+    Models: Array of record
+      Frames: Array [TDirIdx, TAnimIdx] of record
+        base: DWORD;
+        mask: DWORD;
+      end;
+    end;
 
-procedure r_PlayerModel_Initialize;
-var
-  a: Integer;
-begin
-  for a := WP_FIRST + 1 to WP_LAST do
+  procedure ExtAnimFromBaseAnim(MName: String; AIdx: Integer);
+    const
+      CopyAnim: array [A_LASTBASE+1..A_LASTEXT] of Integer = (
+        A_WALK, A_WALK, A_WALK, A_WALK, A_WALK,
+        A_STAND, A_WALK, A_ATTACK, A_WALK, A_SEEUP, A_SEEDOWN,
+        A_ATTACKUP, A_ATTACKDOWN
+      );
+    var
+      OIdx: Integer;
+      AName, OName: String;
+  begin
+    // HACK: shitty workaround to duplicate base animations
+    //       in place of extended, replace with something better later
+
+    Assert((AIdx > A_LASTBASE) and (AIdx <= A_LASTEXT));
+    OIdx := CopyAnim[AIdx];
+
+    AName := MName + '_RIGHTANIM' + IntToStr(AIdx);
+    OName := MName + '_RIGHTANIM' + IntToStr(OIdx);
+    Assert(g_Frames_Dup(AName, OName));
+    Assert(g_Frames_Dup(AName + '_MASK', OName + '_MASK'));
+    AName := MName + '_LEFTANIM' + IntToStr(AIdx);
+    OName := MName + '_LEFTANIM' + IntToStr(OIdx);
+    if g_Frames_Exists(AName) then
+    begin
+      g_Frames_Dup(AName, OName);
+      g_Frames_Dup(AName + '_MASK', OName + '_MASK');
+    end;
+  end;
+
+  procedure r_PlayerModel_Load;
+    var ID1, ID2: DWORD; i, a, b: Integer; prefix, aname: String;
   begin
-    g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a]));
-    g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_FIRE');
-    g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP');
-    g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP_FIRE');
-    g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN');
-    g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN_FIRE');
+    for a := WP_FIRST + 1 to WP_LAST do
+    begin
+      g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a]));
+      g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_FIRE');
+      g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP');
+      g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP_FIRE');
+      g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN');
+      g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN_FIRE');
+    end;
+    Models := nil;
+    if PlayerModelsArray <> nil then
+    begin
+      SetLength(Models, Length(PlayerModelsArray));
+      for i := 0 to High(PlayerModelsArray) do
+      begin
+        prefix := PlayerModelsArray[i].FileName + ':TEXTURES\';
+        for b := A_STAND to A_LAST do
+        begin
+          aname := PlayerModelsArray[i].Info.Name + '_RIGHTANIM' + IntToStr(b);
+          with PlayerModelsArray[i].Anim[TDirection.D_RIGHT, b] do
+          begin
+            if not (g_Frames_CreateWAD(@ID1, aname, prefix + Resource, 64, 64, Frames, Back) and
+                    g_Frames_CreateWAD(@ID2, aname + '_MASK', prefix + Mask, 64, 64, Frames, Back)) then
+            begin
+              if b > A_LASTBASE then
+              begin
+                ExtAnimFromBaseAnim(PlayerModelsArray[i].Info.Name, b);
+                continue
+              end
+            end;
+            Models[i].Frames[TDirection.D_RIGHT, b].base := ID1;
+            Models[i].Frames[TDirection.D_RIGHT, b].mask := ID2;
+          end;
+          with PlayerModelsArray[i].Anim[TDirection.D_LEFT, b] do
+          begin
+            if (Resource <> '') and (Mask <> '') then
+            begin
+              aname := PlayerModelsArray[i].Info.Name + '_LEFTANIM' + IntToStr(b);
+              g_Frames_CreateWAD(@ID1, aname, prefix + Resource, 64, 64, Frames, Back);
+              g_Frames_CreateWAD(@ID2, aname + '_MASK', prefix + Mask, 64, 64, Frames, Back);
+              Models[i].Frames[TDirection.D_LEFT, b].base := ID1;
+              Models[i].Frames[TDirection.D_LEFT, b].mask := ID2;
+            end
+          end
+        end
+      end
+    end
   end;
-end;
 
-  procedure r_PlayerModel_Finalize;
-    var a, b, c: Integer;
+  procedure r_PlayerModel_Free;
+    var i, a, b, c: Integer;
   begin
+    if PlayerModelsArray = nil then Exit;
+    for i := 0 to High(PlayerModelsArray) do
+    begin
+      with PlayerModelsArray[i] do
+      begin
+        for a := A_STAND to A_LAST do
+        begin
+          g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a));
+          g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a)+'_MASK');
+          g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a));
+          g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a)+'_MASK');
+        end;
+        if Gibs <> nil then
+        begin
+          for a := 0 to High(Gibs) do
+          begin
+            e_DeleteTexture(Gibs[a].ID);
+            e_DeleteTexture(Gibs[a].MaskID);
+            Gibs[a].ID := DWORD(-1);
+            Gibs[a].MaskID := DWORD(-1);
+          end
+        end
+      end
+    end;
     for a := WP_FIRST + 1 to WP_LAST do
       for b := W_POS_NORMAL to W_POS_DOWN do
         for c := W_ACT_NORMAL to W_ACT_FIRE do
@@ -67,6 +169,7 @@ var
   Mirror: TMirrorType;
   pos, act: Byte;
   p: TDFPoint;
+  FramesID: DWORD;
 begin
 // Флаги:
   if pm.Direction = TDirection.D_LEFT then
@@ -90,7 +193,7 @@ begin
   else
     Mirror := TMirrorType.Horizontal;
 
-  if pm.DrawWeapon and (not (pm.CurrentAnimation in [A_DIE1, A_DIE2, A_PAIN])) and  (pm.CurrentWeapon in [WP_FIRST + 1..WP_LAST]) then
+  if PlayerModelsArray[pm.id].Info.HaveWeapon and (not (pm.CurrentAnimation in [A_DIE1, A_DIE2, A_PAIN])) and  (pm.CurrentWeapon in [WP_FIRST + 1..WP_LAST]) then
   begin
     if pm.CurrentAnimation in [A_SEEUP, A_ATTACKUP] then
       pos := W_POS_UP
@@ -106,38 +209,45 @@ begin
       act := W_ACT_NORMAL;
 
     if Alpha < 201 then
-      e_Draw(WeaponID[pm.CurrentWeapon][pos][act],
-             X + pm.WeaponPoints[pm.CurrentWeapon, pm.CurrentAnimation, pm.Direction,
-             pm.Anim[TDirection.D_RIGHT][pm.CurrentAnimation].CurrentFrame].X,
-             Y + pm.WeaponPoints[pm.CurrentWeapon, pm.CurrentAnimation, pm.Direction,
-             pm.Anim[TDirection.D_RIGHT][pm.CurrentAnimation].CurrentFrame].Y,
-             0, True, False, Mirror);
+      e_Draw(
+        WeaponID[pm.CurrentWeapon][pos][act],
+        X + PlayerModelsArray[pm.id].WeaponPoints[pm.CurrentWeapon, pm.CurrentAnimation, pm.Direction, pm.AnimState.CurrentFrame].X,
+        Y + PlayerModelsArray[pm.id].WeaponPoints[pm.CurrentWeapon, pm.CurrentAnimation, pm.Direction, pm.AnimState.CurrentFrame].Y,
+        0,
+        True,
+        False,
+        Mirror
+      );
   end;
 
 // Модель:
-  if (pm.Direction = TDirection.D_LEFT) and (pm.Anim[TDirection.D_LEFT][pm.CurrentAnimation] <> nil) then
+  if (pm.Direction = TDirection.D_LEFT) and (Models[pm.id].Frames[TDirection.D_LEFT, pm.CurrentAnimation].base <> 0) then
   begin
-    pm.Anim[TDirection.D_LEFT][pm.CurrentAnimation].Alpha := Alpha;
-    r_Animation_Draw(pm.Anim[TDirection.D_LEFT][pm.CurrentAnimation], X, Y, TMirrorType.None);
+    pm.AnimState.Alpha := Alpha; // !!!
+    FramesID := Models[pm.id].Frames[TDirection.D_LEFT, pm.CurrentAnimation].base;
+    r_AnimationState_Draw(FramesID, pm.AnimState, X, Y, TMirrorType.None);
   end
   else
   begin
-    pm.Anim[TDirection.D_RIGHT][pm.CurrentAnimation].Alpha := Alpha;
-    r_Animation_Draw(pm.Anim[TDirection.D_RIGHT][pm.CurrentAnimation], X, Y, Mirror);
+    pm.AnimState.Alpha := Alpha; // !!!
+    FramesID := Models[pm.id].Frames[TDirection.D_RIGHT, pm.CurrentAnimation].base;
+    r_AnimationState_Draw(FramesID, pm.AnimState, X, Y, Mirror);
   end;
 
 // Маска модели:
   e_Colors := pm.Color;
 
-  if (pm.Direction = TDirection.D_LEFT) and (pm.MaskAnim[TDirection.D_LEFT][pm.CurrentAnimation] <> nil) then
+  if (pm.Direction = TDirection.D_LEFT) and (Models[pm.id].Frames[TDirection.D_LEFT, pm.CurrentAnimation].mask <> 0) then
   begin
-    pm.MaskAnim[TDirection.D_LEFT][pm.CurrentAnimation].Alpha := Alpha;
-    r_Animation_Draw(pm.MaskAnim[TDirection.D_LEFT][pm.CurrentAnimation], X, Y, TMirrorType.None);
+    pm.AnimState.Alpha := Alpha; // !!!
+    FramesID := Models[pm.id].Frames[TDirection.D_LEFT, pm.CurrentAnimation].mask;
+    r_AnimationState_Draw(FramesID, pm.AnimState, X, Y, TMirrorType.None);
   end
   else
   begin
-    pm.MaskAnim[TDirection.D_RIGHT][pm.CurrentAnimation].Alpha := Alpha;
-    r_Animation_Draw(pm.MaskAnim[TDirection.D_RIGHT][pm.CurrentAnimation], X, Y, Mirror);
+    pm.AnimState.Alpha := Alpha; // !!!
+    FramesID := Models[pm.id].Frames[TDirection.D_RIGHT, pm.CurrentAnimation].mask;
+    r_AnimationState_Draw(FramesID, pm.AnimState, X, Y, Mirror);
   end;
 
   e_Colors.R := 255;
index 2a4befff5d4c80de73134046fee432e2b5ec33b3..5deccdbdfd29a40c7983a34bfc26226b53356dcd 100644 (file)
@@ -75,6 +75,7 @@ implementation
 
   procedure r_Render_Load;
   begin
+    r_PlayerModel_Load;
     r_Monsters_Load;
     r_Weapon_Load;
     r_Items_Load;
@@ -87,6 +88,7 @@ implementation
     r_Items_Free;
     r_Weapon_Free;
     r_Monsters_Free;
+    r_PlayerModel_Free;
   end;
 
   procedure r_Render_Initialize;
@@ -96,13 +98,11 @@ implementation
     LoadGL;
     r_Window_Initialize;
     r_Console_Init;
-    r_PlayerModel_Initialize;
   end;
 
   procedure r_Render_Finalize;
   begin
     FreeGL;
-    r_PlayerModel_Finalize;
     e_ReleaseEngine
   end;