DEADSOFTWARE

no more global `gItems[]` array; created DynTree for items (not used yet); also,...
[d2df-sdl.git] / src / game / g_netmsg.pas
index 1c678ecaf025866271191a651e9f38cb58d65dfd..33273f896a9ab3e339d1ca731a0a4ec53a129959 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_netmsg;
 
 interface
@@ -146,7 +146,7 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E
 procedure MH_SEND_Info(ID: Byte);
 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
-procedure MH_SEND_Sound(X, Y: Integer; Name: string; ID: Integer = NET_EVERYONE);
+procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
@@ -430,7 +430,8 @@ end;
 
 function  MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word;
 var
-  Dir: Byte;
+  Dir, i: Byte;
+  WeaponSelect: Word;
   PID: Word;
   kByte: Word;
   Pl: TPlayer;
@@ -452,8 +453,11 @@ begin
     NetTime := GT;
     kByte := e_Raw_Read_Word(P);
     Dir := e_Raw_Read_Byte(P);
+    WeaponSelect := e_Raw_Read_Word(P);
+    //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
     if Direction <> TDirection(Dir) then
       JustTeleported := False;
+
     SetDirection(TDirection(Dir));
     ReleaseKeys;
 
@@ -472,6 +476,15 @@ begin
     if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
     if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
     if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
+
+    for i := 0 to 15 do
+    begin
+      if (WeaponSelect and Word(1 shl i)) <> 0 then
+      begin
+        //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
+        QueueWeaponSwitch(i);
+      end;
+    end;
   end;
 
   // MH_SEND_PlayerPos(False, PID, C^.ID);
@@ -614,11 +627,20 @@ end;
 // GAME (SEND)
 
 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
+
+  function sendItemRespawn (it: PItem): Boolean;
+  begin
+    result := false; // don't stop
+    MH_SEND_ItemSpawn(True, it.myid, ID);
+  end;
+
 var
   I: Integer;
 begin
   if gPlayers <> nil then
+  begin
     for I := Low(gPlayers) to High(gPlayers) do
+    begin
       if gPlayers[I] <> nil then
       begin
         if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
@@ -628,14 +650,11 @@ begin
         if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
           MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
       end;
-
-  if gItems <> nil then
-  begin
-    for I := High(gItems) downto Low(gItems) do
-      if gItems[I].Live then
-        MH_SEND_ItemSpawn(True, I, ID);
+    end;
   end;
 
+  g_Items_ForEachAlive(sendItemRespawn, true); // backwards
+
   if gMonsters <> nil then
     for I := 0 to High(gMonsters) do
       if gMonsters[I] <> nil then
@@ -663,14 +682,22 @@ begin
     for I := Low(gRenderForegrounds) to High(gRenderForegrounds) do
       if gRenderForegrounds[I] <> nil then
         with gRenderForegrounds[I] do
+        begin
           if (GetTextureCount > 1) then
             MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+          if Moved then
+            MH_SEND_PanelState(PanelType, I, ID);
+        end;
   if gRenderBackgrounds <> nil then
     for I := Low(gRenderBackgrounds) to High(gRenderBackgrounds) do
       if gRenderBackgrounds[I] <> nil then
         with gRenderBackgrounds[I] do
-          if GetTextureCount > 1 then
+        begin
+          if (GetTextureCount > 1) then
             MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
+          if Moved then
+            MH_SEND_PanelState(PanelType, I, ID);
+        end;
   if gWater <> nil then
     for I := Low(gWater) to High(gWater) do
       if gWater[I] <> nil then
@@ -830,12 +857,18 @@ begin
   g_Net_Host_Send(ID, False, NET_CHAN_GAME);
 end;
 
-procedure MH_SEND_Sound(X, Y: Integer; Name: string; ID: Integer = NET_EVERYONE);
+procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
 begin
   e_Buffer_Write(@NetOut, Byte(NET_MSG_SND));
   e_Buffer_Write(@NetOut, Name);
-  e_Buffer_Write(@NetOut, X);
-  e_Buffer_Write(@NetOut, Y);
+  if Pos then
+  begin
+    e_Buffer_Write(@NetOut, Byte(1));
+    e_Buffer_Write(@NetOut, X);
+    e_Buffer_Write(@NetOut, Y);
+  end
+  else
+    e_Buffer_Write(@NetOut, Byte(0));
 
   g_Net_Host_Send(ID, False, NET_CHAN_GAME);
 end;
@@ -1047,13 +1080,13 @@ begin
     e_Buffer_Write(@NetOut, Lives);
     e_Buffer_Write(@NetOut, Team);
 
-    for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for I := WP_FIRST to WP_LAST do
       e_Buffer_Write(@NetOut, Byte(FWeapon[I]));
 
-    for I := A_BULLETS to A_CELLS do
+    for I := A_BULLETS to A_HIGH do
       e_Buffer_Write(@NetOut, FAmmo[I]);
 
-    for I := A_BULLETS to A_CELLS do
+    for I := A_BULLETS to A_HIGH do
       e_Buffer_Write(@NetOut, FMaxAmmo[I]);
 
     for I := MR_SUIT to MR_MAX do
@@ -1075,6 +1108,7 @@ begin
     e_Buffer_Write(@NetOut, Byte(FPhysics));
     e_Buffer_Write(@NetOut, Byte(FNoRespawn));
     e_Buffer_Write(@NetOut, Byte(FJetpack));
+    e_Buffer_Write(@NetOut, FFireTime);
   end;
 
   g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
@@ -1151,17 +1185,21 @@ end;
 // ITEM (SEND)
 
 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
+var
+  it: PItem;
 begin
+  it := g_ItemByIdx(IID);
+
   e_Buffer_Write(@NetOut, Byte(NET_MSG_ISPAWN));
   e_Buffer_Write(@NetOut, IID);
   e_Buffer_Write(@NetOut, Byte(Quiet));
-  e_Buffer_Write(@NetOut, gItems[IID].ItemType);
-  e_Buffer_Write(@NetOut, Byte(gItems[IID].Fall));
-  e_Buffer_Write(@NetOut, Byte(gItems[IID].Respawnable));
-  e_Buffer_Write(@NetOut, gItems[IID].Obj.X);
-  e_Buffer_Write(@NetOut, gItems[IID].Obj.Y);
-  e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.X);
-  e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.Y);
+  e_Buffer_Write(@NetOut, it.ItemType);
+  e_Buffer_Write(@NetOut, Byte(it.Fall));
+  e_Buffer_Write(@NetOut, Byte(it.Respawnable));
+  e_Buffer_Write(@NetOut, it.Obj.X);
+  e_Buffer_Write(@NetOut, it.Obj.Y);
+  e_Buffer_Write(@NetOut, it.Obj.Vel.X);
+  e_Buffer_Write(@NetOut, it.Obj.Vel.Y);
 
   g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
 end;
@@ -1223,6 +1261,16 @@ begin
       TP := gWalls[PID];
     PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
       TP := gLifts[PID];
+    PANEL_BACK:
+    begin
+      TP := gRenderBackgrounds[PID];
+      TP.Moved := True;
+    end;
+    PANEL_FORE:
+    begin
+      TP := gRenderForegrounds[PID];
+      TP.Moved := True;
+    end;
     else
       Exit;
   end;
@@ -1232,6 +1280,8 @@ begin
   e_Buffer_Write(@NetOut, PID);
   e_Buffer_Write(@NetOut, Byte(TP.Enabled));
   e_Buffer_Write(@NetOut, TP.LiftType);
+  e_Buffer_Write(@NetOut, TP.X);
+  e_Buffer_Write(@NetOut, TP.Y);
 
   g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
 end;
@@ -1339,6 +1389,7 @@ begin
     e_Buffer_Write(@NetOut, MonsterAmmo);
     e_Buffer_Write(@NetOut, MonsterPain);
     e_Buffer_Write(@NetOut, Byte(AnimIsReverse));
+    e_Buffer_Write(@NetOut, FFireTime);
   end;
 
   g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
@@ -1535,11 +1586,18 @@ procedure MC_RECV_Sound(P: Pointer);
 var
   Name: string;
   X, Y: Integer;
+  Pos: Boolean;
 begin
   Name := e_Raw_Read_String(P);
-  X := e_Raw_Read_LongInt(P);
-  Y := e_Raw_Read_LongInt(P);
-  g_Sound_PlayExAt(Name, X, Y);
+  Pos := e_Raw_Read_Byte(P) <> 0;
+  if Pos then
+  begin
+    X := e_Raw_Read_LongInt(P);
+    Y := e_Raw_Read_LongInt(P);
+    g_Sound_PlayExAt(Name, X, Y);
+  end
+  else
+    g_Sound_PlayEx(Name);
 end;
 
 procedure MC_RECV_CreateShot(P: Pointer);
@@ -2081,13 +2139,13 @@ begin
     Lives := e_Raw_Read_Byte(P);
     NewTeam := e_Raw_Read_Byte(P);
 
-    for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
+    for I := WP_FIRST to WP_LAST do
       FWeapon[I] := (e_Raw_Read_Byte(P) <> 0);
 
-    for I := A_BULLETS to A_CELLS do
+    for I := A_BULLETS to A_HIGH do
       FAmmo[I] := e_Raw_Read_Word(P);
 
-    for I := A_BULLETS to A_CELLS do
+    for I := A_BULLETS to A_HIGH do
       FMaxAmmo[I] := e_Raw_Read_Word(P);
 
     for I := MR_SUIT to MR_MAX do
@@ -2136,6 +2194,7 @@ begin
     FNoRespawn := e_Raw_Read_Byte(P) <> 0;
     OldJet := FJetpack;
     FJetpack := e_Raw_Read_Byte(P) <> 0;
+    FFireTime := e_Raw_Read_LongInt(P);
     if OldJet and not FJetpack then
       JetpackOff
     else if not OldJet and FJetpack then
@@ -2290,6 +2349,7 @@ var
   T: Byte;
   Quiet, Fall{, Resp}: Boolean;
   Anim: TAnimation;
+  it: PItem;
 begin
   if not gGameOn then Exit;
   ID := e_Raw_Read_Word(P);
@@ -2303,8 +2363,10 @@ begin
   VY := e_Raw_Read_LongInt(P);
 
   g_Items_Create(X, Y, T, Fall, False, False, ID);
-  gItems[ID].Obj.Vel.X := VX;
-  gItems[ID].Obj.Vel.Y := VY;
+
+  it := g_ItemByIdx(ID);
+  it.Obj.Vel.X := VX;
+  it.Obj.Vel.Y := VY;
 
   if not Quiet then
   begin
@@ -2312,7 +2374,7 @@ begin
     if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
     begin
       Anim := TAnimation.Create(AID, False, 4);
-      g_GFX_OnceAnim(X+(gItems[ID].Obj.Rect.Width div 2)-16, Y+(gItems[ID].Obj.Rect.Height div 2)-16, Anim);
+      g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim);
       Anim.Free();
     end;
   end;
@@ -2326,42 +2388,10 @@ begin
   if not gGameOn then Exit;
   ID := e_Raw_Read_Word(P);
   Quiet := e_Raw_Read_Byte(P) <> 0;
-  if gItems = nil then Exit;
-  if (ID > High(gItems)) then Exit;
 
-  if not Quiet then
-    if gSoundEffectsDF then
-    begin
-      if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
-                                 ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
-        g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
-          gItems[ID].Obj.X, gItems[ID].Obj.Y)
-        else
-          if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
-                                     ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
-                                     ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_AMMO_BACKPACK] then
-            g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
-              gItems[ID].Obj.X, gItems[ID].Obj.Y)
-            else
-              g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
-                gItems[ID].Obj.X, gItems[ID].Obj.Y);
-    end
-    else
-    begin
-      if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
-                                 ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
-        g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
-          gItems[ID].Obj.X, gItems[ID].Obj.Y)
-      else
-        if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
-                                   ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
-                                   ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET] then
-          g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
-            gItems[ID].Obj.X, gItems[ID].Obj.Y)
-        else
-          g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
-            gItems[ID].Obj.X, gItems[ID].Obj.Y);
-    end;
+  if not g_ItemValidId(ID) then exit;
+
+  if not Quiet then g_Item_EmitPickupSound(ID);
 
   g_Items_Remove(ID);
 end;
@@ -2427,12 +2457,15 @@ var
   E: Boolean;
   Lift: Byte;
   PType: Word;
+  X, Y: Integer;
 begin
   if not gGameOn then Exit;
   PType := e_Raw_Read_Word(P);
   ID := e_Raw_Read_LongWord(P);
   E := (e_Raw_Read_Byte(P) <> 0);
   Lift := e_Raw_Read_Byte(P);
+  X := e_Raw_Read_LongInt(P);
+  Y := e_Raw_Read_LongInt(P);
 
   case PType of
     PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
@@ -2443,6 +2476,18 @@ begin
 
     PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
       g_Map_SetLift(ID, Lift);
+
+    PANEL_BACK:
+    begin
+      gRenderBackgrounds[ID].X := X;
+      gRenderBackgrounds[ID].Y := Y;
+    end;
+
+    PANEL_FORE:
+    begin
+      gRenderForegrounds[ID].X := X;
+      gRenderForegrounds[ID].Y := Y;
+    end;      
   end;
 end;
 
@@ -2560,6 +2605,8 @@ begin
     SetHealth(MHealth);
 
     SetState(MState);
+
+    positionChanged(); // this updates spatial accelerators
   end;
 end;
 
@@ -2580,6 +2627,7 @@ begin
     GameVelX := e_Raw_Read_LongInt(P);
     GameVelY := e_Raw_Read_LongInt(P);
     GameDirection := TDirection(e_Raw_Read_Byte(P));
+    positionChanged(); // this updates spatial accelerators
   end;
 end;
 
@@ -2606,6 +2654,7 @@ begin
     MonsterAmmo := e_Raw_Read_LongInt(P);
     MonsterPain := e_Raw_Read_LongInt(P);
     AnimRevert := e_Raw_Read_Byte(P) <> 0;
+    FFireTime := e_Raw_Read_LongInt(P);
     RevertAnim(AnimRevert);
 
     if MonsterState <> MState then
@@ -2722,10 +2771,20 @@ begin
   g_Net_Client_Send(True, NET_CHAN_CHAT);
 end;
 
+function isKeyPressed (key1: Word; key2: Word): Boolean;
+begin
+  if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
+  if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
+  result := false;
+end;
+
 procedure MC_SEND_PlayerPos();
 var
   kByte: Word;
   Predict: Boolean;
+  strafeDir: Byte;
+  WeaponSelect: Word = 0;
+  I: Integer;
 begin
   if not gGameOn then Exit;
   if gPlayers = nil then Exit;
@@ -2735,25 +2794,30 @@ begin
   Predict := NetPredictSelf; // and (not NetGotKeys);
 
   if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
+  begin
+    strafeDir := P1MoveButton shr 4;
+    P1MoveButton := P1MoveButton and $0F;
     with gGameControls.P1Control do
     begin
-      if e_KeyPressed(KeyLeft) and (not e_KeyPressed(KeyRight)) then
-        P1MoveButton := 1
-      else
-        if (not e_KeyPressed(KeyLeft)) and e_KeyPressed(KeyRight) then
-          P1MoveButton := 2
-        else
-          if (not e_KeyPressed(KeyLeft)) and (not e_KeyPressed(KeyRight)) then
-            P1MoveButton := 0;
+           if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 1
+      else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then P1MoveButton := 2
+      else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 0;
 
-      if (P1MoveButton = 2) and e_KeyPressed(KeyLeft) then
-        gPlayer1.SetDirection(D_LEFT)
+      // strafing
+      if isKeyPressed(KeyStrafe, KeyStrafe2) then
+      begin
+        // new strafe mechanics
+        if (strafeDir = 0) then strafeDir := P1MoveButton; // start strafing
+        // now set direction according to strafe (reversed)
+             if (strafeDir = 2) then gPlayer1.SetDirection(D_LEFT)
+        else if (strafeDir = 1) then gPlayer1.SetDirection(D_RIGHT);
+      end
       else
-       if (P1MoveButton = 1) and e_KeyPressed(KeyRight) then
-          gPlayer1.SetDirection(D_RIGHT)
-        else
-          if P1MoveButton <> 0 then
-            gPlayer1.SetDirection(TDirection(P1MoveButton-1));
+      begin
+             if (P1MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then gPlayer1.SetDirection(D_LEFT)
+        else if (P1MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then gPlayer1.SetDirection(D_RIGHT)
+        else if P1MoveButton <> 0 then gPlayer1.SetDirection(TDirection(P1MoveButton-1));
+      end;
 
       gPlayer1.ReleaseKeys;
       if P1MoveButton = 1 then
@@ -2766,26 +2830,32 @@ begin
         kByte := kByte or NET_KEY_RIGHT;
         if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
       end;
-      if e_KeyPressed(KeyUp) then
+      if isKeyPressed(KeyUp, KeyUp2) then
       begin
         kByte := kByte or NET_KEY_UP;
         gPlayer1.PressKey(KEY_UP, 10000);
       end;
-      if e_KeyPressed(KeyDown) then
+      if isKeyPressed(KeyDown, KeyDown2) then
       begin
         kByte := kByte or NET_KEY_DOWN;
         gPlayer1.PressKey(KEY_DOWN, 10000);
       end;
-      if e_KeyPressed(KeyJump) then
+      if isKeyPressed(KeyJump, KeyJump2) then
       begin
         kByte := kByte or NET_KEY_JUMP;
         // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
       end;
-      if e_KeyPressed(KeyFire) then kByte := kByte or NET_KEY_FIRE;
-      if e_KeyPressed(KeyOpen) then kByte := kByte or NET_KEY_OPEN;
-      if e_KeyPressed(KeyNextWeapon) then kByte := kByte or NET_KEY_NW;
-      if e_KeyPressed(KeyPrevWeapon) then kByte := kByte or NET_KEY_PW;
-    end
+      if isKeyPressed(KeyFire, KeyFire2) then kByte := kByte or NET_KEY_FIRE;
+      if isKeyPressed(KeyOpen, KeyOpen2) then kByte := kByte or NET_KEY_OPEN;
+      if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then kByte := kByte or NET_KEY_NW;
+      if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then kByte := kByte or NET_KEY_PW;
+      for I := 0 to High(KeyWeapon) do
+        if isKeyPressed(KeyWeapon[I], KeyWeapon2[I]) then
+          WeaponSelect := WeaponSelect or Word(1 shl I);
+    end;
+    // fix movebutton state
+    P1MoveButton := P1MoveButton or (strafeDir shl 4);
+  end
   else
     kByte := NET_KEY_CHAT;
 
@@ -2793,6 +2863,8 @@ begin
   e_Buffer_Write(@NetOut, gTime);
   e_Buffer_Write(@NetOut, kByte);
   e_Buffer_Write(@NetOut, Byte(gPlayer1.Direction));
+  e_Buffer_Write(@NetOut, WeaponSelect);
+  //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
   g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
 
   //kBytePrev := kByte;