DEADSOFTWARE

some tree code for monsters
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Sun, 20 Aug 2017 03:35:37 +0000 (06:35 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Sun, 20 Aug 2017 05:33:44 +0000 (08:33 +0300)
src/game/g_basic.pas
src/game/g_grid.pas
src/game/g_items.pas
src/game/g_monsters.pas
src/game/g_netmsg.pas
src/game/g_player.pas
src/game/g_triggers.pas
src/game/g_weapons.pas
src/game/z_aabbtree.pas

index e001f8567a3af1ad810da064330ec9cb595d0183..626596b01878e2154c7ac0caf6ff9b8f275b97b5 100644 (file)
@@ -47,7 +47,6 @@ function g_CollideLevel(X, Y: Integer; Width, Height: Word): Boolean;
 function g_CollideAround(X1, Y1: Integer; Width1, Height1: Word;
                          X2, Y2: Integer; Width2, Height2: Word): Boolean;
 function g_CollidePlayer(X, Y: Integer; Width, Height: Word): Boolean;
-function g_CollideMonster(X, Y: Integer; Width, Height: Word): Boolean;
 function g_PatchLength(X1, Y1, X2, Y2: Integer): Word;
 function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean;
 function g_GetAcidHit(X, Y: Integer; Width, Height: Word): Byte;
@@ -143,11 +142,6 @@ begin
       end;
 end;
 
-function g_CollideMonster(X, Y: Integer; Width, Height: Word): Boolean;
-begin
-  result := g_Mons_AnyAt(X, Y, Width, Height);
-end;
-
 function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean;
 var
   i: Integer;
index d1149adf46c64d47c5790f5731c2ea56e90ea278..8ff01a629340b582ad58c0cb40046f8b80c251a3 100644 (file)
@@ -171,7 +171,7 @@ begin
   mProxyCount := 0;
   mProxyMaxCount := 0;
   mUData := 0;
-  mTagMask := 0;
+  mTagMask := -1;
   mItCB := nil;
   e_WriteLog(Format('created grid with size: %dx%d (tile size: %d); pix: %dx%d', [mWidth, mHeight, mTileSize, mWidth*mTileSize, mHeight*mTileSize]), MSG_NOTIFY);
 end;
@@ -501,7 +501,7 @@ begin
     begin
       if (pi.bodies[f] = -1) then break;
       px := @mProxies[pi.bodies[f]];
-      if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then
+      if (px.mQueryMark <> mLastQuery) and ((mTagMask = -1) or ((px.mTag and mTagMask) <> 0)) then
       begin
         //e_WriteLog(Format('  query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
         px.mQueryMark := mLastQuery;
@@ -513,7 +513,7 @@ begin
     if (mCells[idx].body <> -1) then
     begin
       px := @mProxies[mCells[idx].body];
-      if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then
+      if (px.mQueryMark <> mLastQuery) and ((mTagMask = -1) or ((px.mTag and mTagMask) <> 0)) then
       begin
         //e_WriteLog(Format('  query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
         px.mQueryMark := mLastQuery;
index 95ea919ca31007ae9c90257f1e49cc1b555e2c85..eb122d80c4535816860eaa4710c775e7b43f7dd6 100644 (file)
@@ -25,7 +25,8 @@ Type
   PItem = ^TItem;
   TItem = record
   private
-    treeNode: Integer;
+    //treeNode: Integer;
+    slotIsUsed: Boolean;
     arrIdx: Integer; // in ggItems
 
   public
@@ -60,12 +61,12 @@ procedure g_Items_LoadState(var Mem: TBinMemoryReader);
 
 procedure g_Items_RestartRound ();
 
-function g_ItemValidId (idx: Integer): Boolean; inline;
-function g_ItemByIdx (idx: Integer): PItem;
-function g_ItemObjByIdx (idx: Integer): PObj;
+function g_Items_ValidId (idx: Integer): Boolean; inline;
+function g_Items_ByIdx (idx: Integer): PItem;
+function g_Items_ObjByIdx (idx: Integer): PObj;
 
-procedure g_Item_EmitPickupSound (idx: Integer); // at item position
-procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer);
+procedure g_Items_EmitPickupSound (idx: Integer); // at item position
+procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
 
 
 type
@@ -115,6 +116,7 @@ begin
 end;
 }
 
+
 // ////////////////////////////////////////////////////////////////////////// //
 var
   //itemTree: TDynAABBTreeItem = nil;
@@ -122,27 +124,30 @@ var
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-function g_ItemValidId (idx: Integer): Boolean; inline;
+function g_Items_ValidId (idx: Integer): Boolean; inline;
 begin
   result := false;
   if (idx < 0) or (idx > High(ggItems)) then exit;
-  if (ggItems[idx].treeNode = -1) then exit;
+  //if (ggItems[idx].treeNode = -1) then exit;
+  if not ggItems[idx].slotIsUsed then exit;
   result := true;
 end;
 
 
-function g_ItemByIdx (idx: Integer): PItem;
+function g_Items_ByIdx (idx: Integer): PItem;
 begin
   if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('g_ItemObjByIdx: invalid index');
   result := @ggItems[idx];
-  if (result.treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+  //if (result.treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+  if not result.slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
 end;
 
 
-function g_ItemObjByIdx (idx: Integer): PObj;
+function g_Items_ObjByIdx (idx: Integer): PObj;
 begin
   if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('g_ItemObjByIdx: invalid index');
-  if (ggItems[idx].treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+  //if (ggItems[idx].treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+  if not ggItems[idx].slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
   result := @ggItems[idx].Obj;
 end;
 
@@ -385,10 +390,12 @@ var
 begin
   if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('releaseItem: invalid item id');
   it := @ggItems[idx];
-  if (it.treeNode = -1) then raise Exception.Create('releaseItem: trying to release unallocated item');
+  //if (it.treeNode = -1) then raise Exception.Create('releaseItem: trying to release unallocated item');
+  if not it.slotIsUsed then raise Exception.Create('releaseItem: trying to release unallocated item');
   if (it.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency');
   //itemTree.removeObject(it.treeNode);
-  it.treeNode := -1;
+  //it.treeNode := -1;
+  it.slotIsUsed := false;
   if (it.Animation <> nil) then
   begin
     it.Animation.Free();
@@ -414,7 +421,8 @@ begin
     for i := olen to High(ggItems) do
     begin
       it := @ggItems[i];
-      it.treeNode := -1;
+      //it.treeNode := -1;
+      it.slotIsUsed := false;
       it.arrIdx := i;
       it.ItemType := ITEM_NONE;
       it.Animation := nil;
@@ -451,7 +459,8 @@ begin
     for i := olen to High(ggItems) do
     begin
       it := @ggItems[i];
-      it.treeNode := -1;
+      //it.treeNode := -1;
+      it.slotIsUsed := false;
       it.arrIdx := i;
       it.ItemType := ITEM_NONE;
       it.Animation := nil;
@@ -464,7 +473,7 @@ begin
   end;
 
   it := @ggItems[slot];
-  if (it.treeNode = -1) then
+  if {(it.treeNode = -1)} not it.slotIsUsed then
   begin
     // this is unused slot; get it, and rebuild id list
     if rebuildFreeList then
@@ -472,7 +481,7 @@ begin
       freeIds.clear();
       for i := 0 to High(ggItems) do
       begin
-        if (i <> slot) and (ggItems[i].treeNode = -1) then freeIds.insert(i);
+        if (i <> slot) and {(ggItems[i].treeNode = -1)} (not it.slotIsUsed) then freeIds.insert(i);
       end;
     end;
   end
@@ -480,7 +489,8 @@ begin
   begin
     // it will be readded
     //itemTree.removeObject(it.treeNode);
-    it.treeNode := -1;
+    //it.treeNode := -1;
+    it.slotIsUsed := false;
   end;
 
   result := slot;
@@ -525,6 +535,12 @@ begin
 
   it := @ggItems[find_id];
 
+  //if (it.treeNode <> -1) then raise Exception.Create('g_Items_Create: trying to reuse already allocated item');
+  if (it.arrIdx <> find_id) then raise Exception.Create('g_Items_Create: arrIdx inconsistency');
+  //it.treeNode := -1;
+  //it.arrIdx := find_id;
+  it.slotIsUsed := true;
+
   it.ItemType := ItemType;
   it.Respawnable := Respawnable;
   if g_Game_IsServer and (ITEM_RESPAWNTIME = 0) then it.Respawnable := False;
@@ -535,11 +551,6 @@ begin
   it.Live := True;
   it.QuietRespawn := False;
 
-  if (it.treeNode <> -1) then raise Exception.Create('g_Items_Create: trying to reuse already allocated item');
-  if (it.arrIdx <> find_id) then raise Exception.Create('g_Items_Create: arrIdx inconsistency');
-  //it.treeNode := -1;
-  //it.arrIdx := find_id;
-
   g_Obj_Init(@it.Obj);
   it.Obj.X := X;
   it.Obj.Y := Y;
@@ -647,7 +658,7 @@ begin
                 +2. I_MEGA,I_INVL,I_SUPER
                 3. I_STIM,I_MEDI,I_ARM1,I_ARM2,I_AQUA,I_KEYR,I_KEYG,I_KEYB,I_SUIT,I_RTORCH,I_GTORCH,I_BTORCH,I_GOR1,I_FCAN
               }
-              g_Item_EmitPickupSoundAt(i, gPlayers[j].Obj.X, gPlayers[j].Obj.Y);
+              g_Items_EmitPickupSoundAt(i, gPlayers[j].Obj.X, gPlayers[j].Obj.Y);
 
               // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì
               if r then
@@ -751,7 +762,7 @@ var
   //x, y: Integer;
 {$ENDIF}
 begin
-  if not g_ItemValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id');
+  if not g_Items_ValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id');
 
   it := @ggItems[ID];
   if (it.arrIdx <> ID) then raise Exception.Create('g_Items_Remove: arrIdx desync');
@@ -915,20 +926,20 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-procedure g_Item_EmitPickupSound (idx: Integer);
+procedure g_Items_EmitPickupSound (idx: Integer);
 var
   it: PItem;
 begin
-  if not g_ItemValidId(idx) then exit;
+  if not g_Items_ValidId(idx) then exit;
   it := @ggItems[idx];
-  g_Item_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
+  g_Items_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
 end;
 
-procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer);
+procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
 var
   it: PItem;
 begin
-  if not g_ItemValidId(idx) then exit;
+  if not g_Items_ValidId(idx) then exit;
 
   it := @ggItems[idx];
   if gSoundEffectsDF then
index af98abae75f8936317a489592ebc0656dbb6e000..59b5506a5ee732d02bde51f54f49820dedc7a1c2 100644 (file)
@@ -80,6 +80,9 @@ type
     FFireAttacker: Word;
     vilefire: TAnimation;
 
+    treeNode: Integer; // node in dyntree or -1
+    arrIdx: Integer; // in gMonsters
+
     FDieTriggers: Array of Integer;
     FSpawnTrigger: Integer;
 
@@ -186,7 +189,14 @@ 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;
+function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): 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;
+
+
+var
+  gmon_debug_use_sqaccel: Boolean = true;
 
 
 implementation
@@ -195,8 +205,70 @@ 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;
+  g_language, g_netmsg, z_aabbtree;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+  TDynAABBTreeMonsBase = specialize TDynAABBTreeBase<TMonster>;
+
+  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;
+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;
+end;
+
+
+var
+  monsTree: TDynAABBTreeMons = nil;
+
+
+//WARNING! call this after monster position was changed, or coldet will not work right!
+procedure TMonster.positionChanged ();
+var
+  x, y: Integer;
+begin
+  {$IF DEFINED(D2F_DEBUG)}
+  //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
+  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);
+    {$ENDIF}
+  end
+  else
+  begin
+    monsTree.getNodeXY(treeNode, 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}
+
+    {$IFDEF TRUE}
+    monsTree.updateObject(treeNode);
+    {$ELSE}
+    monsTree.removeObject(treeNode);
+    treeNode := monsTree.insertObject(self);
+    {$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);
+    {$ENDIF}
+  end;
+end;
 
+
+// ////////////////////////////////////////////////////////////////////////// //
 const
   ANIM_SLEEP   = 0;
   ANIM_GO      = 1;
@@ -397,6 +469,7 @@ const
   MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè
   MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ
 
+
 var
   gMonsters: array of TMonster;
 
@@ -407,30 +480,34 @@ var
   pt_ys: Integer = 1;
   soulcount: Integer = 0;
 
-function FindMonster(): DWORD;
+
+function allocMonster(): DWORD;
 var
-  i: Integer;
+  i, olen: Integer;
 begin
-  if gMonsters <> nil then
-    for i := 0 to High(gMonsters) do
-      if gMonsters[i] = nil then
-      begin
-        Result := i;
-        Exit;
-      end;
-
-  if gMonsters = nil then
-    begin
-      SetLength(gMonsters, 32);
-      Result := 0;
-    end
-  else
+  for i := 0 to High(gMonsters) do
+  begin
+    if (gMonsters[i] = nil) then
     begin
-      Result := High(gMonsters) + 1;
-      SetLength(gMonsters, Length(gMonsters) + 32);
+      result := i;
+      exit;
     end;
+  end;
+
+  olen := Length(gMonsters);
+  if (olen = 0) then
+  begin
+    SetLength(gMonsters, 64);
+    result := 0;
+  end
+  else
+  begin
+    result := olen;
+    SetLength(gMonsters, Length(gMonsters)+32);
+  end;
 end;
 
+
 function IsFriend(a, b: Byte): Boolean;
 begin
   Result := True;
@@ -500,23 +577,42 @@ begin
 end;
 
 function isCorpse(o: PObj; immediately: Boolean): Integer;
+
+  function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
+  begin
+    result := false; // don't stop
+    if (mon.FState = STATE_DEAD) and g_Obj_Collide(o, @mon.FObj) then
+    begin
+      case mon.FMonsterType of // Íå âîñêðåñèòü:
+        MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER,
+        MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: exit;
+      end;
+      // Îñòàëüíûõ ìîæíî âîñêðåñèòü
+      result := true;
+    end;
+  end;
+
 var
   a: Integer;
+  mon: TMonster;
 begin
-  Result := -1;
-
-// Åñëè íóæíà âåðîÿòíîñòü:
-  if not immediately then
-    if Random(8) <> 0 then
-      Exit;
+  result := -1;
 
-  if gMonsters = nil then
-    Exit;
+  // Åñëè íóæíà âåðîÿòíîñòü
+  if not immediately and (Random(8) <> 0) then exit;
 
-// Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè:
-  for a := 0 to High(gMonsters) do
-    if (gMonsters[a] <> nil) and (gMonsters[a].FState = STATE_DEAD) then
-      if g_Obj_Collide(o, @gMonsters[a].FObj) then
+  // Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè
+  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);
+    if (mon <> nil) then result := mon.arrIdx;
+  end
+  else
+  begin
+    for a := 0 to High(gMonsters) do
+    begin
+      if (gMonsters[a] <> nil) and (gMonsters[a].FState = STATE_DEAD) and g_Obj_Collide(o, @gMonsters[a].FObj) then
+      begin
         case gMonsters[a].FMonsterType of // Íå âîñêðåñèòü:
           MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER,
           MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: Continue;
@@ -526,6 +622,9 @@ begin
               Exit;
             end;
         end;
+      end;
+    end;
+  end;
 end;
 
 procedure g_Monsters_LoadData();
@@ -765,6 +864,8 @@ begin
   g_Sound_CreateWADEx('SOUND_MONSTER_SPIDER_WALK', GameWAD+':MSOUNDS\SPIDER_WALK');
 
   g_Sound_CreateWADEx('SOUND_MONSTER_FISH_ATTACK', GameWAD+':MSOUNDS\FISH_ATTACK');
+
+  monsTree := TDynAABBTreeMons.Create();
 end;
 
 procedure g_Monsters_FreeData();
@@ -981,6 +1082,8 @@ begin
   g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK');
 
   g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK');
+
+  monsTree.Free();
 end;
 
 procedure g_Monsters_Init();
@@ -996,6 +1099,7 @@ begin
     for a := 0 to High(gMonsters) do
       gMonsters[a].Free();
 
+  monsTree.reset();
   gMonsters := nil;
 end;
 
@@ -1003,6 +1107,7 @@ function g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
            Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
 var
   find_id: DWORD;
+  mon: TMonster;
 begin
   result := nil;
 
@@ -1016,12 +1121,15 @@ begin
     soulcount := soulcount + 1;
   end;
 
-  find_id := FindMonster();
+  find_id := allocMonster();
 
-  gMonsters[find_id] := TMonster.Create(MonsterType, find_id, ForcedUID);
+  mon := TMonster.Create(MonsterType, find_id, ForcedUID);
+  gMonsters[find_id] := mon;
+  mon.arrIdx := find_id;
+  mon.treeNode := -1;
 
   // Íàñòðàèâàåì ïîëîæåíèå
-  with gMonsters[find_id] do
+  with mon do
   begin
     if AdjCoord then
     begin
@@ -1040,7 +1148,9 @@ begin
     FStartY := GameY;
   end;
 
-  result := {find_id}gMonsters[find_id];
+  mon.positionChanged();
+
+  result := mon;
 end;
 
 procedure g_Monsters_killedp();
@@ -1501,6 +1611,9 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
 
+  treeNode := -1;
+  arrIdx := -1;
+
   if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then
     FBloodKind := BLOOD_SPARKS
   else
@@ -1698,8 +1811,8 @@ begin
         it := g_Items_Create(FObj.X + (FObj.Rect.Width div 2),
                              FObj.Y + (FObj.Rect.Height div 2),
                              c, True, False);
-        g_Obj_Push(g_ItemObjByIdx(it), (FObj.Vel.X div 2)-3+Random(7),
-                                       (FObj.Vel.Y div 2)-Random(4));
+        g_Obj_Push(g_Items_ObjByIdx(it), (FObj.Vel.X div 2)-3+Random(7),
+                                        (FObj.Vel.Y div 2)-Random(4));
         positionChanged(); // this updates spatial accelerators
         if g_Game_IsServer and g_Game_IsNet then
           MH_SEND_ItemSpawn(True, it);
@@ -1775,6 +1888,14 @@ begin
 
   vilefire.Free();
 
+  if (treeNode <> -1) then
+  begin
+    {$IF DEFINED(D2F_DEBUG)}
+    e_WriteLog(Format('monster #%d(%u): removed from tree; nodeid=%d', [arrIdx, UID, treeNode]), MSG_NOTIFY);
+    {$ENDIF}
+    monsTree.removeObject(treeNode);
+  end;
+
   inherited Destroy();
 end;
 
@@ -1975,6 +2096,7 @@ begin
 
   FObj.X := X - FObj.Rect.X;
   FObj.Y := Y - FObj.Rect.Y;
+  positionChanged();
 
   if dir = 1 then
     FDirection := D_LEFT
@@ -2389,8 +2511,7 @@ begin
                     if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then
                     begin // "Ñòîèò" òâåðäî
                     // Ðûáà òðåïûõàåòñÿ íà ïîâåðõíîñòè:
-                      if FObj.Accel.Y = 0 then
-                        FObj.Vel.Y := -6;
+                      if FObj.Accel.Y = 0 then FObj.Vel.Y := -6;
                       FObj.Accel.X := FObj.Accel.X - 8 + Random(17);
                     end;
 
@@ -2453,7 +2574,6 @@ begin
                   begin
                     g_Obj_PushA(@gGibs[a].Obj, b, Random(61));    // íàïðàâî
                   end;
-                  positionChanged(); // this updates spatial accelerators
                 end;
               end;
             end;
@@ -4249,11 +4369,6 @@ begin
   end;
 end;
 
-//WARNING! call this after monster position was changed, or coldet will not work right!
-procedure TMonster.positionChanged ();
-begin
-end;
-
 
 // ////////////////////////////////////////////////////////////////////////// //
 function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
@@ -4289,21 +4404,111 @@ begin
 end;
 
 
-function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean;
+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));
+  end;
+
 var
   idx: Integer;
 begin
   result := false;
   if (width < 1) or (height < 1) then exit;
 
-  for idx := 0 to High(gMonsters) do
+  if gmon_debug_use_sqaccel then
   begin
-    if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+    result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+  end
+  else
+  begin
+    for idx := 0 to High(gMonsters) do
     begin
-      if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+      if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
       begin
-        result := True;
-        exit;
+        if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+        begin
+          result := true;
+          exit;
+        end;
+      end;
+    end;
+  end;
+end;
+
+
+function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+
+  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);
+  end;
+
+var
+  idx: Integer;
+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);
+  end
+  else
+  begin
+    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 := cb(idx, gMonsters[idx]);
+          if result then exit;
+        end;
+      end;
+    end;
+  end;
+end;
+
+
+function g_Mons_ForEachAtAlive (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);
+  end;
+
+var
+  idx: Integer;
+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);
+    end
+    else
+    begin
+      result := (monsTree.aabbQuery(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
+      begin
+        if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+        begin
+          result := cb(idx, gMonsters[idx]);
+          if result then exit;
+        end;
       end;
     end;
   end;
index 7499fc260112bda3621a18db0fcb416f737d0a21..94326281ae68a2627669144725ed9f0b9aae50c0 100644 (file)
@@ -1190,7 +1190,7 @@ procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYON
 var
   it: PItem;
 begin
-  it := g_ItemByIdx(IID);
+  it := g_Items_ByIdx(IID);
 
   NetOut.Write(Byte(NET_MSG_ISPAWN));
   NetOut.Write(IID);
@@ -2366,7 +2366,7 @@ begin
 
   g_Items_Create(X, Y, T, Fall, False, False, ID);
 
-  it := g_ItemByIdx(ID);
+  it := g_Items_ByIdx(ID);
   it.Obj.Vel.X := VX;
   it.Obj.Vel.Y := VY;
 
@@ -2391,9 +2391,9 @@ begin
   ID := M.ReadWord();
   Quiet := M.ReadByte() <> 0;
 
-  if not g_ItemValidId(ID) then exit;
+  if not g_Items_ValidId(ID) then exit;
 
-  if not Quiet then g_Item_EmitPickupSound(ID);
+  if not Quiet then g_Items_EmitPickupSound(ID);
 
   g_Items_Remove(ID);
 end;
index 75366d34590e693d5c82b50f235ee20adcc27f04..c1b7c087645cce6d7e413ff3237812e18e5a06ca 100644 (file)
@@ -2980,7 +2980,7 @@ var
     id: DWORD;
   begin
     id := g_Items_Create(FObj.X, FObj.Y, t, True, False);
-    it := g_ItemByIdx(id);
+    it := g_Items_ByIdx(id);
     if KillType = K_EXTRAHARDKILL then // -7..+7; -8..0
     begin
       g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-7+Random(15),
index ec3bb7144f67e6ae7f1c2f05c62890134968af8a..4fefb5d34b612cda301c2ed28f1734235b8e8867 100644 (file)
@@ -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
@@ -1373,14 +1373,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);
@@ -1395,7 +1395,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);
@@ -1410,7 +1410,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);
@@ -2196,7 +2196,7 @@ var
   a, b, i: Integer;
   Affected: array of Integer;
 
-  function monsNear (monidx: Integer; mon: TMonster): Boolean;
+  {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
@@ -2204,6 +2204,13 @@ var
       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
@@ -2440,13 +2447,14 @@ begin
           if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
              (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
           begin
-            g_Mons_ForEach(monsNear);
+            //g_Mons_ForEach(monsNear);
+            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);
index 2cdfe87a09935a9dac8cebba83412243a47ed6cf..f8028eef33fc30d94871d95b80759ae45421593c 100644 (file)
@@ -392,10 +392,10 @@ end;
 function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer;
   SpawnerUID: Word; AllowPush: Boolean): Byte;
 
-  function monsCheck (monidx: Integer; mon: TMonster): Boolean;
+  {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
+    if mon.Live and mon.Collide(X, Y) then
     begin
       if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
       begin
@@ -403,6 +403,16 @@ function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer;
         result := true;
       end;
     end;
+  end;}
+
+  function monsCheck (monidx: Integer; mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    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;
 
 var
@@ -423,7 +433,8 @@ begin
 
   if Result <> 0 then Exit;
 
-  if g_Mons_ForEach(monsCheck) then result := 2;
+  //if g_Mons_ForEach(monsCheck) then result := 2;
+  if g_Mons_ForEachAtAlive(X, Y, 1, 1, monsCheck) then result := 2;
 end;
 
 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
index 3371a1ea82e90e17db293748e4c43b581ba4ce72..996230d258244b9fed0818eca65ab2ecb3a3074a 100644 (file)
@@ -68,6 +68,8 @@ type
     constructor Create (var aabb: AABB2D); overload;
     constructor Create (var aabb0, aabb1: AABB2D); overload;
 
+    constructor CreateWH (ax, ay, w, h: TreeNumber);
+
     procedure copyFrom (var aabb: AABB2D); inline;
     procedure setDims (x0, y0, x1, y1: TreeNumber); inline;
 
@@ -273,7 +275,7 @@ type
     // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
     // AABB for static object will not be "fat" (simple optimization)
     // WARNING! inserting the same object several times *WILL* break everything!
-    function insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer;
+    function insertObject (flesh: TTreeFlesh; tag: Integer=-1; staticObject: Boolean=false): Integer;
 
     // remove an object from the tree
     // WARNING: ids of removed objects can be reused on later insertions!
@@ -394,6 +396,14 @@ begin
   setMergeTwo(aabb0, aabb1);
 end;
 
+constructor AABB2D.CreateWH (ax, ay, w, h: TreeNumber);
+begin
+  minX := ax;
+  minY := ay;
+  maxX := ax+w-1;
+  maxY := ay+h-1;
+end;
+
 function AABB2D.getvalid (): Boolean; inline; begin result := (minX <= maxX) and (minY <= maxY); end;
 
 {$IFDEF aabbtree_use_floats}
@@ -1298,7 +1308,7 @@ begin
         begin
           // call visitor on it
           {$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited);{$ENDIF}
-          if ((node.tag and tagmask) <> 0) then
+          if (tagmask = -1) or ((node.tag and tagmask) <> 0) then
           begin
             if assigned(visitor) then
             begin