Generalized the psprites implementation

This commit is contained in:
Leonard2 2016-05-09 20:03:47 +02:00
commit 1966b61b8f
25 changed files with 744 additions and 478 deletions

View file

@ -93,25 +93,92 @@ static const FGenericButtons ButtonChecks[] =
// CODE --------------------------------------------------------------------
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
IMPLEMENT_POINTY_CLASS(DPSprite)
DECLARE_POINTER(Caller)
DECLARE_POINTER(Next)
END_POINTERS
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
DPSprite::DPSprite(player_t *owner, AInventory *caller, int id)
: processPending(true), firstTic(true), Owner(owner), Caller(caller), ID(id)
{
DPSprite **prev = &Owner->psprites;
while (*prev && (*prev)->ID < ID)
prev = &(*prev)->Next;
Next = *prev;
*prev = this;
if (Next && Next->ID == ID && ID != 0)
Next->Destroy(); // Replace it.
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
DPSprite *player_t::GetPSprite(psprnum_t layer)
{
assert(layer > 0 && layer < NUMPSPRITES);
DPSprite *weapon = nullptr;
DPSprite *pspr = psprites;
while (pspr)
{
if (pspr->ID == layer)
return pspr;
if (pspr->ID == ps_weapon)
weapon = pspr;
pspr = pspr->Next;
}
pspr = new DPSprite(this, ReadyWeapon, layer);
if (layer == ps_flash && weapon)
{
pspr->x = weapon->x;
pspr->y = weapon->y;
}
return pspr;
}
//---------------------------------------------------------------------------
//
// PROC P_NewPspriteTick
//
//---------------------------------------------------------------------------
void P_NewPspriteTick()
void DPSprite::NewTick()
{
// This function should be called after the beginning of a tick, before any possible
// prprite-event, or near the end, after any possible psprite event.
// Because data is reset for every tick (which it must be) this has no impact on savegames.
for (int i = 0; i<MAXPLAYERS; i++)
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
pspdef_t *pspdef = players[i].psprites;
for (int j = 0;j < NUMPSPRITES; j++)
DPSprite *pspr = players[i].psprites;
while (pspr)
{
pspdef[j].processPending = true;
pspr->processPending = true;
pspr = pspr->Next;
}
}
}
@ -123,79 +190,85 @@ void P_NewPspriteTick()
//
//---------------------------------------------------------------------------
void P_SetPsprite (player_t *player, int position, FState *state, bool nofunction)
void DPSprite::SetState(FState *newstate, bool pending)
{
pspdef_t *psp;
if (position == ps_weapon && !nofunction)
if (ID == ps_weapon)
{ // A_WeaponReady will re-set these as needed
player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK |
Owner->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK |
WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK);
}
psp = &player->psprites[position];
psp->processPending = false; // Do not subsequently perform periodic processing within the same tick.
// Special handling for the old hardcoded layers.
if (ID > 0 && ID < NUMPSPRITES)
Caller = Owner->ReadyWeapon;
processPending = pending;
do
{
if (state == NULL)
if (newstate == nullptr)
{ // Object removed itself.
psp->state = NULL;
break;
Destroy();
return;
}
psp->state = state;
State = newstate;
if (state->sprite != SPR_FIXED)
if (newstate->sprite != SPR_FIXED)
{ // okay to change sprite and/or frame
if (!state->GetSameFrame())
if (!newstate->GetSameFrame())
{ // okay to change frame
psp->frame = state->GetFrame();
Frame = newstate->GetFrame();
}
if (state->sprite != SPR_NOCHANGE)
if (newstate->sprite != SPR_NOCHANGE)
{ // okay to change sprite
psp->sprite = state->sprite;
Sprite = newstate->sprite;
}
}
Tics = newstate->GetTics(); // could be 0
if (sv_fastweapons == 2 && position == ps_weapon)
psp->tics = state->ActionFunc == NULL ? 0 : 1;
else if (sv_fastweapons == 3)
psp->tics = (state->GetTics() != 0);
else if (sv_fastweapons)
psp->tics = 1; // great for producing decals :)
else
psp->tics = state->GetTics(); // could be 0
if ((ID > 0 && ID < NUMPSPRITES) || Caller->IsKindOf(RUNTIME_CLASS(AWeapon)))
{ // The targeter layers are affected by this too.
if (sv_fastweapons == 2 && ID == ps_weapon)
Tics = newstate->ActionFunc == nullptr ? 0 : 1;
else if (sv_fastweapons == 3)
Tics = (newstate->GetTics() != 0);
else if (sv_fastweapons)
Tics = 1; // great for producing decals :)
}
if (state->GetMisc1())
if (newstate->GetMisc1())
{ // Set coordinates.
psp->sx = state->GetMisc1();
x = newstate->GetMisc1();
}
if (state->GetMisc2())
if (newstate->GetMisc2())
{
psp->sy = state->GetMisc2();
y = newstate->GetMisc2();
}
if (!nofunction && player->mo != NULL)
if (Owner->mo != nullptr)
{
FState *newstate;
if (state->CallAction(player->mo, player->ReadyWeapon, &newstate))
FState *nextstate;
if (newstate->CallAction(Owner->mo, Caller, &nextstate))
{
if (newstate != NULL)
if (nextstate != nullptr)
{
state = newstate;
psp->tics = 0;
newstate = nextstate;
Tics = 0;
continue;
}
if (psp->state == NULL)
if (State == nullptr)
{
break;
Destroy();
return;
}
}
}
state = psp->state->GetNextState();
} while (!psp->tics); // An initial state of 0 could cycle through.
newstate = State->GetNextState();
} while (!Tics); // An initial state of 0 could cycle through.
return;
}
//---------------------------------------------------------------------------
@ -211,13 +284,14 @@ void P_BringUpWeapon (player_t *player)
{
FState *newstate;
AWeapon *weapon;
DPSprite *psweapon = player->GetPSprite(ps_weapon);
if (player->PendingWeapon == WP_NOCHANGE)
{
if (player->ReadyWeapon != NULL)
if (player->ReadyWeapon != nullptr)
{
player->psprites[ps_weapon].sy = WEAPONTOP;
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
psweapon->y = WEAPONTOP;
psweapon->SetState(player->ReadyWeapon->GetReadyState());
}
return;
}
@ -226,7 +300,7 @@ void P_BringUpWeapon (player_t *player)
// If the player has a tome of power, use this weapon's powered up
// version, if one is available.
if (weapon != NULL &&
if (weapon != nullptr &&
weapon->SisterWeapon &&
weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true))
@ -234,7 +308,7 @@ void P_BringUpWeapon (player_t *player)
weapon = weapon->SisterWeapon;
}
if (weapon != NULL)
if (weapon != nullptr)
{
if (weapon->UpSound)
{
@ -245,20 +319,19 @@ void P_BringUpWeapon (player_t *player)
}
else
{
newstate = NULL;
newstate = nullptr;
}
player->PendingWeapon = WP_NOCHANGE;
player->ReadyWeapon = weapon;
player->psprites[ps_weapon].sy = player->cheats & CF_INSTANTWEAPSWITCH
psweapon->y = player->cheats & CF_INSTANTWEAPSWITCH
? WEAPONTOP : WEAPONBOTTOM;
psweapon->SetState(newstate);
// make sure that the previous weapon's flash state is terminated.
// When coming here from a weapon drop it may still be active.
P_SetPsprite(player, ps_flash, NULL);
P_SetPsprite (player, ps_weapon, newstate);
player->GetPSprite(ps_flash)->SetState(nullptr);
player->mo->weaponspecial = 0;
}
//---------------------------------------------------------------------------
//
// PROC P_FireWeapon
@ -271,24 +344,24 @@ void P_FireWeapon (player_t *player, FState *state)
// [SO] 9/2/02: People were able to do an awful lot of damage
// when they were observers...
if (player->Bot == NULL && bot_observer)
if (player->Bot == nullptr && bot_observer)
{
return;
}
weapon = player->ReadyWeapon;
if (weapon == NULL || !weapon->CheckAmmo (AWeapon::PrimaryFire, true))
if (weapon == nullptr || !weapon->CheckAmmo (AWeapon::PrimaryFire, true))
{
return;
}
player->mo->PlayAttacking ();
weapon->bAltFire = false;
if (state == NULL)
if (state == nullptr)
{
state = weapon->GetAtkState(!!player->refire);
}
P_SetPsprite (player, ps_weapon, state);
player->GetPSprite(ps_weapon)->SetState(state);
if (!(weapon->WeaponFlags & WIF_NOALERT))
{
P_NoiseAlert (player->mo, player->mo, false);
@ -307,13 +380,13 @@ void P_FireWeaponAlt (player_t *player, FState *state)
// [SO] 9/2/02: People were able to do an awful lot of damage
// when they were observers...
if (player->Bot == NULL && bot_observer)
if (player->Bot == nullptr && bot_observer)
{
return;
}
weapon = player->ReadyWeapon;
if (weapon == NULL || weapon->FindState(NAME_AltFire) == NULL || !weapon->CheckAmmo (AWeapon::AltFire, true))
if (weapon == nullptr || weapon->FindState(NAME_AltFire) == nullptr || !weapon->CheckAmmo (AWeapon::AltFire, true))
{
return;
}
@ -321,12 +394,12 @@ void P_FireWeaponAlt (player_t *player, FState *state)
player->mo->PlayAttacking ();
weapon->bAltFire = true;
if (state == NULL)
if (state == nullptr)
{
state = weapon->GetAltAtkState(!!player->refire);
}
P_SetPsprite (player, ps_weapon, state);
player->GetPSprite(ps_weapon)->SetState(state);
if (!(weapon->WeaponFlags & WIF_NOALERT))
{
P_NoiseAlert (player->mo, player->mo, false);
@ -343,15 +416,15 @@ void P_FireWeaponAlt (player_t *player, FState *state)
void P_DropWeapon (player_t *player)
{
if (player == NULL)
if (player == nullptr)
{
return;
}
// Since the weapon is dropping, stop blocking switching.
player->WeaponState &= ~WF_DISABLESWITCH;
if (player->ReadyWeapon != NULL)
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetDownState());
player->GetPSprite(ps_weapon)->SetState(player->ReadyWeapon->GetDownState());
}
}
@ -367,7 +440,7 @@ void P_DropWeapon (player_t *player)
//
//============================================================================
void P_BobWeapon (player_t *player, pspdef_t *psp, float *x, float *y, double ticfrac)
void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac)
{
static float curbob;
double xx[2], yy[2];
@ -377,7 +450,7 @@ void P_BobWeapon (player_t *player, pspdef_t *psp, float *x, float *y, double ti
weapon = player->ReadyWeapon;
if (weapon == NULL || weapon->WeaponFlags & WIF_DONTBOB)
if (weapon == nullptr || weapon->WeaponFlags & WIF_DONTBOB)
{
*x = *y = 0;
return;
@ -528,7 +601,7 @@ void DoReadyWeaponToFire (AActor *self, bool prim, bool alt)
}
// Play ready sound, if any.
if (weapon->ReadySound && player->psprites[ps_weapon].state == weapon->FindState(NAME_Ready))
if (weapon->ReadySound && player->GetPSprite(ps_weapon)->GetState() == weapon->FindState(NAME_Ready))
{
if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128)
{
@ -547,8 +620,8 @@ void DoReadyWeaponToBob (AActor *self)
{
// Prepare for bobbing action.
self->player->WeaponState |= WF_WEAPONBOBBING;
self->player->psprites[ps_weapon].sx = 0;
self->player->psprites[ps_weapon].sy = WEAPONTOP;
self->player->GetPSprite(ps_weapon)->x = 0;
self->player->GetPSprite(ps_weapon)->y = WEAPONTOP;
}
}
@ -673,12 +746,12 @@ void P_CheckWeaponSwitch (player_t *player)
static void P_CheckWeaponButtons (player_t *player)
{
if (player->Bot == NULL && bot_observer)
if (player->Bot == nullptr && bot_observer)
{
return;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon == NULL)
if (weapon == nullptr)
{
return;
}
@ -693,11 +766,11 @@ static void P_CheckWeaponButtons (player_t *player)
// [XA] don't change state if still null, so if the modder
// sets WRF_xxx to true but forgets to define the corresponding
// state, the weapon won't disappear. ;)
if (state != NULL)
if (state != nullptr)
{
P_SetPsprite(player, ps_weapon, state);
player->GetPSprite(ps_weapon)->SetState(state);
return;
}
}
}
}
}
@ -808,31 +881,31 @@ DEFINE_ACTION_FUNCTION(AInventory, A_WeaponOffset)
}
player_t *player = self->player;
pspdef_t *psp;
DPSprite *psp;
if (player && (player->playerstate != PST_DEAD))
{
psp = &player->psprites[ps_weapon];
psp = player->GetPSprite(ps_weapon);
if (!(flags & WOF_KEEPX))
{
if (flags & WOF_ADD)
{
psp->sx += wx;
psp->x += wx;
}
else
{
psp->sx = wx;
psp->x = wx;
}
}
if (!(flags & WOF_KEEPY))
{
if (flags & WOF_ADD)
{
psp->sy += wy;
psp->y += wy;
}
else
{
psp->sy = wy;
psp->y = wy;
}
}
}
@ -851,35 +924,35 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Lower)
PARAM_ACTION_PROLOGUE;
player_t *player = self->player;
pspdef_t *psp;
DPSprite *psp;
if (NULL == player)
if (nullptr == player)
{
return 0;
}
psp = &player->psprites[ps_weapon];
psp = player->GetPSprite(ps_weapon);
if (player->morphTics || player->cheats & CF_INSTANTWEAPSWITCH)
{
psp->sy = WEAPONBOTTOM;
psp->y = WEAPONBOTTOM;
}
else
{
psp->sy += LOWERSPEED;
psp->y += LOWERSPEED;
}
if (psp->sy < WEAPONBOTTOM)
if (psp->y < WEAPONBOTTOM)
{ // Not lowered all the way yet
return 0;
}
if (player->playerstate == PST_DEAD)
{ // Player is dead, so don't bring up a pending weapon
psp->sy = WEAPONBOTTOM;
psp->y = WEAPONBOTTOM;
// Player is dead, so keep the weapon off screen
P_SetPsprite (player, ps_weapon, NULL);
psp->SetState(nullptr);
return 0;
}
// [RH] Clear the flash state. Only needed for Strife.
P_SetPsprite (player, ps_flash, NULL);
player->GetPSprite(ps_flash)->SetState(nullptr);
P_BringUpWeapon (player);
return 0;
}
@ -894,14 +967,14 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Raise)
{
PARAM_ACTION_PROLOGUE;
if (self == NULL)
if (self == nullptr)
{
return 0;
}
player_t *player = self->player;
pspdef_t *psp;
DPSprite *psp;
if (NULL == player)
if (nullptr == player)
{
return 0;
}
@ -910,20 +983,20 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Raise)
P_DropWeapon(player);
return 0;
}
psp = &player->psprites[ps_weapon];
psp->sy -= RAISESPEED;
if (psp->sy > WEAPONTOP)
psp = player->GetPSprite(ps_weapon);
psp->y -= RAISESPEED;
if (psp->y > WEAPONTOP)
{ // Not raised all the way yet
return 0;
}
psp->sy = WEAPONTOP;
if (player->ReadyWeapon != NULL)
psp->y = WEAPONTOP;
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
psp->SetState(player->ReadyWeapon->GetReadyState());
}
else
{
player->psprites[ps_weapon].state = NULL;
psp->SetState(nullptr);
}
return 0;
}
@ -942,12 +1015,12 @@ enum GF_Flags
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash)
{
PARAM_ACTION_PROLOGUE;
PARAM_STATE_OPT(flash) { flash = NULL; }
PARAM_STATE_OPT(flash) { flash = nullptr; }
PARAM_INT_OPT (flags) { flags = 0; }
player_t *player = self->player;
if (NULL == player)
if (nullptr == player)
{
return 0;
}
@ -955,18 +1028,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash)
{
player->mo->PlayAttacking2 ();
}
if (flash == NULL)
if (flash == nullptr)
{
if (player->ReadyWeapon->bAltFire)
{
flash = player->ReadyWeapon->FindState(NAME_AltFlash);
}
if (flash == NULL)
if (flash == nullptr)
{
flash = player->ReadyWeapon->FindState(NAME_Flash);
}
}
P_SetPsprite (player, ps_flash, flash);
player->GetPSprite(ps_flash)->SetState(flash);
return 0;
}
@ -1084,13 +1157,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_Light)
void P_SetupPsprites(player_t *player, bool startweaponup)
{
int i;
// Remove all psprites
for (i = 0; i < NUMPSPRITES; i++)
{
player->psprites[i].state = NULL;
}
player->DestroyPSprites();
// Spawn the ready weapon
player->PendingWeapon = !startweaponup ? player->ReadyWeapon : WP_NOCHANGE;
P_BringUpWeapon (player);
@ -1104,61 +1173,167 @@ void P_SetupPsprites(player_t *player, bool startweaponup)
//
//------------------------------------------------------------------------
void P_MovePsprites (player_t *player)
void player_t::TickPSprites()
{
int i;
pspdef_t *psp;
FState *state;
DPSprite *weapon = nullptr;
DPSprite *flash = nullptr;
bool noweapon = (ReadyWeapon == nullptr && (health > 0 || mo->DamageType != NAME_Fire));
// [RH] If you don't have a weapon, then the psprites should be NULL.
if (player->ReadyWeapon == NULL && (player->health > 0 || player->mo->DamageType != NAME_Fire))
DPSprite *pspr = psprites;
while (pspr)
{
P_SetPsprite (player, ps_weapon, NULL);
P_SetPsprite (player, ps_flash, NULL);
if (player->PendingWeapon != WP_NOCHANGE)
// Destroy the psprite if it's from a weapon that isn't currently selected by the player
// or if it's from an inventory item that the player no longer owns.
// (except for the old hardcoded layers)
if (!(pspr->ID > 0 && pspr->ID < NUMPSPRITES) &&
(pspr->Caller == nullptr ||
(pspr->Caller->IsKindOf(RUNTIME_CLASS(AWeapon)) && pspr->Caller != pspr->Owner->ReadyWeapon) ||
(pspr->Caller->Owner != pspr->Owner->mo)))
{
P_BringUpWeapon (player);
pspr->Destroy();
}
else if (!(pspr->ID > 0 && pspr->ID < NUMPSPRITES && noweapon))
{
pspr->Tick();
}
if (pspr->ID == ps_weapon)
weapon = pspr;
else if (pspr->ID == ps_flash)
flash = pspr;
pspr = pspr->Next;
}
if (noweapon)
{
if (weapon) weapon->SetState(nullptr);
if (flash) flash->SetState(nullptr);
if (PendingWeapon != WP_NOCHANGE)
P_BringUpWeapon(this);
}
else
{
psp = &player->psprites[0];
for (i = 0; i < NUMPSPRITES; i++, psp++)
if (weapon && flash)
{
if ((state = psp->state) != NULL && psp->processPending) // a null state means not active
{
// drop tic count and possibly change state
if (psp->tics != -1) // a -1 tic count never changes
{
psp->tics--;
// [BC] Apply double firing speed.
if ( psp->tics && (player->cheats & CF_DOUBLEFIRINGSPEED))
psp->tics--;
if(!psp->tics)
{
P_SetPsprite (player, i, psp->state->GetNextState());
}
}
}
flash->x = weapon->x;
flash->y = weapon->y;
}
player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
P_CheckWeaponSwitch (player);
if (player->WeaponState & (WF_WEAPONREADY | WF_WEAPONREADYALT))
P_CheckWeaponSwitch(this);
if (WeaponState & (WF_WEAPONREADY | WF_WEAPONREADYALT))
{
P_CheckWeaponFire (player);
P_CheckWeaponFire(this);
}
// Check custom buttons
P_CheckWeaponButtons(player);
}
P_CheckWeaponButtons(this);
}
}
FArchive &operator<< (FArchive &arc, pspdef_t &def)
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
void DPSprite::Tick()
{
arc << def.state << def.tics << def.sx << def.sy
<< def.sprite << def.frame;
return arc;
oldx = x;
oldy = y;
if (processPending)
{
// drop tic count and possibly change state
if (Tics != -1) // a -1 tic count never changes
{
Tics--;
// [BC] Apply double firing speed.
// This is applied to the targeter layers too.
if (((ID > 0 && ID < NUMPSPRITES) || (Caller->IsKindOf(RUNTIME_CLASS(AWeapon)))) &&
(Tics && Owner->cheats & CF_DOUBLEFIRINGSPEED))
Tics--;
if (!Tics)
SetState(State->GetNextState());
}
}
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
void DPSprite::Serialize(FArchive &arc)
{
Super::Serialize(arc);
arc << Next << Caller << Owner
<< State << Tics << Sprite << Frame
<< ID << x << y << oldx << oldy;
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
void player_t::DestroyPSprites()
{
DPSprite *pspr = psprites;
while (pspr)
{
pspr->Destroy();
pspr = pspr->Next;
}
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
void DPSprite::Destroy()
{
DPSprite **prev = &Owner->psprites;
while (*prev != this)
prev = &(*prev)->Next;
*prev = Next;
Super::Destroy();
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
ADD_STAT(psprites)
{
FString out;
DPSprite *pspr;
for (int i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
out.AppendFormat("[psprites] player: %d | layers: ", i);
pspr = players[i].psprites;
while (pspr)
{
out.AppendFormat("%d, ", pspr->GetID());
pspr = pspr->GetNext();
}
out.AppendFormat("\n");
}
return out;
}