DEADSOFTWARE

gui: close menu before show loadscreen
[d2df-sdl.git] / src / game / g_monsters.pas
index cd660a29d5a32b034fa0188b869720ce1a34d724..5fa50114660f43d7a5df764ce662a16ab4b11b0f 100644 (file)
@@ -23,7 +23,7 @@ uses
   SysUtils, Classes,
   mempool,
   MAPDEF,
-  g_base, g_basic, g_phys, g_textures, g_grid,
+  g_base, g_basic, g_phys, g_animations, g_grid,
   g_saveload, g_panel, xprofiler;
 
 const
@@ -52,7 +52,7 @@ const
 }
 
 type
-  ADirectedAnim = Array of Array [TDirection.D_LEFT..TDirection.D_RIGHT] of TAnimationState;
+  ADirectedAnim = Array of Array [TDirection.D_LEFT..TDirection.D_RIGHT] of TAnimState;
 
   PMonster = ^TMonster;
   TMonster = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
@@ -93,7 +93,7 @@ type
     {$ENDIF}
     FFirePainTime: Integer;
     FFireAttacker: Word;
-    vilefire: TAnimationState;
+    FVileFireTime: LongWord;
     mProxyId: Integer; // node in dyntree or -1
     mArrIdx: Integer; // in gMonsters
 
@@ -198,7 +198,7 @@ type
 
     property StartID: Integer read FStartID;
 
-    property VileFireAnim: TAnimationState read vilefire;
+    property VileFireTime: LongWord read FVileFireTime;
     property DirAnim: ADirectedAnim read FAnim;
 
   published
@@ -328,6 +328,7 @@ var
     ANIM_ATTACK  = 4;
     ANIM_ATTACK2 = 5;
     ANIM_PAIN    = 6;
+    ANIM_LAST    = ANIM_PAIN;
 
 // Таблица характеристик монстров:
   MONSTERTABLE: Array [MONSTER_DEMON..MONSTER_MAN] of
@@ -537,8 +538,11 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   e_log, g_sound, g_player, g_game,
-  g_weapons, g_triggers, g_items, g_options,
+  g_weapons, g_triggers, g_items, g_options, g_window,
   g_console, g_map, Math, wadreader,
   g_language, g_netmsg, idpool, utils, xstreams;
 
@@ -892,10 +896,6 @@ end;
 procedure g_Monsters_LoadData();
 begin
   e_WriteLog('Loading monsters data...', TMsgType.Notify);
-
-  g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES], 133, False);
-  g_Game_StepLoading(133);
-
   g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_SOUNDS], 0, False);
 
   g_Sound_CreateWADEx('SOUND_MONSTER_BARREL_DIE', GameWAD+':MSOUNDS\BARREL_DIE');
@@ -1678,11 +1678,11 @@ begin
   SetLength(FAnim, Length(ANIMTABLE));
   for a := ANIM_SLEEP to ANIM_PAIN do
   begin
-    FAnim[a, TDirection.D_RIGHT] := TAnimationState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]);
-    FAnim[a, TDirection.D_LEFT] := TAnimationState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]);
+    FAnim[a, TDirection.D_RIGHT] := TAnimState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]);
+    FAnim[a, TDirection.D_LEFT] := TAnimState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]);
   end;
-  if MonsterType = MONSTER_VILE then
-    vilefire := TAnimationState.Create(True, 2, 8);
+
+  FVileFireTime := gTime;
 end;
 
 function TMonster.Damage(aDamage: Word; VelX, VelY: Integer; SpawnerUID: Word; t: Byte): Boolean;
@@ -1875,11 +1875,11 @@ var
 begin
   for a := 0 to High(FAnim) do
   begin
-    FAnim[a, TDirection.D_LEFT].Free();
-    FAnim[a, TDirection.D_RIGHT].Free();
+    FAnim[a, TDirection.D_LEFT].Invalidate;
+    FAnim[a, TDirection.D_RIGHT].Invalidate;
   end;
 
-  vilefire.Free();
+  FVileFireTime := 0;
 
   if (mProxyId <> -1) then
   begin
@@ -1986,7 +1986,7 @@ begin
 
 // Если анимация новая - перезапускаем её:
   if FCurAnim <> Anim then
-    if FAnim[Anim, FDirection] <> nil then
+    if FAnim[Anim, FDirection].IsValid() then
     begin
       FAnim[Anim, FDirection].Reset();
       FCurAnim := Anim;
@@ -2070,14 +2070,20 @@ begin
 end;
 
 procedure TMonster.Update();
-var
-  a, b, sx, sy, wx, wy, oldvelx: Integer;
-  st: Word;
-  o, co: TObj;
-  fall, bubbles: Boolean;
-  mon: TMonster;
-  mit: PMonster;
-  it: TMonsterGrid.Iter;
+  {$IFDEF ENABLE_CORPSES}
+    var co: TObj;
+  {$ENDIF}
+  {$IF DEFINED(ENABLE_GIBS) OR DEFINED(ENABLE_CORPSES)}
+    var b: Integer;
+  {$ENDIF}
+  var
+    a, sx, sy, wx, wy, oldvelx: Integer;
+    st: Word;
+    o: TObj;
+    fall, bubbles: Boolean;
+    mon: TMonster;
+    mit: PMonster;
+    it: TMonsterGrid.Iter;
 label
   _end;
 begin
@@ -2545,25 +2551,27 @@ begin
                 end;
               end;
             {$ENDIF}
-          // Боссы могут пинать трупы:
-            if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
-               (FObj.Vel.X <> 0) and (gCorpses <> nil) then
-            begin
-              b := Abs(FObj.Vel.X);
-              if b > 1 then b := b * (Random(8 div b) + 1);
-              for a := 0 to High(gCorpses) do
-                if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
-                begin
-                  co := gCorpses[a].Obj;
-                  if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
-                                   FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
-                    // Пинаем трупы
-                    if FObj.Vel.X < 0 then
-                      gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
-                    else
-                      gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
-                end;
-            end;
+            {$IFDEF ENABLE_CORPSES}
+              // Боссы могут пинать трупы:
+              if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
+                 (FObj.Vel.X <> 0) and (gCorpses <> nil) then
+              begin
+                b := Abs(FObj.Vel.X);
+                if b > 1 then b := b * (Random(8 div b) + 1);
+                for a := 0 to High(gCorpses) do
+                  if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
+                  begin
+                    co := gCorpses[a].Obj;
+                    if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
+                                     FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
+                      // Пинаем трупы
+                      if FObj.Vel.X < 0 then
+                        gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
+                      else
+                        gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
+                  end;
+              end;
+            {$ENDIF}
           // Если цель высоко, то, возможно, прыгаем:
             if sy < -40 then
               if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then
@@ -2765,13 +2773,9 @@ _end:
       SetState(MONSTATE_GO);
     end;
 
-// Если есть анимация огня колдуна - пусть она идет:
-  if vilefire <> nil then
-    vilefire.Update();
-
 // Состояние - Умирает и текущая анимация проиграна:
   if (FState = MONSTATE_DIE) and
-     (FAnim[FCurAnim, FDirection] <> nil) and
+     (FAnim[FCurAnim, FDirection].IsValid()) and
      (FAnim[FCurAnim, FDirection].Played) then
     begin
     // Умер:
@@ -2822,7 +2826,7 @@ _end:
 
 // Совершение атаки и стрельбы:
   if (FState = MONSTATE_ATTACK) or (FState = MONSTATE_SHOOT) then
-    if (FAnim[FCurAnim, FDirection] <> nil) then
+    if (FAnim[FCurAnim, FDirection].IsValid()) then
     // Анимация атаки есть - можно атаковать
       if (FAnim[FCurAnim, FDirection].Played) then
         begin // Анимация атаки закончилась => переходим на шаг
@@ -3063,7 +3067,7 @@ _end:
     FObj.Vel.X := oldvelx;
 
 // Если есть анимация, то пусть она идет:
-  if FAnim[FCurAnim, FDirection] <> nil then
+  if FAnim[FCurAnim, FDirection].IsValid() then
     FAnim[FCurAnim, FDirection].Update();
 end;
 
@@ -3089,11 +3093,14 @@ begin
 end;
 
 procedure TMonster.ClientUpdate();
-var
-  a, b, sx, sy, oldvelx: Integer;
-  st: Word;
-  o, co: TObj;
-  fall, bubbles: Boolean;
+  {$IFDEF ENABLE_CORPSES}
+    var a, b: Integer; co: TObj;
+  {$ENDIF}
+  var
+    sx, sy, oldvelx: Integer;
+    st: Word;
+    o: TObj;
+    fall, bubbles: Boolean;
 label
   _end;
 begin
@@ -3420,25 +3427,27 @@ begin
                 end;
               end;
             {$ENDIF}
-          // Боссы могут пинать трупы:
-            if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
-               (FObj.Vel.X <> 0) and (gCorpses <> nil) then
-            begin
-              b := Abs(FObj.Vel.X);
-              if b > 1 then b := b * (Random(8 div b) + 1);
-              for a := 0 to High(gCorpses) do
-                if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
-                begin
-                  co := gCorpses[a].Obj;
-                  if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
-                                   FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
-                    // Пинаем трупы
-                    if FObj.Vel.X < 0 then
-                      gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
-                    else
-                      gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
-                end;
-            end;
+            {$IFDEF ENABLE_CORPSES}
+              // Боссы могут пинать трупы:
+              if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
+                 (FObj.Vel.X <> 0) and (gCorpses <> nil) then
+              begin
+                b := Abs(FObj.Vel.X);
+                if b > 1 then b := b * (Random(8 div b) + 1);
+                for a := 0 to High(gCorpses) do
+                  if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
+                  begin
+                    co := gCorpses[a].Obj;
+                    if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
+                                     FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
+                      // Пинаем трупы
+                      if FObj.Vel.X < 0 then
+                        gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
+                      else
+                        gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
+                  end;
+              end;
+            {$ENDIF}
           end;
 
         FSleep := FSleep + 1;
@@ -3623,13 +3632,9 @@ _end:
       SetState(MONSTATE_GO);
     end;
 
-// Если есть анимация огня колдуна - пусть она идет:
-  if vilefire <> nil then
-    vilefire.Update();
-
 // Состояние - Умирает и текущая анимация проиграна:
   if (FState = MONSTATE_DIE) and
-     (FAnim[FCurAnim, FDirection] <> nil) and
+     (FAnim[FCurAnim, FDirection].IsValid()) and
      (FAnim[FCurAnim, FDirection].Played) then
     begin
     // Умер:
@@ -3646,7 +3651,7 @@ _end:
 
 // Совершение атаки и стрельбы:
   if (FState = MONSTATE_ATTACK) or (FState = MONSTATE_SHOOT) then
-    if (FAnim[FCurAnim, FDirection] <> nil) then
+    if (FAnim[FCurAnim, FDirection].IsValid()) then
     // Анимация атаки есть - можно атаковать
       if (FAnim[FCurAnim, FDirection].Played) then
         begin // Анимация атаки закончилась => переходим на шаг
@@ -3772,7 +3777,7 @@ _end:
     FObj.Vel.X := oldvelx;
 
 // Если есть анимация, то пусть она идет:
-  if FAnim[FCurAnim, FDirection] <> nil then
+  if FAnim[FCurAnim, FDirection].IsValid() then
     FAnim[FCurAnim, FDirection].Update();
 end;
 
@@ -4098,7 +4103,7 @@ begin
         ty := o^.Y+o^.Rect.Y;
         SetState(MONSTATE_SHOOT);
 
-        vilefire.Reset();
+        FVileFireTime := gTime;
 
         g_Sound_PlayExAt('SOUND_MONSTER_VILE_ATTACK', FObj.X, FObj.Y);
         g_Sound_PlayExAt('SOUND_FIRE', o^.X, o^.Y);
@@ -4161,6 +4166,7 @@ var
   i: Integer;
   b: Byte;
   anim: Boolean;
+  stub: TAnimState;
 begin
   assert(st <> nil);
 
@@ -4210,20 +4216,25 @@ begin
   // Объект монстра
   Obj_SaveState(st, @FObj);
   // Есть ли анимация огня колдуна
-  anim := (vilefire <> nil);
+  anim := FMonsterType = MONSTER_VILE;
   utils.writeBool(st, anim);
   // Если есть - сохраняем:
-  if anim then vilefire.SaveState(st, 0, False);
+  if anim then
+  begin
+    stub := TAnimState.Create(true, 2, 8);
+    stub.SaveState(st, 0, False);
+    stub.Invalidate;
+  end;
   // Анимации
   for i := ANIM_SLEEP to ANIM_PAIN do
   begin
     // Есть ли левая анимация
-    anim := (FAnim[i, TDirection.D_LEFT] <> nil);
+    anim := (FAnim[i, TDirection.D_LEFT].IsValid());
     utils.writeBool(st, anim);
     // Если есть - сохраняем
     if anim then FAnim[i, TDirection.D_LEFT].SaveState(st, 0, False);
     // Есть ли правая анимация
-    anim := (FAnim[i, TDirection.D_RIGHT] <> nil);
+    anim := (FAnim[i, TDirection.D_RIGHT].IsValid());
     utils.writeBool(st, anim);
     // Если есть - сохраняем
     if anim then FAnim[i, TDirection.D_RIGHT].SaveState(st, 0, False);
@@ -4236,6 +4247,7 @@ var
   i: Integer;
   b, alpha: Byte;
   anim, blending: Boolean;
+  stub: TAnimState;
 begin
   assert(st <> nil);
 
@@ -4294,9 +4306,11 @@ begin
   // Если есть - загружаем:
   if anim then
   begin
-    Assert(vilefire <> nil, 'TMonster.LoadState: no vilefire anim');
-    vilefire.LoadState(st, alpha, blending);
+    stub := TAnimState.Create(true, 2, 8);
+    stub.LoadState(st, alpha, blending);
+    stub.Invalidate;
   end;
+  FVileFireTime := gTime;
   // Анимации
   for i := ANIM_SLEEP to ANIM_PAIN do
   begin
@@ -4305,7 +4319,7 @@ begin
     // Если есть - загружаем
     if anim then
     begin
-      Assert(FAnim[i, TDirection.D_LEFT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
+      Assert(FAnim[i, TDirection.D_LEFT].IsValid(), 'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
       FAnim[i, TDirection.D_LEFT].LoadState(st, alpha, blending);
     end;
     // Есть ли правая анимация
@@ -4313,7 +4327,7 @@ begin
     // Если есть - загружаем
     if anim then
     begin
-      Assert(FAnim[i, TDirection.D_RIGHT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
+      Assert(FAnim[i, TDirection.D_RIGHT].IsValid(), 'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
       FAnim[i, TDirection.D_RIGHT].LoadState(st, alpha, blending);
     end;
   end;