DEADSOFTWARE

Player: Add sounds for CTF game
[d2df-sdl.git] / src / game / g_player.pas
index 66c676c9466e00bc20ff7abc132c87c41bae873b..854d8119b44c4f0b4fe27b264e7dd273dece20bc 100644 (file)
@@ -95,6 +95,7 @@ const
   PLAYER_AP_SOFT  = 100;
   PLAYER_AP_LIMIT = 200;
   SUICIDE_DAMAGE  = 112;
+  WEAPON_DELAY    = 5;
 
   PLAYER1_DEF_COLOR: TRGB = (R:64; G:175; B:48);
   PLAYER2_DEF_COLOR: TRGB = (R:96; G:96; B:96);
@@ -163,7 +164,7 @@ type
     FSecrets:   Integer;
     FCurrWeap:  Byte;
     FNextWeap:  WORD;
-    FNextWeapDelay: Byte; // frames (unused)
+    FNextWeapDelay: Byte; // frames
     FBFGFireCounter: SmallInt;
     FLastSpawnerUID: Word;
     FLastHit:   Byte;
@@ -200,9 +201,6 @@ type
     FNetTime: LongWord;
     mEDamageType: Integer;
 
-    // client-side only
-    weaponSwitchKeyReleased: array[0..16] of Boolean; // true: was release
-
 
     function CollideLevel(XInc, YInc: Integer): Boolean;
     function StayOnStep(XInc, YInc: Integer): Boolean;
@@ -294,7 +292,7 @@ type
     procedure   BFGHit();
     function    GetFlag(Flag: Byte): Boolean;
     procedure   SetFlag(Flag: Byte);
-    function    DropFlag(): Boolean;
+    function    DropFlag(Silent: Boolean = True): Boolean;
     procedure   AllRulez(Health: Boolean);
     procedure   RestoreHealthArmor();
     procedure   FragCombo();
@@ -313,6 +311,7 @@ type
     procedure   DrawPickup();
     procedure   DrawRulez();
     procedure   DrawAim();
+    procedure   DrawIndicator();
     procedure   DrawBubble();
     procedure   DrawGUI();
     procedure   Update(); virtual;
@@ -336,10 +335,6 @@ type
     procedure getMapBox (out x, y, w, h: Integer); inline;
     procedure moveBy (dx, dy: Integer); inline;
 
-    procedure releaseAllWeaponSwitchKeys ();
-    procedure weaponSwitchKeysStateChange (index: Integer; pressed: Boolean);
-    function isWeaponSwitchKeyReleased (index: Integer): Boolean;
-
   public
     property    Vel: TPoint2i read FObj.Vel;
     property    Obj: TObj read FObj;
@@ -549,6 +544,7 @@ var
   gFly: Boolean = False;
   gAimLine: Boolean = False;
   gChatBubble: Byte = 0;
+  gPlayerIndicator: Boolean = True;
   gNumBots: Word = 0;
   gLMSPID1: Word = 0;
   gLMSPID2: Word = 0;
@@ -2135,38 +2131,8 @@ begin
   FNetTime := 0;
 
   resetWeaponQueue();
-  releaseAllWeaponSwitchKeys();
-end;
-
-
-procedure TPlayer.releaseAllWeaponSwitchKeys ();
-var
-  f: Integer;
-begin
-  for f := 0 to High(weaponSwitchKeyReleased) do weaponSwitchKeyReleased[f] := true;
-end;
-
-procedure TPlayer.weaponSwitchKeysStateChange (index: Integer; pressed: Boolean);
-begin
-  Inc(index, 2); // -2: prev; -1: next
-  if (index < 0) or (index > High(weaponSwitchKeyReleased)) then exit;
-  weaponSwitchKeyReleased[index] := not pressed;
-end;
-
-function TPlayer.isWeaponSwitchKeyReleased (index: Integer): Boolean;
-begin
-  Inc(index, 2); // -2: prev; -1: next
-  if (index < 0) or (index > High(weaponSwitchKeyReleased)) then
-  begin
-    result := true;
-  end
-  else
-  begin
-    result := weaponSwitchKeyReleased[index];
-  end;
 end;
 
-
 procedure TPlayer.positionChanged (); inline;
 begin
 end;
@@ -2310,6 +2276,25 @@ begin
   inherited;
 end;
 
+procedure TPlayer.DrawIndicator();
+var
+  indX, indY: Integer;
+  indW, indH: Word;
+  ID: DWORD;
+begin
+  if FAlive then
+    begin
+      indX := FObj.X+FObj.Rect.X;
+      indY := FObj.Y;
+      if g_Texture_Get('TEXTURE_PLAYER_INDICATOR', ID) then
+        begin
+          e_GetTextureSize(ID, @indW, @indH);
+          e_Draw(ID, indX + indW div 2, indY - indH, 0, True, False);
+        end;
+    end;
+  //e_TextureFontPrint(indX, indY, FName, gStdFont); // Shows player name overhead
+end;
+
 procedure TPlayer.DrawBubble();
 var
   bubX, bubY: Integer;
@@ -2459,7 +2444,9 @@ begin
   end;
 
   if (gChatBubble > 0) and (FKeys[KEY_CHAT].Pressed) and not FGhost then
-    DrawBubble();
+    if (FMegaRulez[MR_INVIS] <= gTime) or ((gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or
+       ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM)))) then
+      DrawBubble();
  // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR
   if gAimLine and alive and
   ((Self = gPlayer1) or (Self = gPlayer2)) then
@@ -3416,7 +3403,7 @@ begin
     end;
 
 // Âûáðîñ ôëàãà:
-    DropFlag();
+    DropFlag(KillType = K_FALLKILL);
   end;
 
   g_Player_CreateCorpse(Self);
@@ -3590,8 +3577,8 @@ begin
   result := false;
   case weapon of
     WEAPON_KASTET, WEAPON_SAW: result := true;
-    WEAPON_SHOTGUN1, WEAPON_SHOTGUN2, WEAPON_SUPERPULEMET: result := (FAmmo[A_SHELLS] > 0);
-    WEAPON_PISTOL, WEAPON_CHAINGUN: result := (FAmmo[A_BULLETS] > 0);
+    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);
@@ -3604,167 +3591,83 @@ function TPlayer.getNextWeaponIndex (): Byte;
 var
   i: Word;
   wantThisWeapon: array[0..64] of Boolean;
-  weaponOrder: array[0..16] of Integer; // value: index in `FWeapon`
-  wwc: Integer;
-  f, dir, cwi, rwidx, curlidx: Integer;
-
-  function real2log (ridx: Integer): Integer;
-  var
-    f: Integer;
-  begin
-    if (ridx >= 0) then
-    begin
-      for f := 0 to High(weaponOrder) do if (weaponOrder[f] = ridx) then begin result := f; exit; end;
-    end;
-    result := -1;
-  end;
-
+  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;
-
-  for f := 0 to High(weaponOrder) do weaponOrder[f] := -1;
-
-  // build weapon order (k8: i know, i know, learn how to do constants and such... gtfo, please!)
-  // two knuckles are for "normal" and "berserk" (see `if` below -- it removes the one that is not needed)
-  // priorities:
-  //   bfg, launcher, plasma, flamethrower, ssg, minigun, sg, pistol, berserk, chainsaw, fist
-  weaponOrder[0] := WEAPON_SUPERPULEMET;
-  weaponOrder[1] := WEAPON_BFG;
-  weaponOrder[2] := WEAPON_ROCKETLAUNCHER;
-  weaponOrder[3] := WEAPON_PLASMA;
-  weaponOrder[4] := WEAPON_FLAMETHROWER;
-  weaponOrder[5] := WEAPON_SHOTGUN2;
-  weaponOrder[6] := WEAPON_CHAINGUN;
-  weaponOrder[7] := WEAPON_SHOTGUN1;
-  weaponOrder[8] := WEAPON_PISTOL;
-  weaponOrder[9] := WEAPON_KASTET+666; // berserk fist
-  weaponOrder[10] := WEAPON_SAW;
-  weaponOrder[11] := WEAPON_KASTET; // normal fist
-
-  for f := 0 to High(weaponOrder) do
-  begin
-    if (weaponOrder[f] = WEAPON_KASTET) then
-    begin
-      // normal fist: remove if we have a berserk pack
-      if (R_BERSERK in FRulez) then weaponOrder[f] := -1;
-    end
-    else
-    if (weaponOrder[f] = WEAPON_KASTET+666) then
-    begin
-      // berserk fist: remove if we don't have a berserk pack
-      if (R_BERSERK in FRulez) then weaponOrder[f] := WEAPON_KASTET else weaponOrder[f] := -1;
-    end;
-  end;
-
-  (*
-  WEAPON_KASTET         = 0;
-  WEAPON_SAW            = 1;
-  WEAPON_PISTOL         = 2;
-  WEAPON_SHOTGUN1       = 3;
-  WEAPON_SHOTGUN2       = 4;
-  WEAPON_CHAINGUN       = 5;
-  WEAPON_ROCKETLAUNCHER = 6;
-  WEAPON_PLASMA         = 7;
-  WEAPON_BFG            = 8;
-  WEAPON_SUPERPULEMET   = 9;
-  WEAPON_FLAMETHROWER   = 10;
-  *)
-
   // cycling has priority
   if (FNextWeap and $C000) <> 0 then
   begin
-    if (FNextWeap and $8000) <> 0 then dir := 1 else dir := -1; // should be reversed if we want "priority-driven cycling"
+    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 := real2log(FCurrWeap);
-    //if (cwi < 0) then cwi := 0;
+    if FNextWeapDelay > 0 then
+      exit; // cooldown time
     cwi := FCurrWeap;
     for i := 0 to High(FWeapon) do
     begin
       cwi := (cwi+length(FWeapon)+dir) mod length(FWeapon);
-      //rwidx := weaponOrder[cwi];
-      rwidx := cwi; // sorry
-      if (rwidx < 0) then continue;
-      if FWeapon[rwidx] then
-      begin
-        //e_WriteLog(Format(' SWITCH: cur=%d; new=%d (dir=%d; log=%d)', [FCurrWeap, rwidx, dir, cwi]), TMsgType.Warning);
-        result := Byte(rwidx);
-        //FNextWeapDelay := 10; //k8: not needed anymore
+      if FWeapon[cwi] then
+      begin
+        //e_WriteLog(Format(' SWITCH: cur=%d; new=%d', [FCurrWeap, cwi]), MSG_WARNING);
+        result := Byte(cwi);
+        FNextWeapDelay := WEAPON_DELAY;
         exit;
       end;
     end;
     resetWeaponQueue();
     exit;
   end;
-
   // no cycling
-  for i := 0 to High(wantThisWeapon) do wantThisWeapon[i] := false;
-  wwc := 0;
+  for i := 0 to High(wantThisWeapon) do
+    wantThisWeapon[i] := false;
   for i := 0 to High(FWeapon) do
-  begin
     if (FNextWeap and (1 shl i)) <> 0 then
     begin
-      cwi := real2log(i);
-      if (cwi >= 0) then wantThisWeapon[cwi] := true;
+      wantThisWeapon[i] := true;
       Inc(wwc);
     end;
-  end;
-
+  // exclude currently selected weapon from the set
+  wantThisWeapon[FCurrWeap] := false;
   // slow down alterations a little
-  if (wwc > 1) then
+  if wwc > 1 then
   begin
-    // more than one weapon requested, assume "alteration", and check alteration delay
+    //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
-      //e_WriteLog(Format(' FNextWeap=%x; delay=%d', [FNextWeap, FNextWeapDelay]), TMsgType.Warning);
       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
+  if wwc < 1 then
   begin
     resetWeaponQueue();
     exit;
   end;
-
-  // exclude currently selected weapon from the set
-  curlidx := real2log(FCurrWeap);
-  //e_WriteLog(Format('*** wwc=%d; currweap=%d; logweap=%d', [wwc, FCurrWeap, curlidx]), TMsgType.Warning);
-  //e_WriteLog(Format('FCurrWeap=%d; curlidx=%d', [FCurrWeap, curlidx]), TMsgType.Warning);
-  //if (curlidx >= 0) then wantThisWeapon[curlidx] := false;
-  //if (wwc = 2) then begin Dec(wwc); end; // easy case: switch between two weapons
-
-  // find next weapon to switch onto
-  cwi := curlidx;
-  for i := 0 to High(weaponOrder) do
-  begin
-    cwi := (cwi+length(weaponOrder)+1) mod length(weaponOrder);
-    if (cwi = curlidx) then continue; // skip current weapon
-    if not wantThisWeapon[cwi] then continue;
-    rwidx := weaponOrder[cwi];
-    if (rwidx < 0) then continue;
-    //e_WriteLog(Format('  trying logical %d (real %d); has=%d, hasammo=%d', [cwi, rwidx, Integer(FWeapon[rwidx]), Integer(hasAmmoForWeapon(rwidx))]), TMsgType.Warning);
-    if FWeapon[rwidx] and ((wwc = 1) or hasAmmoForWeapon(rwidx)) then
+  //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
-      //e_WriteLog('    I FOUND HER!', TMsgType.Warning);
       // i found her!
-      result := Byte(rwidx);
+      result := Byte(i);
       resetWeaponQueue();
-      //FNextWeapDelay := 10; // anyway, 'cause why not; k8: not needed anymore
+      FNextWeapDelay := WEAPON_DELAY; // 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;
@@ -3790,9 +3693,7 @@ var
 begin
   //e_WriteLog(Format('***RealizeCurrentWeapon: FNextWeap=%x; FNextWeapDelay=%d', [FNextWeap, FNextWeapDelay]), MSG_WARNING);
   //FNextWeap := FNextWeap and $1FFF;
-  //HACK: alteration delay will be reset when player released any weapon switch key
-  FNextWeapDelay := 0; //k8: just in case
-  //if FNextWeapDelay > 0 then Dec(FNextWeapDelay); // "alteration delay"
+  if FNextWeapDelay > 0 then Dec(FNextWeapDelay); // "alteration delay"
 
   if not switchAllowed then
   begin
@@ -3806,7 +3707,7 @@ begin
   if nw > High(FWeapon) then
   begin
     // don't forget to reset queue here!
-    //e_WriteLog(Format(' RealizeCurrentWeapon: WUTAFUUUU?! (%d)', [nw]), TMsgType.Warning);
+    //e_WriteLog(' RealizeCurrentWeapon: WUTAFUUUU', MSG_WARNING);
     resetWeaponQueue();
     exit;
   end;
@@ -5645,7 +5546,7 @@ end;
 function TPlayer.GetFlag(Flag: Byte): Boolean;
 var
   s, ts: String;
-  evtype: Byte;
+  evtype, a: Byte;
 begin
   Result := False;
 
@@ -5673,6 +5574,16 @@ begin
     g_Map_ResetFlag(FFlag);
     g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
 
+    if ((Self = gPlayer1) or (Self = gPlayer2)
+    or ((gPlayer1 <> nil) and (gPlayer1.Team = FTeam))
+    or ((gPlayer2 <> nil) and (gPlayer2.Team = FTeam))) then
+      a := 0
+    else
+      a := 1;
+
+    if not sound_cap_flag[a].IsPlaying() then
+      sound_cap_flag[a].Play();
+
     gTeamStat[FTeam].Goals := gTeamStat[FTeam].Goals + 1;
 
     Result := True;
@@ -5704,6 +5615,16 @@ begin
     g_Map_ResetFlag(Flag);
     g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
 
+    if ((Self = gPlayer1) or (Self = gPlayer2)
+    or ((gPlayer1 <> nil) and (gPlayer1.Team = FTeam))
+    or ((gPlayer2 <> nil) and (gPlayer2.Team = FTeam))) then
+      a := 0
+    else
+      a := 1;
+
+    if not sound_ret_flag[a].IsPlaying() then
+      sound_ret_flag[a].Play();
+
     Result := True;
     if g_Game_IsNet then
     begin
@@ -5731,6 +5652,16 @@ begin
 
     gFlags[Flag].State := FLAG_STATE_CAPTURED;
 
+    if ((Self = gPlayer1) or (Self = gPlayer2)
+    or ((gPlayer1 <> nil) and (gPlayer1.Team = FTeam))
+    or ((gPlayer2 <> nil) and (gPlayer2.Team = FTeam))) then
+      a := 0
+    else
+      a := 1;
+
+    if not sound_get_flag[a].IsPlaying() then
+      sound_get_flag[a].Play();
+
     Result := True;
     if g_Game_IsNet then
     begin
@@ -5747,9 +5678,10 @@ begin
     FModel.SetFlag(FFlag);
 end;
 
-function TPlayer.DropFlag(): Boolean;
+function TPlayer.DropFlag(Silent: Boolean = True): Boolean;
 var
   s: String;
+  a: Byte;
 begin
   Result := False;
   if (not g_Game_IsServer) or (FFlag = FLAG_NONE) then
@@ -5774,6 +5706,16 @@ begin
     g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [FName, s]), True);
     g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
 
+    if ((Self = gPlayer1) or (Self = gPlayer2)
+    or ((gPlayer1 <> nil) and (gPlayer1.Team = FTeam))
+    or ((gPlayer2 <> nil) and (gPlayer2.Team = FTeam))) then
+      a := 0
+    else
+      a := 1;
+
+    if (not Silent) and (not sound_lost_flag[a].IsPlaying()) then
+      sound_lost_flag[a].Play();
+
     if g_Game_IsNet then
       MH_SEND_FlagEvent(FLAG_STATE_DROPPED, Flag, FUID, False);
   end;