DEADSOFTWARE

alot of debugging code
[d2df-sdl.git] / src / game / g_monsters.pas
index 1f1e71183c52b302e6b3f33927bc405c799506c8..719a2b759d9da4b136d3c97275d411f187963cae 100644 (file)
@@ -19,8 +19,8 @@ unit g_monsters;
 interface
 
 uses
-  g_basic, e_graphics, g_phys, g_textures,
-  g_saveload, BinEditor, g_panel;
+  g_basic, e_graphics, g_phys, g_textures, g_grid,
+  g_saveload, BinEditor, g_panel, xprofiler;
 
 const
   MONSTATE_SLEEP  = 0;
@@ -80,8 +80,14 @@ type
     FFireAttacker: Word;
     vilefire: TAnimation;
 
-    treeNode: Integer; // node in dyntree or -1
+  {$IF DEFINED(D2F_DEBUG)}
+  public
+  {$ENDIF}
+    proxyId: Integer; // node in dyntree or -1
     arrIdx: Integer; // in gMonsters
+  {$IF DEFINED(D2F_DEBUG)}
+  private
+  {$ENDIF}
 
     FDieTriggers: Array of Integer;
     FSpawnTrigger: Integer;
@@ -134,6 +140,8 @@ type
 
     procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
 
+    procedure getMapBox (out x, y, w, h: Integer); inline;
+
     property MonsterType: Byte read FMonsterType;
     property MonsterHealth: Integer read FHealth write FHealth;
     property MonsterAmmo: Integer read FAmmo write FAmmo;
@@ -162,6 +170,9 @@ type
   end;
 
 
+// will be called from map loader
+procedure g_Mons_InitTree (x, y, w, h: Integer);
+
 procedure g_Monsters_LoadData ();
 procedure g_Monsters_FreeData ();
 procedure g_Monsters_Init ();
@@ -181,9 +192,7 @@ function  g_Monsters_GetKilledBy (MonsterType: Byte): String;
 
 
 type
-  TEachMonsterCB = function (monidx: Integer; mon: TMonster): Boolean is nested; // return `true` to stop
-
-function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+  TEachMonsterCB = function (mon: TMonster): Boolean is nested; // return `true` to stop
 
 // throws on invalid uid
 function g_Mons_ByIdx (uid: Integer): TMonster; inline;
@@ -193,51 +202,104 @@ function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
 
 function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
 
+function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean;
+
 function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
-function g_Mons_ForEachAtAlive (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
 
 function g_Mons_getNewTrapFrameId (): DWord;
 
 
+type
+  TMonsAlongLineCB = function (mon: TMonster; tag: Integer): Boolean is nested;
+
+function g_Mons_AlongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB; log: Boolean=false): TMonster;
+
+
 var
   gmon_debug_use_sqaccel: Boolean = true;
 
 
+//HACK!
+procedure g_Mons_ProfilersBegin ();
+procedure g_Mons_ProfilersEnd ();
+
+procedure g_Mons_LOS_Start (); inline;
+procedure g_Mons_LOS_End (); inline;
+
+var
+  profMonsLOS: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
+
+
+type
+  TMonsterGrid = specialize TBodyGridBase<TMonster>;
+
+var
+  monsGrid: TMonsterGrid = nil;
+
+
+var
+  gmon_debug_think: Boolean = true;
+
+
 implementation
 
 uses
   e_log, g_main, g_sound, g_gfx, g_player, g_game,
   g_weapons, g_triggers, MAPDEF, g_items, g_options,
   g_console, g_map, Math, SysUtils, g_menu, wadreader,
-  g_language, g_netmsg, z_aabbtree;
+  g_language, g_netmsg;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-var
-  monCheckTrapLastFrameId: DWord;
+procedure g_Mons_ProfilersBegin ();
+begin
+  if (profMonsLOS = nil) then profMonsLOS := TProfiler.Create('LOS CALC', g_profile_history_size);
+  profMonsLOS.mainBegin(g_profile_los);
+  if g_profile_los then
+  begin
+    profMonsLOS.sectionBegin('loscalc');
+    profMonsLOS.sectionEnd();
+  end;
+end;
+
+procedure g_Mons_ProfilersEnd ();
+begin
+  if (profMonsLOS <> nil) and (g_profile_los) then profMapCollision.mainEnd();
+end;
+
+procedure g_Mons_LOS_Start (); inline;
+begin
+  profMonsLOS.sectionBeginAccum('loscalc');
+end;
+
+procedure g_Mons_LOS_End (); inline;
+begin
+  profMonsLOS.sectionEnd();
+end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-type
-  TDynAABBTreeMonsBase = specialize TDynAABBTreeBase<TMonster>;
+var
+  monCheckTrapLastFrameId: DWord;
 
-  TDynAABBTreeMons = class(TDynAABBTreeMonsBase)
-    function getFleshAABB (out aabb: AABB2D; flesh: TMonster; tag: Integer): Boolean; override;
-  end;
 
-function TDynAABBTreeMons.getFleshAABB (out aabb: AABB2D; flesh: TMonster; tag: Integer): Boolean;
+procedure TMonster.getMapBox (out x, y, w, h: Integer); inline;
 begin
-  result := false;
-  if (flesh = nil) then raise Exception.Create('DynTree: trying to get dimensions of inexistant monsters');
-  if (flesh.Obj.Rect.Width < 1) or (flesh.Obj.Rect.Height < 1) then raise Exception.Create('DynTree: monster without size, wtf?!');
-  aabb := AABB2D.CreateWH(flesh.Obj.X+flesh.Obj.Rect.X, flesh.Obj.Y+flesh.Obj.Rect.Y, flesh.Obj.Rect.Width, flesh.Obj.Rect.Height);
-  if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
-  result := true;
+  x := FObj.X+FObj.Rect.X;
+  y := FObj.Y+FObj.Rect.Y;
+  w := FObj.Rect.Width;
+  h := FObj.Rect.Height;
 end;
 
 
-var
-  monsTree: TDynAABBTreeMons = nil;
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Mons_AlongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB; log: Boolean=false): TMonster;
+begin
+  if not assigned(cb) then begin result := nil; exit; end;
+  result := monsGrid.forEachAlongLine(x0, y0, x1, y1, cb, -1, log);
+end;
 
 
 //WARNING! call this after monster position was changed, or coldet will not work right!
@@ -245,33 +307,33 @@ procedure TMonster.positionChanged ();
 var
   x, y: Integer;
 begin
-  {$IF DEFINED(D2F_DEBUG)}
+  {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
   //e_WriteLog(Format('monster #%d(%u): pos=(%d,%d); rpos=(%d,%d)', [arrIdx, UID, FObj.X, FObj.Y, FObj.Rect.X, FObj.Rect.Y]), MSG_NOTIFY);
   {$ENDIF}
-  if (treeNode = -1) then
+  if (proxyId = -1) then
   begin
-    treeNode := monsTree.insertObject(self, 0);
-    {$IF DEFINED(D2F_DEBUG)}
-    monsTree.getNodeXY(treeNode, x, y);
-    e_WriteLog(Format('monster #%d(%u): inserted into the tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);
+    proxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
+    {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+    monsGrid.getBodyXY(proxyId, x, y);
+    e_WriteLog(Format('monster #%d(%u): inserted into the grid; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY);
     {$ENDIF}
   end
   else
   begin
-    monsTree.getNodeXY(treeNode, x, y);
+    monsGrid.getBodyXY(proxyId, x, y);
     if (FObj.X+FObj.Rect.X = x) and (FObj.Y+FObj.Rect.Y = y) then exit; // nothing to do
-    {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('monster #%d(%u): updating tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
+    {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}e_WriteLog(Format('monster #%d(%u): updating tree; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY);{$ENDIF}
 
-    {$IFDEF TRUE}
-    monsTree.updateObject(treeNode);
+    {$IF TRUE}
+    monsGrid.moveBody(proxyId, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y);
     {$ELSE}
-    monsTree.removeObject(treeNode);
-    treeNode := monsTree.insertObject(self);
+    monsGrid.removeBody(proxyId);
+    proxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
     {$ENDIF}
 
-    {$IF DEFINED(D2F_DEBUG)}
-    monsTree.getNodeXY(treeNode, x, y);
-    e_WriteLog(Format('monster #%d(%u): updated tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);
+    {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+    monsGrid.getBodyXY(proxyId, x, y);
+    e_WriteLog(Format('monster #%d(%u): updated tree; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY);
     {$ENDIF}
   end;
 end;
@@ -572,6 +634,7 @@ begin
   Result := False;
 end;
 
+
 function BehaviourDamage(SpawnerUID: Word; BH, SelfType: Byte): Boolean;
 var
   m: TMonster;
@@ -601,6 +664,7 @@ begin
   end;
 end;
 
+
 function canShoot(m: Byte): Boolean;
 begin
   Result := False;
@@ -613,10 +677,12 @@ begin
   end;
 end;
 
-function isCorpse(o: PObj; immediately: Boolean): Integer;
+
+function isCorpse (o: PObj; immediately: Boolean): Integer;
 
   function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
   begin
+    atag := atag; // shut up, fpc!
     result := false; // don't stop
     if (mon.FState = STATE_DEAD) and g_Obj_Collide(o, @mon.FObj) then
     begin
@@ -641,7 +707,7 @@ begin
   // Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè
   if gmon_debug_use_sqaccel then
   begin
-    mon := monsTree.aabbQuery(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height, monsCollCheck);
+    mon := monsGrid.forEachInAABB(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height, monsCollCheck);
     if (mon <> nil) then result := mon.arrIdx;
   end
   else
@@ -902,7 +968,6 @@ begin
 
   g_Sound_CreateWADEx('SOUND_MONSTER_FISH_ATTACK', GameWAD+':MSOUNDS\FISH_ATTACK');
 
-  monsTree := TDynAABBTreeMons.Create();
   clearUidMap();
   monCheckTrapLastFrameId := 0;
 end;
@@ -1121,8 +1186,6 @@ begin
   g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK');
 
   g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK');
-
-  monsTree.Free();
 end;
 
 procedure g_Monsters_Init();
@@ -1134,13 +1197,23 @@ procedure g_Monsters_Free();
 var
   a: Integer;
 begin
+  monsGrid.Free();
+  monsGrid := nil;
   for a := 0 to High(gMonsters) do gMonsters[a].Free();
-  monsTree.reset();
   gMonsters := nil;
   clearUidMap();
   monCheckTrapLastFrameId := 0;
 end;
 
+
+// will be called from map loader
+procedure g_Mons_InitTree (x, y, w, h: Integer);
+begin
+  monsGrid.Free();
+  monsGrid := TMonsterGrid.Create(x, y, w, h);
+end;
+
+
 function g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
            Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
 var
@@ -1164,7 +1237,7 @@ begin
   mon := TMonster.Create(MonsterType, find_id, ForcedUID);
   gMonsters[find_id] := mon;
   mon.arrIdx := find_id;
-  mon.treeNode := -1;
+  mon.proxyId := -1;
 
   uidMap[mon.FUID] := mon;
 
@@ -1230,20 +1303,23 @@ begin
 
   gMon := True; // Äëÿ ðàáîòû BlockMon'à
 
-  for a := 0 to High(gMonsters) do
+  if (gmon_debug_think) then
   begin
-    if (gMonsters[a] = nil) then continue;
-    if not gMonsters[a].FRemoved then
+    for a := 0 to High(gMonsters) do
     begin
-      if g_Game_IsClient then
-        gMonsters[a].ClientUpdate()
+      if (gMonsters[a] = nil) then continue;
+      if not gMonsters[a].FRemoved then
+      begin
+        if g_Game_IsClient then
+          gMonsters[a].ClientUpdate()
+        else
+          gMonsters[a].Update();
+      end
       else
-        gMonsters[a].Update();
-    end
-    else
-    begin
-      gMonsters[a].Free();
-      gMonsters[a] := nil;
+      begin
+        gMonsters[a].Free();
+        gMonsters[a] := nil;
+      end;
     end;
   end;
 
@@ -1621,7 +1697,7 @@ end;
 constructor TMonster.Create(MonsterType: Byte; aID: Integer; ForcedUID: Integer = -1);
 var
   a: Integer;
-  FramesID: DWORD;
+  FramesID: DWORD = 0;
   s: String;
   res: Boolean;
 begin
@@ -1651,7 +1727,7 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
 
-  treeNode := -1;
+  proxyId := -1;
   arrIdx := -1;
   trapCheckFrameId := 0;
 
@@ -1927,18 +2003,20 @@ begin
 
   vilefire.Free();
 
-  if (treeNode <> -1) then
+  if (proxyId <> -1) then
   begin
-    {$IF DEFINED(D2F_DEBUG)}
-    e_WriteLog(Format('monster #%d(%u): removed from tree; nodeid=%d', [arrIdx, UID, treeNode]), MSG_NOTIFY);
-    {$ENDIF}
-    if monsTree.isValidId(treeNode) then monsTree.removeObject(treeNode);
+    if (monsGrid <> nil) then
+    begin
+      monsGrid.removeBody(proxyId);
+      {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+      e_WriteLog(Format('monster #%d(%u): removed from tree; proxyid=%d', [arrIdx, UID, proxyId]), MSG_NOTIFY);
+      {$ENDIF}
+    end;
+    proxyId := -1;
   end;
 
-  if (arrIdx <> -1) then
-  begin
-    gMonsters[arrIdx] := nil;
-  end;
+  if (arrIdx <> -1) and (arrIdx < Length(gMonsters)) then gMonsters[arrIdx] := nil;
+  arrIdx := -1;
 
   uidMap[FUID] := nil;
 
@@ -2863,7 +2941,7 @@ _end:
           mon.SetState(STATE_GO);
           mon.FNoRespawn := True;
           Inc(gTotalMonsters);
-          if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+          if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
         end;
 
         mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
@@ -2873,7 +2951,7 @@ _end:
           mon.SetState(STATE_GO);
           mon.FNoRespawn := True;
           Inc(gTotalMonsters);
-          if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+          if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
         end;
 
         mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
@@ -2883,9 +2961,10 @@ _end:
           mon.SetState(STATE_GO);
           mon.FNoRespawn := True;
           Inc(gTotalMonsters);
-          if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+          if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
         end;
-        if g_Game_IsNet then MH_SEND_CoopStats(gMonsters[sx].UID);
+
+        if g_Game_IsNet then MH_SEND_CoopStats();
       end;
 
     // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ:
@@ -3058,7 +3137,7 @@ _end:
                       mon.shoot(@o, True);
                       Inc(gTotalMonsters);
 
-                      if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+                      if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
                     end;
                   end;
               end;
@@ -4414,36 +4493,57 @@ 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_ForEach (cb: TEachMonsterCB): Boolean;
 var
   idx: Integer;
+  mon: TMonster;
 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
+    mon := gMonsters[idx];
+    if (mon <> nil) then
     begin
-      result := cb(idx, gMonsters[idx]);
+      result := cb(mon);
       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;
+function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean;
+var
+  idx: Integer;
+  mon: TMonster;
 begin
-  if (uid < 0) or (uid > High(gMonsters)) then begin result := nil; exit; end;
-  result := gMonsters[uid];
+  result := false;
+  if (gMonsters = nil) or not assigned(cb) then exit;
+  for idx := 0 to High(gMonsters) do
+  begin
+    mon := gMonsters[idx];
+    if (mon <> nil) and mon.Live then
+    begin
+      result := cb(mon);
+      if result then exit;
+    end;
+  end;
 end;
 
 
@@ -4451,26 +4551,27 @@ function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
 
   function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
   begin
-    result := (mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj));
+    result := mon.Live;// and g_Obj_Collide(x, y, width, height, @mon.Obj));
   end;
 
 var
   idx: Integer;
+  mon: TMonster;
 begin
   result := false;
   if (width < 1) or (height < 1) then exit;
-
   if gmon_debug_use_sqaccel then
   begin
-    result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+    result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
   end
   else
   begin
     for idx := 0 to High(gMonsters) do
     begin
-      if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+      mon := gMonsters[idx];
+      if (mon <> nil) and mon.Live then
       begin
-        if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+        if g_Obj_Collide(x, y, width, height, @mon.Obj) then
         begin
           result := true;
           exit;
@@ -4485,29 +4586,29 @@ function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonst
 
   function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
   begin
-    result := false;
-    if g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon.arrIdx, mon);
+    result := cb(mon);
   end;
 
 var
   idx: Integer;
+  mon: TMonster;
 begin
   result := false;
   if (width < 1) or (height < 1) then exit;
-
   if gmon_debug_use_sqaccel then
   begin
-    result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+    result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
   end
   else
   begin
     for idx := 0 to High(gMonsters) do
     begin
-      if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+      mon := gMonsters[idx];
+      if (mon <> nil) and mon.Live then
       begin
-        if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+        if g_Obj_Collide(x, y, width, height, @mon.Obj) then
         begin
-          result := cb(idx, gMonsters[idx]);
+          result := cb(mon);
           if result then exit;
         end;
       end;
@@ -4516,40 +4617,42 @@ begin
 end;
 
 
-function g_Mons_ForEachAtAlive (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
 
   function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
   begin
-    result := false;
-    if mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon.arrIdx, mon);
+    //result := false;
+    //if mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon);
+    if mon.Live then result := cb(mon) else result := false;
   end;
 
 var
   idx: Integer;
+  mon: TMonster;
 begin
   result := false;
   if (width < 1) or (height < 1) then exit;
-
   if gmon_debug_use_sqaccel then
   begin
     if (width = 1) and (height = 1) then
     begin
-      result := (monsTree.pointQuery(x, y, monsCollCheck) <> nil);
+      result := (monsGrid.forEachAtPoint(x, y, monsCollCheck) <> nil);
     end
     else
     begin
-      result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+      result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
     end;
   end
   else
   begin
     for idx := 0 to High(gMonsters) do
     begin
-      if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+      mon := gMonsters[idx];
+      if (mon <> nil) and mon.Live then
       begin
-        if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+        if g_Obj_Collide(x, y, width, height, @mon.Obj) then
         begin
-          result := cb(idx, gMonsters[idx]);
+          result := cb(mon);
           if result then exit;
         end;
       end;