DEADSOFTWARE

idpool: it is now possible to request the specified id
[d2df-sdl.git] / src / game / g_items.pas
index eb122d80c4535816860eaa4710c775e7b43f7dd6..b0a053d5c1ca83ba8883a318379008c91dbb6444 100644 (file)
@@ -28,6 +28,7 @@ Type
     //treeNode: Integer;
     slotIsUsed: Boolean;
     arrIdx: Integer; // in ggItems
+    nextFree: Integer; // in item array
 
   public
     ItemType:      Byte;
@@ -77,7 +78,7 @@ function g_Items_ForEachAlive (cb: TItemEachAliveCB; backwards: Boolean=false):
 
 var
   gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
-  gMaxDist: Integer = 1;
+  gMaxDist: Integer = 1; // for sounds
   ITEM_RESPAWNTIME: Integer = 60 * 36;
 
 implementation
@@ -86,41 +87,17 @@ uses
   g_basic, e_graphics, g_sound, g_main, g_gfx, g_map,
   Math, g_game, g_triggers, g_console, SysUtils, g_player, g_net, g_netmsg,
   e_log,
-  g_grid, z_aabbtree, binheap;
+  g_grid, binheap;
 
 
 var
   ggItems: Array of TItem = nil;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-{
-type
-  TDynAABBTreeItemBase = specialize TDynAABBTreeBase<Integer>;
-
-  TDynAABBTreeItem = class(TDynAABBTreeItemBase)
-    function getFleshAABB (out aabb: AABB2D; flesh: Integer; tag: Integer): Boolean; override;
-  end;
-
-function TDynAABBTreeItem.getFleshAABB (out aabb: AABB2D; flesh: Integer; tag: Integer): Boolean;
-var
-  it: PItem;
-begin
-  result := false;
-  if (flesh < 0) or (flesh > High(ggItems)) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
-  it := @ggItems[flesh];
-  if (it.Obj.Rect.Width < 1) or (it.Obj.Rect.Height < 1) then exit;
-  aabb := AABB2D.Create(it.Obj.X, it.Obj.Y, it.Obj.X+it.Obj.Rect.Width-1, it.Obj.Y+it.Obj.Rect.Height-1);
-  if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
-  result := true;
-end;
-}
-
-
 // ////////////////////////////////////////////////////////////////////////// //
 var
-  //itemTree: TDynAABBTreeItem = nil;
-  freeIds: TBinaryHeapInt = nil; // free item ids
+  //freeIds: TBinaryHeapInt = nil; // free item ids
+  freeListHead: Integer = -1;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
@@ -128,7 +105,6 @@ 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 not ggItems[idx].slotIsUsed then exit;
   result := true;
 end;
@@ -138,7 +114,6 @@ 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 not result.slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
 end;
 
@@ -146,7 +121,6 @@ end;
 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 not ggItems[idx].slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
   result := @ggItems[idx].Obj;
 end;
@@ -154,37 +128,7 @@ end;
 
 // ////////////////////////////////////////////////////////////////////////// //
 procedure TItem.positionChanged ();
-//var
-//  x, y: Integer;
 begin
-  (*
-  if (treeNode = -1) then
-  begin
-    treeNode := itemTree.insertObject(arrIdx, 0, true); // static object
-    {$IF DEFINED(D2F_DEBUG)}
-    itemTree.getNodeXY(treeNode, x, y);
-    e_WriteLog(Format('item #%d: inserted into the tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);
-    {$ENDIF}
-  end
-  else
-  begin
-    itemTree.getNodeXY(treeNode, x, y);
-    if (Obj.X = x) and (Obj.Y = y) then exit; // nothing to do
-    {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('item #%d: updating tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
-
-    {$IFDEF TRUE}
-    itemTree.updateObject(treeNode);
-    {$ELSE}
-    itemTree.removeObject(treeNode);
-    treeNode := itemTree.insertObject(arrIdx, 0, true); // static object
-    {$ENDIF}
-
-    {$IF DEFINED(D2F_DEBUG)}
-    itemTree.getNodeXY(treeNode, x, y);
-    e_WriteLog(Format('item #%d: updated tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);
-    {$ENDIF}
-  end;
-  *)
 end;
 
 
@@ -321,8 +265,7 @@ begin
 
   InitTextures();
 
-  //itemTree := TDynAABBTreeItem.Create();
-  freeIds := binHeapNewIntLess();
+  //freeIds := binHeapNewIntLess();
 end;
 
 
@@ -379,8 +322,7 @@ begin
   g_Texture_Delete('ITEM_MEDKIT_BLACK');
   g_Texture_Delete('ITEM_JETPACK');
 
-  //itemTree.Free();
-  freeIds.Free();
+  //freeIds.Free();
 end;
 
 
@@ -390,11 +332,8 @@ 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 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.slotIsUsed := false;
   if (it.Animation <> nil) then
   begin
@@ -404,94 +343,97 @@ begin
   it.Live := False;
   it.SpawnTrigger := -1;
   it.ItemType := ITEM_NONE;
-  freeIds.insert(it.arrIdx);
+  //freeIds.insert(it.arrIdx);
+  it.nextFree := freeListHead;
+  freeListHead := idx;
 end;
 
 
-function allocItem (): DWORD;
+procedure rebuildFreeList (reservedIdx: Integer=-1);
 var
-  i, olen: Integer;
+  i, lfi: Integer;
   it: PItem;
 begin
-  if (freeIds.count = 0) then
+  // rebuild free list
+  freeListHead := -1;
+  lfi := -1;
+  for i := 0 to High(ggItems) do
   begin
-    // no free slots
-    olen := Length(ggItems);
-    SetLength(ggItems, olen+64);
-    for i := olen to High(ggItems) do
+    it := @ggItems[i];
+    if (i <> reservedIdx) and (not it.slotIsUsed) then
     begin
-      it := @ggItems[i];
-      //it.treeNode := -1;
-      it.slotIsUsed := false;
-      it.arrIdx := i;
-      it.ItemType := ITEM_NONE;
-      it.Animation := nil;
-      it.Live := false;
-      it.SpawnTrigger := -1;
-      it.Respawnable := false;
-      freeIds.insert(i);
+      if (lfi = -1) then freeListHead := i else ggItems[lfi].nextFree := lfi;
+      lfi := i;
     end;
   end;
+  if (lfi <> -1) then ggItems[lfi].nextFree := -1;
+end;
 
-  result := freeIds.front;
-  freeIds.popFront();
 
-  if (result > High(ggItems)) then raise Exception.Create('allocItem: freeid list corrupted');
-  if (ggItems[result].arrIdx <> result) then raise Exception.Create('allocItem: arrIdx inconsistency');
+procedure growItemArrayTo (newsz: Integer; rebuildList: Boolean=true);
+var
+  i, olen: Integer;
+  it: PItem;
+begin
+  if (newsz < Length(ggItems)) then exit;
+  // no free slots
+  olen := Length(ggItems);
+  SetLength(ggItems, newsz);
+  for i := olen to High(ggItems) do
+  begin
+    it := @ggItems[i];
+    it.slotIsUsed := false;
+    it.arrIdx := i;
+    it.nextFree := i+1;
+    it.ItemType := ITEM_NONE;
+    it.Animation := nil;
+    it.Live := false;
+    it.SpawnTrigger := -1;
+    it.Respawnable := false;
+    //freeIds.insert(i);
+  end;
+  if rebuildList then rebuildFreeList();
+end;
+
+
+function allocItem (): DWORD;
+begin
+  if (freeListHead = -1) then
+  begin
+    growItemArrayTo(Length(ggItems)+64);
+    if (freeListHead = -1) then raise Exception.Create('internal error in item manager');
+  end;
+
+  result := DWORD(freeListHead);
+  freeListHead := ggItems[freeListHead].nextFree;
+
+  if (Integer(result) > High(ggItems)) then raise Exception.Create('allocItem: freeid list corrupted');
+  if (ggItems[result].arrIdx <> Integer(result)) then raise Exception.Create('allocItem: arrIdx inconsistency');
 end;
 
 
 // it will be slow if the slot is free (we have to rebuild the heap)
 function wantItemSlot (slot: Integer): Integer;
 var
-  i, olen: Integer;
+  olen: Integer;
   it: PItem;
-  rebuildFreeList: Boolean = true;
+  rebuildList: Boolean = false;
 begin
   if (slot < 0) or (slot > $0fffffff) then raise Exception.Create('wantItemSlot: bad item slot request');
   // do we need to grow item storate?
   olen := Length(ggItems);
   if (slot >= olen) then
   begin
-    // need more spice!
-    SetLength(ggItems, slot+64);
-    // add free slots to free list
-    for i := olen to High(ggItems) do
-    begin
-      it := @ggItems[i];
-      //it.treeNode := -1;
-      it.slotIsUsed := false;
-      it.arrIdx := i;
-      it.ItemType := ITEM_NONE;
-      it.Animation := nil;
-      it.Live := false;
-      it.SpawnTrigger := -1;
-      it.Respawnable := false;
-      if (i <> slot) then freeIds.insert(i);
-    end;
-    rebuildFreeList := false;
+    growItemArrayTo(slot+64, false);
+    rebuildList := true;
   end;
 
   it := @ggItems[slot];
-  if {(it.treeNode = -1)} not it.slotIsUsed then
+  if rebuildList or (not it.slotIsUsed) then
   begin
-    // this is unused slot; get it, and rebuild id list
-    if rebuildFreeList then
-    begin
-      freeIds.clear();
-      for i := 0 to High(ggItems) do
-      begin
-        if (i <> slot) and {(ggItems[i].treeNode = -1)} (not it.slotIsUsed) then freeIds.insert(i);
-      end;
-    end;
-  end
-  else
-  begin
-    // it will be readded
-    //itemTree.removeObject(it.treeNode);
-    //it.treeNode := -1;
-    it.slotIsUsed := false;
+    rebuildFreeList(slot);
   end;
+  it.slotIsUsed := false;
 
   result := slot;
 end;
@@ -517,8 +459,8 @@ begin
     for i := 0 to High(ggItems) do ggItems[i].Animation.Free();
     ggItems := nil;
   end;
-  //if (itemTree <> nil) then itemTree.reset();
-  freeIds.clear();
+  //freeIds.clear();
+  freeListHead := -1;
 end;
 
 
@@ -531,13 +473,11 @@ var
 begin
   if ForcedID < 0 then find_id := allocItem() else find_id := wantItemSlot(ForcedID);
 
-  {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('allocated item #%d', [Integer(find_id)]), MSG_NOTIFY);{$ENDIF}
+  //{$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('allocated item #%d', [Integer(find_id)]), MSG_NOTIFY);{$ENDIF}
 
   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;
+  if (it.arrIdx <> Integer(find_id)) then raise Exception.Create('g_Items_Create: arrIdx inconsistency');
   //it.arrIdx := find_id;
   it.slotIsUsed := true;
 
@@ -758,19 +698,11 @@ procedure g_Items_Remove (ID: DWORD);
 var
   it: PItem;
   trig: Integer;
-{$IF DEFINED(D2F_DEBUG)}
-  //x, y: Integer;
-{$ENDIF}
 begin
   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');
-
-  {$IF DEFINED(D2F_DEBUG)}
-  //itemTree.getNodeXY(it.treeNode, x, y);
-  //e_WriteLog(Format('removing item #%d: updating tree; nodeid=%d; x=%d; y=%d (%d,%d)', [it.arrIdx, it.treeNode, x, y, it.Obj.X, it.Obj.Y]), MSG_NOTIFY);
-  {$ENDIF}
+  if (it.arrIdx <> Integer(ID)) then raise Exception.Create('g_Items_Remove: arrIdx desync');
 
   trig := it.SpawnTrigger;