DEADSOFTWARE

cosmetix
[d2df-sdl.git] / src / game / g_monsters.pas
index 4a20c4853a01e09aa08920b90785922338a355d9..55a02104f4a56e602de638d507b13490936100cf 100644 (file)
@@ -93,6 +93,7 @@ type
   public
     FNoRespawn: Boolean;
     FFireTime: Integer;
+    trapCheckFrameId: DWord; // for `g_weapons.CheckTrap()`
 
     constructor Create(MonsterType: Byte; aID: Integer; ForcedUID: Integer = -1);
     destructor Destroy(); override;
@@ -182,8 +183,6 @@ 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;
-
 // throws on invalid uid
 function g_Mons_ByIdx (uid: Integer): TMonster; inline;
 
@@ -192,8 +191,13 @@ 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;
 
 
 var
@@ -209,6 +213,11 @@ uses
   g_language, g_netmsg, z_aabbtree;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+var
+  monCheckTrapLastFrameId: DWord;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 type
   TDynAABBTreeMonsBase = specialize TDynAABBTreeBase<TMonster>;
@@ -484,6 +493,24 @@ begin
 end;
 
 
+function g_Mons_getNewTrapFrameId (): DWord;
+var
+  f: Integer;
+begin
+  Inc(monCheckTrapLastFrameId);
+  if monCheckTrapLastFrameId = 0 then
+  begin
+    // wraparound
+    monCheckTrapLastFrameId := 1;
+    for f := 0 to High(gMonsters) do
+    begin
+      if (gMonsters[f] <> nil) then gMonsters[f].trapCheckFrameId := 0;
+    end;
+  end;
+  result := monCheckTrapLastFrameId;
+end;
+
+
 var
   pt_x: Integer = 0;
   pt_xs: Integer = 1;
@@ -878,6 +905,7 @@ begin
 
   monsTree := TDynAABBTreeMons.Create();
   clearUidMap();
+  monCheckTrapLastFrameId := 0;
 end;
 
 procedure g_Monsters_FreeData();
@@ -1107,10 +1135,11 @@ procedure g_Monsters_Free();
 var
   a: Integer;
 begin
-  for a := 0 to High(gMonsters) do gMonsters[a].Free();
   monsTree.reset();
+  for a := 0 to High(gMonsters) do gMonsters[a].Free();
   gMonsters := nil;
   clearUidMap();
+  monCheckTrapLastFrameId := 0;
 end;
 
 function g_Monsters_Create(MonsterType: Byte; X, Y: Integer;
@@ -1625,6 +1654,7 @@ begin
 
   treeNode := -1;
   arrIdx := -1;
+  trapCheckFrameId := 0;
 
   if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then
     FBloodKind := BLOOD_SPARKS
@@ -1900,10 +1930,13 @@ begin
 
   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);
+    if monsTree.isValidId(treeNode) 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;
   end;
 
   if (arrIdx <> -1) then
@@ -2834,7 +2867,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),
@@ -2844,7 +2877,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,
@@ -2854,9 +2887,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;
 
     // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ:
@@ -3029,7 +3063,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;
@@ -4385,15 +4419,33 @@ 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]);
       if result then exit;
@@ -4402,19 +4454,22 @@ begin
 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(idx, gMonsters[idx]);
+      if result then exit;
+    end;
+  end;
 end;
 
 
@@ -4427,10 +4482,10 @@ function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
 
 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);
@@ -4439,9 +4494,10 @@ begin
   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;
@@ -4462,10 +4518,10 @@ function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonst
 
 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);
@@ -4474,11 +4530,12 @@ begin
   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(idx, mon);
           if result then exit;
         end;
       end;
@@ -4487,7 +4544,7 @@ 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
@@ -4497,10 +4554,10 @@ function g_Mons_ForEachAtAlive (x, y: Integer; width, height: Integer; cb: TEach
 
 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
@@ -4516,11 +4573,12 @@ begin
   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(idx, mon);
           if result then exit;
         end;
       end;