X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_player.pas;h=1d036616a4884835132d9bde3643baae1f5f80c8;hb=84a25340ac602b712defba27d7e8a421a211914c;hp=989f11a095e2055a116a8a9316742798ac7ff684;hpb=8bce2673b0700750c270ec61fb8ed42b6956549a;p=d2df-sdl.git diff --git a/src/game/g_player.pas b/src/game/g_player.pas index 989f11a..1d03661 100644 --- a/src/game/g_player.pas +++ b/src/game/g_player.pas @@ -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; @@ -207,9 +209,9 @@ type procedure Jump(); procedure Use(); - procedure cycleWeapon (dir: Integer); function getNextWeaponIndex (): Byte; // return 255 for "no switch" procedure resetWeaponQueue (); + function hasAmmoForWeapon (weapon: Byte): Boolean; public FDamageBuffer: Integer; @@ -807,6 +809,8 @@ begin Mem.ReadByte(gPlayers[a].FCurrWeap); // Ñëåäóþùåå æåëàåìîå îðóæèå: Mem.ReadWord(gPlayers[a].FNextWeap); +// ...è ïàóçà: + Mem.ReadByte(gPlayers[a].FNextWeapDelay); // Âðåìÿ çàðÿäêè BFG: Mem.ReadSmallInt(gPlayers[a].FBFGFireCounter); // Áóôåð óðîíà: @@ -1264,10 +1268,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(); @@ -1969,6 +1987,8 @@ begin FBFGFireCounter := -1; FJustTeleported := False; FNetTime := 0; + + resetWeaponQueue(); end; procedure TPlayer.Damage(value: Word; SpawnerUID: Word; vx, vy: Integer; t: Byte); @@ -3270,53 +3290,121 @@ end; procedure TPlayer.resetWeaponQueue (); begin FNextWeap := 0; + FNextWeapDelay := 0; +end; + +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"; resets `FNextWeap` +// 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 + cwi := (cwi+length(FWeapon)+dir) mod length(FWeapon); + if FWeapon[cwi] then + begin + //e_WriteLog(Format(' SWITCH: cur=%d; new=%d', [FCurrWeap, cwi]), MSG_WARNING); + result := Byte(cwi); + FNextWeapDelay := 10; + exit; + end; + 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 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; + //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; - // 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 + // 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] then + 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.RealizeCurrentWeapon(); + function switchAllowed (): Boolean; + var + i: Byte; + begin + result := false; + 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; + result := true; + end; + var - i, nw: Byte; + nw: Byte; begin - nw := getNextWeaponIndex(); - if nw > High(FWeapon) then begin resetWeaponQueue(); exit; end; // don't forget to reset queue here! + //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 FBFGFireCounter <> -1 then exit; - if FTime[T_SWITCH] > gTime then exit; + if not switchAllowed then + begin + //HACK for weapon cycling + if (FNextWeap and $7000) <> 0 then FNextWeap := 0; + exit; + end; - for i := WEAPON_KASTET to WEAPON_SUPERPULEMET do if FReloading[i] > 0 then exit; + 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 FWeapon[nw] then begin @@ -3326,39 +3414,18 @@ begin 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.cycleWeapon (dir: Integer); -var - i, cwi: Integer; -begin - 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 - QueueWeaponSwitch(Byte(cwi)); - exit; - end; - end; end; procedure TPlayer.NextWeapon(); begin if g_Game_IsClient then Exit; - cycleWeapon(1); + FNextWeap := $8000; end; procedure TPlayer.PrevWeapon(); begin if g_Game_IsClient then Exit; - cycleWeapon(-1); + FNextWeap := $4000; end; procedure TPlayer.SetWeapon(W: Byte); @@ -4428,6 +4495,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 @@ -4451,8 +4522,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() @@ -5200,6 +5271,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 +5294,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 +5375,8 @@ begin Mem.WriteByte(FCurrWeap); // Æåëàåìîå îðóæèå: Mem.WriteWord(FNextWeap); +// ...è ïàóçà + Mem.WriteByte(FNextWeapDelay); // Âðåìÿ çàðÿäêè BFG: Mem.WriteSmallInt(FBFGFireCounter); // Áóôåð óðîíà: @@ -5440,6 +5515,8 @@ begin Mem.ReadByte(FCurrWeap); // Æåëàåìîå îðóæèå: Mem.ReadWord(FNextWeap); +// ...è ïàóçà + Mem.ReadByte(FNextWeapDelay); // Âðåìÿ çàðÿäêè BFG: Mem.ReadSmallInt(FBFGFireCounter); // Áóôåð óðîíà: @@ -5618,6 +5695,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; @@ -6480,6 +6571,10 @@ begin begin UpdateMove(); UpdateCombat(); + end + else + begin + RealizeCurrentWeapon(); end; end;