DEADSOFTWARE

cosmetix
[d2df-sdl.git] / src / game / g_triggers.pas
index 80d26f2bc028f46bb7f6136cfbc750cbd6dd9ecc..999db6b3ad32f1dad2dd127da468182a24abcdd6 100644 (file)
@@ -13,7 +13,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *)
-{$MODE DELPHI}
+{$INCLUDE ../shared/a_modes.inc}
 unit g_triggers;
 
 interface
@@ -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_scripts;
+  g_options, g_net, g_netmsg;
 
 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 (monidx: Integer; 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;
 
@@ -924,12 +952,29 @@ begin
   end;
 end;
 
+function tr_ShotAimCheck(var Trigger: TTrigger; Obj: PObj): Boolean;
+begin
+  result := false;
+  with Trigger do
+  begin
+    if TriggerType <> TRIGGER_SHOT then
+      Exit;
+    Result := (Data.ShotAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
+              or g_Obj_Collide(X, Y, Width, Height, Obj);
+    if Result and (Data.ShotAim and TRIGGER_SHOT_AIM_TRACE > 0) then
+      Result := g_TraceVector(Data.ShotPos.X,
+                              Data.ShotPos.Y,
+                              Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
+                              Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
+  end;
+end;
+
 function ActivateTrigger(var Trigger: TTrigger; actType: Byte): Boolean;
 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;
@@ -937,6 +982,45 @@ var
   Anim: TAnimation;
   UIDType: Byte;
   TargetUID: Word;
+  it: PItem;
+  mon: TMonster;
+
+  function monsShotTarget (monidx: Integer; 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 (monidx: Integer; 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 (monidx: Integer; 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
@@ -1166,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);
 
@@ -1174,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;
 
@@ -1204,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
@@ -1218,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
@@ -1232,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;
@@ -1288,51 +1372,55 @@ begin
 
                 if Data.ItemMax > 0 then
                 begin
-                  gItems[iid].SpawnTrigger := ID;
+                  it := g_Items_ByIdx(iid);
+                  it.SpawnTrigger := ID;
                   Inc(SpawnedCount);
                 end;
 
                 case Data.ItemEffect of
                   EFFECT_TELEPORT: begin
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 3);
                       g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.ItemPos.X, Data.ItemPos.Y);
-                      g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-32, Anim);
+                      g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+                                     it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
                       Anim.Free();
                     end;
                     if g_Game_IsServer and g_Game_IsNet then
-                      MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-32, 1,
+                      MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+                                     it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
                                      NET_GFX_TELE);
                   end;
                   EFFECT_RESPAWN: begin
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 4);
                       g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.ItemPos.X, Data.ItemPos.Y);
-                      g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-16,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-16, Anim);
+                      g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
+                                     it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
                       Anim.Free();
                     end;
                     if g_Game_IsServer and g_Game_IsNet then
-                      MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-16,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-16, 1,
+                      MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
+                                     it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
                                      NET_GFX_RESPAWN);
                   end;
                   EFFECT_FIRE: begin
+                    it := g_Items_ByIdx(iid);
                     if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
                     begin
                       Anim := TAnimation.Create(FramesID, False, 4);
                       g_Sound_PlayExAt('SOUND_FIRE', Data.ItemPos.X, Data.ItemPos.Y);
-                      g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+gItems[iid].Obj.Rect.Height-128, Anim);
+                      g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+                                     it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
                       Anim.Free();
                     end;
                     if g_Game_IsServer and g_Game_IsNet then
-                      MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
-                                     gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+gItems[iid].Obj.Rect.Height-128, 1,
+                      MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+                                     it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
                                      NET_GFX_FIRE);
                   end;
                 end;
@@ -1744,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
@@ -1791,7 +1879,7 @@ begin
 
                 UID_MONSTER:
                   begin
-                    m := g_Monsters_Get(ActivateUID);
+                    m := g_Monsters_ByUID(ActivateUID);
                     if m = nil then
                       Exit;
 
@@ -1811,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;
@@ -1841,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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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;
@@ -1917,31 +1990,31 @@ 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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
-                     (Data.ShotAllMap or g_Obj_Collide(X, Y, Width, Height, @(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
+              begin
+                //TODO: accelerate this!
+                g_Mons_ForEachAlive(monShotTargetPlrMon);
+              end;
             end;
 
-            else TargetUID := ActivateUID;
+            else begin
+              if (Data.ShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
+                 (Data.ShotType <> TRIGGER_SHOT_REV) then
+                TargetUID := ActivateUID;
+            end;
           end;
 
-          if (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) then
+          if (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
+            ((Data.ShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
           begin
             Result := True;
             if (Data.ShotIntSight = 0) or
@@ -1964,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:
@@ -1993,17 +2066,10 @@ 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;
-
-      TRIGGER_SCRIPT:
-        begin
-          g_Scripts_ProcExec(Data.SCRProc, [ID, ActivateUID, actType, Data.SCRArg], 'map');
-          TimeOut := 0;
-          Result := True;
-        end;
     end;
   end;
 
@@ -2134,6 +2200,24 @@ 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;}
+
+  function monsNear (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    gTriggers[a].ActivateUID := mon.UID;
+    ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
+  end;
+
 begin
   if gTriggers = nil then
     Exit;
@@ -2364,23 +2448,19 @@ 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?!
+            g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
+          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);
@@ -2501,7 +2581,7 @@ begin
 
   for a := 0 to High(gTriggers) do
     if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
-     (gTriggers[a].TriggerType <> TRIGGER_NONE) and
+       (gTriggers[a].TriggerType <> TRIGGER_NONE) and
        (gTriggers[a].TimeOut = 0) and
        ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
        ByteBool(gTriggers[a].ActivateType and ActivateType) then