DEADSOFTWARE

more flamer tweaks
[d2df-sdl.git] / src / game / g_player.pas
index 5dc5a096cfc3fff8a2e6a64b8a40a2e8c508aa0a..050e0e650950b20e5e51a65d23f40564b87d3638 100644 (file)
@@ -1,3 +1,18 @@
+(* Copyright (C)  DooM 2D:Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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}
 unit g_player;
 
@@ -35,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;
@@ -97,9 +118,11 @@ type
     Air:        Integer;
     JetFuel:    Integer;
     CurrWeap:   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;
+    NextWeap:   WORD;
+    NextWeapDelay: Byte;
+    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;
@@ -136,12 +159,16 @@ type
     FFlag:      Byte;
     FSecrets:   Integer;
     FCurrWeap:  Byte;
+    FNextWeap:  WORD;
+    FNextWeapDelay: Byte; // frames
     FBFGFireCounter: SmallInt;
     FLastSpawnerUID: Word;
     FLastHit:   Byte;
     FObj:       TObj;
     FXTo, FYTo: Integer;
     FSpectatePlayer: Integer;
+    FFirePainTime:   Integer;
+    FFireAttacker:   Word;
 
     FSavedState: TPlayerSavedState;
 
@@ -176,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;
@@ -190,16 +218,20 @@ type
     procedure   Jump();
     procedure   Use();
 
+    function getNextWeaponIndex (): Byte; // return 255 for "no switch"
+    procedure resetWeaponQueue ();
+    function hasAmmoForWeapon (weapon: Byte): Boolean;
+
   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;
@@ -215,6 +247,7 @@ type
     FPing:      Word;
     FLoss:      Byte;
     FDummy:     Boolean;
+    FFireTime:  Integer;
 
     constructor Create(); virtual;
     destructor  Destroy(); override;
@@ -272,8 +305,11 @@ type
     procedure   NetFire(Wpn: Byte; X, Y, AX, AY: Integer; WID: Integer = -1);
     procedure   DoLerp(Level: Integer = 2);
     procedure   SetLerp(XTo, YTo: Integer);
+    procedure   QueueWeaponSwitch(Weapon: Byte);
+    procedure   RealizeCurrentWeapon();
     procedure   JetpackOn;
     procedure   JetpackOff;
+    procedure   CatchFire(Attacker: Word);
 
     property    Name: String read FName write FName;
     property    Model: TPlayerModel read FModel;
@@ -316,9 +352,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
@@ -486,9 +522,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
@@ -517,27 +553,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'
@@ -782,6 +821,10 @@ begin
   Mem.ReadInt(gPlayers[a].FSecrets);
 // Òåêóùåå îðóæèå:
   Mem.ReadByte(gPlayers[a].FCurrWeap);
+// Ñëåäóþùåå æåëàåìîå îðóæèå:
+  Mem.ReadWord(gPlayers[a].FNextWeap);
+// ...è ïàóçà:
+  Mem.ReadByte(gPlayers[a].FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.ReadSmallInt(gPlayers[a].FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -793,16 +836,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);
@@ -968,7 +1011,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];
@@ -1046,7 +1089,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];
@@ -1239,10 +1282,24 @@ var
 begin
   if gPlayers = nil then Exit;
 
+  //e_WriteLog('***g_Player_UpdateAll: ENTER', MSG_WARNING);
   for i := 0 to High(gPlayers) do
+  begin
     if gPlayers[i] <> nil then
-      if gPlayers[i] is TPlayer then gPlayers[i].Update()
-      else TBot(gPlayers[i]).Update();
+    begin
+      if gPlayers[i] is TPlayer then
+      begin
+        gPlayers[i].Update();
+        gPlayers[i].RealizeCurrentWeapon(); // WARNING! DO NOT MOVE THIS INTO `Update()`!
+      end
+      else
+      begin
+        // bot updates weapons in `UpdateCombat()`
+        TBot(gPlayers[i]).Update();
+      end;
+    end;
+  end;
+  //e_WriteLog('***g_Player_UpdateAll: EXIT', MSG_WARNING);
 end;
 
 procedure g_Player_DrawAll();
@@ -1935,6 +1992,9 @@ begin
   FLoss := 0;
   FSavedState.WaitRecall := False;
   FShellTimer := -1;
+  FFireTime := 0;
+  FFirePainTime := 0;
+  FFireAttacker := 0;
 
   FActualModelName := 'doomer';
 
@@ -1944,6 +2004,8 @@ begin
   FBFGFireCounter := -1;
   FJustTeleported := False;
   FNetTime := 0;
+
+  resetWeaponQueue();
 end;
 
 procedure TPlayer.Damage(value: Word; SpawnerUID: Word; vx, vy: Integer; t: Byte);
@@ -2161,6 +2223,7 @@ procedure TPlayer.Draw();
 var
   ID: DWORD;
   w, h: Word;
+  dr: Boolean;
 begin
   if FLive then
   begin
@@ -2180,7 +2243,16 @@ begin
     begin
       if (gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or
          ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM))) then
-        FModel.Draw(FObj.X, FObj.Y, 200)
+      begin
+        if (FMegaRulez[MR_INVIS] - gTime) <= 2100 then
+          dr := not Odd((FMegaRulez[MR_INVIS] - gTime) div 300)
+        else
+          dr := True;
+        if dr then
+          FModel.Draw(FObj.X, FObj.Y, 200)
+        else
+          FModel.Draw(FObj.X, FObj.Y);
+      end
       else
         FModel.Draw(FObj.X, FObj.Y, 254);
     end
@@ -2432,6 +2504,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);
@@ -2739,6 +2812,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
@@ -2769,6 +2853,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;
@@ -2797,6 +2882,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
@@ -3028,7 +3121,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
@@ -3040,6 +3133,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;
 
@@ -3225,87 +3319,157 @@ begin
               150, 0, 0);
 end;
 
-procedure TPlayer.NextWeapon();
-var
-  i: Byte;
-  ok: Boolean;
+procedure TPlayer.QueueWeaponSwitch(Weapon: Byte);
 begin
   if g_Game_IsClient then Exit;
-  if FBFGFireCounter <> -1 then Exit;
-
-  if FTime[T_SWITCH] > gTime then Exit;
+  if Weapon > High(FWeapon) then Exit;
+  FNextWeap := FNextWeap or (1 shl Weapon);
+end;
 
-  for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do
-    if FReloading[i] > 0 then Exit;
+procedure TPlayer.resetWeaponQueue ();
+begin
+  FNextWeap := 0;
+  FNextWeapDelay := 0;
+end;
 
-  ok := False;
+function TPlayer.hasAmmoForWeapon (weapon: Byte): Boolean;
+begin
+  result := false;
+  case weapon of
+    WEAPON_KASTET, WEAPON_SAW: result := true;
+    WEAPON_SHOTGUN1, WEAPON_SHOTGUN2: result := (FAmmo[A_SHELLS] > 0);
+    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;
 
-  for i := FCurrWeap+1 to WEAPON_SUPERPULEMET do
-    if FWeapon[i] then
+// return 255 for "no switch"
+function TPlayer.getNextWeaponIndex (): Byte;
+var
+  i: Word;
+  wantThisWeapon: array[0..64] of Boolean;
+  wwc: Integer = 0; //HACK!
+  dir, cwi: Integer;
+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;
+  // cycling has priority
+  if (FNextWeap and $C000) <> 0 then
+  begin
+    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
+    cwi := FCurrWeap;
+    for i := 0 to High(FWeapon) do
     begin
-      FCurrWeap := i;
-      ok := True;
-      Break;
-    end;
-
-  if not ok then
-    for i := WEAPON_KASTET to FCurrWeap-1 do
-      if FWeapon[i] then
+      cwi := (cwi+length(FWeapon)+dir) mod length(FWeapon);
+      if FWeapon[cwi] then
       begin
-        FCurrWeap := i;
-        Break;
+        //e_WriteLog(Format(' SWITCH: cur=%d; new=%d', [FCurrWeap, cwi]), MSG_WARNING);
+        result := Byte(cwi);
+        FNextWeapDelay := 10;
+        exit;
       end;
-
-  FTime[T_SWITCH] := gTime+156;
-
-  if FCurrWeap = WEAPON_SAW then
-    FSawSoundSelect.PlayAt(FObj.X, FObj.Y);
-
-  FModel.SetWeapon(FCurrWeap);
-
-  if g_Game_IsNet then MH_SEND_PlayerStats(FUID);
+    end;
+    resetWeaponQueue();
+    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;
+  // exclude currently selected weapon from the set
+  wantThisWeapon[FCurrWeap] := false;
+  // slow down alterations a little
+  if wwc > 1 then
+  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
+  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;
+  //e_WriteLog(Format('wwc=%d', [wwc]), MSG_WARNING);
+  // try weapons in descending order
+  for i := High(FWeapon) downto 0 do
+  begin
+    if wantThisWeapon[i] and FWeapon[i] and ((wwc = 1) or hasAmmoForWeapon(i)) then
+    begin
+      // i found her!
+      result := Byte(i);
+      resetWeaponQueue();
+      FNextWeapDelay := 10; // anyway, 'cause why not
+      exit;
+    end;
+  end;
+  // no suitable weapon found, so reset the queue, to avoid accidental "queuing" of weapon w/o ammo
+  resetWeaponQueue();
 end;
 
-procedure TPlayer.PrevWeapon();
+procedure TPlayer.RealizeCurrentWeapon();
+  function switchAllowed (): Boolean;
+  var
+    i: Byte;
+  begin
+    result := false;
+    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;
+
 var
-  i: Byte;
-  ok: Boolean;
+  nw: Byte;
 begin
-  if g_Game_IsClient then Exit;
-  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;
-
-  ok := False;
+  //e_WriteLog(Format('***RealizeCurrentWeapon: FNextWeap=%x; FNextWeapDelay=%d', [FNextWeap, FNextWeapDelay]), MSG_WARNING);
+  //FNextWeap := FNextWeap and $1FFF;
+  if FNextWeapDelay > 0 then Dec(FNextWeapDelay); // "alteration delay"
 
-  if FCurrWeap > 0 then
-    for i := FCurrWeap-1 downto WEAPON_KASTET do
-      if FWeapon[i] then
-      begin
-        FCurrWeap := i;
-        ok := True;
-        Break;
-      end;
-
-  if not ok then
-    for i := WEAPON_SUPERPULEMET downto FCurrWeap+1 do
-      if FWeapon[i] then
-      begin
-        FCurrWeap := i;
-        Break;
-      end;
+  if not switchAllowed then
+  begin
+    //HACK for weapon cycling
+    if (FNextWeap and $7000) <> 0 then FNextWeap := 0;
+    exit;
+  end;
 
-  FTime[T_SWITCH] := gTime+156;
+  nw := getNextWeaponIndex();
+  if nw = 255 then exit; // don't reset anything here
+  if nw > High(FWeapon) then
+  begin
+    // don't forget to reset queue here!
+    //e_WriteLog(' RealizeCurrentWeapon: WUTAFUUUU', MSG_WARNING);
+    resetWeaponQueue();
+    exit;
+  end;
 
-  if FCurrWeap = WEAPON_SAW then
-    FSawSoundSelect.PlayAt(FObj.X, FObj.Y);
+  if FWeapon[nw] then
+  begin
+    FCurrWeap := nw;
+    FTime[T_SWITCH] := gTime+156;
+    if FCurrWeap = WEAPON_SAW then FSawSoundSelect.PlayAt(FObj.X, FObj.Y);
+    FModel.SetWeapon(FCurrWeap);
+    if g_Game_IsNet then MH_SEND_PlayerStats(FUID);
+  end;
+end;
 
-  FModel.SetWeapon(FCurrWeap);
+procedure TPlayer.NextWeapon();
+begin
+  if g_Game_IsClient then Exit;
+  FNextWeap := $8000;
+end;
 
-  if g_Game_IsNet then MH_SEND_PlayerStats(FUID);
+procedure TPlayer.PrevWeapon();
+begin
+  if g_Game_IsClient then Exit;
+  FNextWeap := $4000;
 end;
 
 procedure TPlayer.SetWeapon(W: Byte);
@@ -3316,6 +3480,7 @@ begin
 
   FCurrWeap := W;
   FModel.SetWeapon(CurrWeap);
+  resetWeaponQueue();
 end;
 
 function TPlayer.PickItem(ItemType: Byte; respawn: Boolean; var remove: Boolean): Boolean;
@@ -3481,6 +3646,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
@@ -3553,17 +3730,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]);
@@ -3636,6 +3824,7 @@ begin
           if FBFGFireCounter = -1 then
           begin
             FCurrWeap := WEAPON_KASTET;
+            resetWeaponQueue();
             FModel.SetWeapon(WEAPON_KASTET);
           end;
           if gFlash <> 0 then
@@ -3981,7 +4170,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;
@@ -3990,18 +4179,20 @@ begin
     FWeapon[WEAPON_PISTOL] := True;
     FWeapon[WEAPON_KASTET] := True;
     FCurrWeap := WEAPON_PISTOL;
+    resetWeaponQueue();
 
     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]
@@ -4048,6 +4239,9 @@ begin
   FDamageBuffer := 0;
   FJetpack := False;
   FCanJetpack := False;
+  FFireTime := 0;
+  FFirePainTime := 0;
+  FFireAttacker := 0;
 
 // Àíèìàöèÿ âîçðîæäåíèÿ:
   if (not gLoadGameMode) and (not Silent) then
@@ -4372,6 +4566,10 @@ begin
     FIncCam := FIncCam*i;
   end;
 
+  // no need to do that each second frame, weapon queue will take care of it
+  if FLive and FKeys[KEY_NEXTWEAPON].Pressed and AnyServer then NextWeapon();
+  if FLive and FKeys[KEY_PREVWEAPON].Pressed and AnyServer then PrevWeapon();
+
   if gTime mod (GAME_TICK*2) <> 0 then
   begin
     if (FObj.Vel.X = 0) and FLive then
@@ -4395,8 +4593,8 @@ begin
     // Let alive player do some actions
     if FKeys[KEY_LEFT].Pressed then Run(D_LEFT);
     if FKeys[KEY_RIGHT].Pressed then Run(D_RIGHT);
-    if FKeys[KEY_NEXTWEAPON].Pressed and AnyServer then NextWeapon();
-    if FKeys[KEY_PREVWEAPON].Pressed and AnyServer then PrevWeapon();
+    //if FKeys[KEY_NEXTWEAPON].Pressed and AnyServer then NextWeapon();
+    //if FKeys[KEY_PREVWEAPON].Pressed and AnyServer then PrevWeapon();
     if FKeys[KEY_FIRE].Pressed and AnyServer then Fire();
     if FKeys[KEY_OPEN].Pressed and AnyServer then Use();
     if FKeys[KEY_JUMP].Pressed then Jump()
@@ -4561,7 +4759,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
@@ -4635,6 +4833,29 @@ 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
+      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
@@ -4906,6 +5127,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;
@@ -5143,6 +5371,8 @@ begin
   FSavedState.Air := FAir;
   FSavedState.JetFuel := FJetFuel;
   FSavedState.CurrWeap := FCurrWeap;
+  FSavedState.NextWeap := FNextWeap;
+  FSavedState.NextWeapDelay := FNextWeapDelay;
 
   for i := 0 to 3 do
     FSavedState.Ammo[i] := FAmmo[i];
@@ -5164,6 +5394,8 @@ begin
   FAir := FSavedState.Air;
   FJetFuel := FSavedState.JetFuel;
   FCurrWeap := FSavedState.CurrWeap;
+  FNextWeap := FSavedState.NextWeap;
+  FNextWeapDelay := FSavedState.NextWeapDelay;
 
   for i := 0 to 3 do
     FAmmo[i] := FSavedState.Ammo[i];
@@ -5242,6 +5474,10 @@ begin
   Mem.WriteInt(FSecrets);
 // Òåêóùåå îðóæèå:
   Mem.WriteByte(FCurrWeap);
+// Æåëàåìîå îðóæèå:
+  Mem.WriteWord(FNextWeap);
+// ...è ïàóçà
+  Mem.WriteByte(FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.WriteSmallInt(FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -5253,16 +5489,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
@@ -5378,6 +5614,10 @@ begin
   Mem.ReadInt(FSecrets);
 // Òåêóùåå îðóæèå:
   Mem.ReadByte(FCurrWeap);
+// Æåëàåìîå îðóæèå:
+  Mem.ReadWord(FNextWeap);
+// ...è ïàóçà
+  Mem.ReadByte(FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.ReadSmallInt(FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -5389,16 +5629,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);
@@ -5461,8 +5701,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;
 
@@ -5524,6 +5764,7 @@ begin
           if FBFGFireCounter < 1 then
           begin
             FCurrWeap := WEAPON_KASTET;
+            resetWeaponQueue();
             FModel.SetWeapon(WEAPON_KASTET);
           end;
           if gFlash <> 0 then
@@ -5555,6 +5796,68 @@ begin
         FJetFuel := JET_MAX;
       end;
 
+    ITEM_MEDKIT_SMALL: if FHealth < PLAYER_HP_SOFT then IncMax(FHealth, 10, PLAYER_HP_SOFT);
+    ITEM_MEDKIT_LARGE: if FHealth < PLAYER_HP_SOFT then IncMax(FHealth, 25, PLAYER_HP_SOFT);
+
+    ITEM_ARMOR_GREEN: if FArmor < PLAYER_AP_SOFT then FArmor := PLAYER_AP_SOFT;
+    ITEM_ARMOR_BLUE: if FArmor < PLAYER_AP_LIMIT then FArmor := PLAYER_AP_LIMIT;
+
+    ITEM_SPHERE_BLUE: if FHealth < PLAYER_HP_LIMIT then IncMax(FHealth, 100, PLAYER_HP_LIMIT);
+    ITEM_SPHERE_WHITE:
+      if (FHealth < PLAYER_HP_LIMIT) or (FArmor < PLAYER_AP_LIMIT) then
+      begin
+        if FHealth < PLAYER_HP_LIMIT then FHealth := PLAYER_HP_LIMIT;
+        if FArmor < PLAYER_AP_LIMIT then FArmor := PLAYER_AP_LIMIT;
+      end;
+
+    ITEM_WEAPON_SAW: FWeapon[WEAPON_SAW] := True;
+    ITEM_WEAPON_SHOTGUN1: FWeapon[WEAPON_SHOTGUN1] := True;
+    ITEM_WEAPON_SHOTGUN2: FWeapon[WEAPON_SHOTGUN2] := True;
+    ITEM_WEAPON_CHAINGUN: FWeapon[WEAPON_CHAINGUN] := True;
+    ITEM_WEAPON_ROCKETLAUNCHER: FWeapon[WEAPON_ROCKETLAUNCHER] := True;
+    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]);
+    ITEM_AMMO_SHELLS: if FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS] then IncMax(FAmmo[A_SHELLS], 4, FMaxAmmo[A_SHELLS]);
+    ITEM_AMMO_SHELLS_BOX: if FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS] then IncMax(FAmmo[A_SHELLS], 25, FMaxAmmo[A_SHELLS]);
+    ITEM_AMMO_ROCKET: if FAmmo[A_ROCKETS] < FMaxAmmo[A_ROCKETS] then IncMax(FAmmo[A_ROCKETS], 1, FMaxAmmo[A_ROCKETS]);
+    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]) or
+         (FMaxAmmo[A_FUEL] < AmmoLimits[1, A_FUEL]) then
+      begin
+        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]);
+        if FAmmo[A_ROCKETS] < FMaxAmmo[A_ROCKETS] then IncMax(FAmmo[A_ROCKETS], 1, FMaxAmmo[A_ROCKETS]);
+        if FAmmo[A_CELLS] < FMaxAmmo[A_CELLS] then IncMax(FAmmo[A_CELLS], 40, FMaxAmmo[A_CELLS]);
+
+        FRulez := FRulez + [R_ITEM_BACKPACK];
+      end;
+
+    ITEM_KEY_RED: if not (R_KEY_RED in FRulez) then Include(FRulez, R_KEY_RED);
+    ITEM_KEY_GREEN: if not (R_KEY_GREEN in FRulez) then Include(FRulez, R_KEY_GREEN);
+    ITEM_KEY_BLUE: if not (R_KEY_BLUE in FRulez) then Include(FRulez, R_KEY_BLUE);
+
+    ITEM_BOTTLE: if FHealth < PLAYER_HP_LIMIT then IncMax(FHealth, 4, PLAYER_HP_LIMIT);
+    ITEM_HELMET: if FArmor < PLAYER_AP_LIMIT then IncMax(FArmor, 5, PLAYER_AP_LIMIT);
+
     else
       Exit;
   end;
@@ -5594,6 +5897,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);
@@ -5653,8 +5977,9 @@ begin
         g_Player_CreateGibs(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
                             FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2),
                             FModelName, FColor);
+        // Çâóê ìÿñà îò òðóïà:
         pm := g_PlayerModel_Get(FModelName);
-        pm.PlaySound(MODELSOUND_DIE, 3, FObj.X, FObj.Y);
+        pm.PlaySound(MODELSOUND_DIE, 5, FObj.X, FObj.Y);
         pm.Free;
       end;
     end
@@ -5811,7 +6136,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];
@@ -5839,6 +6164,7 @@ begin
 
   FAIFlags := nil;
   FSelectedWeapon := FCurrWeap;
+  resetWeaponQueue();
   FTargetUID := 0;
 end;
 
@@ -5901,7 +6227,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;
@@ -6265,6 +6591,9 @@ begin
             end;
         end;
 
+  //HACK! (does it belongs there?)
+  RealizeCurrentWeapon();
+
 // Åñëè åñòü âîçìîæíûå öåëè:
 // (Ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëÿì)
   if (targets <> nil) and (GetAIFlag('NEEDFIRE') <> '') then
@@ -6368,6 +6697,10 @@ begin
     begin
       UpdateMove();
       UpdateCombat();
+    end
+    else
+    begin
+      RealizeCurrentWeapon();
     end;
   end;
 
@@ -6866,6 +7199,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;