DEADSOFTWARE

no more global `gItems[]` array; created DynTree for items (not used yet); also,...
[d2df-sdl.git] / src / game / g_player.pas
index a6f07ac6f6e258522a8f867445f588e8efa9eab3..0ee9365f2adbee0cf6183a774a83500781d6ea23 100644 (file)
@@ -13,7 +13,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *)
-{$MODE DELPHI}
+{$INCLUDE ../shared/a_modes.inc}
 unit g_player;
 
 interface
@@ -260,7 +260,7 @@ type
     procedure   SetWeapon(W: Byte);
     function    IsKeyPressed(K: Byte): Boolean;
     function    GetKeys(): Byte;
-    function    PickItem(ItemType: Byte; respawn: Boolean; var remove: Boolean): Boolean; virtual;
+    function    PickItem(ItemType: Byte; arespawn: Boolean; var remove: Boolean): Boolean; virtual;
     function    Collide(X, Y: Integer; Width, Height: Word): Boolean; overload;
     function    Collide(Panel: TPanel): Boolean; overload;
     function    Collide(X, Y: Integer): Boolean; overload;
@@ -311,6 +311,9 @@ type
     procedure   JetpackOff;
     procedure   CatchFire(Attacker: Word);
 
+    //WARNING! this does nothing for now, but still call it!
+    procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
+
     property    Name: String read FName write FName;
     property    Model: TPlayerModel read FModel;
     property    Health: Integer read FHealth write FHealth;
@@ -376,9 +379,9 @@ type
     function    FullInStep(XInc, YInc: Integer): Boolean;
     //function    NeedItem(Item: Byte): Byte;
     procedure   SelectWeapon(Dist: Integer);
-    procedure   SetAIFlag(fName, fValue: String20);
-    function    GetAIFlag(fName: String20): String20;
-    procedure   RemoveAIFlag(fName: String20);
+    procedure   SetAIFlag(aName, fValue: String20);
+    function    GetAIFlag(aName: String20): String20;
+    procedure   RemoveAIFlag(aName: String20);
     function    Healthy(): Byte;
     procedure   UpdateMove();
     procedure   UpdateCombat();
@@ -406,8 +409,11 @@ type
     RAngle:   Integer;
     Color:    TRGB;
     Obj:      TObj;
+
+    procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
   end;
 
+
   TShell = record
     SpriteID: DWORD;
     Live:     Boolean;
@@ -416,6 +422,8 @@ type
     Timeout:  Cardinal;
     CX, CY:   Integer;
     Obj:      TObj;
+
+    procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
   end;
 
   TCorpse = class (TObject)
@@ -438,6 +446,8 @@ type
     procedure   SaveState(var Mem: TBinMemoryWriter);
     procedure   LoadState(var Mem: TBinMemoryReader);
 
+    procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
+
     property    Obj: TObj read FObj;
     property    State: Byte read FState;
     property    Mess: Boolean read FMess;
@@ -593,6 +603,11 @@ var
   BotNames: Array of String;
   BotList: Array of TBotProfile;
 
+
+procedure TGib.positionChanged (); begin end;
+procedure TShell.positionChanged (); begin end;
+
+
 function Lerp(X, Y, Factor: Integer): Integer;
 begin
   Result := X + ((Y - X) div Factor);
@@ -1521,6 +1536,7 @@ begin
     Obj.X := fX;
     Obj.Y := fY;
     g_Obj_Push(@Obj, dX + Random(4)-Random(4), dY-Random(4));
+    positionChanged(); // this updates spatial accelerators
     RAngle := Random(360);
     Timeout := gTime + SHELL_TIMEOUT;
 
@@ -1553,6 +1569,7 @@ begin
       Obj.X := fX-GibsArray[a].Rect.X-(GibsArray[a].Rect.Width div 2);
       Obj.Y := fY-GibsArray[a].Rect.Y-(GibsArray[a].Rect.Height div 2);
       g_Obj_PushA(@Obj, 25 + Random(10), Random(361));
+      positionChanged(); // this updates spatial accelerators
       RAngle := Random(360);
 
       if gBloodCount > 0 then
@@ -1592,6 +1609,7 @@ begin
         begin
           vel := Obj.Vel;
           mr := g_Obj_Move(@Obj, True, False, True);
+          positionChanged(); // this updates spatial accelerators
 
           if WordBool(mr and MOVE_FALLOUT) then
           begin
@@ -1641,6 +1659,7 @@ begin
         begin
           vel := Obj.Vel;
           mr := g_Obj_Move(@Obj, True, False, True);
+          positionChanged(); // this updates spatial accelerators
 
           if WordBool(mr and MOVE_FALLOUT) or (gShells[i].Timeout < gTime) then
           begin
@@ -1835,13 +1854,13 @@ end;
 
 procedure TPlayer.ChangeModel(ModelName: string);
 var
-  Model: TPlayerModel;
+  locModel: TPlayerModel;
 begin
-  Model := g_PlayerModel_Get(ModelName);
-  if Model = nil then Exit;
+  locModel := g_PlayerModel_Get(ModelName);
+  if locModel = nil then Exit;
 
   FModel.Free();
-  FModel := Model;
+  FModel := locModel;
 end;
 
 procedure TPlayer.SetModel(ModelName: string);
@@ -2008,6 +2027,10 @@ begin
   resetWeaponQueue();
 end;
 
+procedure TPlayer.positionChanged ();
+begin
+end;
+
 procedure TPlayer.Damage(value: Word; SpawnerUID: Word; vx, vy: Integer; t: Byte);
 var
   c: Word;
@@ -2640,7 +2663,7 @@ procedure TPlayer.Fire();
 var
   f, DidFire: Boolean;
   wx, wy, xd, yd: Integer;
-  obj: TObj;
+  locobj: TObj;
 begin
   if g_Game_IsClient then Exit;
 // FBFGFireCounter - âðåìÿ ïåðåä âûñòðåëîì (äëÿ BFG)
@@ -2668,18 +2691,18 @@ begin
       if R_BERSERK in FRulez then
       begin
         //g_Weapon_punch(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, 75, FUID);
-        obj.X := FObj.X+FObj.Rect.X;
-        obj.Y := FObj.Y+FObj.Rect.Y;
-        obj.rect.X := 0;
-        obj.rect.Y := 0;
-        obj.rect.Width := 39;
-        obj.rect.Height := 52;
-        obj.Vel.X := (xd-wx) div 2;
-        obj.Vel.Y := (yd-wy) div 2;
-        obj.Accel.X := xd-wx;
-        obj.Accel.y := yd-wy;
-
-        if g_Weapon_Hit(@obj, 50, FUID, HIT_SOME) <> 0 then
+        locobj.X := FObj.X+FObj.Rect.X;
+        locobj.Y := FObj.Y+FObj.Rect.Y;
+        locobj.rect.X := 0;
+        locobj.rect.Y := 0;
+        locobj.rect.Width := 39;
+        locobj.rect.Height := 52;
+        locobj.Vel.X := (xd-wx) div 2;
+        locobj.Vel.Y := (yd-wy) div 2;
+        locobj.Accel.X := xd-wx;
+        locobj.Accel.y := yd-wy;
+
+        if g_Weapon_Hit(@locobj, 50, FUID, HIT_SOME) <> 0 then
           g_Sound_PlayExAt('SOUND_WEAPON_HITBERSERK', FObj.X, FObj.Y)
         else
           g_Sound_PlayExAt('SOUND_WEAPON_MISSBERSERK', FObj.X, FObj.Y);
@@ -2884,8 +2907,6 @@ end;
 
 procedure TPlayer.CatchFire(Attacker: Word);
 begin
-  if FMegaRulez[MR_SUIT] >= gTime then
-    Exit;
   FFireTime := 100;
   FFireAttacker := Attacker;
   if g_Game_IsNet and g_Game_IsServer then
@@ -2952,22 +2973,34 @@ var
   DoFrags: Boolean;
   OldLR: Byte;
   KP: TPlayer;
+  it: PItem;
 
   procedure PushItem(t: Byte);
   var
     id: DWORD;
   begin
     id := g_Items_Create(FObj.X, FObj.Y, t, True, False);
+    it := g_ItemByIdx(id);
     if KillType = K_EXTRAHARDKILL then // -7..+7; -8..0
-      g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-7+Random(15),
-                                  (FObj.Vel.Y div 2)-Random(9))
+    begin
+      g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-7+Random(15),
+                          (FObj.Vel.Y div 2)-Random(9));
+      it.positionChanged(); // this updates spatial accelerators
+    end
     else
+    begin
       if KillType = K_HARDKILL then // -5..+5; -5..0
-        g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-5+Random(11),
-                                    (FObj.Vel.Y div 2)-Random(6))
+      begin
+        g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-5+Random(11),
+                            (FObj.Vel.Y div 2)-Random(6));
+      end
       else // -3..+3; -3..0
-        g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-3+Random(7),
-                                    (FObj.Vel.Y div 2)-Random(4));
+      begin
+        g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-3+Random(7),
+                            (FObj.Vel.Y div 2)-Random(4));
+      end;
+      it.positionChanged(); // this updates spatial accelerators
+    end;
 
     if g_Game_IsNet and g_Game_IsServer then
       MH_SEND_ItemSpawn(True, id);
@@ -3358,13 +3391,21 @@ var
 begin
   result := 255; // default result: "no switch"
   // had weapon cycling on previous frame? remove that flag
-  if (FNextWeap and $2000) <> 0 then begin FNextWeap := FNextWeap and $1FFF; FNextWeapDelay := 0; end;
+  if (FNextWeap and $2000) <> 0 then
+  begin
+    FNextWeap := FNextWeap and $1FFF;
+    FNextWeapDelay := 0;
+  end;
   // cycling has priority
   if (FNextWeap and $C000) <> 0 then
   begin
-    if (FNextWeap and $8000) <> 0 then dir := 1 else dir := -1;
+    if (FNextWeap and $8000) <> 0 then
+      dir := 1
+    else
+      dir := -1;
     FNextWeap := FNextWeap or $2000; // we need this
-    if FNextWeapDelay > 0 then exit; // cooldown time
+    if FNextWeapDelay > 0 then
+      exit; // cooldown time
     cwi := FCurrWeap;
     for i := 0 to High(FWeapon) do
     begin
@@ -3381,8 +3422,14 @@ begin
     exit;
   end;
   // no cycling
-  for i := 0 to High(wantThisWeapon) do wantThisWeapon[i] := false;
-  for i := 0 to High(FWeapon) do if (FNextWeap and (1 shl i)) <> 0 then begin wantThisWeapon[i] := true; Inc(wwc); end;
+  for i := 0 to High(wantThisWeapon) do
+    wantThisWeapon[i] := false;
+  for i := 0 to High(FWeapon) do
+    if (FNextWeap and (1 shl i)) <> 0 then
+    begin
+      wantThisWeapon[i] := true;
+      Inc(wwc);
+    end;
   // exclude currently selected weapon from the set
   wantThisWeapon[FCurrWeap] := false;
   // slow down alterations a little
@@ -3390,11 +3437,19 @@ begin
   begin
     //e_WriteLog(Format(' FNextWeap=%x; delay=%d', [FNextWeap, FNextWeapDelay]), MSG_WARNING);
     // more than one weapon requested, assume "alteration" and check alteration delay
-    if FNextWeapDelay > 0 then begin FNextWeap := 0; exit; end; // yeah
+    if FNextWeapDelay > 0 then
+    begin
+      FNextWeap := 0;
+      exit;
+    end; // yeah
   end;
   // do not reset weapon queue, it will be done in `RealizeCurrentWeapon()`
   // but clear all counters if no weapon should be switched
-  if wwc < 1 then begin resetWeaponQueue(); exit; end;
+  if wwc < 1 then
+  begin
+    resetWeaponQueue();
+    exit;
+  end;
   //e_WriteLog(Format('wwc=%d', [wwc]), MSG_WARNING);
   // try weapons in descending order
   for i := High(FWeapon) downto 0 do
@@ -3485,7 +3540,7 @@ begin
   resetWeaponQueue();
 end;
 
-function TPlayer.PickItem(ItemType: Byte; respawn: Boolean; var remove: Boolean): Boolean;
+function TPlayer.PickItem(ItemType: Byte; arespawn: Boolean; var remove: Boolean): Boolean;
 var
   a: Boolean;
 begin
@@ -3493,7 +3548,7 @@ begin
   if g_Game_IsClient then Exit;
 
   // a = true - ìåñòî ñïàâíà ïðåäìåòà:
-  a := LongBool(gGameSettings.Options and GAME_OPTION_WEAPONSTAY) and respawn;
+  a := LongBool(gGameSettings.Options and GAME_OPTION_WEAPONSTAY) and arespawn;
   remove := not a;
 
   case ItemType of
@@ -3559,7 +3614,7 @@ begin
       end;
 
     ITEM_WEAPON_SAW:
-      if (not FWeapon[WEAPON_SAW]) or ((not respawn) and (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) then
+      if (not FWeapon[WEAPON_SAW]) or ((not arespawn) and (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) then
       begin
         FWeapon[WEAPON_SAW] := True;
         Result := True;
@@ -4357,14 +4412,23 @@ begin
     b := Abs(FObj.Vel.X);
     if b > 1 then b := b * (Random(8 div b) + 1);
     for a := 0 to High(gGibs) do
+    begin
       if gGibs[a].Live and
          g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
                        FObj.Rect.Width, 8, @gGibs[a].Obj) and (Random(3) = 0) then
+      begin
         // Ïèíàåì êóñêè
         if FObj.Vel.X < 0 then
+        begin
           g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120) // íàëåâî
+        end
         else
+        begin
           g_Obj_PushA(@gGibs[a].Obj, b, Random(61));    // íàïðàâî
+        end;
+        gGibs[a].positionChanged(); // this updates spatial accelerators
+      end;
+    end;
   end;
 
   SetAction(A_WALK);
@@ -4591,7 +4655,10 @@ begin
     end;
 
     if FPhysics then
+    begin
       g_Obj_Move(@FObj, True, True, True);
+      positionChanged(); // this updates spatial accelerators
+    end;
 
     Exit;
   end;
@@ -4714,7 +4781,10 @@ begin
   end;
 
   if FPhysics then
-    g_Obj_Move(@FObj, True, True, True)
+  begin
+    g_Obj_Move(@FObj, True, True, True);
+    positionChanged(); // this updates spatial accelerators
+  end
   else
   begin
     FObj.Vel.X := 0;
@@ -4850,6 +4920,12 @@ begin
         FFireTime := 0;
         FFirePainTime := 0;
       end
+      else if FMegaRulez[MR_SUIT] >= gTime then
+      begin
+        if FMegaRulez[MR_SUIT] = gTime then
+          FFireTime := 1;
+        FFirePainTime := 0;
+      end
       else
       begin
         OnFireFlame(1);
@@ -5016,7 +5092,7 @@ end;
 
 procedure TPlayer.NetFire(Wpn: Byte; X, Y, AX, AY: Integer; WID: Integer = -1);
 var
-  Obj: TObj;
+  locObj: TObj;
   F: Boolean;
   WX, WY, XD, YD: Integer;
 begin
@@ -5032,18 +5108,18 @@ begin
       if R_BERSERK in FRulez then
       begin
         //g_Weapon_punch(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, 75, FUID);
-        obj.X := FObj.X+FObj.Rect.X;
-        obj.Y := FObj.Y+FObj.Rect.Y;
-        obj.rect.X := 0;
-        obj.rect.Y := 0;
-        obj.rect.Width := 39;
-        obj.rect.Height := 52;
-        obj.Vel.X := (xd-wx) div 2;
-        obj.Vel.Y := (yd-wy) div 2;
-        obj.Accel.X := xd-wx;
-        obj.Accel.y := yd-wy;
-
-        if g_Weapon_Hit(@obj, 50, FUID, HIT_SOME) <> 0 then
+        locobj.X := FObj.X+FObj.Rect.X;
+        locobj.Y := FObj.Y+FObj.Rect.Y;
+        locobj.rect.X := 0;
+        locobj.rect.Y := 0;
+        locobj.rect.Width := 39;
+        locobj.rect.Height := 52;
+        locobj.Vel.X := (xd-wx) div 2;
+        locobj.Vel.Y := (yd-wy) div 2;
+        locobj.Accel.X := xd-wx;
+        locobj.Accel.y := yd-wy;
+
+        if g_Weapon_Hit(@locobj, 50, FUID, HIT_SOME) <> 0 then
           g_Sound_PlayExAt('SOUND_WEAPON_HITBERSERK', FObj.X, FObj.Y)
         else
           g_Sound_PlayExAt('SOUND_WEAPON_MISSBERSERK', FObj.X, FObj.Y);
@@ -5316,6 +5392,7 @@ begin
     Count := FLAG_TIME;
     g_Obj_Push(@Obj, (FObj.Vel.X div 2)-2+Random(5),
                      (FObj.Vel.Y div 2)-2+Random(5));
+    positionChanged(); // this updates spatial accelerators
 
     if FFlag = FLAG_RED then
       s := _lc[I_PLAYER_FLAG_RED]
@@ -5966,6 +6043,8 @@ begin
   inherited;
 end;
 
+procedure TCorpse.positionChanged (); begin end;
+
 procedure TCorpse.Damage(Value: Word; vx, vy: Integer);
 var
   pm: TPlayerModel;
@@ -6032,7 +6111,7 @@ begin
   if gTime mod (GAME_TICK*2) <> 0 then
   begin
     g_Obj_Move(@FObj, True, True, True);
-
+    positionChanged(); // this updates spatial accelerators
     Exit;
   end;
 
@@ -6040,6 +6119,7 @@ begin
   FObj.Vel.X := z_dec(FObj.Vel.X, 1);
 
   st := g_Obj_Move(@FObj, True, True, True);
+  positionChanged(); // this updates spatial accelerators
 
   if WordBool(st and MOVE_FALLOUT) then
   begin
@@ -6220,7 +6300,7 @@ var
   firew, fireh: Integer;
   angle: SmallInt;
   mon: TMonster;
-  pla: TPlayer;
+  pla, tpla: TPlayer;
   vsPlayer, vsMonster, ok: Boolean;
 begin
   vsPlayer := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER);
@@ -6254,14 +6334,16 @@ begin
       if (g_GetUIDType(Target.UID) = UID_PLAYER) and
           vsPlayer then
         begin // Èãðîê
-          with g_Player_Get(Target.UID) do
-            begin
-              if (@FObj) <> nil then
+          tpla := g_Player_Get(Target.UID);
+          if tpla <> nil then
+            with tpla do
               begin
-                Target.X := FObj.X;
-                Target.Y := FObj.Y;
+                if (@FObj) <> nil then
+                begin
+                  Target.X := FObj.X;
+                  Target.Y := FObj.Y;
+                end;
               end;
-            end;
 
           Target.cX := Target.X + PLAYER_RECT_CX;
           Target.cY := Target.Y + PLAYER_RECT_CY;
@@ -6731,33 +6813,33 @@ begin
   Result := FKeys[Key].Pressed;
 end;
 
-function TBot.GetAIFlag(fName: String20): String20;
+function TBot.GetAIFlag(aName: String20): String20;
 var
   a: Integer;
 begin
   Result := '';
 
-  fName := LowerCase(fName);
+  aName := LowerCase(aName);
 
   if FAIFlags <> nil then
     for a := 0 to High(FAIFlags) do
-      if LowerCase(FAIFlags[a].Name) = fName then
+      if LowerCase(FAIFlags[a].Name) = aName then
       begin
         Result := FAIFlags[a].Value;
         Break;
       end;
 end;
 
-procedure TBot.RemoveAIFlag(fName: String20);
+procedure TBot.RemoveAIFlag(aName: String20);
 var
   a, b: Integer;
 begin
   if FAIFlags = nil then Exit;
 
-  fName := LowerCase(fName);
+  aName := LowerCase(aName);
 
   for a := 0 to High(FAIFlags) do
-    if LowerCase(FAIFlags[a].Name) = fName then
+    if LowerCase(FAIFlags[a].Name) = aName then
     begin
       if a <> High(FAIFlags) then
         for b := a to High(FAIFlags)-1 do
@@ -6768,7 +6850,7 @@ begin
     end;
 end;
 
-procedure TBot.SetAIFlag(fName, fValue: String20);
+procedure TBot.SetAIFlag(aName, fValue: String20);
 var
   a: Integer;
   ok: Boolean;
@@ -6776,11 +6858,11 @@ begin
   a := 0;
   ok := False;
 
-  fName := LowerCase(fName);
+  aName := LowerCase(aName);
 
   if FAIFlags <> nil then
     for a := 0 to High(FAIFlags) do
-      if LowerCase(FAIFlags[a].Name) = fName then
+      if LowerCase(FAIFlags[a].Name) = aName then
       begin
         ok := True;
         Break;
@@ -6792,7 +6874,7 @@ begin
     SetLength(FAIFlags, Length(FAIFlags)+1);
     with FAIFlags[High(FAIFlags)] do
     begin
-      Name := fName;
+      Name := aName;
       Value := fValue;
     end;
   end;