DEADSOFTWARE

no more old mapreader: use textmap reader both for text and for binary maps
[d2df-sdl.git] / src / game / g_triggers.pas
index 7238c8046c4bec7b4a75921f1de6f47b38a1807c..c327fdb55c2afcd0257ec015562191cdcc0aade1 100644 (file)
@@ -19,7 +19,7 @@ unit g_triggers;
 interface
 
 uses
-  MAPSTRUCT, e_graphics, MAPDEF, g_basic, g_sound,
+  MAPDEF, e_graphics, g_basic, g_sound,
   BinEditor;
 
 type
@@ -101,7 +101,7 @@ uses
   g_player, g_map, Math, g_gfx, g_game, g_textures,
   g_console, g_monsters, g_items, g_phys, g_weapons,
   wadreader, g_main, SysUtils, e_log, g_language,
-  g_options, g_net, g_netmsg;
+  g_options, g_net, g_netmsg, utils;
 
 const
   TRIGGER_SIGNATURE = $52475254; // 'TRGR'
@@ -144,7 +144,7 @@ begin
     with gWalls[PanelID] do
     begin
       if g_CollidePlayer(X, Y, Width, Height) or
-         g_CollideMonster(X, Y, Width, Height) then Exit;
+         g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
 
       if not Enabled then
       begin
@@ -181,7 +181,7 @@ begin
       with gWalls[gDoorMap[c, b]] do
       begin
         if g_CollidePlayer(X, Y, Width, Height) or
-          g_CollideMonster(X, Y, Width, Height) then Exit;
+          g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
       end;
 
     if not NoSound then
@@ -209,18 +209,33 @@ end;
 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
 var
   a, b, c: Integer;
+  wx, wy, wh, ww: Integer;
+
+  function monsDamage (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
+  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 +245,8 @@ 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);
+      g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
 
       if not Enabled then g_Map_EnableWall(PanelID);
     end;
@@ -247,30 +259,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 +302,19 @@ begin
             gPlayers[a].Collide(X, Y, Width, Height) then
               gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
 
+        //g_Mons_ForEach(monsDamage);
+        g_Mons_ForEachAliveAt(wx, wy, ww, wh, 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;
 
@@ -619,7 +647,7 @@ begin
 
       dx := dx + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
       dy := dy + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
-      
+
       tr_SpawnShot(Data.ShotType, wx, wy, dx, dy, Data.ShotSound, TargetUID);
     end
     else
@@ -726,7 +754,7 @@ begin
 
     UID_MONSTER:
       begin
-        m := g_Monsters_Get(ActivateUID);
+        m := g_Monsters_ByUID(ActivateUID);
         if m = nil then
           Exit;
 
@@ -772,7 +800,7 @@ begin
 
     UID_MONSTER:
       begin
-        m := g_Monsters_Get(ActivateUID);
+        m := g_Monsters_ByUID(ActivateUID);
         if m = nil then
           Exit;
 
@@ -946,7 +974,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 +983,44 @@ var
   UIDType: Byte;
   TargetUID: Word;
   it: PItem;
+  mon: TMonster;
+
+  function monsShotTarget (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if 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 (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if 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 (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if 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 +1250,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 +1258,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 +1288,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 +1302,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 +1316,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;
@@ -1306,14 +1372,14 @@ begin
 
                 if Data.ItemMax > 0 then
                 begin
-                  it := g_ItemByIdx(iid);
+                  it := g_Items_ByIdx(iid);
                   it.SpawnTrigger := ID;
                   Inc(SpawnedCount);
                 end;
 
                 case Data.ItemEffect of
                   EFFECT_TELEPORT: begin
-                    it := g_ItemByIdx(iid);
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 3);
@@ -1328,7 +1394,7 @@ begin
                                      NET_GFX_TELE);
                   end;
                   EFFECT_RESPAWN: begin
-                    it := g_ItemByIdx(iid);
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 4);
@@ -1343,7 +1409,7 @@ begin
                                      NET_GFX_RESPAWN);
                   end;
                   EFFECT_FIRE: begin
-                    it := g_ItemByIdx(iid);
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 4);
@@ -1766,10 +1832,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
@@ -1813,7 +1879,7 @@ begin
 
                 UID_MONSTER:
                   begin
-                    m := g_Monsters_Get(ActivateUID);
+                    m := g_Monsters_ByUID(ActivateUID);
                     if m = nil then
                       Exit;
 
@@ -1833,12 +1899,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 +1929,60 @@ 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;
+              //TODO: accelerate this!
+              g_Mons_ForEachAlive(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;
+              //TODO: accelerate this!
+              g_Mons_ForEachAlive(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 +1990,20 @@ 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
-                  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
+                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 := 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
+              begin
+                //TODO: accelerate this!
+                g_Mons_ForEachAlive(monShotTargetPlrMon);
+              end;
             end;
 
             else begin
@@ -1991,9 +2037,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 +2066,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;
@@ -2150,11 +2196,34 @@ begin
   Result := find_id;
 end;
 
+
+// sorry; grid doesn't support recursive queries, so we have to do this
+type
+  TSimpleMonsterList = specialize TSimpleList<TMonster>;
+
+var
+  tgMonsList: TSimpleMonsterList = nil;
+
 procedure g_Triggers_Update();
 var
   a, b, i: Integer;
   Affected: array of Integer;
+
+  function monsNear (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    {
+    gTriggers[a].ActivateUID := mon.UID;
+    ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
+    }
+    tgMonsList.append(mon);
+  end;
+
+var
+  mon: TMonster;
 begin
+  if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
+
   if gTriggers = nil then
     Exit;
   SetLength(Affected, 0);
@@ -2384,23 +2453,26 @@ 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);
+            //Alive?!
+            tgMonsList.reset();
+            g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
+            for mon in tgMonsList do
+            begin
+              gTriggers[a].ActivateUID := mon.UID;
+              ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
+            end;
+            tgMonsList.reset(); // just in case
+          end;
 
-        // "Ìîíñòðîâ íåò":
+          // "Ìîíñòðîâ íåò"
           if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
              (TimeOut = 0) and (Keys = 0) then
-            if not g_CollideMonster(X, Y, Width, Height) then
+            if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
             begin
               gTriggers[a].ActivateUID := 0;
               ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);