DEADSOFTWARE

fixed bug with weapon cycling direction
[d2df-sdl.git] / src / game / g_player.pas
index 989f11a095e2055a116a8a9316742798ac7ff684..bfba263a40cf6fb9dbd08781f419d92a7b0b1f9d 100644 (file)
@@ -113,6 +113,7 @@ type
     JetFuel:    Integer;
     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;
@@ -153,6 +154,7 @@ type
     FSecrets:   Integer;
     FCurrWeap:  Byte;
     FNextWeap:  WORD;
+    FNextWeapDelay: Byte; // frames
     FBFGFireCounter: SmallInt;
     FLastSpawnerUID: Word;
     FLastHit:   Byte;
@@ -210,6 +212,7 @@ type
     procedure cycleWeapon (dir: Integer);
     function getNextWeaponIndex (): Byte; // return 255 for "no switch"
     procedure resetWeaponQueue ();
+    function hasAmmoForWeapon (weapon: Byte): Boolean;
 
   public
     FDamageBuffer:   Integer;
@@ -807,6 +810,8 @@ begin
   Mem.ReadByte(gPlayers[a].FCurrWeap);
 // Ñëåäóþùåå æåëàåìîå îðóæèå:
   Mem.ReadWord(gPlayers[a].FNextWeap);
+// ...è ïàóçà:
+  Mem.ReadByte(gPlayers[a].FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.ReadSmallInt(gPlayers[a].FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -1266,8 +1271,11 @@ begin
 
   for i := 0 to High(gPlayers) do
     if gPlayers[i] <> nil then
+    begin
+      gPlayers[i].RealizeCurrentWeapon();
       if gPlayers[i] is TPlayer then gPlayers[i].Update()
       else TBot(gPlayers[i]).Update();
+    end;
 end;
 
 procedure g_Player_DrawAll();
@@ -1969,6 +1977,8 @@ begin
   FBFGFireCounter := -1;
   FJustTeleported := False;
   FNetTime := 0;
+
+  resetWeaponQueue();
 end;
 
 procedure TPlayer.Damage(value: Word; SpawnerUID: Word; vx, vy: Integer; t: Byte);
@@ -3270,40 +3280,55 @@ end;
 procedure TPlayer.resetWeaponQueue ();
 begin
   FNextWeap := 0;
+  FNextWeapDelay := 0;
 end;
 
-// return 255 for "no switch"; resets `FNextWeap`
+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);
+    else result := (weapon < length(FWeapon));
+  end;
+end;
+
+// return 255 for "no switch"
 function TPlayer.getNextWeaponIndex (): Byte;
 var
   i: Word;
   wantThisWeapon: array[0..64] of Boolean;
+  wwc: Integer = 0; //HACK!
 begin
   result := 255; // default result: "no switch"
   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 wantThisWeapon[i] := true;
-  if wantThisWeapon[FCurrWeap] then
+  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
-    // these hacks implements alternating between SG and SSG; sorry
-    if FCurrWeap = WEAPON_SHOTGUN1 then wantThisWeapon[WEAPON_SHOTGUN2] := true;
-    if FCurrWeap = WEAPON_SHOTGUN2 then wantThisWeapon[WEAPON_SHOTGUN1] := true;
-    // these hacks implements alternating between knuckles and chainsaw; sorry
-    if FCurrWeap = WEAPON_KASTET then wantThisWeapon[WEAPON_SAW] := true;
-    if FCurrWeap = WEAPON_SAW then wantThisWeapon[WEAPON_KASTET] := true;
+    // more than one weapon requested, assume "alteration" and check alteration delay
+    if FNextWeapDelay > 0 then begin FNextWeap := 0; exit; end; // yeah
   end;
-  // now exclude currently selected weapon from the set
-  wantThisWeapon[FCurrWeap] := false;
-  // no more hacks (yet)
   // do not reset weapon queue, it will be done in `RealizeCurrentWeapon()`
-  // now try weapons in descending order
+  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] then
+    if wantThisWeapon[i] and FWeapon[i] and ((wwc = 1) or hasAmmoForWeapon(i)) then
     begin
       // i found her!
       result := Byte(i);
       exit;
     end;
   end;
+  // no suitable weapon found, so reset the queue, to avoid accidental "queuing" of weapon w/o ammo
+  resetWeaponQueue();
 end;
 
 procedure TPlayer.RealizeCurrentWeapon();
@@ -3311,6 +3336,8 @@ var
   i, nw: Byte;
 begin
   nw := getNextWeaponIndex();
+  if FNextWeapDelay > 0 then Dec(FNextWeapDelay); // "alteration delay"
+  if nw = 255 then exit; // don't reset anything here
   if nw > High(FWeapon) then begin resetWeaponQueue(); exit; end; // don't forget to reset queue here!
 
   if FBFGFireCounter <> -1 then exit;
@@ -3328,19 +3355,18 @@ begin
   end;
   // reset weapon queue; `getNextWeaponIndex()` guarantees to not select a weapon player don't have
   resetWeaponQueue();
+  FNextWeapDelay := 10; // anyway, 'cause why not
 end;
 
 procedure TPlayer.cycleWeapon (dir: Integer);
 var
   i, cwi: Integer;
 begin
-  if dir < 0 then dir := 1 else if dir > 0 then dir := 1 else exit;
+  if dir < 0 then dir := -1 else if dir > 0 then dir := 1 else exit;
   cwi := FCurrWeap;
   for i := 0 to High(FWeapon) do
   begin
-    cwi := cwi+dir;
-         if cwi < 0 then cwi += length(FWeapon)
-    else if cwi > High(FWeapon) then cwi := cwi-length(FWeapon);
+    cwi := (cwi+length(FWeapon)+dir) mod length(FWeapon);
     if FWeapon[cwi] then
     begin
       QueueWeaponSwitch(Byte(cwi));
@@ -5200,6 +5226,7 @@ begin
   FSavedState.JetFuel := FJetFuel;
   FSavedState.CurrWeap := FCurrWeap;
   FSavedState.NextWeap := FNextWeap;
+  FSavedState.NextWeapDelay := FNextWeapDelay;
 
   for i := 0 to 3 do
     FSavedState.Ammo[i] := FAmmo[i];
@@ -5222,6 +5249,7 @@ begin
   FJetFuel := FSavedState.JetFuel;
   FCurrWeap := FSavedState.CurrWeap;
   FNextWeap := FSavedState.NextWeap;
+  FNextWeapDelay := FSavedState.NextWeapDelay;
 
   for i := 0 to 3 do
     FAmmo[i] := FSavedState.Ammo[i];
@@ -5302,6 +5330,8 @@ begin
   Mem.WriteByte(FCurrWeap);
 // Æåëàåìîå îðóæèå:
   Mem.WriteWord(FNextWeap);
+// ...è ïàóçà
+  Mem.WriteByte(FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.WriteSmallInt(FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -5440,6 +5470,8 @@ begin
   Mem.ReadByte(FCurrWeap);
 // Æåëàåìîå îðóæèå:
   Mem.ReadWord(FNextWeap);
+// ...è ïàóçà
+  Mem.ReadByte(FNextWeapDelay);
 // Âðåìÿ çàðÿäêè BFG:
   Mem.ReadSmallInt(FBFGFireCounter);
 // Áóôåð óðîíà:
@@ -5618,6 +5650,20 @@ 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;