gzdoom/src/g_shared/a_quake.cpp

385 lines
11 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
#include "templates.h"
#include "doomtype.h"
#include "doomstat.h"
#include "p_local.h"
#include "actor.h"
#include "m_bbox.h"
#include "m_random.h"
#include "s_sound.h"
#include "a_sharedglobal.h"
#include "statnums.h"
#include "farchive.h"
#include "d_player.h"
#include "r_utility.h"
static FRandom pr_quake ("Quake");
IMPLEMENT_POINTY_CLASS (DEarthquake)
DECLARE_POINTER (m_Spot)
END_POINTERS
//==========================================================================
//
// DEarthquake :: DEarthquake private constructor
//
//==========================================================================
DEarthquake::DEarthquake()
: DThinker(STAT_EARTHQUAKE)
{
}
//==========================================================================
//
// DEarthquake :: DEarthquake public constructor
//
//==========================================================================
2016-03-23 19:45:48 +00:00
DEarthquake::DEarthquake(AActor *center, int intensityX, int intensityY, int intensityZ, int duration,
int damrad, int tremrad, FSoundID quakesound, int flags,
double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, int rollIntensity,
double rollWave)
2016-03-23 19:45:48 +00:00
: DThinker(STAT_EARTHQUAKE)
2016-03-01 15:47:10 +00:00
{
m_QuakeSFX = quakesound;
m_Spot = center;
// Radii are specified in tile units (64 pixels)
2016-03-23 19:45:48 +00:00
m_DamageRadius = damrad;
m_TremorRadius = tremrad;
m_Intensity = DVector3(intensityX, intensityY, intensityZ);
2016-03-01 15:47:10 +00:00
m_CountdownStart = duration;
m_Countdown = duration;
m_Flags = flags;
2016-03-23 19:45:48 +00:00
m_WaveSpeed = DVector3(waveSpeedX, waveSpeedY, waveSpeedZ);
m_Falloff = falloff;
m_Highpoint = highpoint;
m_MiniCount = highpoint;
m_RollIntensity = rollIntensity;
m_RollWave = rollWave;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// DEarthquake :: Serialize
//
//==========================================================================
void DEarthquake::Serialize (FArchive &arc)
{
Super::Serialize (arc);
2016-03-23 19:45:48 +00:00
arc << m_Spot << m_Intensity << m_Countdown
2016-03-01 15:47:10 +00:00
<< m_TremorRadius << m_DamageRadius
2016-03-23 19:45:48 +00:00
<< m_QuakeSFX << m_Flags << m_CountdownStart
<< m_WaveSpeed
<< m_Falloff << m_Highpoint << m_MiniCount
<< m_RollIntensity;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// DEarthquake :: Tick
//
// Deals damage to any players near the earthquake and makes sure it's
// making noise.
//
//==========================================================================
void DEarthquake::Tick ()
{
int i;
if (m_Spot == NULL)
{
Destroy ();
return;
}
if (!S_IsActorPlayingSomething (m_Spot, CHAN_BODY, m_QuakeSFX))
{
S_Sound (m_Spot, CHAN_BODY | CHAN_LOOP, m_QuakeSFX, 1, ATTN_NORM);
}
2016-03-01 15:47:10 +00:00
if (m_DamageRadius > 0)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !(players[i].cheats & CF_NOCLIP))
{
AActor *victim = players[i].mo;
2016-03-23 19:45:48 +00:00
double dist;
2016-03-01 15:47:10 +00:00
2016-03-23 19:45:48 +00:00
dist = m_Spot->Distance2D(victim, true);
// Check if in damage radius
2016-03-20 18:52:35 +00:00
if (dist < m_DamageRadius && victim->Z() <= victim->floorz)
2016-03-01 15:47:10 +00:00
{
if (pr_quake() < 50)
{
P_DamageMobj (victim, NULL, NULL, pr_quake.HitDice (1), NAME_None);
}
// Thrust player around
DAngle an = victim->Angles.Yaw + pr_quake();
2016-03-23 19:45:48 +00:00
victim->Vel.X += m_Intensity.X * an.Cos() * 0.5;
victim->Vel.Y += m_Intensity.Y * an.Sin() * 0.5;
2016-03-01 15:47:10 +00:00
}
}
}
}
if (m_MiniCount > 0)
m_MiniCount--;
2016-03-01 15:47:10 +00:00
if (--m_Countdown == 0)
{
if (S_IsActorPlayingSomething(m_Spot, CHAN_BODY, m_QuakeSFX))
{
S_StopSound(m_Spot, CHAN_BODY);
}
Destroy();
}
}
//==========================================================================
//
// DEarthquake :: GetModWave
//
// QF_WAVE converts intensity into amplitude and unlocks a new property, the
// wave length. This is, in short, waves per second. Named waveMultiplier
// because that's as the name implies: adds more waves per second.
//
//==========================================================================
2016-03-23 19:45:48 +00:00
double DEarthquake::GetModWave(double waveMultiplier) const
2016-03-01 15:47:10 +00:00
{
double time = m_Countdown - r_TicFracF;
2016-03-23 19:45:48 +00:00
return g_sin(waveMultiplier * time * (M_PI * 2 / TICRATE));
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// DEarthquake :: GetModIntensity
//
// Given a base intensity, modify it according to the quake's flags.
//
//==========================================================================
2016-03-23 19:45:48 +00:00
double DEarthquake::GetModIntensity(double intensity) const
2016-03-01 15:47:10 +00:00
{
assert(m_CountdownStart >= m_Countdown);
2016-03-01 15:47:10 +00:00
intensity += intensity; // always doubled
if (m_Flags & (QF_SCALEDOWN | QF_SCALEUP))
{
// Adjustable maximums must use a range between 1 and m_CountdownStart to constrain between no quake and full quake.
bool check = !!(m_Highpoint > 0 && m_Highpoint < m_CountdownStart);
int divider = (check) ? m_Highpoint : m_CountdownStart;
2016-03-01 15:47:10 +00:00
int scalar;
2016-03-01 15:47:10 +00:00
if ((m_Flags & (QF_SCALEDOWN | QF_SCALEUP)) == (QF_SCALEDOWN | QF_SCALEUP))
{
if (check)
{
if (m_MiniCount > 0)
scalar = (m_Flags & QF_MAX) ? m_MiniCount : (m_Highpoint - m_MiniCount);
else
{
divider = m_CountdownStart - m_Highpoint;
scalar = (m_Flags & QF_MAX) ? (divider - m_Countdown) : m_Countdown;
}
}
else
{
// Defaults to middle of the road.
divider = m_CountdownStart;
scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown)
: MIN(m_Countdown, m_CountdownStart - m_Countdown);
}
scalar = (scalar > divider) ? divider : scalar;
2016-03-01 15:47:10 +00:00
if (m_Flags & QF_FULLINTENSITY)
{
scalar *= 2;
}
}
else
2016-03-01 15:47:10 +00:00
{
if (m_Flags & QF_SCALEDOWN)
{
scalar = m_Countdown;
}
else // QF_SCALEUP
{
scalar = m_CountdownStart - m_Countdown;
if (m_Highpoint > 0)
{
if ((m_Highpoint - m_MiniCount) < divider)
scalar = m_Highpoint - m_MiniCount;
else
scalar = divider;
}
}
scalar = (scalar > divider) ? divider : scalar;
}
assert(divider > 0);
2016-03-23 19:45:48 +00:00
intensity = intensity * scalar / divider;
2016-03-01 15:47:10 +00:00
}
return intensity;
}
//==========================================================================
//
// DEarthquake :: GetFalloff
//
// Given the distance of the player from the quake, find the multiplier.
//
//==========================================================================
2016-03-23 19:45:48 +00:00
double DEarthquake::GetFalloff(double dist) const
{
if ((dist < m_Falloff) || (m_Falloff >= m_TremorRadius) || (m_Falloff <= 0) || (m_TremorRadius - m_Falloff <= 0))
{ //Player inside the minimum falloff range, or safety check kicked in.
2016-03-23 19:45:48 +00:00
return 1.;
}
else if ((dist > m_Falloff) && (dist < m_TremorRadius))
{ //Player inside the radius, and outside the min distance for falloff.
2016-03-23 19:45:48 +00:00
double tremorsize = m_TremorRadius - m_Falloff;
assert(tremorsize > 0);
return (1. - ((dist - m_Falloff) / tremorsize));
}
else
{ //Shouldn't happen.
2016-03-23 19:45:48 +00:00
return 1.;
}
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// DEarthquake::StaticGetQuakeIntensity
//
// Searches for all quakes near the victim and returns their combined
// intensity.
//
// Pre: jiggers was pre-zeroed by the caller.
//
//==========================================================================
int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jiggers)
{
if (victim->player != NULL && (victim->player->cheats & CF_NOCLIP))
{
return 0;
}
TThinkerIterator<DEarthquake> iterator(STAT_EARTHQUAKE);
DEarthquake *quake;
int count = 0;
while ( (quake = iterator.Next()) != NULL)
{
if (quake->m_Spot != NULL)
{
2016-03-23 19:45:48 +00:00
double dist = quake->m_Spot->Distance2D (victim, true);
2016-03-01 15:47:10 +00:00
if (dist < quake->m_TremorRadius)
{
2016-03-23 19:45:48 +00:00
double falloff = quake->GetFalloff(dist);
2016-03-01 15:47:10 +00:00
++count;
2016-03-23 19:45:48 +00:00
double x = quake->GetModIntensity(quake->m_Intensity.X);
double y = quake->GetModIntensity(quake->m_Intensity.Y);
double z = quake->GetModIntensity(quake->m_Intensity.Z);
double r = quake->GetModIntensity(quake->m_RollIntensity);
2016-03-01 15:47:10 +00:00
if (!(quake->m_Flags & QF_WAVE))
{
jiggers.Falloff = MAX(falloff, jiggers.Falloff);
jiggers.RollIntensity = MAX(r, jiggers.RollIntensity);
2016-03-01 15:47:10 +00:00
if (quake->m_Flags & QF_RELATIVE)
{
2016-03-23 19:45:48 +00:00
jiggers.RelIntensity.X = MAX(x, jiggers.RelIntensity.X);
jiggers.RelIntensity.Y = MAX(y, jiggers.RelIntensity.Y);
jiggers.RelIntensity.Z = MAX(z, jiggers.RelIntensity.Z);
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-23 19:45:48 +00:00
jiggers.Intensity.X = MAX(x, jiggers.Intensity.X);
jiggers.Intensity.Y = MAX(y, jiggers.Intensity.Y);
jiggers.Intensity.Z = MAX(z, jiggers.Intensity.Z);
2016-03-01 15:47:10 +00:00
}
}
else
{
jiggers.WFalloff = MAX(falloff, jiggers.WFalloff);
double mr = r * quake->GetModWave(quake->m_RollWave);
2016-03-23 19:45:48 +00:00
double mx = x * quake->GetModWave(quake->m_WaveSpeed.X);
double my = y * quake->GetModWave(quake->m_WaveSpeed.Y);
double mz = z * quake->GetModWave(quake->m_WaveSpeed.Z);
jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave);
2016-03-01 15:47:10 +00:00
// [RH] This only gives effect to the last sine quake. I would
// prefer if some way was found to make multiples coexist
// peacefully, but just summing them together is undesirable
// because they could cancel each other out depending on their
// relative phases.
if (quake->m_Flags & QF_RELATIVE)
{
2016-03-23 19:45:48 +00:00
jiggers.RelOffset.X = mx;
jiggers.RelOffset.Y = my;
jiggers.RelOffset.Z = mz;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-23 19:45:48 +00:00
jiggers.Offset.X = mx;
jiggers.Offset.Y = my;
jiggers.Offset.Z = mz;
2016-03-01 15:47:10 +00:00
}
}
}
}
}
return count;
}
//==========================================================================
//
// P_StartQuake
//
//==========================================================================
bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration,
int damrad, int tremrad, FSoundID quakesfx, int flags,
double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint,
int rollIntensity, double rollWave)
2016-03-01 15:47:10 +00:00
{
AActor *center;
bool res = false;
if (intensityX) intensityX = clamp(intensityX, 1, 9);
if (intensityY) intensityY = clamp(intensityY, 1, 9);
if (intensityZ) intensityZ = clamp(intensityZ, 1, 9);
if (tid == 0)
{
if (activator != NULL)
{
new DEarthquake(activator, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint, rollIntensity, rollWave);
2016-03-01 15:47:10 +00:00
return true;
}
}
else
{
FActorIterator iterator (tid);
while ( (center = iterator.Next ()) )
{
res = true;
new DEarthquake(center, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint, rollIntensity, rollWave);
2016-03-01 15:47:10 +00:00
}
}
return res;
}
bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx)
{ //Maintains original behavior by passing 0 to intensityZ, flags, and everything else after QSFX.
return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0, 0, 0);
2016-03-01 15:47:10 +00:00
}