2006-02-24 04:48:15 +00:00
|
|
|
// Emacs style mode select -*- C++ -*-
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// $Id:$
|
|
|
|
//
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
//
|
|
|
|
// This source is available for distribution and/or modification
|
|
|
|
// only under the terms of the DOOM Source Code License as
|
|
|
|
// published by id Software. All rights reserved.
|
|
|
|
//
|
|
|
|
// The source is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
|
|
// for more details.
|
|
|
|
//
|
|
|
|
// $Log:$
|
|
|
|
//
|
|
|
|
// DESCRIPTION:
|
|
|
|
// Player related stuff.
|
|
|
|
// Bobbing POV/weapon, movement.
|
|
|
|
// Pending weapon.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "d_event.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "r_draw.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "p_pspr.h"
|
|
|
|
#include "p_enemy.h"
|
|
|
|
#include "p_effect.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "a_sharedglobal.h"
|
|
|
|
#include "statnums.h"
|
|
|
|
#include "v_palette.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "sbar.h"
|
|
|
|
#include "f_finale.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
|
|
|
|
static FRandom pr_healradius ("HealRadius");
|
|
|
|
|
|
|
|
// [RH] # of ticks to complete a turn180
|
|
|
|
#define TURN180_TICKS ((TICRATE / 4) + 1)
|
|
|
|
|
|
|
|
// Variables for prediction
|
|
|
|
CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
static player_t PredictionPlayerBackup;
|
|
|
|
static BYTE PredictionActorBackup[sizeof(AActor)];
|
|
|
|
static TArray<sector_t *> PredictionTouchingSectorsBackup;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Movement.
|
|
|
|
//
|
|
|
|
|
|
|
|
// 16 pixels of bob
|
|
|
|
#define MAXBOB 0x100000
|
|
|
|
|
|
|
|
BOOL onground;
|
|
|
|
|
|
|
|
|
|
|
|
// This function supplements the pointer cleanup in dobject.cpp, because
|
|
|
|
// player_s is not derived from DObject. (I tried it, and DestroyScan was
|
|
|
|
// unable to properly determine the player object's type--possibly
|
|
|
|
// because it gets staticly allocated in an array.)
|
|
|
|
//
|
|
|
|
// This function checks all the DObject pointers in a player_s and NULLs any
|
|
|
|
// that match the pointer passed in. If you add any pointers that point to
|
|
|
|
// DObject (or a subclass), add them here too.
|
|
|
|
|
|
|
|
void player_s::FixPointers (const DObject *old, DObject *rep)
|
|
|
|
{
|
|
|
|
APlayerPawn *replacement = static_cast<APlayerPawn *>(rep);
|
|
|
|
if (mo == old) mo = replacement;
|
|
|
|
if (poisoner == old) poisoner = replacement;
|
|
|
|
if (attacker == old) attacker = replacement;
|
|
|
|
if (camera == old) camera = replacement;
|
|
|
|
if (dest == old) dest = replacement;
|
|
|
|
if (prev == old) prev = replacement;
|
|
|
|
if (enemy == old) enemy = replacement;
|
|
|
|
if (missile == old) missile = replacement;
|
|
|
|
if (mate == old) mate = replacement;
|
|
|
|
if (last_mate == old) last_mate = replacement;
|
|
|
|
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep);
|
|
|
|
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_s::SetLogNumber (int num)
|
|
|
|
{
|
|
|
|
char lumpname[16];
|
|
|
|
int lumpnum;
|
|
|
|
|
|
|
|
sprintf (lumpname, "LOG%d", num);
|
|
|
|
lumpnum = Wads.CheckNumForName (lumpname);
|
|
|
|
if (lumpnum == -1)
|
|
|
|
{
|
|
|
|
// Leave the log message alone if this one doesn't exist.
|
|
|
|
//SetLogText (lumpname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
int length=Wads.LumpLength(lumpnum);
|
|
|
|
char *data= new char[length+1];
|
|
|
|
Wads.ReadLump (lumpnum, data);
|
|
|
|
data[length]=0;
|
|
|
|
SetLogText (data);
|
|
|
|
delete[] data;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Print log text to console
|
|
|
|
AddToConsole(-1, TEXTCOLOR_GOLD);
|
|
|
|
AddToConsole(-1, LogText);
|
|
|
|
AddToConsole(-1, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_s::SetLogText (const char *text)
|
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
LogText = text;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
IMPLEMENT_ABSTRACT_ACTOR (APlayerPawn)
|
|
|
|
IMPLEMENT_ABSTRACT_ACTOR (APlayerChunk)
|
|
|
|
|
|
|
|
void APlayerPawn::Serialize (FArchive &arc)
|
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
arc << JumpZ;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::BeginPlay ()
|
|
|
|
{
|
|
|
|
Super::BeginPlay ();
|
|
|
|
ChangeStatNum (STAT_PLAYER);
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: Tick
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::Tick()
|
|
|
|
{
|
|
|
|
if (player != NULL && player->mo == this && player->morphTics == 0 && player->playerstate != PST_DEAD)
|
|
|
|
{
|
|
|
|
yscale = FixedMul(GetDefault()->yscale, player->crouchfactor);
|
|
|
|
height = FixedMul(GetDefault()->height, player->crouchfactor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Ensure that PlayerPawns not connected to a player or morphed players are always un-crouched.
|
|
|
|
yscale = GetDefault()->yscale;
|
|
|
|
if (health > 0) height = GetDefault()->height;
|
|
|
|
}
|
|
|
|
Super::Tick();
|
|
|
|
|
|
|
|
// Here's the place where crouching sprites should be handled
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: AddInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::AddInventory (AInventory *item)
|
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
// Don't add to the inventory of dead players.
|
|
|
|
if (health<=0) return;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// Adding inventory to a voodoo doll should add it to the real player instead.
|
|
|
|
if (player != NULL && player->mo != this)
|
|
|
|
{
|
|
|
|
player->mo->AddInventory (item);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Super::AddInventory (item);
|
|
|
|
|
|
|
|
// If nothing is selected, select this item.
|
|
|
|
if (player != NULL && player->InvSel == NULL && (item->ItemFlags & IF_INVBAR))
|
|
|
|
{
|
|
|
|
player->InvSel = item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: RemoveInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::RemoveInventory (AInventory *item)
|
|
|
|
{
|
|
|
|
bool pickWeap = false;
|
|
|
|
|
|
|
|
// Since voodoo dolls aren't supposed to have an inventory, there should be
|
|
|
|
// no need to redirect them to the real player here as there is with AddInventory.
|
|
|
|
|
|
|
|
// If the item removed is the selected one, select something else, either the next
|
|
|
|
// item, if there is one, or the previous item.
|
|
|
|
if (player != NULL)
|
|
|
|
{
|
|
|
|
if (player->InvSel == item)
|
|
|
|
{
|
|
|
|
player->InvSel = item->NextInv ();
|
|
|
|
if (player->InvSel == NULL)
|
|
|
|
{
|
|
|
|
player->InvSel = item->PrevInv ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (player->InvFirst == item)
|
|
|
|
{
|
|
|
|
player->InvFirst = item->NextInv ();
|
|
|
|
if (player->InvFirst == NULL)
|
|
|
|
{
|
|
|
|
player->InvFirst = item->PrevInv ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (item == player->PendingWeapon)
|
|
|
|
{
|
|
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
|
|
}
|
|
|
|
if (item == player->ReadyWeapon)
|
|
|
|
{
|
|
|
|
// If the current weapon is removed, pick a new one.
|
|
|
|
pickWeap = true;
|
|
|
|
player->ReadyWeapon = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Super::RemoveInventory (item);
|
|
|
|
if (pickWeap && player->mo == this && player->PendingWeapon == WP_NOCHANGE)
|
|
|
|
{
|
|
|
|
PickNewWeapon (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: UseInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
bool APlayerPawn::UseInventory (AInventory *item)
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
const PClass *itemtype = item->GetClass();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (player->cheats & CF_TOTALLYFROZEN)
|
|
|
|
{ // You can't use items if you're totally frozen
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!Super::UseInventory (item))
|
|
|
|
{
|
|
|
|
// Heretic and Hexen advance the inventory cursor if the use failed.
|
|
|
|
// Should this behavior be retained?
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (player == &players[consoleplayer])
|
|
|
|
{
|
|
|
|
S_SoundID (this, CHAN_ITEM, item->UseSound, 1, ATTN_NORM);
|
|
|
|
StatusBar->FlashItem (itemtype);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: BestWeapon
|
|
|
|
//
|
|
|
|
// Returns the best weapon a player has, possibly restricted to a single
|
|
|
|
// type of ammo.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeapon *APlayerPawn::BestWeapon (const PClass *ammotype)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AWeapon *bestMatch = NULL;
|
|
|
|
int bestOrder = INT_MAX;
|
|
|
|
AInventory *item;
|
|
|
|
AWeapon *weap;
|
|
|
|
bool tomed = NULL != FindInventory (RUNTIME_CLASS(APowerWeaponLevel2));
|
|
|
|
|
|
|
|
// Find the best weapon the player has.
|
|
|
|
for (item = Inventory; item != NULL; item = item->Inventory)
|
|
|
|
{
|
|
|
|
if (!item->IsKindOf (RUNTIME_CLASS(AWeapon)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
weap = static_cast<AWeapon *> (item);
|
|
|
|
|
|
|
|
// Don't select it if it's worse than what was already found.
|
|
|
|
if (weap->SelectionOrder > bestOrder)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if its primary fire doesn't use the desired ammo.
|
|
|
|
if (ammotype != NULL &&
|
|
|
|
(weap->Ammo1 == NULL ||
|
|
|
|
weap->Ammo1->GetClass() != ammotype))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if the Tome is active and this isn't the powered-up version.
|
|
|
|
if (tomed && weap->SisterWeapon != NULL && weap->SisterWeapon->WeaponFlags & WIF_POWERED_UP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if it's powered-up and the Tome is not active.
|
|
|
|
if (!tomed && weap->WeaponFlags & WIF_POWERED_UP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if there isn't enough ammo to use its primary fire.
|
|
|
|
if (!(weap->WeaponFlags & WIF_AMMO_OPTIONAL) &&
|
|
|
|
!weap->CheckAmmo (AWeapon::PrimaryFire, false))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// This weapon is usable!
|
|
|
|
bestOrder = weap->SelectionOrder;
|
|
|
|
bestMatch = weap;
|
|
|
|
}
|
|
|
|
return bestMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: PickNewWeapon
|
|
|
|
//
|
|
|
|
// Picks a new weapon for this player. Used mostly for running out of ammo,
|
|
|
|
// but it also works when an ACS script explicitly takes the ready weapon
|
|
|
|
// away or the player picks up some ammo they had previously run out of.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeapon *APlayerPawn::PickNewWeapon (const PClass *ammotype)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AWeapon *best = BestWeapon (ammotype);
|
|
|
|
|
|
|
|
if (best != NULL)
|
|
|
|
{
|
|
|
|
player->PendingWeapon = best;
|
|
|
|
if (player->ReadyWeapon != NULL)
|
|
|
|
{
|
|
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->DownState);
|
|
|
|
}
|
|
|
|
else if (player->PendingWeapon != WP_NOCHANGE)
|
|
|
|
{
|
|
|
|
P_BringUpWeapon (player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *APlayerPawn::GetSoundClass ()
|
|
|
|
{
|
|
|
|
if (player != NULL &&
|
|
|
|
player->userinfo.skin != 0 &&
|
|
|
|
(unsigned)player->userinfo.skin < numskins)
|
|
|
|
{
|
|
|
|
return skins[player->userinfo.skin].name;
|
|
|
|
}
|
|
|
|
return "player";
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayIdle ()
|
|
|
|
{
|
|
|
|
if (state >= SeeState && state < MissileState)
|
|
|
|
SetState (SpawnState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayRunning ()
|
|
|
|
{
|
|
|
|
if (state == SpawnState)
|
|
|
|
SetState (SeeState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayAttacking ()
|
|
|
|
{
|
|
|
|
SetState (MissileState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayAttacking2 ()
|
|
|
|
{
|
|
|
|
SetState (MissileState+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::ThrowPoisonBag ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::GiveDefaultInventory ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::MorphPlayerThink ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::ActivateMorphWeapon ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
fixed_t APlayerPawn::GetJumpZ ()
|
|
|
|
{
|
|
|
|
return 8*FRACUNIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::Die (AActor *source, AActor *inflictor)
|
|
|
|
{
|
|
|
|
Super::Die (source, inflictor);
|
|
|
|
|
|
|
|
if (player != NULL && player->mo != this)
|
|
|
|
{ // Make the real player die, too
|
|
|
|
player->mo->Die (source, inflictor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (player != NULL && (dmflags2 & DF2_YES_WEAPONDROP))
|
|
|
|
{ // Voodoo dolls don't drop weapons
|
|
|
|
AWeapon *weap = player->ReadyWeapon;
|
|
|
|
if (weap != NULL)
|
|
|
|
{
|
|
|
|
AInventory *item;
|
|
|
|
|
|
|
|
if (weap->SpawnState != NULL &&
|
|
|
|
weap->SpawnState != &AActor::States[AActor::S_NULL])
|
|
|
|
{
|
|
|
|
item = P_DropItem (this, weap->GetClass(), -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
if (weap->AmmoGive1 && weap->Ammo1)
|
|
|
|
{
|
|
|
|
static_cast<AWeapon *>(item)->AmmoGive1 = weap->Ammo1->Amount;
|
|
|
|
}
|
|
|
|
if (weap->AmmoGive2 && weap->Ammo2)
|
|
|
|
{
|
|
|
|
static_cast<AWeapon *>(item)->AmmoGive2 = weap->Ammo2->Amount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item = P_DropItem (this, weap->AmmoType1, -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
item->Amount = weap->Ammo1->Amount;
|
|
|
|
}
|
|
|
|
item = P_DropItem (this, weap->AmmoType2, -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
item->Amount = weap->Ammo2->Amount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Kill the player's inventory
|
|
|
|
while (Inventory != NULL)
|
|
|
|
{
|
|
|
|
Inventory->Destroy ();
|
|
|
|
}
|
|
|
|
if (!multiplayer && (level.flags & LEVEL_DEATHSLIDESHOW))
|
|
|
|
{
|
|
|
|
F_StartSlideshow ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::TweakSpeeds (int &forward, int &side)
|
|
|
|
{
|
|
|
|
if ((player->Powers & PW_SPEED) && !player->morphTics)
|
|
|
|
{ // Adjust for a player with a speed artifact
|
|
|
|
forward = (3*forward)>>1;
|
|
|
|
side = (3*side)>>1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The standard healing radius behavior is the cleric's
|
|
|
|
bool APlayerPawn::DoHealingRadius (APlayerPawn *other)
|
|
|
|
{
|
|
|
|
if (P_GiveBody (other, 50 + (pr_healradius()%50)))
|
|
|
|
{
|
|
|
|
S_Sound (other, CHAN_AUTO, "MysticIncant", 1, ATTN_NORM);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-04-23 20:12:27 +00:00
|
|
|
void APlayerPawn::SpecialInvulnerabilityHandling (EInvulState setting, fixed_t * pAlpha)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-23 20:12:27 +00:00
|
|
|
if (setting == INVUL_GetAlpha && pAlpha!=NULL) *pAlpha=FIXED_MAX; // indicates no change
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= P_Thrust
|
|
|
|
=
|
|
|
|
= moves the given origin along a given angle
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void P_SideThrust (player_t *player, angle_t angle, fixed_t move)
|
|
|
|
{
|
|
|
|
angle = (angle - ANGLE_90) >> ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
player->mo->momx += FixedMul (move, finecosine[angle]);
|
|
|
|
player->mo->momy += FixedMul (move, finesine[angle]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void P_ForwardThrust (player_t *player, angle_t angle, fixed_t move)
|
|
|
|
{
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
if ((player->mo->waterlevel || (player->mo->flags & MF_NOGRAVITY))
|
|
|
|
&& player->mo->pitch != 0)
|
|
|
|
{
|
|
|
|
angle_t pitch = (angle_t)player->mo->pitch >> ANGLETOFINESHIFT;
|
|
|
|
fixed_t zpush = FixedMul (move, finesine[pitch]);
|
|
|
|
if (player->mo->waterlevel && player->mo->waterlevel < 2 && zpush < 0)
|
|
|
|
zpush = 0;
|
|
|
|
player->mo->momz -= zpush;
|
|
|
|
move = FixedMul (move, finecosine[pitch]);
|
|
|
|
}
|
|
|
|
player->mo->momx += FixedMul (move, finecosine[angle]);
|
|
|
|
player->mo->momy += FixedMul (move, finesine[angle]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_Bob
|
|
|
|
// Same as P_Thrust, but only affects bobbing.
|
|
|
|
//
|
|
|
|
// killough 10/98: We apply thrust separately between the real physical player
|
|
|
|
// and the part which affects bobbing. This way, bobbing only comes from player
|
|
|
|
// motion, nothing external, avoiding many problems, e.g. bobbing should not
|
|
|
|
// occur on conveyors, unless the player walks on one, and bobbing should be
|
|
|
|
// reduced at a regular rate, even on ice (where the player coasts).
|
|
|
|
//
|
|
|
|
|
|
|
|
void P_Bob (player_t *player, angle_t angle, fixed_t move)
|
|
|
|
{
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
player->momx += FixedMul(move,finecosine[angle]);
|
|
|
|
player->momy += FixedMul(move,finesine[angle]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= P_CalcHeight
|
|
|
|
=
|
|
|
|
=
|
|
|
|
Calculate the walking / running height adjustment
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void P_CalcHeight (player_t *player)
|
|
|
|
{
|
|
|
|
int angle;
|
|
|
|
fixed_t bob;
|
|
|
|
bool still = false;
|
|
|
|
|
|
|
|
// Regular movement bobbing
|
|
|
|
// (needs to be calculated for gun swing even if not on ground)
|
|
|
|
// OPTIMIZE: tablify angle
|
|
|
|
|
|
|
|
// killough 10/98: Make bobbing depend only on player-applied motion.
|
|
|
|
//
|
|
|
|
// Note: don't reduce bobbing here if on ice: if you reduce bobbing here,
|
|
|
|
// it causes bobbing jerkiness when the player moves from ice to non-ice,
|
|
|
|
// and vice-versa.
|
|
|
|
|
|
|
|
if ((player->mo->flags & MF_NOGRAVITY) && !onground)
|
|
|
|
{
|
|
|
|
player->bob = FRACUNIT / 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->bob = DMulScale16 (player->momx, player->momx, player->momy, player->momy);
|
|
|
|
if (player->bob == 0)
|
|
|
|
{
|
|
|
|
still = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->bob = FixedMul (player->bob, player->userinfo.MoveBob);
|
|
|
|
|
|
|
|
if (player->bob > MAXBOB)
|
|
|
|
player->bob = MAXBOB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
fixed_t defaultviewheight = player->defaultviewheight + player->crouchviewdelta;
|
2006-04-11 16:27:41 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->cheats & CF_NOMOMENTUM)
|
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewz = player->mo->z + defaultviewheight;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
|
|
|
|
player->viewz = player->mo->ceilingz-4*FRACUNIT;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (still)
|
|
|
|
{
|
|
|
|
if (player->health > 0)
|
|
|
|
{
|
|
|
|
angle = DivScale13 (level.time, 120*TICRATE/35) & FINEMASK;
|
|
|
|
bob = FixedMul (player->userinfo.StillBob, finesine[angle]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bob = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// DivScale 13 because FINEANGLES == (1<<13)
|
|
|
|
angle = DivScale13 (level.time, 20*TICRATE/35) & FINEMASK;
|
|
|
|
bob = FixedMul (player->bob>>(player->mo->waterlevel > 1 ? 2 : 1), finesine[angle]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// move viewheight
|
|
|
|
if (player->playerstate == PST_LIVE)
|
|
|
|
{
|
|
|
|
player->viewheight += player->deltaviewheight;
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
if (player->viewheight > defaultviewheight)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewheight = defaultviewheight;
|
2006-02-24 04:48:15 +00:00
|
|
|
player->deltaviewheight = 0;
|
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
else if (player->viewheight < (defaultviewheight>>1))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewheight = defaultviewheight>>1;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->deltaviewheight <= 0)
|
|
|
|
player->deltaviewheight = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->deltaviewheight)
|
|
|
|
{
|
|
|
|
player->deltaviewheight += FRACUNIT/4;
|
|
|
|
if (!player->deltaviewheight)
|
|
|
|
player->deltaviewheight = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->morphTics)
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->z + player->viewheight + bob;
|
|
|
|
}
|
|
|
|
if (player->mo->floorclip && player->playerstate != PST_DEAD
|
|
|
|
&& player->mo->z <= player->mo->floorz)
|
|
|
|
{
|
|
|
|
player->viewz -= player->mo->floorclip;
|
|
|
|
}
|
|
|
|
if (player->viewz > player->mo->ceilingz - 4*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->ceilingz - 4*FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->viewz < player->mo->floorz + 4*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->floorz + 4*FRACUNIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
=
|
|
|
|
= P_MovePlayer
|
|
|
|
=
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
CUSTOM_CVAR (Float, sv_aircontrol, 0.00390625f, CVAR_SERVERINFO|CVAR_NOSAVE)
|
|
|
|
{
|
|
|
|
level.aircontrol = (fixed_t)(self * 65536.f);
|
|
|
|
G_AirControlChanged ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void P_MovePlayer (player_t *player)
|
|
|
|
{
|
|
|
|
ticcmd_t *cmd = &player->cmd;
|
|
|
|
APlayerPawn *mo = player->mo;
|
|
|
|
|
|
|
|
// [RH] 180-degree turn overrides all other yaws
|
|
|
|
if (player->turnticks)
|
|
|
|
{
|
|
|
|
player->turnticks--;
|
|
|
|
mo->angle += (ANGLE_180 / TURN180_TICKS);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mo->angle += cmd->ucmd.yaw << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ);
|
|
|
|
|
|
|
|
// killough 10/98:
|
|
|
|
//
|
|
|
|
// We must apply thrust to the player and bobbing separately, to avoid
|
|
|
|
// anomalies. The thrust applied to bobbing is always the same strength on
|
|
|
|
// ice, because the player still "works just as hard" to move, while the
|
|
|
|
// thrust applied to the movement varies with 'movefactor'.
|
|
|
|
|
|
|
|
if (cmd->ucmd.forwardmove | cmd->ucmd.sidemove)
|
|
|
|
{
|
|
|
|
fixed_t forwardmove, sidemove;
|
|
|
|
int bobfactor;
|
|
|
|
int friction, movefactor;
|
|
|
|
int fm, sm;
|
|
|
|
|
|
|
|
movefactor = P_GetMoveFactor (mo, &friction);
|
|
|
|
bobfactor = friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR;
|
|
|
|
if (!onground && !(player->mo->flags & MF_NOGRAVITY) && !player->mo->waterlevel)
|
|
|
|
{
|
|
|
|
// [RH] allow very limited movement if not on ground.
|
|
|
|
movefactor = FixedMul (movefactor, level.aircontrol);
|
|
|
|
bobfactor = FixedMul (bobfactor, level.aircontrol);
|
|
|
|
}
|
|
|
|
|
|
|
|
fm = cmd->ucmd.forwardmove;
|
|
|
|
sm = cmd->ucmd.sidemove;
|
|
|
|
mo->TweakSpeeds (fm, sm);
|
|
|
|
fm = FixedMul (fm, player->mo->Speed);
|
|
|
|
sm = FixedMul (sm, player->mo->Speed);
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
// When crouching speed and bobbing have to be reduced
|
|
|
|
if (player->morphTics==0 && player->crouchfactor != FRACUNIT)
|
|
|
|
{
|
|
|
|
fm = FixedMul(fm, player->crouchfactor);
|
|
|
|
sm = FixedMul(sm, player->crouchfactor);
|
|
|
|
bobfactor = FixedMul(bobfactor, player->crouchfactor);
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
forwardmove = Scale (fm, movefactor * 35, TICRATE << 8);
|
|
|
|
sidemove = Scale (sm, movefactor * 35, TICRATE << 8);
|
|
|
|
|
|
|
|
if (forwardmove)
|
|
|
|
{
|
|
|
|
P_Bob (player, mo->angle, (cmd->ucmd.forwardmove * bobfactor) >> 8);
|
|
|
|
P_ForwardThrust (player, mo->angle, forwardmove);
|
|
|
|
}
|
|
|
|
if (sidemove)
|
|
|
|
{
|
|
|
|
P_Bob (player, mo->angle-ANG90, (cmd->ucmd.sidemove * bobfactor) >> 8);
|
|
|
|
P_SideThrust (player, mo->angle, sidemove);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugfile)
|
|
|
|
{
|
|
|
|
fprintf (debugfile, "move player for pl %d%c: (%ld,%ld,%ld) (%ld,%ld) %d %d w%d [", player-players,
|
|
|
|
player->cheats&CF_PREDICTING?'p':' ',
|
|
|
|
player->mo->x, player->mo->y, player->mo->z,forwardmove, sidemove, movefactor, friction, player->mo->waterlevel);
|
|
|
|
msecnode_t *n = player->mo->touching_sectorlist;
|
|
|
|
while (n != NULL)
|
|
|
|
{
|
|
|
|
fprintf (debugfile, "%d ", n->m_sector-sectors);
|
|
|
|
n = n->m_tnext;
|
|
|
|
}
|
|
|
|
fprintf (debugfile, "]\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
player->mo->PlayRunning ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->cheats & CF_REVERTPLEASE)
|
|
|
|
{
|
|
|
|
player->cheats &= ~CF_REVERTPLEASE;
|
|
|
|
player->camera = player->mo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// P_FallingDamage
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void P_FallingDamage (AActor *actor)
|
|
|
|
{
|
|
|
|
int damagestyle;
|
|
|
|
int damage;
|
|
|
|
fixed_t mom;
|
|
|
|
|
|
|
|
damagestyle = ((level.flags >> 15) | (dmflags)) &
|
|
|
|
(DF_FORCE_FALLINGZD | DF_FORCE_FALLINGHX);
|
|
|
|
|
|
|
|
if (damagestyle == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mom = abs (actor->momz);
|
|
|
|
|
|
|
|
// Since Hexen falling damage is stronger than ZDoom's, it takes
|
|
|
|
// precedence. ZDoom falling damage may not be as strong, but it
|
|
|
|
// gets felt sooner.
|
|
|
|
|
|
|
|
switch (damagestyle)
|
|
|
|
{
|
|
|
|
case DF_FORCE_FALLINGHX: // Hexen falling damage
|
|
|
|
if (mom <= 23*FRACUNIT)
|
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mom >= 63*FRACUNIT)
|
|
|
|
{ // automatic death
|
|
|
|
damage = 1000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mom = FixedMul (mom, 16*FRACUNIT/23);
|
|
|
|
damage = ((FixedMul (mom, mom) / 10) >> FRACBITS) - 24;
|
|
|
|
if (actor->momz > -39*FRACUNIT && damage > actor->health
|
|
|
|
&& actor->health != 1)
|
|
|
|
{ // No-death threshold
|
|
|
|
damage = actor->health-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DF_FORCE_FALLINGZD: // ZDoom falling damage
|
|
|
|
if (mom <= 19*FRACUNIT)
|
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mom >= 84*FRACUNIT)
|
|
|
|
{ // automatic death
|
|
|
|
damage = 1000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
damage = ((MulScale23 (mom, mom*11) >> FRACBITS) - 30) / 2;
|
|
|
|
if (damage < 1)
|
|
|
|
{
|
|
|
|
damage = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DF_FORCE_FALLINGST: // Strife falling damage
|
|
|
|
if (mom <= 20*FRACUNIT)
|
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// The minimum amount of damage you take from falling in Strife
|
|
|
|
// is 52. Ouch!
|
|
|
|
damage = mom / 25000;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actor->player)
|
|
|
|
{
|
|
|
|
S_Sound (actor, CHAN_AUTO, "*land", 1, ATTN_NORM);
|
|
|
|
P_NoiseAlert (actor, actor, true);
|
|
|
|
if (damage == 1000000 && (actor->player->cheats & CF_GODMODE))
|
|
|
|
{
|
|
|
|
damage = 999;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
P_DamageMobj (actor, NULL, NULL, damage, MOD_FALLING);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// P_DeathThink
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void P_DeathThink (player_t *player)
|
|
|
|
{
|
|
|
|
int dir;
|
|
|
|
angle_t delta;
|
|
|
|
int lookDelta;
|
|
|
|
|
|
|
|
P_MovePsprites (player);
|
|
|
|
|
|
|
|
onground = (player->mo->z <= player->mo->floorz);
|
|
|
|
if (player->mo->IsKindOf (RUNTIME_CLASS(APlayerChunk)))
|
|
|
|
{ // Flying bloody skull or flying ice chunk
|
|
|
|
player->viewheight = 6 * FRACUNIT;
|
|
|
|
player->deltaviewheight = 0;
|
|
|
|
if (onground)
|
|
|
|
{
|
|
|
|
if (player->mo->pitch > -(int)ANGLE_1*19)
|
|
|
|
{
|
|
|
|
lookDelta = (-(int)ANGLE_1*19 - player->mo->pitch) / 8;
|
|
|
|
player->mo->pitch += lookDelta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (player->mo->DamageType != MOD_ICE)
|
|
|
|
{ // Fall to ground (if not frozen)
|
|
|
|
player->deltaviewheight = 0;
|
|
|
|
if (player->viewheight > 6*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewheight -= FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->viewheight < 6*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewheight = 6*FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->mo->pitch < 0)
|
|
|
|
{
|
|
|
|
player->mo->pitch += ANGLE_1*3;
|
|
|
|
}
|
|
|
|
else if (player->mo->pitch > 0)
|
|
|
|
{
|
|
|
|
player->mo->pitch -= ANGLE_1*3;
|
|
|
|
}
|
|
|
|
if (abs(player->mo->pitch) < ANGLE_1*3)
|
|
|
|
{
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
P_CalcHeight (player);
|
|
|
|
|
|
|
|
if (player->attacker && player->attacker != player->mo)
|
|
|
|
{ // Watch killer
|
|
|
|
dir = P_FaceMobj (player->mo, player->attacker, &delta);
|
|
|
|
if (delta < ANGLE_1*10)
|
|
|
|
{ // Looking at killer, so fade damage and poison counters
|
|
|
|
if (player->damagecount)
|
|
|
|
{
|
|
|
|
player->damagecount--;
|
|
|
|
}
|
|
|
|
if (player->poisoncount)
|
|
|
|
{
|
|
|
|
player->poisoncount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delta /= 8;
|
|
|
|
if (delta > ANGLE_1*5)
|
|
|
|
{
|
|
|
|
delta = ANGLE_1*5;
|
|
|
|
}
|
|
|
|
if (dir)
|
|
|
|
{ // Turn clockwise
|
|
|
|
player->mo->angle += delta;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Turn counter clockwise
|
|
|
|
player->mo->angle -= delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (player->damagecount)
|
|
|
|
{
|
|
|
|
player->damagecount--;
|
|
|
|
}
|
|
|
|
if (player->poisoncount)
|
|
|
|
{
|
|
|
|
player->poisoncount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->cmd.ucmd.buttons & BT_USE ||
|
|
|
|
((deathmatch || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN)))
|
|
|
|
{
|
|
|
|
if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->isbot))
|
|
|
|
{
|
|
|
|
player->cls = NULL; // Force a new class if the player is using a random class
|
|
|
|
player->playerstate = multiplayer ? PST_REBORN : PST_ENTER;
|
|
|
|
if (player->mo->special1 > 2)
|
|
|
|
{
|
|
|
|
player->mo->special1 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC P_CrouchMove
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void P_CrouchMove(player_t * player, int direction)
|
|
|
|
{
|
|
|
|
fixed_t defaultheight = player->mo->GetDefault()->height;
|
|
|
|
fixed_t savedheight = player->mo->height;
|
|
|
|
fixed_t crouchspeed = direction * CROUCHSPEED;
|
|
|
|
|
|
|
|
player->crouchdir = (signed char) direction;
|
|
|
|
player->crouchfactor += crouchspeed;
|
|
|
|
|
|
|
|
// check whether the move is ok
|
|
|
|
player->mo->height = FixedMul(defaultheight, player->crouchfactor);
|
|
|
|
if (!P_TryMove(player->mo, player->mo->x, player->mo->y, false, false))
|
|
|
|
{
|
|
|
|
player->mo->height = savedheight;
|
|
|
|
if (direction > 0)
|
|
|
|
{
|
|
|
|
// doesn't fit
|
|
|
|
player->crouchfactor -= crouchspeed;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
player->mo->height = savedheight;
|
|
|
|
|
|
|
|
player->crouchfactor = clamp<fixed_t>(player->crouchfactor, FRACUNIT/2, FRACUNIT);
|
|
|
|
player->viewheight = FixedMul(player->defaultviewheight, player->crouchfactor);
|
|
|
|
player->crouchviewdelta = player->viewheight - player->defaultviewheight;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC P_PlayerThink
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void P_PlayerThink (player_t *player)
|
|
|
|
{
|
|
|
|
ticcmd_t *cmd;
|
|
|
|
|
|
|
|
if (player->mo == NULL)
|
|
|
|
{
|
|
|
|
I_Error ("No player %d start\n", player - players + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugfile && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
fprintf (debugfile, "tic %d for pl %d: (%ld, %ld, %ld, %lu) b:%02x p:%d y:%d f:%d s:%d u:%d\n",
|
|
|
|
gametic, player-players, player->mo->x, player->mo->y, player->mo->z,
|
|
|
|
player->mo->angle>>ANGLETOFINESHIFT, player->cmd.ucmd.buttons,
|
|
|
|
player->cmd.ucmd.pitch, player->cmd.ucmd.yaw, player->cmd.ucmd.forwardmove,
|
|
|
|
player->cmd.ucmd.sidemove, player->cmd.ucmd.upmove);
|
|
|
|
}
|
|
|
|
|
|
|
|
player->xviewshift = 0; // [RH] Make sure view is in right place
|
|
|
|
|
|
|
|
// [RH] Zoom the player's FOV
|
|
|
|
if (player->FOV != player->DesiredFOV)
|
|
|
|
{
|
|
|
|
if (fabsf (player->FOV - player->DesiredFOV) < 7.f)
|
|
|
|
{
|
|
|
|
player->FOV = player->DesiredFOV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float zoom = MAX(7.f, fabsf (player->FOV - player->DesiredFOV) * 0.025f);
|
|
|
|
if (player->FOV > player->DesiredFOV)
|
|
|
|
{
|
|
|
|
player->FOV = player->FOV - zoom;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->FOV = player->FOV + zoom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (player->inventorytics)
|
|
|
|
{
|
|
|
|
player->inventorytics--;
|
|
|
|
}
|
|
|
|
// No-clip cheat
|
|
|
|
if (player->cheats & CF_NOCLIP)
|
|
|
|
{
|
|
|
|
player->mo->flags |= MF_NOCLIP;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->mo->flags &= ~MF_NOCLIP;
|
|
|
|
}
|
|
|
|
cmd = &player->cmd;
|
|
|
|
if (player->mo->flags & MF_JUSTATTACKED)
|
|
|
|
{ // Chainsaw/Gauntlets attack auto forward motion
|
|
|
|
cmd->ucmd.yaw = 0;
|
|
|
|
cmd->ucmd.forwardmove = 0xc800/2;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
player->mo->flags &= ~MF_JUSTATTACKED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Being totally frozen zeros out most input parameters.
|
|
|
|
if (player->cheats & CF_TOTALLYFROZEN || gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
if (gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
cmd->ucmd.buttons = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cmd->ucmd.buttons &= BT_USE;
|
|
|
|
}
|
|
|
|
cmd->ucmd.pitch = 0;
|
|
|
|
cmd->ucmd.yaw = 0;
|
|
|
|
cmd->ucmd.roll = 0;
|
|
|
|
cmd->ucmd.forwardmove = 0;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
cmd->ucmd.upmove = 0;
|
|
|
|
player->turnticks = 0;
|
|
|
|
}
|
|
|
|
else if (player->cheats & CF_FROZEN)
|
|
|
|
{
|
|
|
|
cmd->ucmd.forwardmove = 0;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
cmd->ucmd.upmove = 0;
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
// Handle crouching
|
|
|
|
if (player->morphTics == 0)
|
|
|
|
{
|
|
|
|
if (!(player->cheats & CF_TOTALLYFROZEN))
|
|
|
|
{
|
|
|
|
int crouchdir = player->crouching;
|
|
|
|
|
|
|
|
if (crouchdir==0)
|
|
|
|
{
|
|
|
|
crouchdir = (player->cmd.ucmd.buttons & BT_DUCK)? -1 : 1;
|
|
|
|
}
|
|
|
|
else if (player->cmd.ucmd.buttons & BT_DUCK)
|
|
|
|
{
|
|
|
|
player->crouching=0;
|
|
|
|
}
|
|
|
|
if (crouchdir == 1 && player->crouchfactor < FRACUNIT &&
|
|
|
|
player->mo->z + player->mo->height < player->mo->ceilingz)
|
|
|
|
{
|
|
|
|
P_CrouchMove(player, 1);
|
|
|
|
}
|
|
|
|
else if (crouchdir == -1 && player->crouchfactor > FRACUNIT/2)
|
|
|
|
{
|
|
|
|
P_CrouchMove(player, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->Uncrouch();
|
|
|
|
}
|
|
|
|
|
|
|
|
player->crouchoffset = -FixedMul(player->defaultviewheight, (FRACUNIT - player->crouchfactor));
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->playerstate == PST_DEAD)
|
|
|
|
{
|
2006-05-14 14:30:13 +00:00
|
|
|
player->Uncrouch();
|
2006-02-24 04:48:15 +00:00
|
|
|
P_DeathThink (player);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (player->jumpTics)
|
|
|
|
{
|
|
|
|
player->jumpTics--;
|
|
|
|
}
|
|
|
|
if (player->morphTics && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
player->mo->MorphPlayerThink ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Look up/down stuff
|
|
|
|
if (dmflags & DF_NO_FREELOOK)
|
|
|
|
{
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int look = cmd->ucmd.pitch << 16;
|
|
|
|
|
|
|
|
// The player's view pitch is clamped between -32 and +56 degrees,
|
|
|
|
// which translates to about half a screen height up and (more than)
|
|
|
|
// one full screen height down from straight ahead when view panning
|
|
|
|
// is used.
|
|
|
|
if (look)
|
|
|
|
{
|
|
|
|
if (look == -32768 << 16)
|
|
|
|
{ // center view
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->mo->pitch -= look;
|
|
|
|
if (look > 0)
|
|
|
|
{ // look up
|
|
|
|
if (player->mo->pitch < -ANGLE_1*MAX_UP_ANGLE)
|
|
|
|
player->mo->pitch = -ANGLE_1*MAX_UP_ANGLE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // look down
|
|
|
|
if (player->mo->pitch > ANGLE_1*MAX_DN_ANGLE)
|
|
|
|
player->mo->pitch = ANGLE_1*MAX_DN_ANGLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Check for fast turn around
|
|
|
|
if (cmd->ucmd.buttons & BT_TURN180 && !(player->oldbuttons & BT_TURN180))
|
|
|
|
{
|
|
|
|
player->turnticks = TURN180_TICKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle movement
|
|
|
|
if (player->mo->reactiontime)
|
|
|
|
{ // Player is frozen
|
|
|
|
player->mo->reactiontime--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P_MovePlayer (player);
|
|
|
|
|
|
|
|
// [RH] check for jump
|
|
|
|
if (cmd->ucmd.buttons & BT_JUMP)
|
|
|
|
{
|
2006-05-14 14:30:13 +00:00
|
|
|
if (player->crouchoffset!=0)
|
|
|
|
{
|
|
|
|
// Jumping while crouching will force an un-crouch but not jump
|
|
|
|
player->crouching = 1;
|
|
|
|
}
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->waterlevel >= 2)
|
|
|
|
{
|
|
|
|
player->mo->momz = 4*FRACUNIT;
|
|
|
|
}
|
|
|
|
else if (player->mo->flags & MF_NOGRAVITY)
|
|
|
|
{
|
|
|
|
player->mo->momz = 3*FRACUNIT;
|
|
|
|
}
|
|
|
|
else if (!(dmflags & DF_NO_JUMP) && onground && !player->jumpTics)
|
|
|
|
{
|
|
|
|
fixed_t JumpZ = (player->mo->JumpZ > 0 ? player->mo->JumpZ : player->mo->GetJumpZ ()); // [GRB]
|
|
|
|
player->mo->momz += JumpZ*35/TICRATE;
|
|
|
|
S_Sound (player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM);
|
|
|
|
player->mo->flags2 &= ~MF2_ONMOBJ;
|
|
|
|
player->jumpTics = 18*TICRATE/35;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd->ucmd.upmove == -32768)
|
|
|
|
{ // Only land if in the air
|
|
|
|
if ((player->mo->flags & MF_NOGRAVITY) && player->mo->waterlevel < 2)
|
|
|
|
{
|
|
|
|
//player->mo->flags2 &= ~MF2_FLY;
|
|
|
|
player->mo->flags &= ~MF_NOGRAVITY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cmd->ucmd.upmove != 0)
|
|
|
|
{
|
|
|
|
// Clamp the speed to some reasonable maximum.
|
|
|
|
int magnitude = abs (cmd->ucmd.upmove);
|
|
|
|
if (magnitude > 0x300)
|
|
|
|
{
|
|
|
|
cmd->ucmd.upmove = ksgn (cmd->ucmd.upmove) * 0x300;
|
|
|
|
}
|
|
|
|
if (player->mo->waterlevel >= 2 || (player->mo->flags2 & MF2_FLY))
|
|
|
|
{
|
|
|
|
player->mo->momz = cmd->ucmd.upmove << 9;
|
|
|
|
if (player->mo->waterlevel < 2 && !(player->mo->flags & MF_NOGRAVITY))
|
|
|
|
{
|
|
|
|
player->mo->flags2 |= MF2_FLY;
|
|
|
|
player->mo->flags |= MF_NOGRAVITY;
|
|
|
|
if (player->mo->momz <= -39*FRACUNIT)
|
|
|
|
{ // Stop falling scream
|
|
|
|
S_StopSound (player->mo, CHAN_VOICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cmd->ucmd.upmove > 0 && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
AInventory *fly = player->mo->FindInventory (PClass::FindClass (NAME_ArtiFly));
|
2006-02-24 04:48:15 +00:00
|
|
|
if (fly != NULL)
|
|
|
|
{
|
|
|
|
player->mo->UseInventory (fly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
P_CalcHeight (player);
|
|
|
|
|
|
|
|
if (!(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
if (player->mo->Sector->special || player->mo->Sector->damage)
|
|
|
|
{
|
|
|
|
P_PlayerInSpecialSector (player);
|
|
|
|
}
|
|
|
|
P_PlayerOnSpecialFlat (player, P_GetThingFloorType (player->mo));
|
|
|
|
if (player->mo->momz <= -35*FRACUNIT
|
|
|
|
&& player->mo->momz >= -40*FRACUNIT && !player->morphTics)
|
|
|
|
{
|
|
|
|
int id = S_FindSkinnedSound (player->mo, "*falling");
|
|
|
|
if (id != 0 && !S_GetSoundPlayingInfo (player->mo, id))
|
|
|
|
{
|
|
|
|
S_SoundID (player->mo, CHAN_VOICE, id, 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check for use
|
|
|
|
if ((cmd->ucmd.buttons & BT_USE) && !(player->oldbuttons & BT_USE))
|
|
|
|
{
|
|
|
|
P_UseLines (player);
|
|
|
|
}
|
|
|
|
// Morph counter
|
|
|
|
if (player->morphTics)
|
|
|
|
{
|
|
|
|
if (player->chickenPeck)
|
|
|
|
{ // Chicken attack counter
|
|
|
|
player->chickenPeck -= 3;
|
|
|
|
}
|
|
|
|
if (!--player->morphTics)
|
|
|
|
{ // Attempt to undo the chicken/pig
|
|
|
|
P_UndoPlayerMorph (player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Cycle psprites
|
|
|
|
P_MovePsprites (player);
|
|
|
|
|
|
|
|
// Other Counters
|
|
|
|
if (player->damagecount)
|
|
|
|
player->damagecount--;
|
|
|
|
|
|
|
|
if (player->bonuscount)
|
|
|
|
player->bonuscount--;
|
|
|
|
|
|
|
|
if (player->hazardcount)
|
|
|
|
{
|
|
|
|
player->hazardcount--;
|
|
|
|
if (!(level.time & 31) && player->hazardcount > 16*TICRATE)
|
|
|
|
P_DamageMobj (player->mo, NULL, NULL, 5, MOD_SLIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->poisoncount && !(level.time & 15))
|
|
|
|
{
|
|
|
|
player->poisoncount -= 5;
|
|
|
|
if (player->poisoncount < 0)
|
|
|
|
{
|
|
|
|
player->poisoncount = 0;
|
|
|
|
}
|
|
|
|
P_PoisonDamage (player, player->poisoner, 1, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle air supply
|
|
|
|
if (level.airsupply>0)
|
|
|
|
{
|
|
|
|
if (player->mo->waterlevel < 3 ||
|
|
|
|
(player->mo->flags2 & MF2_INVULNERABLE) ||
|
|
|
|
(player->cheats & CF_GODMODE))
|
|
|
|
{
|
|
|
|
player->air_finished = level.time + level.airsupply;
|
|
|
|
}
|
|
|
|
else if (player->air_finished <= level.time && !(level.time & 31))
|
|
|
|
{
|
|
|
|
P_DamageMobj (player->mo, NULL, NULL, 2 + 2*((level.time-player->air_finished)/TICRATE), MOD_WATER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save buttons
|
|
|
|
player->oldbuttons = cmd->ucmd.buttons;
|
|
|
|
}
|
|
|
|
|
|
|
|
void P_PredictPlayer (player_t *player)
|
|
|
|
{
|
|
|
|
int maxtic;
|
|
|
|
|
|
|
|
if (cl_noprediction ||
|
|
|
|
singletics ||
|
|
|
|
demoplayback ||
|
|
|
|
player->mo == NULL ||
|
|
|
|
player != &players[consoleplayer] ||
|
|
|
|
player->playerstate != PST_LIVE ||
|
|
|
|
!netgame ||
|
|
|
|
/*player->morphTics ||*/
|
|
|
|
(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxtic = maketic;
|
|
|
|
|
|
|
|
if (gametic == maxtic)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save original values for restoration later
|
|
|
|
PredictionPlayerBackup = *player;
|
|
|
|
|
|
|
|
AActor *act = player->mo;
|
|
|
|
memcpy (PredictionActorBackup, &act->x, sizeof(AActor)-((BYTE *)&act->x-(BYTE *)act));
|
|
|
|
|
|
|
|
act->flags &= ~MF_PICKUP;
|
|
|
|
act->flags2 &= ~MF2_PUSHWALL;
|
|
|
|
player->cheats |= CF_PREDICTING;
|
|
|
|
|
|
|
|
// The ordering of the touching_sectorlist needs to remain unchanged
|
|
|
|
msecnode_t *mnode = act->touching_sectorlist;
|
|
|
|
PredictionTouchingSectorsBackup.Clear ();
|
|
|
|
|
|
|
|
while (mnode != NULL)
|
|
|
|
{
|
|
|
|
PredictionTouchingSectorsBackup.Push (mnode->m_sector);
|
|
|
|
mnode = mnode->m_tnext;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Blockmap ordering also needs to stay the same, so unlink the block nodes
|
|
|
|
// without releasing them. (They will be used again in P_UnpredictPlayer).
|
|
|
|
FBlockNode *block = act->BlockNode;
|
|
|
|
|
|
|
|
while (block != NULL)
|
|
|
|
{
|
|
|
|
if (block->NextActor != NULL)
|
|
|
|
{
|
|
|
|
block->NextActor->PrevActor = block->PrevActor;
|
|
|
|
}
|
|
|
|
*(block->PrevActor) = block->NextActor;
|
|
|
|
block = block->NextBlock;
|
|
|
|
}
|
|
|
|
act->BlockNode = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = gametic; i < maxtic; ++i)
|
|
|
|
{
|
|
|
|
player->cmd = localcmds[i % LOCALCMDTICS];
|
|
|
|
P_PlayerThink (player);
|
|
|
|
player->mo->Tick ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode);
|
|
|
|
|
|
|
|
void P_UnPredictPlayer ()
|
|
|
|
{
|
|
|
|
player_t *player = &players[consoleplayer];
|
|
|
|
|
|
|
|
if (player->cheats & CF_PREDICTING)
|
|
|
|
{
|
|
|
|
AActor *act = player->mo;
|
|
|
|
|
|
|
|
*player = PredictionPlayerBackup;
|
|
|
|
|
|
|
|
act->UnlinkFromWorld ();
|
|
|
|
memcpy (&act->x, PredictionActorBackup, sizeof(AActor)-((BYTE *)&act->x-(BYTE *)act));
|
|
|
|
|
|
|
|
// Make the sector_list match the player's touching_sectorlist before it got predicted.
|
|
|
|
P_DelSeclist (sector_list);
|
|
|
|
sector_list = NULL;
|
|
|
|
for (unsigned int i = PredictionTouchingSectorsBackup.Size (); i-- > 0; )
|
|
|
|
{
|
|
|
|
sector_list = P_AddSecnode (PredictionTouchingSectorsBackup[i], act, sector_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The blockmap ordering needs to remain unchanged, too. Right now, act has the right
|
|
|
|
// pointers, so temporarily set its MF_NOBLOCKMAP flag so that LinkToWorld() does not
|
|
|
|
// mess with them.
|
|
|
|
act->flags |= MF_NOBLOCKMAP;
|
|
|
|
act->LinkToWorld ();
|
|
|
|
act->flags &= ~MF_NOBLOCKMAP;
|
|
|
|
|
|
|
|
// Now fix the pointers in the blocknode chain
|
|
|
|
FBlockNode *block = act->BlockNode;
|
|
|
|
|
|
|
|
while (block != NULL)
|
|
|
|
{
|
|
|
|
*(block->PrevActor) = block;
|
|
|
|
if (block->NextActor != NULL)
|
|
|
|
{
|
|
|
|
block->NextActor->PrevActor = &block->NextActor;
|
|
|
|
}
|
|
|
|
block = block->NextBlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_s::Serialize (FArchive &arc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
- Fixed compilation with mingw again.
- Added multiple-choice sound sequences. These overcome one of the major
deficiences of the Hexen-inherited SNDSEQ system while still being Hexen
compatible: Custom door sounds can now use different opening and closing
sequences, for both normal and blazing speeds.
- Added a serializer for TArray.
- Added a countof macro to doomtype.h. See the1's blog to find out why
it's implemented the way it is.
<http://blogs.msdn.com/the1/articles/210011.aspx>
- Added a new method to FRandom for getting random numbers larger than 255,
which lets me:
- Fixed: SNDSEQ delayrand commands could delay for no more than 255 tics.
- Fixed: If you're going to have sector_t.SoundTarget, then they need to
be included in the pointer cleanup scans.
- Ported back newer name code from 2.1.
- Fixed: Using -warp with only one parameter in Doom and Heretic to
select a map on episode 1 no longer worked.
- New: Loading a multiplayer save now restores the players based on
their names rather than on their connection order. Using connection
order was sensible when -net was the only way to start a network game,
but with -host/-join, it's not so nice. Also, if there aren't enough
players in the save, then the extra players will be spawned normally,
so you can continue a saved game with more players than you started it
with.
- Added some new SNDSEQ commands to make it possible to define Heretic's
ambient sounds in SNDSEQ: volumerel, volumerand, slot, randomsequence,
delayonce, and restart. With these, it is basically possible to obsolete
all of the $ambient SNDINFO commands.
- Fixed: Sound sequences would only execute one command each time they were
ticked.
- Fixed: No bounds checking was done on the volume sound sequences played at.
- Fixed: The tic parameter to playloop was useless and caused it to
act like a redundant playrepeat. I have removed all the logic that
caused playloop to play repeating sounds, and now it acts like an
infinite sequence of play/delay commands until the sequence is
stopped.
- Fixed: Sound sequences were ticked every frame, not every tic, so all
the delay commands were timed incorrectly and varied depending on your
framerate. Since this is useful for restarting looping sounds that got
cut off, I have not changed this. Instead, the delay commands now
record the tic when execution should resume, not the number of tics
left to delay.
SVN r57 (trunk)
2006-04-21 01:22:55 +00:00
|
|
|
arc << cls
|
|
|
|
<< mo
|
2006-02-24 04:48:15 +00:00
|
|
|
<< camera
|
|
|
|
<< playerstate
|
|
|
|
<< cmd
|
- Fixed compilation with mingw again.
- Added multiple-choice sound sequences. These overcome one of the major
deficiences of the Hexen-inherited SNDSEQ system while still being Hexen
compatible: Custom door sounds can now use different opening and closing
sequences, for both normal and blazing speeds.
- Added a serializer for TArray.
- Added a countof macro to doomtype.h. See the1's blog to find out why
it's implemented the way it is.
<http://blogs.msdn.com/the1/articles/210011.aspx>
- Added a new method to FRandom for getting random numbers larger than 255,
which lets me:
- Fixed: SNDSEQ delayrand commands could delay for no more than 255 tics.
- Fixed: If you're going to have sector_t.SoundTarget, then they need to
be included in the pointer cleanup scans.
- Ported back newer name code from 2.1.
- Fixed: Using -warp with only one parameter in Doom and Heretic to
select a map on episode 1 no longer worked.
- New: Loading a multiplayer save now restores the players based on
their names rather than on their connection order. Using connection
order was sensible when -net was the only way to start a network game,
but with -host/-join, it's not so nice. Also, if there aren't enough
players in the save, then the extra players will be spawned normally,
so you can continue a saved game with more players than you started it
with.
- Added some new SNDSEQ commands to make it possible to define Heretic's
ambient sounds in SNDSEQ: volumerel, volumerand, slot, randomsequence,
delayonce, and restart. With these, it is basically possible to obsolete
all of the $ambient SNDINFO commands.
- Fixed: Sound sequences would only execute one command each time they were
ticked.
- Fixed: No bounds checking was done on the volume sound sequences played at.
- Fixed: The tic parameter to playloop was useless and caused it to
act like a redundant playrepeat. I have removed all the logic that
caused playloop to play repeating sounds, and now it acts like an
infinite sequence of play/delay commands until the sequence is
stopped.
- Fixed: Sound sequences were ticked every frame, not every tic, so all
the delay commands were timed incorrectly and varied depending on your
framerate. Since this is useful for restarting looping sounds that got
cut off, I have not changed this. Instead, the delay commands now
record the tic when execution should resume, not the number of tics
left to delay.
SVN r57 (trunk)
2006-04-21 01:22:55 +00:00
|
|
|
<< userinfo
|
2006-02-24 04:48:15 +00:00
|
|
|
<< DesiredFOV << FOV
|
|
|
|
<< viewz
|
|
|
|
<< viewheight
|
2006-04-11 16:27:41 +00:00
|
|
|
<< defaultviewheight
|
2006-02-24 04:48:15 +00:00
|
|
|
<< deltaviewheight
|
|
|
|
<< bob
|
|
|
|
<< momx
|
|
|
|
<< momy
|
|
|
|
<< centering
|
|
|
|
<< health
|
|
|
|
<< inventorytics
|
|
|
|
<< InvFirst << InvSel
|
|
|
|
<< pieces
|
|
|
|
<< backpack
|
|
|
|
<< fragcount
|
|
|
|
<< spreecount
|
|
|
|
<< multicount
|
|
|
|
<< lastkilltime
|
|
|
|
<< ReadyWeapon << PendingWeapon
|
|
|
|
<< cheats
|
|
|
|
<< refire
|
|
|
|
<< inconsistant
|
|
|
|
<< killcount
|
|
|
|
<< itemcount
|
|
|
|
<< secretcount
|
|
|
|
<< damagecount
|
|
|
|
<< bonuscount
|
|
|
|
<< hazardcount
|
|
|
|
<< poisoncount
|
|
|
|
<< poisoner
|
|
|
|
<< attacker
|
|
|
|
<< extralight
|
|
|
|
<< fixedcolormap
|
|
|
|
<< xviewshift
|
|
|
|
<< morphTics
|
|
|
|
<< PremorphWeapon
|
|
|
|
<< chickenPeck
|
|
|
|
<< jumpTics
|
|
|
|
<< respawn_time
|
|
|
|
<< air_finished
|
|
|
|
<< turnticks
|
|
|
|
<< oldbuttons
|
|
|
|
<< isbot
|
|
|
|
<< BlendR
|
|
|
|
<< BlendG
|
|
|
|
<< BlendB
|
|
|
|
<< BlendA
|
|
|
|
<< accuracy << stamina
|
|
|
|
<< LogText;
|
2006-04-11 16:27:41 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
arc << frags[i];
|
|
|
|
for (i = 0; i < NUMPSPRITES; i++)
|
|
|
|
arc << psprites[i];
|
|
|
|
|
|
|
|
arc << CurrentPlayerClass;
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
arc << crouchfactor
|
|
|
|
<< crouching
|
|
|
|
<< crouchdir
|
|
|
|
<< crouchviewdelta;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (isbot)
|
|
|
|
{
|
|
|
|
arc << angle
|
|
|
|
<< dest
|
|
|
|
<< prev
|
|
|
|
<< enemy
|
|
|
|
<< missile
|
|
|
|
<< mate
|
|
|
|
<< last_mate
|
|
|
|
<< skill
|
|
|
|
<< t_active
|
|
|
|
<< t_respawn
|
|
|
|
<< t_strafe
|
|
|
|
<< t_react
|
|
|
|
<< t_fight
|
|
|
|
<< t_roam
|
|
|
|
<< t_rocket
|
|
|
|
<< first_shot
|
|
|
|
<< sleft
|
|
|
|
<< allround
|
|
|
|
<< oldx
|
|
|
|
<< oldy;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest = prev = enemy = missile = mate = last_mate = NULL;
|
|
|
|
}
|
|
|
|
if (arc.IsLoading ())
|
|
|
|
{
|
|
|
|
// If the player reloaded because they pressed +use after dying, we
|
|
|
|
// don't want +use to still be down after the game is loaded.
|
|
|
|
oldbuttons = ~0;
|
|
|
|
}
|
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
|