DEADSOFTWARE

no more public `gMonsters`
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Sun, 20 Aug 2017 00:21:14 +0000 (03:21 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Sun, 20 Aug 2017 00:22:21 +0000 (03:22 +0300)
src/game/g_basic.pas
src/game/g_game.pas
src/game/g_map.pas
src/game/g_monsters.pas
src/game/g_netmsg.pas
src/game/g_player.pas
src/game/g_saveload.pas
src/game/g_triggers.pas
src/game/g_weapons.pas

index 8430c9f98eecc88eba2aceba3096c4078e3f1704..e001f8567a3af1ad810da064330ec9cb595d0183 100644 (file)
@@ -144,20 +144,8 @@ begin
 end;
 
 function g_CollideMonster(X, Y: Integer; Width, Height: Word): Boolean;
-var
-  a: Integer;
 begin
-  Result := False;
-
-  if gMonsters = nil then Exit;
-
-  for a := 0 to High(gMonsters) do
-    if (gMonsters[a] <> nil) and gMonsters[a].Live then
-      if g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
-      begin
-        Result := True;
-        Exit;
-      end;
+  result := g_Mons_AnyAt(X, Y, Width, Height);
 end;
 
 function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean;
@@ -240,19 +228,12 @@ begin
 
     UID_MONSTER:
     begin
-      repeat
-        Result := UID_MAX_PLAYER+$1+Random(UID_MAX_MONSTER-UID_MAX_GAME-UID_MAX_PLAYER+$1);
-
-        ok := True;
-        if gMonsters <> nil then
-          for i := 0 to High(gMonsters) do
-            if gMonsters[i] <> nil then
-              if Result = gMonsters[i].UID then
-              begin
-                ok := False;
-                Break;
-              end;
-      until ok;
+      //FIXME!!!
+      while true do
+      begin
+        result := UID_MAX_PLAYER+$1+Random(UID_MAX_MONSTER-UID_MAX_GAME-UID_MAX_PLAYER+$1);
+        if (g_Monsters_Get(result) = nil) then break;
+      end;
     end;
   end;
 end;
index 497567b23ccf7e36e3e8e5f011c411211d65502c..2a52eccfb90dd8b19c144ee23530224f9684315a 100644 (file)
@@ -1439,6 +1439,24 @@ var
   a: Byte;
   w: Word;
   i, b: Integer;
+
+  function sendMonsPos (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon.MonsterType = MONSTER_BARREL) then
+    begin
+      if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
+    end
+    else
+      if (mon.MonsterState <> MONSTATE_SLEEP) then
+      begin
+        if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then
+        begin
+          MH_SEND_MonsterPos(mon.UID);
+        end;
+      end;
+  end;
+
 begin
   g_ResetDynlights();
 // Ïîðà âûêëþ÷àòü èãðó:
@@ -1808,22 +1826,7 @@ begin
           if gPlayers[I] <> nil then
             MH_SEND_PlayerPos(True, gPlayers[I].UID);
 
-        if gMonsters <> nil then
-          for I := 0 to High(gMonsters) do
-            if gMonsters[I] <> nil then
-            begin
-              if (gMonsters[I].MonsterType = MONSTER_BARREL) then
-              begin
-                if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
-                  MH_SEND_MonsterPos(gMonsters[I].UID);
-              end
-              else
-                if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
-                  if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
-                     (gMonsters[I].GameVelX <> 0) or
-                     (gMonsters[I].GameVelY <> 0) then
-                  MH_SEND_MonsterPos(gMonsters[I].UID);
-            end;
+        g_Mons_ForEach(sendMonsPos);
 
         NetTimeToReliable := 0;
         NetTimeToUpdate := NetUpdateRate;
@@ -1835,22 +1838,7 @@ begin
             if gPlayers[I] <> nil then
               MH_SEND_PlayerPos(False, gPlayers[I].UID);
 
-        if gMonsters <> nil then
-          for I := 0 to High(gMonsters) do
-            if gMonsters[I] <> nil then
-            begin
-              if (gMonsters[I].MonsterType = MONSTER_BARREL) then
-              begin
-                if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
-                  MH_SEND_MonsterPos(gMonsters[I].UID);
-              end
-              else
-                if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
-                  if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
-                     (gMonsters[I].GameVelX <> 0) or
-                     (gMonsters[I].GameVelY <> 0) then
-                  MH_SEND_MonsterPos(gMonsters[I].UID);
-            end;
+        g_Mons_ForEach(sendMonsPos);
 
         NetTimeToUpdate := 0;
       end;
@@ -2453,6 +2441,28 @@ end;
 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
 var
   a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
+
+  function monDraw (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    with mon do
+    begin
+      if Live then
+      begin
+        // Ëåâûé âåðõíèé óãîë
+        aX := Obj.X div ScaleSz + 1;
+        aY := Obj.Y div ScaleSz + 1;
+        // Ðàçìåðû
+        aX2 := max(Obj.Rect.Width div ScaleSz, 1);
+        aY2 := max(Obj.Rect.Height div ScaleSz, 1);
+        // Ïðàâûé íèæíèé óãîë
+        aX2 := aX + aX2 - 1;
+        aY2 := aY + aY2 - 1;
+        e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
+      end;
+    end;
+  end;
+
 begin
   if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
      (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
@@ -2621,25 +2631,8 @@ begin
               end;
           end;
     end;
-    if gMonsters <> nil then
-    begin
-    // Ðèñóåì ìîíñòðîâ:
-      for a := 0 to High(gMonsters) do
-        if gMonsters[a] <> nil then with gMonsters[a] do
-          if Live then begin
-          // Ëåâûé âåðõíèé óãîë:
-            aX := Obj.X div ScaleSz + 1;
-            aY := Obj.Y div ScaleSz + 1;
-          // Ðàçìåðû:
-            aX2 := max(Obj.Rect.Width div ScaleSz, 1);
-            aY2 := max(Obj.Rect.Height div ScaleSz, 1);
-          // Ïðàâûé íèæíèé óãîë:
-            aX2 := aX + aX2 - 1;
-            aY2 := aY + aY2 - 1;
-
-            e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
-          end;
-    end;
+    // Ðèñóåì ìîíñòðîâ
+    g_Mons_ForEach(monDraw);
   end;
 end;
 
@@ -4175,6 +4168,13 @@ end;
 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
 var
   i, n, nb, nr: Integer;
+
+  function monRespawn (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if not mon.FNoRespawn then mon.Respawn();
+  end;
+
 begin
   if not g_Game_IsServer then Exit;
   if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
@@ -4243,11 +4243,8 @@ begin
 
   g_Items_RestartRound();
 
-  for i := Low(gMonsters) to High(gMonsters) do
-  begin
-    if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
-      gMonsters[i].Respawn;
-  end;
+
+  g_Mons_ForEach(monRespawn);
 
   gLMSSoftSpawn := False;
 end;
@@ -5003,6 +5000,7 @@ var
   a, b: Integer;
   cmd: string;
   //pt: TPoint;
+  mon: TMonster;
 begin
 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
   if gDebugMode then
@@ -5080,12 +5078,14 @@ begin
           else
             begin
               with gPlayer1.Obj do
-                b := g_Monsters_Create(a,
+              begin
+                mon := g_Monsters_Create(a,
                      X + Rect.X + (Rect.Width div 2),
                      Y + Rect.Y + Rect.Height,
                      gPlayer1.Direction, True);
-              if (Length(P) > 2) and (b >= 0) then
-                gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
+              end;
+              if (Length(P) > 2) and (mon <> nil) then
+                mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
             end;
         end;
     end
index 0543abe30aa620cb0506c08d8e2c480dc06a0356..b79c972159203bd65018e370b88a54088893508e 100644 (file)
@@ -935,45 +935,52 @@ end;
 
 procedure CreateMonster(monster: TMonsterRec_1);
 var
-  a, i: Integer;
+  a: Integer;
+  mon: TMonster;
 begin
   if g_Game_IsClient then Exit;
 
   if (gGameSettings.GameType = GT_SINGLE)
   or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
   begin
-    i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
-                           TDirection(monster.Direction));
+    mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
 
     if gTriggers <> nil then
+    begin
       for a := 0 to High(gTriggers) do
-        if gTriggers[a].TriggerType in [TRIGGER_PRESS,
-             TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
-          if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
-            gMonsters[i].AddTrigger(a);
+      begin
+        if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
+        begin
+          if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
+        end;
+      end;
+    end;
 
-    if monster.MonsterType <> MONSTER_BARREL then
-      Inc(gTotalMonsters);
+    if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
   end;
 end;
 
 procedure g_Map_ReAdd_DieTriggers();
-var
-  i, a: Integer;
-begin
-  if g_Game_IsClient then Exit;
 
-  for i := 0 to High(gMonsters) do
-    if gMonsters[i] <> nil then
+  function monsDieTrig (monidx: Integer; mon: TMonster): Boolean;
+  var
+    a: Integer;
+  begin
+    result := false; // don't stop
+    mon.ClearTriggers();
+    for a := 0 to High(gTriggers) do
+    begin
+      if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
       begin
-        gMonsters[i].ClearTriggers();
-
-        for a := 0 to High(gTriggers) do
-          if gTriggers[a].TriggerType in [TRIGGER_PRESS,
-               TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
-            if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
-              gMonsters[i].AddTrigger(a);
+        if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
       end;
+    end;
+  end;
+
+begin
+  if g_Game_IsClient then Exit;
+
+  g_Mons_ForEach(monsDieTrig);
 end;
 
 function extractWadName(resourceName: string): string;
index 690626078d17713d17936ec942a69007ca21ae1d..af98abae75f8936317a489592ebc0656dbb6e000 100644 (file)
@@ -161,8 +161,8 @@ procedure g_Monsters_LoadData();
 procedure g_Monsters_FreeData();
 procedure g_Monsters_Init();
 procedure g_Monsters_Free();
-function  g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
-            Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): Integer;
+function g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
+            Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
 procedure g_Monsters_Update();
 procedure g_Monsters_Draw();
 procedure g_Monsters_DrawHealth();
@@ -174,8 +174,20 @@ function  g_Monsters_GetIDByName(name: String): Integer;
 function  g_Monsters_GetNameByID(MonsterType: Byte): String;
 function  g_Monsters_GetKilledBy(MonsterType: Byte): String;
 
-var
-  gMonsters: array of TMonster;
+
+type
+  TEachMonsterCB = function (monidx: Integer; mon: TMonster): Boolean is nested; // return `true` to stop
+
+function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+
+// throws on invalid uid
+function g_Mons_ByIdx (uid: Integer): TMonster; inline;
+
+// can return null
+function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
+
+function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean;
+
 
 implementation
 
@@ -385,6 +397,9 @@ const
   MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè
   MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ
 
+var
+  gMonsters: array of TMonster;
+
 var
   pt_x: Integer = 0;
   pt_xs: Integer = 1;
@@ -985,40 +1000,39 @@ begin
 end;
 
 function g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
-           Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): Integer;
+           Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
 var
   find_id: DWORD;
 begin
-  Result := -1;
+  result := nil;
 
-// Íåò òàêîãî ìîíñòðà:
-  if (MonsterType > MONSTER_MAN) or (MonsterType = 0) then
-    Exit;
+  // Íåò òàêîãî ìîíñòðà
+  if (MonsterType > MONSTER_MAN) or (MonsterType = 0) then exit;
 
-// Ñîáëþäàåì îãðàíè÷åíèå Lost_Soul'îâ:
+  // Ñîáëþäàåì îãðàíè÷åíèå Lost_Soul'îâ
   if MonsterType = MONSTER_SOUL then
-    if soulcount > MAX_SOUL then
-      Exit
-    else
-      soulcount := soulcount + 1;
+  begin
+    if soulcount > MAX_SOUL then exit;
+    soulcount := soulcount + 1;
+  end;
 
   find_id := FindMonster();
 
   gMonsters[find_id] := TMonster.Create(MonsterType, find_id, ForcedUID);
 
-// Íàñòðàèâàåì ïîëîæåíèå:
+  // Íàñòðàèâàåì ïîëîæåíèå
   with gMonsters[find_id] do
   begin
     if AdjCoord then
-      begin
-        FObj.X := X-FObj.Rect.X - (FObj.Rect.Width div 2);
-        FObj.Y := Y-FObj.Rect.Y - FObj.Rect.Height;
-      end
+    begin
+      FObj.X := X-FObj.Rect.X - (FObj.Rect.Width div 2);
+      FObj.Y := Y-FObj.Rect.Y - FObj.Rect.Height;
+    end
     else
-      begin
-        FObj.X := X-FObj.Rect.X;
-        FObj.Y := Y-FObj.Rect.Y;
-      end;
+    begin
+      FObj.X := X-FObj.Rect.X;
+      FObj.Y := Y-FObj.Rect.Y;
+    end;
 
     FDirection := Direction;
     FStartDirection := Direction;
@@ -1026,7 +1040,7 @@ begin
     FStartY := GameY;
   end;
 
-  Result := find_id;
+  result := {find_id}gMonsters[find_id];
 end;
 
 procedure g_Monsters_killedp();
@@ -1172,39 +1186,35 @@ end;
 
 procedure g_Monsters_LoadState(var Mem: TBinMemoryReader);
 var
-  count, i, a: Integer;
+  count, a: Integer;
   b: Byte;
+  mon: TMonster;
 begin
-  if Mem = nil then
-    Exit;
+  if Mem = nil then exit;
 
   g_Monsters_Free();
 
-// Çàãðóæàåì èíôîðìàöèþ öåëåóêàçàòåëÿ:
+  // Çàãðóæàåì èíôîðìàöèþ öåëåóêàçàòåëÿ
   Mem.ReadInt(pt_x);
   Mem.ReadInt(pt_xs);
   Mem.ReadInt(pt_y);
   Mem.ReadInt(pt_ys);
 
-// Êîëè÷åñòâî ìîíñòðîâ:
+  // Êîëè÷åñòâî ìîíñòðîâ
   Mem.ReadInt(count);
 
-  if count = 0 then
-    Exit;
+  if count = 0 then exit;
 
-// Çàãðóæàåì ìîíñòðîâ:
+  // Çàãðóæàåì ìîíñòðîâ
   for a := 0 to count-1 do
   begin
-  // Òèï ìîíñòðà:
+    // Òèï ìîíñòðà
     Mem.ReadByte(b);
-  // Ñîçäàåì ìîíñòðà:
-    i := g_Monsters_Create(b, 0, 0, D_LEFT);
-    if i < 0 then
-    begin
-      raise EBinSizeError.Create('g_Monsters_LoadState: ID = -1 (Can''t create)');
-    end;
-  // Çàãðóæàåì äàííûå ìîíñòðà:
-    gMonsters[i].LoadState(Mem);
+    // Ñîçäàåì ìîíñòðà
+    mon := g_Monsters_Create(b, 0, 0, D_LEFT);
+    if mon = nil then raise EBinSizeError.Create('g_Monsters_LoadState: ID = -1 (Can''t create)');
+    // Çàãðóæàåì äàííûå ìîíñòðà
+    mon.LoadState(Mem);
   end;
 end;
 
@@ -2004,6 +2014,7 @@ var
   st: Word;
   o, co: TObj;
   fall: Boolean;
+  mon: TMonster;
 label
   _end;
 begin
@@ -2681,32 +2692,32 @@ _end:
     // Pain_Elemental ïðè ñìåðòè âûïóñêàåò 3 Lost_Soul'à:
       if (FMonsterType = MONSTER_PAIN) then
       begin
-        sx := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-30,
-                                FObj.Y+FObj.Rect.Y+20, D_LEFT);
-        if sx <> -1 then
+        mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-30,
+                                 FObj.Y+FObj.Rect.Y+20, D_LEFT);
+        if mon <> nil then
         begin
-          gMonsters[sx].SetState(STATE_GO);
-          gMonsters[sx].FNoRespawn := True;
+          mon.SetState(STATE_GO);
+          mon.FNoRespawn := True;
           Inc(gTotalMonsters);
           if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
         end;
 
-        sx := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
-                                FObj.Y+FObj.Rect.Y+20, D_RIGHT);
-        if sx <> -1 then
+        mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
+                                 FObj.Y+FObj.Rect.Y+20, D_RIGHT);
+        if mon <> nil then
         begin
-          gMonsters[sx].SetState(STATE_GO);
-          gMonsters[sx].FNoRespawn := True;
+          mon.SetState(STATE_GO);
+          mon.FNoRespawn := True;
           Inc(gTotalMonsters);
           if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
         end;
 
-        sx := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
-                                FObj.Y+FObj.Rect.Y, D_RIGHT);
-        if sx <> -1 then
+        mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
+                                 FObj.Y+FObj.Rect.Y, D_RIGHT);
+        if mon <> nil then
         begin
-          gMonsters[sx].SetState(STATE_GO);
-          gMonsters[sx].FNoRespawn := True;
+          mon.SetState(STATE_GO);
+          mon.FNoRespawn := True;
           Inc(gTotalMonsters);
           if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
         end;
@@ -2870,17 +2881,17 @@ _end:
                   g_Weapon_ball2(wx, wy, tx, ty, FUID);
                 MONSTER_PAIN:
                   begin // Ñîçäàåì Lost_Soul:
-                    sx := g_Monsters_Create(MONSTER_SOUL, FObj.X+(FObj.Rect.Width div 2),
-                                            FObj.Y+FObj.Rect.Y, FDirection);
+                    mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+(FObj.Rect.Width div 2),
+                                             FObj.Y+FObj.Rect.Y, FDirection);
 
-                    if sx <> -1 then
+                    if mon <> nil then
                     begin // Öåëü - öåëü Pain_Elemental'à. Ëåòèì ê íåé:
-                      gMonsters[sx].FTargetUID := FTargetUID;
+                      mon.FTargetUID := FTargetUID;
                       GetPos(FTargetUID, @o);
-                      gMonsters[sx].FTargetTime := 0;
-                      gMonsters[sx].FNoRespawn := True;
-                      gMonsters[sx].SetState(STATE_GO);
-                      gMonsters[sx].shoot(@o, True);
+                      mon.FTargetTime := 0;
+                      mon.FNoRespawn := True;
+                      mon.SetState(STATE_GO);
+                      mon.shoot(@o, True);
                       Inc(gTotalMonsters);
 
                       if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
@@ -4243,4 +4254,60 @@ procedure TMonster.positionChanged ();
 begin
 end;
 
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+var
+  idx: Integer;
+begin
+  result := false;
+  if (gMonsters = nil) or not assigned(cb) then exit;
+  for idx := 0 to High(gMonsters) do
+  begin
+    if (gMonsters[idx] <> nil) then
+    begin
+      result := cb(idx, gMonsters[idx]);
+      if result then exit;
+    end;
+  end;
+end;
+
+
+// throws on invalid uid
+function g_Mons_ByIdx (uid: Integer): TMonster; inline;
+begin
+  result := g_Mons_ByIdx_NC(uid);
+  if (result = nil) then raise Exception.Create('g_Mons_ByIdx: invalid monster id');
+end;
+
+
+// can return null
+function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
+begin
+  if (uid < 0) or (uid > High(gMonsters)) then begin result := nil; exit; end;
+  result := gMonsters[uid];
+end;
+
+
+function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean;
+var
+  idx: Integer;
+begin
+  result := false;
+  if (width < 1) or (height < 1) then exit;
+
+  for idx := 0 to High(gMonsters) do
+  begin
+    if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+    begin
+      if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+  end;
+end;
+
+
 end.
index 33273f896a9ab3e339d1ca731a0a4ec53a129959..a60f95fe10e2e313b1fd2512029fe5db397c2527 100644 (file)
@@ -634,6 +634,12 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E
     MH_SEND_ItemSpawn(True, it.myid, ID);
   end;
 
+  function sendMonSpawn (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    MH_SEND_MonsterSpawn(mon.UID, ID);
+  end;
+
 var
   I: Integer;
 begin
@@ -654,11 +660,7 @@ begin
   end;
 
   g_Items_ForEachAlive(sendItemRespawn, true); // backwards
-
-  if gMonsters <> nil then
-    for I := 0 to High(gMonsters) do
-      if gMonsters[I] <> nil then
-        MH_SEND_MonsterSpawn(gMonsters[I].UID, ID);
+  g_Mons_ForEach(sendMonSpawn);
 
   if gWalls <> nil then
     for I := Low(gWalls) to High(gWalls) do
@@ -2697,14 +2699,15 @@ end;
 procedure MC_RECV_MonsterDelete(P: Pointer);
 var
   ID: Integer;
-  M: TMonster;
+  M, mon: TMonster;
 begin
   ID := e_Raw_Read_Word(P);
   M := g_Monsters_Get(ID);
   if M = nil then Exit;
 
-  gMonsters[ID].SetState(5);
-  gMonsters[ID].MonsterRemoved := True;
+  mon := g_Mons_ByIdx(ID);
+  mon.SetState(5);
+  mon.MonsterRemoved := True;
 end;
 
 procedure MC_RECV_TimeSync(P: Pointer);
index 0ee9365f2adbee0cf6183a774a83500781d6ea23..75366d34590e693d5c82b50f235ee20adcc27f04 100644 (file)
@@ -6302,6 +6302,41 @@ var
   mon: TMonster;
   pla, tpla: TPlayer;
   vsPlayer, vsMonster, ok: Boolean;
+
+
+  function monsUpdate (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and (mon.Live) and (mon.MonsterType <> MONSTER_BARREL) then
+    begin
+      if not TargetOnScreen(mon.Obj.X+mon.Obj.Rect.X, mon.Obj.Y+mon.Obj.Rect.Y) then exit;
+
+      x2 := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2);
+      y2 := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2);
+
+      // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé
+      if g_TraceVector(x1, y1, x2, y2) then
+      begin
+        // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé
+        SetLength(targets, Length(targets)+1);
+        with targets[High(targets)] do
+        begin
+          UID := mon.UID;
+          X := mon.Obj.X;
+          Y := mon.Obj.Y;
+          cX := x2;
+          cY := y2;
+          Rect := mon.Obj.Rect;
+          Dist := g_PatchLength(x1, y1, x2, y2);
+          Line := (y1+4 < Target.Y + mon.Obj.Rect.Y + mon.Obj.Rect.Height) and
+                  (y1-4 > Target.Y + mon.Obj.Rect.Y);
+          Visible := True;
+          IsPlayer := False;
+        end;
+      end;
+    end;
+  end;
+
 begin
   vsPlayer := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER);
   vsMonster := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER);
@@ -6431,41 +6466,7 @@ begin
           end;
 
   // Ìîíñòðû:
-    if vsMonster and (gMonsters <> nil) then
-      for a := 0 to High(gMonsters) do
-        if (gMonsters[a] <> nil) and (gMonsters[a].Live) and
-           (gMonsters[a].MonsterType <> MONSTER_BARREL) then
-          begin
-            mon := gMonsters[a];
-
-            if not TargetOnScreen(mon.Obj.X + mon.Obj.Rect.X,
-                                  mon.Obj.Y + mon.Obj.Rect.Y) then
-              Continue;
-
-            x2 := mon.Obj.X + mon.Obj.Rect.X + (mon.Obj.Rect.Width div 2);
-            y2 := mon.Obj.Y + mon.Obj.Rect.Y + (mon.Obj.Rect.Height div 2);
-
-          // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé:
-            if g_TraceVector(x1, y1, x2, y2) then
-              begin
-              // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé:
-                SetLength(targets, Length(targets)+1);
-                with targets[High(targets)] do
-                  begin
-                    UID := mon.UID;
-                    X := mon.Obj.X;
-                    Y := mon.Obj.Y;
-                    cX := x2;
-                    cY := y2;
-                    Rect := mon.Obj.Rect;
-                    Dist := g_PatchLength(x1, y1, x2, y2);
-                    Line := (y1+4 < Target.Y + mon.Obj.Rect.Y + mon.Obj.Rect.Height) and
-                            (y1-4 > Target.Y + mon.Obj.Rect.Y);
-                    Visible := True;
-                    IsPlayer := False;
-                  end;
-              end;
-          end;
+    if vsMonster then g_Mons_ForEach(monsUpdate);
   end;
 
 // Åñëè åñòü âîçìîæíûå öåëè:
index 40cf5cc16040931f7480f06f890b1d15bf9fa4e8..aaf01b5efd86bcac97a56a56f64787127644bc6d 100644 (file)
@@ -565,7 +565,7 @@ begin
   ///// /////
 
   // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ:
-    if (gMonsters <> nil) and (gTriggers <> nil) then
+    if {(gMonsters <> nil) and} (gTriggers <> nil) then
       g_Map_ReAdd_DieTriggers();
 
   // Çàêðûâàåì ôàéë çàãðóçêè:
index 7238c8046c4bec7b4a75921f1de6f47b38a1807c..ec3bb7144f67e6ae7f1c2f05c62890134968af8a 100644 (file)
@@ -209,18 +209,36 @@ end;
 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
 var
   a, b, c: Integer;
+  wx, wy, wh, ww: Integer;
+
+  function monsDamage (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then
+    begin
+      mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
+    end;
+  end;
+
 begin
   if PanelID = -1 then Exit;
 
   if not d2d then
   begin
     with gWalls[PanelID] do
+    begin
       if (not NoSound) and (not Enabled) then
       begin
         g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
         if g_Game_IsServer and g_Game_IsNet then
           MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
       end;
+    end;
+
+    wx := gWalls[PanelID].X;
+    wy := gWalls[PanelID].Y;
+    ww := gWalls[PanelID].Width;
+    wh := gWalls[PanelID].Height;
 
     with gWalls[PanelID] do
     begin
@@ -230,11 +248,7 @@ begin
               gPlayers[a].Collide(X, Y, Width, Height) then
             gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
 
-      if gMonsters <> nil then
-        for a := 0 to High(gMonsters) do
-          if (gMonsters[a] <> nil) and gMonsters[a].Live and
-          g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
-            gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
+      g_Mons_ForEach(monsDamage);
 
       if not Enabled then g_Map_EnableWall(PanelID);
     end;
@@ -247,30 +261,41 @@ begin
     for a := 0 to High(gDoorMap) do
     begin
       for b := 0 to High(gDoorMap[a]) do
+      begin
         if gDoorMap[a, b] = DWORD(PanelID) then
         begin
           c := a;
           Break;
         end;
+      end;
 
       if c <> -1 then Break;
     end;
     if c = -1 then Exit;
 
     if not NoSound then
+    begin
       for b := 0 to High(gDoorMap[c]) do
+      begin
         if not gWalls[gDoorMap[c, b]].Enabled then
         begin
           with gWalls[PanelID] do
           begin
             g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
-            if g_Game_IsServer and g_Game_IsNet then
-              MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
+            if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
           end;
           Break;
         end;
+      end;
+    end;
 
     for b := 0 to High(gDoorMap[c]) do
+    begin
+      wx := gWalls[gDoorMap[c, b]].X;
+      wy := gWalls[gDoorMap[c, b]].Y;
+      ww := gWalls[gDoorMap[c, b]].Width;
+      wh := gWalls[gDoorMap[c, b]].Height;
+
       with gWalls[gDoorMap[c, b]] do
       begin
         if gPlayers <> nil then
@@ -279,14 +304,18 @@ begin
             gPlayers[a].Collide(X, Y, Width, Height) then
               gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
 
+        g_Mons_ForEach(monsDamage);
+        (*
         if gMonsters <> nil then
           for a := 0 to High(gMonsters) do
             if (gMonsters[a] <> nil) and gMonsters[a].Live and
             g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
               gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
+        *)
 
         if not Enabled then g_Map_EnableWall(gDoorMap[c, b]);
       end;
+    end;
   end;
 end;
 
@@ -946,7 +975,7 @@ var
   animonce: Boolean;
   p: TPlayer;
   m: TMonster;
-  i, k, wx, wy, xd, yd: Integer;
+  idx, k, wx, wy, xd, yd: Integer;
   iid: LongWord;
   coolDown: Boolean;
   pAngle: Real;
@@ -955,6 +984,44 @@ var
   UIDType: Byte;
   TargetUID: Word;
   it: PItem;
+  mon: TMonster;
+
+  function monsShotTarget (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
+    begin
+      xd := mon.GameX + mon.Obj.Rect.Width div 2;
+      yd := mon.GameY + mon.Obj.Rect.Height div 2;
+      TargetUID := mon.UID;
+      result := true; // stop
+    end;
+  end;
+
+  function monsShotTargetMonPlr (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
+    begin
+      xd := mon.GameX + mon.Obj.Rect.Width div 2;
+      yd := mon.GameY + mon.Obj.Rect.Height div 2;
+      TargetUID := mon.UID;
+      result := true; // stop
+    end;
+  end;
+
+  function monShotTargetPlrMon (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
+    begin
+      xd := mon.GameX + mon.Obj.Rect.Width div 2;
+      yd := mon.GameY + mon.Obj.Rect.Height div 2;
+      TargetUID := mon.UID;
+      result := true; // stop
+    end;
+  end;
+
 begin
   Result := False;
   if g_Game_IsClient then
@@ -1184,7 +1251,7 @@ begin
               if (Data.MonMax > 0) and (SpawnedCount >= Data.MonMax) then
                 Break;
 
-              i := g_Monsters_Create(Data.MonType,
+              mon := g_Monsters_Create(Data.MonType,
                      Data.MonPos.X, Data.MonPos.Y,
                      TDirection(Data.MonDir), True);
 
@@ -1192,27 +1259,27 @@ begin
 
             // Çäîðîâüå:
               if (Data.MonHealth > 0) then
-                gMonsters[i].SetHealth(Data.MonHealth);
+                mon.SetHealth(Data.MonHealth);
             // Óñòàíàâëèâàåì ïîâåäåíèå:
-              gMonsters[i].MonsterBehaviour := Data.MonBehav;
-              gMonsters[i].FNoRespawn := True;
+              mon.MonsterBehaviour := Data.MonBehav;
+              mon.FNoRespawn := True;
               if g_Game_IsNet then
-                MH_SEND_MonsterSpawn(gMonsters[i].UID);
+                MH_SEND_MonsterSpawn(mon.UID);
             // Èäåì èñêàòü öåëü, åñëè íàäî:
               if Data.MonActive then
-                gMonsters[i].WakeUp();
+                mon.WakeUp();
 
               if Data.MonType <> MONSTER_BARREL then Inc(gTotalMonsters);
 
               if g_Game_IsNet then
               begin
                 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
-                gMonstersSpawned[High(gMonstersSpawned)] := gMonsters[i].UID;
+                gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
               end;
 
               if Data.MonMax > 0 then
               begin
-                gMonsters[i].SpawnTrigger := ID;
+                mon.SpawnTrigger := ID;
                 Inc(SpawnedCount);
               end;
 
@@ -1222,13 +1289,13 @@ begin
                   begin
                     Anim := TAnimation.Create(FramesID, False, 3);
                     g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.MonPos.X, Data.MonPos.Y);
-                    g_GFX_OnceAnim(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-32,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+(gMonsters[i].Obj.Rect.Height div 2)-32, Anim);
+                    g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, Anim);
                     Anim.Free();
                   end;
                   if g_Game_IsServer and g_Game_IsNet then
-                    MH_SEND_Effect(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-32,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+(gMonsters[i].Obj.Rect.Height div 2)-32, 1,
+                    MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
                                    NET_GFX_TELE);
                 end;
                 EFFECT_RESPAWN: begin
@@ -1236,13 +1303,13 @@ begin
                   begin
                     Anim := TAnimation.Create(FramesID, False, 4);
                     g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.MonPos.X, Data.MonPos.Y);
-                    g_GFX_OnceAnim(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-16,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+(gMonsters[i].Obj.Rect.Height div 2)-16, Anim);
+                    g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, Anim);
                     Anim.Free();
                   end;
                   if g_Game_IsServer and g_Game_IsNet then
-                    MH_SEND_Effect(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-16,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+(gMonsters[i].Obj.Rect.Height div 2)-16, 1,
+                    MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
                                    NET_GFX_RESPAWN);
                 end;
                 EFFECT_FIRE: begin
@@ -1250,13 +1317,13 @@ begin
                   begin
                     Anim := TAnimation.Create(FramesID, False, 4);
                     g_Sound_PlayExAt('SOUND_FIRE', Data.MonPos.X, Data.MonPos.Y);
-                    g_GFX_OnceAnim(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-32,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+gMonsters[i].Obj.Rect.Height-128, Anim);
+                    g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, Anim);
                     Anim.Free();
                   end;
                   if g_Game_IsServer and g_Game_IsNet then
-                    MH_SEND_Effect(gMonsters[i].Obj.X+gMonsters[i].Obj.Rect.X+(gMonsters[i].Obj.Rect.Width div 2)-32,
-                                   gMonsters[i].Obj.Y+gMonsters[i].Obj.Rect.Y+gMonsters[i].Obj.Rect.Height-128, 1,
+                    MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
+                                   mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, 1,
                                    NET_GFX_FIRE);
                 end;
               end;
@@ -1766,10 +1833,10 @@ begin
             if coolDown then
             begin
               // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
-              for i := 0 to High(Activators) do
-                if Activators[i].UID = ActivateUID then
+              for idx := 0 to High(Activators) do
+                if Activators[idx].UID = ActivateUID then
                 begin
-                  k := i;
+                  k := idx;
                   Break;
                 end;
               if k = -1 then
@@ -1833,12 +1900,12 @@ begin
               end;
               // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
               if TriggerType = TRIGGER_DAMAGE then
-                i := Data.DamageInterval
+                idx := Data.DamageInterval
               else
-                i := Data.HealInterval;
+                idx := Data.HealInterval;
               if coolDown then
-                if i > 0 then
-                  Activators[k].TimeOut := i
+                if idx > 0 then
+                  Activators[k].TimeOut := idx
                 else
                   Activators[k].TimeOut := 65535;
             end;
@@ -1863,75 +1930,58 @@ begin
 
           case Data.ShotTarget of
             TRIGGER_SHOT_TARGET_MON: // monsters
-              if gMonsters <> nil then
-                for i := Low(gMonsters) to High(gMonsters) do
-                  if (gMonsters[i] <> nil) and gMonsters[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gMonsters[i].Obj)) then
-                  begin
-                    xd := gMonsters[i].GameX + gMonsters[i].Obj.Rect.Width div 2;
-                    yd := gMonsters[i].GameY + gMonsters[i].Obj.Rect.Height div 2;
-                    TargetUID := gMonsters[i].UID;
-                    break;
-                  end;
+              g_Mons_ForEach(monsShotTarget);
 
             TRIGGER_SHOT_TARGET_PLR: // players
               if gPlayers <> nil then
-                for i := Low(gPlayers) to High(gPlayers) do
-                  if (gPlayers[i] <> nil) and gPlayers[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gPlayers[i].Obj)) then
+                for idx := Low(gPlayers) to High(gPlayers) do
+                  if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
+                     tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
                   begin
-                    xd := gPlayers[i].GameX + PLAYER_RECT_CX;
-                    yd := gPlayers[i].GameY + PLAYER_RECT_CY;
-                    TargetUID := gPlayers[i].UID;
+                    xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
+                    yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
+                    TargetUID := gPlayers[idx].UID;
                     break;
                   end;
 
             TRIGGER_SHOT_TARGET_RED: // red team
               if gPlayers <> nil then
-                for i := Low(gPlayers) to High(gPlayers) do
-                  if (gPlayers[i] <> nil) and gPlayers[i].Live and
-                     (gPlayers[i].Team = TEAM_RED) and
-                     tr_ShotAimCheck(Trigger, @(gPlayers[i].Obj)) then
+                for idx := Low(gPlayers) to High(gPlayers) do
+                  if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
+                     (gPlayers[idx].Team = TEAM_RED) and
+                     tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
                   begin
-                    xd := gPlayers[i].GameX + PLAYER_RECT_CX;
-                    yd := gPlayers[i].GameY + PLAYER_RECT_CY;
-                    TargetUID := gPlayers[i].UID;
+                    xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
+                    yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
+                    TargetUID := gPlayers[idx].UID;
                     break;
                   end;
 
             TRIGGER_SHOT_TARGET_BLUE: // blue team
               if gPlayers <> nil then
-                for i := Low(gPlayers) to High(gPlayers) do
-                  if (gPlayers[i] <> nil) and gPlayers[i].Live and
-                     (gPlayers[i].Team = TEAM_BLUE) and
-                     tr_ShotAimCheck(Trigger, @(gPlayers[i].Obj)) then
+                for idx := Low(gPlayers) to High(gPlayers) do
+                  if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
+                     (gPlayers[idx].Team = TEAM_BLUE) and
+                     tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
                   begin
-                    xd := gPlayers[i].GameX + PLAYER_RECT_CX;
-                    yd := gPlayers[i].GameY + PLAYER_RECT_CY;
-                    TargetUID := gPlayers[i].UID;
+                    xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
+                    yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
+                    TargetUID := gPlayers[idx].UID;
                     break;
                   end;
 
             TRIGGER_SHOT_TARGET_MONPLR: // monsters then players
             begin
-              if gMonsters <> nil then
-                for i := Low(gMonsters) to High(gMonsters) do
-                  if (gMonsters[i] <> nil) and gMonsters[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gMonsters[i].Obj)) then
-                  begin
-                    xd := gMonsters[i].GameX + gMonsters[i].Obj.Rect.Width div 2;
-                    yd := gMonsters[i].GameY + gMonsters[i].Obj.Rect.Height div 2;
-                    TargetUID := gMonsters[i].UID;
-                    break;
-                  end;
+              g_Mons_ForEach(monsShotTargetMonPlr);
+
               if (TargetUID = 0) and (gPlayers <> nil) then
-                for i := Low(gPlayers) to High(gPlayers) do
-                  if (gPlayers[i] <> nil) and gPlayers[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gPlayers[i].Obj)) then
+                for idx := Low(gPlayers) to High(gPlayers) do
+                  if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
+                     tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
                   begin
-                    xd := gPlayers[i].GameX + PLAYER_RECT_CX;
-                    yd := gPlayers[i].GameY + PLAYER_RECT_CY;
-                    TargetUID := gPlayers[i].UID;
+                    xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
+                    yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
+                    TargetUID := gPlayers[idx].UID;
                     break;
                   end;
             end;
@@ -1939,25 +1989,16 @@ begin
             TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
             begin
               if gPlayers <> nil then
-                for i := Low(gPlayers) to High(gPlayers) do
-                  if (gPlayers[i] <> nil) and gPlayers[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gPlayers[i].Obj)) then
+                for idx := Low(gPlayers) to High(gPlayers) do
+                  if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
+                     tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
                   begin
-                    xd := gPlayers[i].GameX + PLAYER_RECT_CX;
-                    yd := gPlayers[i].GameY + PLAYER_RECT_CY;
-                    TargetUID := gPlayers[i].UID;
-                    break;
-                  end;
-              if (TargetUID = 0) and (gMonsters <> nil) then
-                for i := Low(gMonsters) to High(gMonsters) do
-                  if (gMonsters[i] <> nil) and gMonsters[i].Live and
-                     tr_ShotAimCheck(Trigger, @(gMonsters[i].Obj)) then
-                  begin
-                    xd := gMonsters[i].GameX + gMonsters[i].Obj.Rect.Width div 2;
-                    yd := gMonsters[i].GameY + gMonsters[i].Obj.Rect.Height div 2;
-                    TargetUID := gMonsters[i].UID;
+                    xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
+                    yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
+                    TargetUID := gPlayers[idx].UID;
                     break;
                   end;
+              if TargetUID = 0 then g_Mons_ForEach(monShotTargetPlrMon);
             end;
 
             else begin
@@ -1991,9 +2032,9 @@ begin
 
       TRIGGER_EFFECT:
         begin
-          i := Data.FXCount;
+          idx := Data.FXCount;
 
-          while i > 0 do
+          while idx > 0 do
           begin
             case Data.FXPos of
               TRIGGER_EFFECT_POS_CENTER:
@@ -2020,7 +2061,7 @@ begin
             tr_MakeEffect(wx, wy, xd, yd,
                        Data.FXType, Data.FXSubType,
                        Data.FXColorR, Data.FXColorG, Data.FXColorB, True, False);
-            Dec(i);
+            Dec(idx);
           end;
           TimeOut := Data.FXWait;
         end;
@@ -2154,6 +2195,17 @@ procedure g_Triggers_Update();
 var
   a, b, i: Integer;
   Affected: array of Integer;
+
+  function monsNear (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if mon.Collide(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height) then
+    begin
+      gTriggers[a].ActivateUID := mon.UID;
+      ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
+    end;
+  end;
+
 begin
   if gTriggers = nil then
     Exit;
@@ -2384,20 +2436,14 @@ begin
           ActivateTrigger(gTriggers[a], 0);
         end else
         begin
-        // "Ìîíñòð áëèçêî":
+          // "Ìîíñòð áëèçêî"
           if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
              (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
-            if gMonsters <> nil then
-              for b := 0 to High(gMonsters) do
-                if (gMonsters[b] <> nil) then
-                  with gMonsters[b] do
-                    if Collide(X, Y, Width, Height) then
-                    begin
-                      gTriggers[a].ActivateUID := UID;
-                      ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
-                    end;
+          begin
+            g_Mons_ForEach(monsNear);
+          end;
 
-        // "Ìîíñòðîâ íåò":
+          // "Ìîíñòðîâ íåò"
           if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
              (TimeOut = 0) and (Keys = 0) then
             if not g_CollideMonster(X, Y, Width, Height) then
index 3585ef2b4d136f148003fcc717d9a172bc28f4e6..2cdfe87a09935a9dac8cebba83412243a47ed6cf 100644 (file)
@@ -244,9 +244,21 @@ begin
 end;
 
 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
+
 var
   a, b, c, d, i1, i2: Integer;
   pl, mn: WArray;
+
+  function monsWaterCheck (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, mn)) and (i2 < 1023) then //FIXME
+    begin
+      i2 += 1;
+      mn[i2] := monidx;
+    end;
+  end;
+
 begin
   if (gWater = nil) or (WaterMap = nil) then Exit;
 
@@ -280,18 +292,7 @@ begin
                   end;
         end;
 
-        if gMonsters <> nil then
-        begin
-          for d := 0 to High(gMonsters) do
-            if (gMonsters[d] <> nil) and (gMonsters[d].Live) then
-              if gMonsters[d].Collide(gWater[WaterMap[a][c]]) then
-                if not InWArray(d, mn) then
-                  if i2 < 1023 then
-                  begin
-                    i2 := i2+1;
-                    mn[i2] := d;
-                  end;
-        end;
+        g_Mons_ForEach(monsWaterCheck);
       end;
 
       if i1 <> -1 then
@@ -300,7 +301,7 @@ begin
 
       if i2 <> -1 then
         for d := 0 to i2 do
-          gMonsters[mn[d]].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
+          g_Mons_ByIdx(mn[d]).Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
     end;
 
   pl := nil;
@@ -377,7 +378,7 @@ begin
   if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
     Exit;
 
-  if g_Game_IsServer then 
+  if g_Game_IsServer then
   begin
     if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
       p.Damage(d, SpawnerUID, vx, vy, t);
@@ -390,6 +391,20 @@ end;
 
 function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer;
   SpawnerUID: Word; AllowPush: Boolean): Byte;
+
+  function monsCheck (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and mon.Collide(X, Y) then
+    begin
+      if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
+      begin
+        if AllowPush then mon.Push(vx, vy);
+        result := true;
+      end;
+    end;
+  end;
+
 var
   i, h: Integer;
 begin
@@ -408,20 +423,29 @@ begin
 
   if Result <> 0 then Exit;
 
-  h := High(gMonsters);
+  if g_Mons_ForEach(monsCheck) then result := 2;
+end;
 
-  if h <> -1 then
-    for i := 0 to h do
-      if (gMonsters[i] <> nil) and gMonsters[i].Live and gMonsters[i].Collide(X, Y) then
-        if HitMonster(gMonsters[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
+procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
+
+  function monsCheck (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon <> nil) and (mon.Live) and (mon.UID <> SpawnerUID) then
+    begin
+      with mon do
+      begin
+        if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
+                                Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
+            g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
+                                Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
         begin
-          if AllowPush then gMonsters[i].Push(vx, vy);
-          Result := 2;
-          Exit;
+          if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
         end;
-end;
+      end;
+    end;
+  end;
 
-procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
 var
   i, h: Integer;
   st: Byte;
@@ -470,17 +494,7 @@ begin
               gPlayers[i].BFGHit();
           end;
 
-  h := High(gMonsters);
-
-  if h <> -1 then
-    for i := 0 to h do
-      if (gMonsters[i] <> nil) and (gMonsters[i].Live) and (gMonsters[i].UID <> SpawnerUID) then
-        with gMonsters[i] do
-          if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
-                            Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
-              g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
-                            Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
-            if HitMonster(gMonsters[i], 50, 0, 0, SpawnerUID, HIT_SOME) then gMonsters[i].BFGHit();
+  g_Mons_ForEach(monsCheck);
 end;
 
 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
@@ -694,7 +708,7 @@ begin
   Shots[i].Stopped := 0;
   if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
     Shots[i].Timeout := 900 // ~25 sec
-  else 
+  else
   begin
     if Shots[i].ShotType = WEAPON_FLAMETHROWER then
       Shots[i].Timeout := SHOT_FLAME_LIFETIME
@@ -740,25 +754,29 @@ var
             end;
         end;
   end;
-  function MonsterHit(): Boolean;
-  var
-    i: Integer;
+
+  function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
   begin
-    Result := False;
-    h := High(gMonsters);
+    result := false; // don't stop
+    if (mon <> nil) and mon.Live and g_Obj_Collide(obj, @mon.Obj) then
+    begin
+      if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
+      begin
+        if (t <> HIT_FLAME) then
+        begin
+          mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
+                              (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
+        end;
+        result := True;
+      end;
+    end;
+  end;
 
-    if h <> -1 then
-      for i := 0 to h do
-        if (gMonsters[i] <> nil) and gMonsters[i].Live and g_Obj_Collide(obj, @gMonsters[i].Obj) then
-          if HitMonster(gMonsters[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
-          begin
-            if t <> HIT_FLAME then
-              gMonsters[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
-                                (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
-            Result := True;
-            break;
-          end;
+  function MonsterHit(): Boolean;
+  begin
+    result := g_Mons_ForEach(monsCheckHit);
   end;
+
 begin
   Result := 0;
 
@@ -854,8 +872,44 @@ end;
 
 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
 var
-  i, h, r, dx, dy, m, mm: Integer;
+  r: Integer;
+
+  function monsExCheck (monidx: Integer; mon: TMonster): Boolean;
+  var
+    dx, dy, mm: Integer;
+  begin
+    result := false; // don't stop
+    if mon <> nil then
+    begin
+      with mon do
+      begin
+        dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
+        dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
+
+        if dx > 1000 then dx := 1000;
+        if dy > 1000 then dy := 1000;
+
+        if (dx*dx+dy*dy < r) then
+        begin
+          //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
+
+          mm := Max(abs(dx), abs(dy));
+          if mm = 0 then mm := 1;
+
+          if mon.Live then
+            HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad,
+                       0, 0, SpawnerUID, HIT_ROCKET);
+
+          mon.Push((dx*7) div mm, (dy*7) div mm);
+        end;
+      end;
+    end;
+  end;
+
+var
+  i, h, dx, dy, m, mm: Integer;
   _angle: SmallInt;
+
 begin
   Result := False;
 
@@ -889,34 +943,8 @@ begin
           end;
         end;
 
-  h := High(gMonsters);
-
-  if h <> -1 then
-    for i := 0 to h do
-      if gMonsters[i] <> nil then
-        with gMonsters[i] do
-        begin
-          dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
-          dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
-
-          if dx > 1000 then dx := 1000;
-          if dy > 1000 then dy := 1000;
-
-          if dx*dx+dy*dy < r then
-          begin
-            //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
-            //                 Obj.Rect.Width, Obj.Rect.Height);
-
-            mm := Max(abs(dx), abs(dy));
-            if mm = 0 then mm := 1;
-
-            if gMonsters[i].Live then
-              HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad,
-                         0, 0, SpawnerUID, HIT_ROCKET);
+  g_Mons_ForEach(monsExCheck);
 
-            gMonsters[i].Push((dx*7) div mm, (dy*7) div mm);
-          end;
-        end;
 
   h := High(gCorpses);
 
@@ -1545,7 +1573,7 @@ begin
     throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
 
     triggers := nil;
-    
+
     g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
     Animation := TAnimation.Create(FramesID, True, 4);
   end;