DEADSOFTWARE

center player when the game is scaled (lighting is not working correctly yet, tho)
[d2df-sdl.git] / src / game / g_items.pas
index 27dbc75bf19abf1ced60905a43afd6212a3ac526..6365d7bbe2bee8fe88233355921860a0dec509fa 100644 (file)
@@ -34,7 +34,7 @@ Type
     Respawnable:   Boolean;
     InitX, InitY:  Integer;
     RespawnTime:   Word;
-    Live:          Boolean;
+    alive:         Boolean;
     Fall:          Boolean;
     QuietRespawn:  Boolean;
     SpawnTrigger:  Integer;
@@ -68,6 +68,8 @@ function g_Items_ObjByIdx (idx: Integer): PObj;
 procedure g_Items_EmitPickupSound (idx: Integer); // at item position
 procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
 
+procedure g_Items_AddDynLights();
+
 
 type
   TItemEachAliveCB = function (it: PItem): Boolean is nested; // return `true` to stop
@@ -77,7 +79,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,7 +88,7 @@ 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, binheap;
+  g_grid, binheap, idpool;
 
 
 var
@@ -95,7 +97,7 @@ var
 
 // ////////////////////////////////////////////////////////////////////////// //
 var
-  freeIds: TBinaryHeapInt = nil; // free item ids
+  freeIds: TIdPool = nil;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
@@ -263,7 +265,7 @@ begin
 
   InitTextures();
 
-  freeIds := binHeapNewIntLess();
+  freeIds := TIdPool.Create();
 end;
 
 
@@ -321,6 +323,7 @@ begin
   g_Texture_Delete('ITEM_JETPACK');
 
   freeIds.Free();
+  freeIds := nil;
 end;
 
 
@@ -329,8 +332,9 @@ var
   it: PItem;
 begin
   if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('releaseItem: invalid item id');
+  if not freeIds.hasAlloced[LongWord(idx)] then raise Exception.Create('releaseItem: trying to release unallocated item (0)');
   it := @ggItems[idx];
-  if not it.slotIsUsed then raise Exception.Create('releaseItem: trying to release unallocated item');
+  if not it.slotIsUsed then raise Exception.Create('releaseItem: trying to release unallocated item (1)');
   if (it.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency');
   it.slotIsUsed := false;
   if (it.Animation <> nil) then
@@ -338,40 +342,41 @@ begin
     it.Animation.Free();
     it.Animation := nil;
   end;
-  it.Live := False;
+  it.alive := False;
   it.SpawnTrigger := -1;
   it.ItemType := ITEM_NONE;
-  freeIds.insert(it.arrIdx);
+  freeIds.release(LongWord(idx));
 end;
 
 
-function allocItem (): DWORD;
+procedure growItemArrayTo (newsz: Integer);
 var
   i, olen: Integer;
   it: PItem;
 begin
-  if (freeIds.count = 0) then
+  if (newsz < Length(ggItems)) then exit;
+  // no free slots
+  olen := Length(ggItems);
+  SetLength(ggItems, newsz);
+  for i := olen to High(ggItems) do
   begin
-    // no free slots
-    olen := Length(ggItems);
-    SetLength(ggItems, olen+64);
-    for i := olen to High(ggItems) do
-    begin
-      it := @ggItems[i];
-      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);
-    end;
+    it := @ggItems[i];
+    it.slotIsUsed := false;
+    it.arrIdx := i;
+    it.ItemType := ITEM_NONE;
+    it.Animation := nil;
+    it.alive := false;
+    it.SpawnTrigger := -1;
+    it.Respawnable := false;
+    //if not freeIds.hasFree[LongWord(i)] then raise Exception.Create('internal error in item idx manager');
   end;
+end;
 
-  result := freeIds.front;
-  freeIds.popFront();
 
+function allocItem (): DWORD;
+begin
+  result := freeIds.alloc();
+  if (result >= Length(ggItems)) then growItemArrayTo(Integer(result)+64);
   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;
@@ -380,51 +385,24 @@ 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;
 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.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;
-  end;
+  if (slot >= olen) then growItemArrayTo(slot+64);
 
   it := @ggItems[slot];
   if 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 (not it.slotIsUsed) then freeIds.insert(i);
-      end;
-    end;
+    freeIds.alloc(LongWord(slot));
   end
   else
   begin
-    // it will be readded
-    it.slotIsUsed := false;
+    if not freeIds.hasAlloced[slot] then raise Exception.Create('wantItemSlot: internal error in item idx manager');
   end;
+  it.slotIsUsed := false;
 
   result := slot;
 end;
@@ -463,7 +441,7 @@ 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];
 
@@ -478,7 +456,7 @@ begin
   it.InitY := Y;
   it.RespawnTime := 0;
   it.Fall := Fall;
-  it.Live := True;
+  it.alive := True;
   it.QuietRespawn := False;
 
   g_Obj_Init(@it.Obj);
@@ -538,7 +516,7 @@ begin
     begin
       nxt := False;
 
-      if Live then
+      if alive then
       begin
         if Fall then
         begin
@@ -574,7 +552,7 @@ begin
             Inc(j);
             if j > High(gPlayers) then j := 0;
 
-            if (gPlayers[j] <> nil) and gPlayers[j].Live and g_Obj_Collide(@gPlayers[j].Obj, @Obj) then
+            if (gPlayers[j] <> nil) and gPlayers[j].alive and g_Obj_Collide(@gPlayers[j].Obj, @Obj) then
             begin
               if g_Game_IsClient then continue;
 
@@ -608,7 +586,7 @@ begin
       if Respawnable and g_Game_IsServer then
       begin
         DecMin(RespawnTime, 0);
-        if (RespawnTime = 0) and (not Live) then
+        if (RespawnTime = 0) and (not alive) then
         begin
           if not QuietRespawn then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', InitX, InitY);
 
@@ -627,7 +605,7 @@ begin
           Obj.Accel.Y := 0;
           positionChanged(); // this updates spatial accelerators
 
-          Live := true;
+          alive := true;
 
           if g_Game_IsNet then MH_SEND_ItemSpawn(QuietRespawn, i);
           QuietRespawn := false;
@@ -648,7 +626,7 @@ begin
 
   for i := 0 to High(ggItems) do
   begin
-    if not ggItems[i].Live then continue;
+    if not ggItems[i].alive then continue;
 
     with ggItems[i] do
     begin
@@ -679,7 +657,7 @@ end;
 
 procedure g_Items_Pick (ID: DWORD);
 begin
-  ggItems[ID].Live := false;
+  ggItems[ID].alive := false;
   ggItems[ID].RespawnTime := ITEM_RESPAWNTIME;
 end;
 
@@ -738,7 +716,7 @@ begin
       // Âðåìÿ äî ðåñïàóíà
       Mem.WriteWord(ggItems[i].RespawnTime);
       // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
-      Mem.WriteBoolean(ggItems[i].Live);
+      Mem.WriteBoolean(ggItems[i].alive);
       // Ìîæåò ëè îí ïàäàòü
       Mem.WriteBoolean(ggItems[i].Fall);
       // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
@@ -782,7 +760,7 @@ begin
     // Âðåìÿ äî ðåñïàóíà
     Mem.ReadWord(ggItems[i].RespawnTime);
     // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
-    Mem.ReadBoolean(ggItems[i].Live);
+    Mem.ReadBoolean(ggItems[i].alive);
     // Ìîæåò ëè îí ïàäàòü
     Mem.ReadBoolean(ggItems[i].Fall);
     // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
@@ -826,7 +804,7 @@ begin
   begin
     for idx := High(ggItems) downto 0 do
     begin
-      if ggItems[idx].Live then
+      if ggItems[idx].alive then
       begin
         result := cb(@ggItems[idx]);
         if result then exit;
@@ -837,7 +815,7 @@ begin
   begin
     for idx := 0 to High(ggItems) do
     begin
-      if ggItems[idx].Live then
+      if ggItems[idx].alive then
       begin
         result := cb(@ggItems[idx]);
         if result then exit;
@@ -903,4 +881,31 @@ begin
   end;
 end;
 
+
+procedure g_Items_AddDynLights();
+var
+  f: Integer;
+  it: PItem;
+begin
+  for f := 0 to High(ggItems) do
+  begin
+    it := @ggItems[f];
+    if not it.alive then continue;
+    case it.ItemType of
+      ITEM_KEY_RED: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24,  1.0, 0.0, 0.0, 0.6);
+      ITEM_KEY_GREEN: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24,  0.0, 1.0, 0.0, 0.6);
+      ITEM_KEY_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24,  0.0, 0.0, 1.0, 0.6);
+      ITEM_ARMOR_GREEN: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 42,  0.0, 1.0, 0.0, 0.6);
+      ITEM_ARMOR_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 42,  0.0, 0.0, 1.0, 0.6);
+      ITEM_SPHERE_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32,  0.0, 1.0, 0.0, 0.6);
+      ITEM_SPHERE_WHITE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32,  1.0, 1.0, 1.0, 0.6);
+      ITEM_INVUL: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32,  1.0, 0.0, 0.0, 0.6);
+      ITEM_INVIS: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32,  1.0, 1.0, 0.0, 0.6);
+      ITEM_BOTTLE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 16,  0.0, 0.0, 0.8, 0.6);
+      ITEM_HELMET: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 16,  0.0, 0.8, 0.0, 0.6);
+    end;
+  end;
+end;
+
+
 end.