DEADSOFTWARE

removed all mentions of dynaabb tree from the sources; WARNING! EVERYTHING IS BROKEN!
[d2df-sdl.git] / src / game / g_player.pas
index 1d036616a4884835132d9bde3643baae1f5f80c8..45f9e6b5d80369ca2c9ea62ec458404871947ad1 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
@@ -50,6 +50,12 @@ const
   A_SHELLS          = 1;
   A_ROCKETS         = 2;
   A_CELLS           = 3;
+  A_FUEL            = 4;
+  A_HIGH            = 4;
+
+  AmmoLimits: Array [0..1] of Array [A_BULLETS..A_HIGH] of Word =
+  ((200,  50,  50, 300, 100),
+   (400, 100, 100, 600, 200));
 
   K_SIMPLEKILL      = 0;
   K_HARDKILL        = 1;
@@ -114,9 +120,9 @@ type
     CurrWeap:   Byte;
     NextWeap:   WORD;
     NextWeapDelay: Byte;
-    Ammo:       Array [A_BULLETS..A_CELLS] of Word;
-    MaxAmmo:    Array [A_BULLETS..A_CELLS] of Word;
-    Weapon:     Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Boolean;
+    Ammo:       Array [A_BULLETS..A_HIGH] of Word;
+    MaxAmmo:    Array [A_BULLETS..A_HIGH] of Word;
+    Weapon:     Array [WP_FIRST..WP_LAST] of Boolean;
     Rulez:      Set of R_ITEM_BACKPACK..R_BERSERK;
     WaitRecall: Boolean;
   end;
@@ -161,6 +167,8 @@ type
     FObj:       TObj;
     FXTo, FYTo: Integer;
     FSpectatePlayer: Integer;
+    FFirePainTime:   Integer;
+    FFireAttacker:   Word;
 
     FSavedState: TPlayerSavedState;
 
@@ -195,6 +203,7 @@ type
     function    FullInLift(XInc, YInc: Integer): Integer;
     {procedure   CollideItem();}
     procedure   FlySmoke(Times: DWORD = 1);
+    procedure   OnFireFlame(Times: DWORD = 1);
     function    GetAmmoByWeapon(Weapon: Byte): Word;
     procedure   SetAction(Action: Byte; Force: Boolean = False);
     procedure   OnDamage(Angle: SmallInt); virtual;
@@ -216,13 +225,13 @@ type
   public
     FDamageBuffer:   Integer;
 
-    FAmmo:      Array [A_BULLETS..A_CELLS] of Word;
-    FMaxAmmo:   Array [A_BULLETS..A_CELLS] of Word;
-    FWeapon:    Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Boolean;
+    FAmmo:      Array [A_BULLETS..A_HIGH] of Word;
+    FMaxAmmo:   Array [A_BULLETS..A_HIGH] of Word;
+    FWeapon:    Array [WP_FIRST..WP_LAST] of Boolean;
     FRulez:     Set of R_ITEM_BACKPACK..R_BERSERK;
     FBerserk:   Integer;
     FMegaRulez: Array [MR_SUIT..MR_MAX] of DWORD;
-    FReloading: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Word;
+    FReloading: Array [WP_FIRST..WP_LAST] of Word;
     FTime:      Array [T_RESPAWN..T_FLAGCAP] of DWORD;
     FKeys:      Array [KEY_LEFT..KEY_CHAT] of TKeyState;
     FColor:     TRGB;
@@ -238,6 +247,7 @@ type
     FPing:      Word;
     FLoss:      Byte;
     FDummy:     Boolean;
+    FFireTime:  Integer;
 
     constructor Create(); virtual;
     destructor  Destroy(); override;
@@ -250,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;
@@ -299,6 +309,12 @@ type
     procedure   RealizeCurrentWeapon();
     procedure   JetpackOn;
     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!
+
+    procedure getMapBox (out x, y, w, h: Integer); inline;
 
     property    Name: String read FName write FName;
     property    Model: TPlayerModel read FModel;
@@ -341,9 +357,9 @@ type
     FlyPrecision: Byte;
     Cover: Byte;
     CloseJump: Byte;
-    WeaponPrior: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
-    CloseWeaponPrior: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
-    //SafeWeaponPrior: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
+    WeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
+    CloseWeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
+    //SafeWeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
   end;
 
   TAIFlag = record
@@ -365,9 +381,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();
@@ -395,8 +411,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;
@@ -405,6 +424,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)
@@ -427,6 +448,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;
@@ -511,9 +534,9 @@ type
     fly_precision: Byte;
     cover: Byte;
     close_jump: Byte;
-    w_prior1: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
-    w_prior2: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
-    w_prior3: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
+    w_prior1: Array [WP_FIRST..WP_LAST] of Byte;
+    w_prior2: Array [WP_FIRST..WP_LAST] of Byte;
+    w_prior3: Array [WP_FIRST..WP_LAST] of Byte;
   end;
 
 const
@@ -542,27 +565,30 @@ const
                                                    (R:0; G:0; B:255));
   DIFFICULT_EASY: TDifficult = (DiagFire: 32; InvisFire: 32; DiagPrecision: 32;
                                 FlyPrecision: 32; Cover: 32; CloseJump: 32;
-                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0));
+                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0,0));
   DIFFICULT_MEDIUM: TDifficult = (DiagFire: 127; InvisFire: 127; DiagPrecision: 127;
                                   FlyPrecision: 127; Cover: 127; CloseJump: 127;
-                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0));
+                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0,0));
   DIFFICULT_HARD: TDifficult = (DiagFire: 255; InvisFire: 255; DiagPrecision: 255;
                                 FlyPrecision: 255; Cover: 255; CloseJump: 255;
-                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0));
-  WEAPON_PRIOR1: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte =
-                                (WEAPON_SUPERPULEMET, WEAPON_SHOTGUN2, WEAPON_SHOTGUN1,
+                                WeaponPrior:(0,0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0,0));
+  WEAPON_PRIOR1: Array [WP_FIRST..WP_LAST] of Byte =
+                                (WEAPON_FLAMETHROWER, WEAPON_SUPERPULEMET,
+                                 WEAPON_SHOTGUN2, WEAPON_SHOTGUN1,
                                  WEAPON_CHAINGUN, WEAPON_PLASMA, WEAPON_ROCKETLAUNCHER,
                                  WEAPON_BFG, WEAPON_PISTOL, WEAPON_SAW, WEAPON_KASTET);
-  WEAPON_PRIOR2: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte =
-                                (WEAPON_SUPERPULEMET, WEAPON_BFG, WEAPON_ROCKETLAUNCHER,
+  WEAPON_PRIOR2: Array [WP_FIRST..WP_LAST] of Byte =
+                                (WEAPON_FLAMETHROWER, WEAPON_SUPERPULEMET,
+                                 WEAPON_BFG, WEAPON_ROCKETLAUNCHER,
                                  WEAPON_SHOTGUN2, WEAPON_PLASMA, WEAPON_SHOTGUN1,
                                  WEAPON_CHAINGUN, WEAPON_PISTOL, WEAPON_SAW, WEAPON_KASTET);
-  //WEAPON_PRIOR3: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte =
-  //                              (WEAPON_SUPERPULEMET, WEAPON_BFG, WEAPON_PLASMA,
-  //                               WEAPON_SHOTGUN2, WEAPON_CHAINGUN, WEAPON_SHOTGUN1,
-  //                               WEAPON_SAW, WEAPON_ROCKETLAUNCHER, WEAPON_PISTOL, WEAPON_KASTET);
-  WEAPON_RELOAD: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte =
-                                (5, 2, 6, 18, 36, 2, 12, 2, 14, 2);
+  //WEAPON_PRIOR3: Array [WP_FIRST..WP_LAST] of Byte =
+  //                              (WEAPON_FLAMETHROWER, WEAPON_SUPERPULEMET,
+  //                               WEAPON_BFG, WEAPON_PLASMA, WEAPON_SHOTGUN2,
+  //                               WEAPON_CHAINGUN, WEAPON_SHOTGUN1, WEAPON_SAW,
+  //                               WEAPON_ROCKETLAUNCHER, WEAPON_PISTOL, WEAPON_KASTET);
+  WEAPON_RELOAD: Array [WP_FIRST..WP_LAST] of Byte =
+                                (5, 2, 6, 18, 36, 2, 12, 2, 14, 2, 2);
 
   PLAYER_SIGNATURE = $52594C50; // 'PLYR'
   CORPSE_SIGNATURE = $50524F43; // 'CORP'
@@ -579,6 +605,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);
@@ -822,16 +853,16 @@ begin
 // Îáúåêò èãðîêà:
   Obj_LoadState(@gPlayers[a].FObj, Mem);
 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.ReadWord(gPlayers[a].FAmmo[i]);
 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.ReadWord(gPlayers[a].FMaxAmmo[i]);
 // Íàëè÷èå îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.ReadBoolean(gPlayers[a].FWeapon[i]);
 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.ReadWord(gPlayers[a].FReloading[i]);
 // Íàëè÷èå ðþêçàêà:
   Mem.ReadByte(b);
@@ -997,7 +1028,7 @@ begin
       else FDifficult := DIFFICULT_HARD;
     end;
 
-    for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for a := WP_FIRST to WP_LAST do
     begin
       FDifficult.WeaponPrior[a] := WEAPON_PRIOR1[a];
       FDifficult.CloseWeaponPrior[a] := WEAPON_PRIOR2[a];
@@ -1075,7 +1106,7 @@ begin
     FDifficult.Cover := BotList[num].cover;
     FDifficult.CloseJump := BotList[num].close_jump;
 
-    for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for a := WP_FIRST to WP_LAST do
     begin
       FDifficult.WeaponPrior[a] := BotList[num].w_prior1[a];
       FDifficult.CloseWeaponPrior[a] := BotList[num].w_prior2[a];
@@ -1507,6 +1538,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;
 
@@ -1539,6 +1571,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
@@ -1578,6 +1611,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
@@ -1627,6 +1661,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
@@ -1821,13 +1856,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);
@@ -1978,6 +2013,9 @@ begin
   FLoss := 0;
   FSavedState.WaitRecall := False;
   FShellTimer := -1;
+  FFireTime := 0;
+  FFirePainTime := 0;
+  FFireAttacker := 0;
 
   FActualModelName := 'doomer';
 
@@ -1991,6 +2029,10 @@ begin
   resetWeaponQueue();
 end;
 
+procedure TPlayer.positionChanged ();
+begin
+end;
+
 procedure TPlayer.Damage(value: Word; SpawnerUID: Word; vx, vy: Integer; t: Byte);
 var
   c: Word;
@@ -2487,6 +2529,7 @@ begin
     WEAPON_ROCKETLAUNCHER: ID := gItemsTexturesID[ITEM_WEAPON_ROCKETLAUNCHER];
     WEAPON_PLASMA: ID := gItemsTexturesID[ITEM_WEAPON_PLASMA];
     WEAPON_BFG: ID := gItemsTexturesID[ITEM_WEAPON_BFG];
+    WEAPON_FLAMETHROWER: ID := gItemsTexturesID[ITEM_WEAPON_FLAMETHROWER];
   end;
 
   e_CharFont_GetSize(gMenuFont, s, tw, th);
@@ -2622,7 +2665,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)
@@ -2650,18 +2693,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);
@@ -2794,6 +2837,17 @@ begin
         g_Player_CreateShell(GameX+PLAYER_RECT_CX, GameY+PLAYER_RECT_CX,
                              GameVelX, GameVelY-2, SHELL_SHELL);
       end;
+
+    WEAPON_FLAMETHROWER:
+      if FAmmo[A_FUEL] > 0 then
+      begin
+        g_Weapon_flame(wx, wy, xd, yd, FUID);
+        FReloading[FCurrWeap] := WEAPON_RELOAD[FCurrWeap];
+        Dec(FAmmo[A_FUEL]);
+        FFireAngle := FAngle;
+        f := True;
+        DidFire := True;
+      end;
   end;
 
   if g_Game_IsNet then
@@ -2824,6 +2878,7 @@ begin
     WEAPON_SHOTGUN1, WEAPON_SHOTGUN2, WEAPON_SUPERPULEMET: Result := FAmmo[A_SHELLS];
     WEAPON_ROCKETLAUNCHER: Result := FAmmo[A_ROCKETS];
     WEAPON_PLASMA, WEAPON_BFG: Result := FAmmo[A_CELLS];
+    WEAPON_FLAMETHROWER: Result := FAmmo[A_FUEL];
     else Result := 0;
   end;
 end;
@@ -2852,6 +2907,14 @@ begin
   FJetSoundOff.PlayAt(FObj.X, FObj.Y);
 end;
 
+procedure TPlayer.CatchFire(Attacker: Word);
+begin
+  FFireTime := 100;
+  FFireAttacker := Attacker;
+  if g_Game_IsNet and g_Game_IsServer then
+    MH_SEND_PlayerStats(FUID);
+end;
+
 procedure TPlayer.Jump();
 begin
   if gFly or FJetpack then
@@ -2912,22 +2975,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_Items_ByIdx(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);
@@ -3048,7 +3123,7 @@ begin
       end
     else if g_GetUIDType(SpawnerUID) = UID_MONSTER then
       begin // Óáèò ìîíñòðîì
-        mon := g_Monsters_Get(SpawnerUID);
+        mon := g_Monsters_ByUID(SpawnerUID);
         if mon = nil then
           s := '?'
         else
@@ -3083,7 +3158,7 @@ begin
   if Srv then
   begin
 // Âûáðîñ îðóæèÿ:
-    for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for a := WP_FIRST to WP_LAST do
       if FWeapon[a] then
       begin
         case a of
@@ -3095,6 +3170,7 @@ begin
           WEAPON_PLASMA: i := ITEM_WEAPON_PLASMA;
           WEAPON_BFG: i := ITEM_WEAPON_BFG;
           WEAPON_SUPERPULEMET: i := ITEM_WEAPON_SUPERPULEMET;
+          WEAPON_FLAMETHROWER: i := ITEM_WEAPON_FLAMETHROWER;
           else i := 0;
         end;
 
@@ -3302,6 +3378,7 @@ begin
     WEAPON_PISTOL, WEAPON_CHAINGUN, WEAPON_SUPERPULEMET: result := (FAmmo[A_BULLETS] > 0);
     WEAPON_ROCKETLAUNCHER: result := (FAmmo[A_ROCKETS] > 0);
     WEAPON_PLASMA, WEAPON_BFG: result := (FAmmo[A_CELLS] > 0);
+    WEAPON_FLAMETHROWER: result := (FAmmo[A_FUEL] > 0);
     else result := (weapon < length(FWeapon));
   end;
 end;
@@ -3316,13 +3393,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
@@ -3339,8 +3424,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
@@ -3348,11 +3439,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
@@ -3376,9 +3475,13 @@ procedure TPlayer.RealizeCurrentWeapon();
     i: Byte;
   begin
     result := false;
-    if FBFGFireCounter <> -1 then exit;
-    if FTime[T_SWITCH] > gTime then exit;
-    for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do if FReloading[i] > 0 then exit;
+    if FBFGFireCounter <> -1 then
+      exit;
+    if FTime[T_SWITCH] > gTime then
+      exit;
+    for i := WP_FIRST to WP_LAST do
+      if FReloading[i] > 0 then
+        exit;
     result := true;
   end;
 
@@ -3439,7 +3542,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
@@ -3447,7 +3550,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
@@ -3457,6 +3560,7 @@ begin
         IncMax(FHealth, 10, PLAYER_HP_SOFT);
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
@@ -3466,6 +3570,7 @@ begin
         IncMax(FHealth, 25, PLAYER_HP_SOFT);
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
@@ -3493,6 +3598,7 @@ begin
         IncMax(FHealth, 100, PLAYER_HP_LIMIT);
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
@@ -3505,11 +3611,12 @@ begin
           FArmor := PLAYER_AP_LIMIT;
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       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;
@@ -3602,6 +3709,18 @@ begin
         if a and g_Game_IsNet then MH_SEND_Sound(GameX, GameY, 'SOUND_ITEM_GETWEAPON');
       end;
 
+    ITEM_WEAPON_FLAMETHROWER:
+      if (FAmmo[A_FUEL] < FMaxAmmo[A_FUEL]) or not FWeapon[WEAPON_FLAMETHROWER] then
+      begin
+        if a and FWeapon[WEAPON_FLAMETHROWER] then Exit;
+
+        IncMax(FAmmo[A_FUEL], 100, FMaxAmmo[A_FUEL]);
+        FWeapon[WEAPON_FLAMETHROWER] := True;
+        Result := True;
+        if gFlash = 2 then Inc(FPickup, 5);
+        if a and g_Game_IsNet then MH_SEND_Sound(GameX, GameY, 'SOUND_ITEM_GETWEAPON');
+      end;
+
     ITEM_AMMO_BULLETS:
       if FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS] then
       begin
@@ -3674,17 +3793,28 @@ begin
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
+    ITEM_AMMO_FUELCAN:
+      if FAmmo[A_FUEL] < FMaxAmmo[A_FUEL] then
+      begin
+        IncMax(FAmmo[A_FUEL], 100, FMaxAmmo[A_FUEL]);
+        Result := True;
+        remove := True;
+        if gFlash = 2 then Inc(FPickup, 5);
+      end;
+
     ITEM_AMMO_BACKPACK:
       if not(R_ITEM_BACKPACK in FRulez) or
             (FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS]) or
             (FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS]) or
             (FAmmo[A_ROCKETS] < FMaxAmmo[A_ROCKETS]) or
-            (FAmmo[A_CELLS] < FMaxAmmo[A_CELLS]) then
+            (FAmmo[A_CELLS] < FMaxAmmo[A_CELLS]) or
+            (FMaxAmmo[A_FUEL] < AmmoLimits[1, A_FUEL]) then
       begin
-        FMaxAmmo[A_BULLETS] := 400;
-        FMaxAmmo[A_SHELLS] := 100;
-        FMaxAmmo[A_ROCKETS] := 100;
-        FMaxAmmo[A_CELLS] := 600;
+        FMaxAmmo[A_BULLETS] := AmmoLimits[1, A_BULLETS];
+        FMaxAmmo[A_SHELLS] := AmmoLimits[1, A_SHELLS];
+        FMaxAmmo[A_ROCKETS] := AmmoLimits[1, A_ROCKETS];
+        FMaxAmmo[A_CELLS] := AmmoLimits[1, A_CELLS];
+        FMaxAmmo[A_FUEL] := AmmoLimits[1, A_FUEL];
 
         if FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS] then
           IncMax(FAmmo[A_BULLETS], 10, FMaxAmmo[A_BULLETS]);
@@ -3737,6 +3867,7 @@ begin
         FMegaRulez[MR_SUIT] := gTime+PLAYER_SUIT_TIME;
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
@@ -3766,6 +3897,7 @@ begin
           FBerserk := gTime+30000;
           Result := True;
           remove := True;
+          FFireTime := 0;
         end;
         if FHealth < PLAYER_HP_SOFT then
         begin
@@ -3773,6 +3905,7 @@ begin
           FBerserk := gTime+30000;
           Result := True;
           remove := True;
+          FFireTime := 0;
         end;
       end;
 
@@ -3791,6 +3924,7 @@ begin
         IncMax(FHealth, 4, PLAYER_HP_LIMIT);
         Result := True;
         remove := True;
+        FFireTime := 0;
         if gFlash = 2 then Inc(FPickup, 5);
       end;
 
@@ -4103,7 +4237,7 @@ begin
     FAir := AIR_DEF;
     FJetFuel := 0;
 
-    for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for a := WP_FIRST to WP_LAST do
     begin
       FWeapon[a] := False;
       FReloading[a] := 0;
@@ -4116,15 +4250,16 @@ begin
 
     FModel.SetWeapon(FCurrWeap);
 
-    for b := A_BULLETS to A_CELLS do
+    for b := A_BULLETS to A_HIGH do
       FAmmo[b] := 0;
 
     FAmmo[A_BULLETS] := 50;
 
-    FMaxAmmo[A_BULLETS] := 200;
-    FMaxAmmo[A_SHELLS] := 50;
-    FMaxAmmo[A_ROCKETS] := 50;
-    FMaxAmmo[A_CELLS] := 300;
+    FMaxAmmo[A_BULLETS] := AmmoLimits[0, A_BULLETS];
+    FMaxAmmo[A_SHELLS] := AmmoLimits[0, A_SHELLS];
+    FMaxAmmo[A_ROCKETS] := AmmoLimits[0, A_SHELLS];
+    FMaxAmmo[A_CELLS] := AmmoLimits[0, A_CELLS];
+    FMaxAmmo[A_FUEL] := AmmoLimits[0, A_FUEL];
 
     if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
       FRulez := [R_KEY_RED, R_KEY_GREEN, R_KEY_BLUE]
@@ -4171,6 +4306,9 @@ begin
   FDamageBuffer := 0;
   FJetpack := False;
   FCanJetpack := False;
+  FFireTime := 0;
+  FFirePainTime := 0;
+  FFireAttacker := 0;
 
 // Àíèìàöèÿ âîçðîæäåíèÿ:
   if (not gLoadGameMode) and (not Silent) then
@@ -4276,14 +4414,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);
@@ -4510,7 +4657,10 @@ begin
     end;
 
     if FPhysics then
+    begin
       g_Obj_Move(@FObj, True, True, True);
+      positionChanged(); // this updates spatial accelerators
+    end;
 
     Exit;
   end;
@@ -4633,7 +4783,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;
@@ -4662,7 +4815,7 @@ begin
   DecMin(FPain, 5, 0);
   DecMin(FPickup, 1, 0);
 
-  if FLive and (FObj.Y > gMapInfo.Height+128) and AnyServer then
+  if FLive and (FObj.Y > Integer(gMapInfo.Height)+128) and AnyServer then
   begin
     // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë
     FMegaRulez[MR_SUIT] := 0;
@@ -4688,7 +4841,7 @@ begin
         FJetSoundFly.PlayAt(FObj.X, FObj.Y);
       end;
 
-    for b := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for b := WP_FIRST to WP_LAST do
       if FReloading[b] > 0 then
         if FNoReload then
           FReloading[b] := 0
@@ -4762,6 +4915,35 @@ begin
     end else if FAir < AIR_DEF then
       FAir := AIR_DEF;
 
+    if FFireTime > 0 then
+    begin
+      if BodyInLiquid(0, 0) then
+      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);
+        if FFirePainTime <= 0 then
+        begin
+          if g_Game_IsServer then
+            Damage(5, FFireAttacker, 0, 0, HIT_FLAME);
+          FFirePainTime := 18;
+        end;
+        FFirePainTime := FFirePainTime - 1;
+        FFireTime := FFireTime - 1;
+        if (FFireTime = 0) and g_Game_IsNet and g_Game_IsServer then
+          MH_SEND_PlayerStats(FUID);
+      end;
+    end;
+
     if FDamageBuffer > 0 then
     begin
       if FDamageBuffer >= 9 then
@@ -4818,6 +5000,14 @@ begin
     if FKeys[b].Time = 0 then FKeys[b].Pressed := False else Dec(FKeys[b].Time);
 end;
 
+procedure TPlayer.getMapBox (out x, y, w, h: Integer); inline;
+begin
+  x := FObj.X+PLAYER_RECT.X;
+  y := FObj.Y+PLAYER_RECT.Y;
+  w := PLAYER_RECT.Width;
+  h := PLAYER_RECT.Height;
+end;
+
 function TPlayer.Collide(X, Y: Integer; Width, Height: Word): Boolean;
 begin
   Result := g_Collide(FObj.X+PLAYER_RECT.X,
@@ -4912,7 +5102,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
@@ -4928,18 +5118,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);
@@ -5033,6 +5223,13 @@ begin
       g_Player_CreateShell(GameX+PLAYER_RECT_CX, GameY+PLAYER_RECT_CX,
                              GameVelX, GameVelY-2, SHELL_SHELL);
     end;
+
+    WEAPON_FLAMETHROWER:
+    begin
+      g_Weapon_flame(wx, wy, xd, yd, FUID, WID);
+      FFireAngle := FAngle;
+      f := True;
+    end;
   end;
 
   if not f then Exit;
@@ -5205,6 +5402,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]
@@ -5388,16 +5586,16 @@ begin
 // Îáúåêò èãðîêà:
   Obj_SaveState(@FObj, Mem);
 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.WriteWord(FAmmo[i]);
 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.WriteWord(FMaxAmmo[i]);
 // Íàëè÷èå îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.WriteBoolean(FWeapon[i]);
 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.WriteWord(FReloading[i]);
 // Íàëè÷èå ðþêçàêà:
   if R_ITEM_BACKPACK in FRulez then
@@ -5528,16 +5726,16 @@ begin
 // Îáúåêò èãðîêà:
   Obj_LoadState(@FObj, Mem);
 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.ReadWord(FAmmo[i]);
 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_CELLS do
+  for i := A_BULLETS to A_HIGH do
     Mem.ReadWord(FMaxAmmo[i]);
 // Íàëè÷èå îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.ReadBoolean(FWeapon[i]);
 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for i := WP_FIRST to WP_LAST do
     Mem.ReadWord(FReloading[i]);
 // Íàëè÷èå ðþêçàêà:
   Mem.ReadByte(b);
@@ -5600,8 +5798,8 @@ begin
     Exit;
   end;
 
-  for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do FWeapon[a] := True;
-  for a := A_BULLETS to A_CELLS do FAmmo[a] := 30000;
+  for a := WP_FIRST to WP_LAST do FWeapon[a] := True;
+  for a := A_BULLETS to A_HIGH do FAmmo[a] := 30000;
   FRulez := FRulez+[R_KEY_RED, R_KEY_GREEN, R_KEY_BLUE];
 end;
 
@@ -5717,6 +5915,7 @@ begin
     ITEM_WEAPON_PLASMA: FWeapon[WEAPON_PLASMA] := True;
     ITEM_WEAPON_BFG: FWeapon[WEAPON_BFG] := True;
     ITEM_WEAPON_SUPERPULEMET: FWeapon[WEAPON_SUPERPULEMET] := True;
+    ITEM_WEAPON_FLAMETHROWER: FWeapon[WEAPON_FLAMETHROWER] := True;
 
     ITEM_AMMO_BULLETS: if FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS] then IncMax(FAmmo[A_BULLETS], 10, FMaxAmmo[A_BULLETS]);
     ITEM_AMMO_BULLETS_BOX: if FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS] then IncMax(FAmmo[A_BULLETS], 50, FMaxAmmo[A_BULLETS]);
@@ -5726,17 +5925,20 @@ begin
     ITEM_AMMO_ROCKET_BOX: if FAmmo[A_ROCKETS] < FMaxAmmo[A_ROCKETS] then IncMax(FAmmo[A_ROCKETS], 5, FMaxAmmo[A_ROCKETS]);
     ITEM_AMMO_CELL: if FAmmo[A_CELLS] < FMaxAmmo[A_CELLS] then IncMax(FAmmo[A_CELLS], 40, FMaxAmmo[A_CELLS]);
     ITEM_AMMO_CELL_BIG: if FAmmo[A_CELLS] < FMaxAmmo[A_CELLS] then IncMax(FAmmo[A_CELLS], 100, FMaxAmmo[A_CELLS]);
+    ITEM_AMMO_FUELCAN: if FAmmo[A_FUEL] < FMaxAmmo[A_FUEL] then IncMax(FAmmo[A_FUEL], 100, FMaxAmmo[A_FUEL]);
 
     ITEM_AMMO_BACKPACK:
       if (FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS]) or
          (FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS]) or
          (FAmmo[A_ROCKETS] < FMaxAmmo[A_ROCKETS]) or
-         (FAmmo[A_CELLS] < FMaxAmmo[A_CELLS]) then
+         (FAmmo[A_CELLS] < FMaxAmmo[A_CELLS]) or
+         (FMaxAmmo[A_FUEL] < AmmoLimits[1, A_FUEL]) then
       begin
-        FMaxAmmo[A_BULLETS] := 400;
-        FMaxAmmo[A_SHELLS] := 100;
-        FMaxAmmo[A_ROCKETS] := 100;
-        FMaxAmmo[A_CELLS] := 600;
+        FMaxAmmo[A_BULLETS] := AmmoLimits[1, A_BULLETS];
+        FMaxAmmo[A_SHELLS] := AmmoLimits[1, A_SHELLS];
+        FMaxAmmo[A_ROCKETS] := AmmoLimits[1, A_ROCKETS];
+        FMaxAmmo[A_CELLS] := AmmoLimits[1, A_CELLS];
+        FMaxAmmo[A_FUEL] := AmmoLimits[1, A_FUEL];
 
         if FAmmo[A_BULLETS] < FMaxAmmo[A_BULLETS] then IncMax(FAmmo[A_BULLETS], 10, FMaxAmmo[A_BULLETS]);
         if FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS] then IncMax(FAmmo[A_SHELLS], 4, FMaxAmmo[A_SHELLS]);
@@ -5792,6 +5994,27 @@ begin
   end;
 end;
 
+procedure TPlayer.OnFireFlame(Times: DWORD = 1);
+var
+  id, i: DWORD;
+  Anim: TAnimation;
+begin
+  if (Random(10) = 1) and (Times = 1) then
+    Exit;
+
+  if g_Frames_Get(id, 'FRAMES_FLAME') then
+  begin
+    for i := 1 to Times do
+    begin
+      Anim := TAnimation.Create(id, False, 3);
+      Anim.Alpha := 0;
+      g_GFX_OnceAnim(Obj.X+Obj.Rect.X+Random(Obj.Rect.Width+Times*2)-(Anim.Width div 2),
+                   Obj.Y+8+Random(8+Times*2), Anim, ONCEANIM_SMOKE);
+      Anim.Free();
+    end;
+  end;
+end;
+
 procedure TPlayer.PauseSounds(Enable: Boolean);
 begin
   FSawSound.Pause(Enable);
@@ -5830,6 +6053,8 @@ begin
   inherited;
 end;
 
+procedure TCorpse.positionChanged (); begin end;
+
 procedure TCorpse.Damage(Value: Word; vx, vy: Integer);
 var
   pm: TPlayerModel;
@@ -5896,7 +6121,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;
 
@@ -5904,6 +6129,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
@@ -6010,7 +6236,7 @@ begin
 
   Inc(gNumBots);
 
-  for a := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+  for a := WP_FIRST to WP_LAST do
   begin
     FDifficult.WeaponPrior[a] := WEAPON_PRIOR1[a];
     FDifficult.CloseWeaponPrior[a] := WEAPON_PRIOR2[a];
@@ -6084,8 +6310,43 @@ var
   firew, fireh: Integer;
   angle: SmallInt;
   mon: TMonster;
-  pla: TPlayer;
+  pla, tpla: TPlayer;
   vsPlayer, vsMonster, ok: Boolean;
+
+
+  function monsUpdate (mon: TMonster): Boolean;
+  begin
+    result := false; // don't stop
+    if mon.Live and (mon.MonsterType <> MONSTER_BARREL) then
+    begin
+      if not TargetOnScreen(mon.Obj.X+mon.Obj.Rect.X, mon.Obj.Y+mon.Obj.Rect.Y) then exit;
+
+      x2 := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2);
+      y2 := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2);
+
+      // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé
+      if g_TraceVector(x1, y1, x2, y2) then
+      begin
+        // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé
+        SetLength(targets, Length(targets)+1);
+        with targets[High(targets)] do
+        begin
+          UID := mon.UID;
+          X := mon.Obj.X;
+          Y := mon.Obj.Y;
+          cX := x2;
+          cY := y2;
+          Rect := mon.Obj.Rect;
+          Dist := g_PatchLength(x1, y1, x2, y2);
+          Line := (y1+4 < Target.Y + mon.Obj.Rect.Y + mon.Obj.Rect.Height) and
+                  (y1-4 > Target.Y + mon.Obj.Rect.Y);
+          Visible := True;
+          IsPlayer := False;
+        end;
+      end;
+    end;
+  end;
+
 begin
   vsPlayer := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER);
   vsMonster := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER);
@@ -6101,7 +6362,7 @@ begin
 
       case FCurrWeap of
         WEAPON_PLASMA, WEAPON_SUPERPULEMET, WEAPON_CHAINGUN: PressKey(KEY_FIRE, 20);
-        WEAPON_SAW, WEAPON_KASTET, WEAPON_MEGAKASTET: PressKey(KEY_FIRE, 40);
+        WEAPON_SAW, WEAPON_KASTET, WEAPON_FLAMETHROWER: PressKey(KEY_FIRE, 40);
         else PressKey(KEY_FIRE);
       end;
     end;
@@ -6118,14 +6379,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;
@@ -6140,7 +6403,7 @@ begin
         if (g_GetUIDType(Target.UID) = UID_MONSTER) and
             vsMonster then
           begin // Ìîíñòð
-            mon := g_Monsters_Get(Target.UID);
+            mon := g_Monsters_ByUID(Target.UID);
             if mon <> nil then
               begin
                 Target.X := mon.Obj.X;
@@ -6213,41 +6476,7 @@ begin
           end;
 
   // Ìîíñòðû:
-    if vsMonster and (gMonsters <> nil) then
-      for a := 0 to High(gMonsters) do
-        if (gMonsters[a] <> nil) and (gMonsters[a].Live) and
-           (gMonsters[a].MonsterType <> MONSTER_BARREL) then
-          begin
-            mon := gMonsters[a];
-
-            if not TargetOnScreen(mon.Obj.X + mon.Obj.Rect.X,
-                                  mon.Obj.Y + mon.Obj.Rect.Y) then
-              Continue;
-
-            x2 := mon.Obj.X + mon.Obj.Rect.X + (mon.Obj.Rect.Width div 2);
-            y2 := mon.Obj.Y + mon.Obj.Rect.Y + (mon.Obj.Rect.Height div 2);
-
-          // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé:
-            if g_TraceVector(x1, y1, x2, y2) then
-              begin
-              // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé:
-                SetLength(targets, Length(targets)+1);
-                with targets[High(targets)] do
-                  begin
-                    UID := mon.UID;
-                    X := mon.Obj.X;
-                    Y := mon.Obj.Y;
-                    cX := x2;
-                    cY := y2;
-                    Rect := mon.Obj.Rect;
-                    Dist := g_PatchLength(x1, y1, x2, y2);
-                    Line := (y1+4 < Target.Y + mon.Obj.Rect.Y + mon.Obj.Rect.Height) and
-                            (y1-4 > Target.Y + mon.Obj.Rect.Y);
-                    Visible := True;
-                    IsPlayer := False;
-                  end;
-              end;
-          end;
+    if vsMonster then g_Mons_ForEach(monsUpdate);
   end;
 
 // Åñëè åñòü âîçìîæíûå öåëè:
@@ -6407,7 +6636,7 @@ begin
             end
           else
             begin // Öåëü - ìîíñòð
-              mon := g_Monsters_Get(Target.UID);
+              mon := g_Monsters_ByUID(Target.UID);
               if (mon = nil) or (not mon.Live) then
                 Target.UID := 0; // òî çàáûòü öåëü
             end;
@@ -6595,33 +6824,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
@@ -6632,7 +6861,7 @@ begin
     end;
 end;
 
-procedure TBot.SetAIFlag(fName, fValue: String20);
+procedure TBot.SetAIFlag(aName, fValue: String20);
 var
   a: Integer;
   ok: Boolean;
@@ -6640,11 +6869,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;
@@ -6656,7 +6885,7 @@ begin
     SetLength(FAIFlags, Length(FAIFlags)+1);
     with FAIFlags[High(FAIFlags)] do
     begin
-      Name := fName;
+      Name := aName;
       Value := fValue;
     end;
   end;
@@ -7073,6 +7302,7 @@ var
       WEAPON_PLASMA: Result := FAmmo[A_CELLS] >= 10;
       WEAPON_BFG: Result := FAmmo[A_CELLS] >= 40;
       WEAPON_SUPERPULEMET: Result := FAmmo[A_SHELLS] >= 1;
+      WEAPON_FLAMETHROWER: Result := FAmmo[A_FUEL] >= 1;
       else Result := True;
     end;
   end;
@@ -7158,7 +7388,7 @@ begin
         if (g_GetUIDType(FLastSpawnerUID) = UID_MONSTER) and
            LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER) then
         begin // Ìîíñòð
-          mon := g_Monsters_Get(FLastSpawnerUID);
+          mon := g_Monsters_ByUID(FLastSpawnerUID);
           ok := not TargetOnScreen(mon.Obj.X + mon.Obj.Rect.X,
                                    mon.Obj.Y + mon.Obj.Rect.Y);
         end;