From 8bce2673b0700750c270ec61fb8ed42b6956549a Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Sun, 6 Aug 2017 04:54:36 +0300 Subject: [PATCH] implemented weapon queue (no network code for it yet) it is now possible to assign one key to several weapons it is now possible to switch between sg/ssg with one key switching to a new weapon while current one is reloading is correctly processed when reload complete --- src/game/g_game.pas | 14 ++- src/game/g_netmsg.pas | 2 +- src/game/g_player.pas | 227 +++++++++++++++++++++++++++--------------- 3 files changed, 155 insertions(+), 88 deletions(-) diff --git a/src/game/g_game.pas b/src/game/g_game.pas index c3f4b58..91e0a9d 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -1283,10 +1283,7 @@ begin for i := 0 to High(KeyWeapon) do if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then - begin - plr.ForceWeapon(i); - Break; - end; + plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best end; end; @@ -1536,6 +1533,9 @@ begin begin if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000); end; + // process weapon switch queue + if gPlayer1 <> nil then gPlayer1.RealizeCurrentWeapon(); + if gPlayer2 <> nil then gPlayer2.RealizeCurrentWeapon(); end; // if server // Íàáëþäàòåëü @@ -4839,6 +4839,12 @@ begin if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got jetpack'); continue; end; if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got envirosuit'); continue; end; if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got berserk pack'); continue; end; + if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a shotgun'); continue; end; + if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a supershotgun'); continue; end; + if (cmd = 'chain') or (cmd = 'chaingun') then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a chaingun'); continue; end; + if (cmd = 'launcher') or (cmd = 'rocketlauncher') then begin plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER); plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got a rocket launcher'); continue; end; + if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a plasma gun'); continue; end; + if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a BFG-9000'); continue; end; g_Console_Add('i don''t know how to give '''+cmd+'''!'); end; exit; diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index abc65a0..6e8106f 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -476,7 +476,7 @@ begin if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000); if WeaponSelect <> 255 then - ForceWeapon(WeaponSelect); + QueueWeaponSwitch(WeaponSelect); end; // MH_SEND_PlayerPos(False, PID, C^.ID); diff --git a/src/game/g_player.pas b/src/game/g_player.pas index caddc0d..989f11a 100644 --- a/src/game/g_player.pas +++ b/src/game/g_player.pas @@ -112,6 +112,7 @@ type Air: Integer; JetFuel: Integer; CurrWeap: Byte; + NextWeap: WORD; Ammo: Array [A_BULLETS..A_CELLS] of Word; MaxAmmo: Array [A_BULLETS..A_CELLS] of Word; Weapon: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Boolean; @@ -151,6 +152,7 @@ type FFlag: Byte; FSecrets: Integer; FCurrWeap: Byte; + FNextWeap: WORD; FBFGFireCounter: SmallInt; FLastSpawnerUID: Word; FLastHit: Byte; @@ -205,6 +207,10 @@ type procedure Jump(); procedure Use(); + procedure cycleWeapon (dir: Integer); + function getNextWeaponIndex (): Byte; // return 255 for "no switch" + procedure resetWeaponQueue (); + public FDamageBuffer: Integer; @@ -287,7 +293,8 @@ type procedure NetFire(Wpn: Byte; X, Y, AX, AY: Integer; WID: Integer = -1); procedure DoLerp(Level: Integer = 2); procedure SetLerp(XTo, YTo: Integer); - procedure ForceWeapon(Weapon: Byte); + procedure QueueWeaponSwitch(Weapon: Byte); + procedure RealizeCurrentWeapon(); procedure JetpackOn; procedure JetpackOff; @@ -798,6 +805,8 @@ begin Mem.ReadInt(gPlayers[a].FSecrets); // Òåêóùåå îðóæèå: Mem.ReadByte(gPlayers[a].FCurrWeap); +// Ñëåäóþùåå æåëàåìîå îðóæèå: + Mem.ReadWord(gPlayers[a].FNextWeap); // Âðåìÿ çàðÿäêè BFG: Mem.ReadSmallInt(gPlayers[a].FBFGFireCounter); // Áóôåð óðîíà: @@ -3251,111 +3260,105 @@ begin 150, 0, 0); end; -procedure TPlayer.ForceWeapon(Weapon: Byte); -var - i: Byte; +procedure TPlayer.QueueWeaponSwitch(Weapon: Byte); begin if g_Game_IsClient then Exit; if Weapon > High(FWeapon) then Exit; - if FBFGFireCounter <> -1 then Exit; + FNextWeap := FNextWeap or (1 shl Weapon); +end; - if FTime[T_SWITCH] > gTime then Exit; +procedure TPlayer.resetWeaponQueue (); +begin + FNextWeap := 0; +end; - for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do - if FReloading[i] > 0 then Exit; +// return 255 for "no switch"; resets `FNextWeap` +function TPlayer.getNextWeaponIndex (): Byte; +var + i: Word; + wantThisWeapon: array[0..64] of Boolean; +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 + 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; + 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 + for i := High(FWeapon) downto 0 do + begin + if wantThisWeapon[i] and FWeapon[i] then + begin + // i found her! + result := Byte(i); + exit; + end; + end; +end; + +procedure TPlayer.RealizeCurrentWeapon(); +var + i, nw: Byte; +begin + nw := getNextWeaponIndex(); + if nw > High(FWeapon) then begin resetWeaponQueue(); exit; end; // don't forget to reset queue here! + + if FBFGFireCounter <> -1 then exit; + if FTime[T_SWITCH] > gTime then exit; - if FWeapon[Weapon] then + for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do if FReloading[i] > 0 then exit; + + if FWeapon[nw] then begin - FCurrWeap := Weapon; + FCurrWeap := nw; FTime[T_SWITCH] := gTime+156; - if FCurrWeap = WEAPON_SAW then - FSawSoundSelect.PlayAt(FObj.X, FObj.Y); + if FCurrWeap = WEAPON_SAW then FSawSoundSelect.PlayAt(FObj.X, FObj.Y); FModel.SetWeapon(FCurrWeap); if g_Game_IsNet then MH_SEND_PlayerStats(FUID); end; + // reset weapon queue; `getNextWeaponIndex()` guarantees to not select a weapon player don't have + resetWeaponQueue(); end; -procedure TPlayer.NextWeapon(); +procedure TPlayer.cycleWeapon (dir: Integer); var - i: Byte; - ok: Boolean; + i, cwi: Integer; 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; - - for i := FCurrWeap+1 to WEAPON_SUPERPULEMET do - if FWeapon[i] then + 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); + if FWeapon[cwi] then begin - FCurrWeap := i; - ok := True; - Break; + QueueWeaponSwitch(Byte(cwi)); + exit; end; + end; +end; - if not ok then - for i := WEAPON_KASTET to FCurrWeap-1 do - if FWeapon[i] then - begin - FCurrWeap := i; - Break; - 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); +procedure TPlayer.NextWeapon(); +begin + if g_Game_IsClient then Exit; + cycleWeapon(1); end; procedure TPlayer.PrevWeapon(); -var - i: Byte; - ok: Boolean; 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; - - 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; - - 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); + cycleWeapon(-1); end; procedure TPlayer.SetWeapon(W: Byte); @@ -3366,6 +3369,7 @@ begin FCurrWeap := W; FModel.SetWeapon(CurrWeap); + resetWeaponQueue(); end; function TPlayer.PickItem(ItemType: Byte; respawn: Boolean; var remove: Boolean): Boolean; @@ -3686,6 +3690,7 @@ begin if FBFGFireCounter = -1 then begin FCurrWeap := WEAPON_KASTET; + resetWeaponQueue(); FModel.SetWeapon(WEAPON_KASTET); end; if gFlash <> 0 then @@ -4040,6 +4045,7 @@ begin FWeapon[WEAPON_PISTOL] := True; FWeapon[WEAPON_KASTET] := True; FCurrWeap := WEAPON_PISTOL; + resetWeaponQueue(); FModel.SetWeapon(FCurrWeap); @@ -5193,6 +5199,7 @@ begin FSavedState.Air := FAir; FSavedState.JetFuel := FJetFuel; FSavedState.CurrWeap := FCurrWeap; + FSavedState.NextWeap := FNextWeap; for i := 0 to 3 do FSavedState.Ammo[i] := FAmmo[i]; @@ -5214,6 +5221,7 @@ begin FAir := FSavedState.Air; FJetFuel := FSavedState.JetFuel; FCurrWeap := FSavedState.CurrWeap; + FNextWeap := FSavedState.NextWeap; for i := 0 to 3 do FAmmo[i] := FSavedState.Ammo[i]; @@ -5292,6 +5300,8 @@ begin Mem.WriteInt(FSecrets); // Òåêóùåå îðóæèå: Mem.WriteByte(FCurrWeap); +// Æåëàåìîå îðóæèå: + Mem.WriteWord(FNextWeap); // Âðåìÿ çàðÿäêè BFG: Mem.WriteSmallInt(FBFGFireCounter); // Áóôåð óðîíà: @@ -5428,6 +5438,8 @@ begin Mem.ReadInt(FSecrets); // Òåêóùåå îðóæèå: Mem.ReadByte(FCurrWeap); +// Æåëàåìîå îðóæèå: + Mem.ReadWord(FNextWeap); // Âðåìÿ çàðÿäêè BFG: Mem.ReadSmallInt(FBFGFireCounter); // Áóôåð óðîíà: @@ -5574,6 +5586,7 @@ begin if FBFGFireCounter < 1 then begin FCurrWeap := WEAPON_KASTET; + resetWeaponQueue(); FModel.SetWeapon(WEAPON_KASTET); end; if gFlash <> 0 then @@ -5605,6 +5618,50 @@ begin FJetFuel := JET_MAX; 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_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_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 + begin + FMaxAmmo[A_BULLETS] := 400; + FMaxAmmo[A_SHELLS] := 100; + FMaxAmmo[A_ROCKETS] := 100; + FMaxAmmo[A_CELLS] := 600; + + 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; @@ -5890,6 +5947,7 @@ begin FAIFlags := nil; FSelectedWeapon := FCurrWeap; + resetWeaponQueue(); FTargetUID := 0; end; @@ -6316,6 +6374,9 @@ begin end; end; + //HACK! (does it belongs there?) + RealizeCurrentWeapon(); + // Åñëè åñòü âîçìîæíûå öåëè: // (Ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëÿì) if (targets <> nil) and (GetAIFlag('NEEDFIRE') <> '') then -- 2.29.2