1 #include "templates.h"
2 #include "doomtype.h"
3 #include "doomstat.h"
4 #include "p_local.h"
5 #include "actor.h"
6 #include "m_bbox.h"
7 #include "m_random.h"
8 #include "s_sound.h"
9 #include "a_sharedglobal.h"
10 #include "statnums.h"
11 #include "farchive.h"
12
13 static FRandom pr_quake ("Quake");
14
15 IMPLEMENT_POINTY_CLASS (DEarthquake)
DECLARE_POINTER(m_Spot)16 DECLARE_POINTER (m_Spot)
17 END_POINTERS
18
19 //==========================================================================
20 //
21 // DEarthquake :: DEarthquake private constructor
22 //
23 //==========================================================================
24
25 DEarthquake::DEarthquake()
26 : DThinker(STAT_EARTHQUAKE)
27 {
28 }
29
30 //==========================================================================
31 //
32 // DEarthquake :: DEarthquake public constructor
33 //
34 //==========================================================================
35
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)36 DEarthquake::DEarthquake (AActor *center, int intensityX, int intensityY, int intensityZ, int duration,
37 int damrad, int tremrad, FSoundID quakesound, int flags,
38 double waveSpeedX, double waveSpeedY, double waveSpeedZ)
39 : DThinker(STAT_EARTHQUAKE)
40 {
41 m_QuakeSFX = quakesound;
42 m_Spot = center;
43 // Radii are specified in tile units (64 pixels)
44 m_DamageRadius = damrad << FRACBITS;
45 m_TremorRadius = tremrad << FRACBITS;
46 m_IntensityX = intensityX << FRACBITS;
47 m_IntensityY = intensityY << FRACBITS;
48 m_IntensityZ = intensityZ << FRACBITS;
49 m_CountdownStart = duration;
50 m_Countdown = duration;
51 m_Flags = flags;
52 m_WaveSpeedX = (float)waveSpeedX;
53 m_WaveSpeedY = (float)waveSpeedY;
54 m_WaveSpeedZ = (float)waveSpeedZ;
55 }
56
57 //==========================================================================
58 //
59 // DEarthquake :: Serialize
60 //
61 //==========================================================================
62
Serialize(FArchive & arc)63 void DEarthquake::Serialize (FArchive &arc)
64 {
65 Super::Serialize (arc);
66 arc << m_Spot << m_IntensityX << m_Countdown
67 << m_TremorRadius << m_DamageRadius
68 << m_QuakeSFX;
69 if (SaveVersion < 4519)
70 {
71 m_IntensityY = m_IntensityX;
72 m_IntensityZ = 0;
73 m_Flags = 0;
74 }
75 else
76 {
77 arc << m_IntensityY << m_IntensityZ << m_Flags;
78 }
79 if (SaveVersion < 4520)
80 {
81 m_CountdownStart = 0;
82 }
83 else
84 {
85 arc << m_CountdownStart;
86 }
87 if (SaveVersion < 4521)
88 {
89 m_WaveSpeedX = m_WaveSpeedY = m_WaveSpeedZ = 0;
90 m_IntensityX <<= FRACBITS;
91 m_IntensityY <<= FRACBITS;
92 m_IntensityZ <<= FRACBITS;
93 }
94 else
95 {
96 arc << m_WaveSpeedX << m_WaveSpeedY << m_WaveSpeedZ;
97 }
98 }
99
100 //==========================================================================
101 //
102 // DEarthquake :: Tick
103 //
104 // Deals damage to any players near the earthquake and makes sure it's
105 // making noise.
106 //
107 //==========================================================================
108
Tick()109 void DEarthquake::Tick ()
110 {
111 int i;
112
113 if (m_Spot == NULL)
114 {
115 Destroy ();
116 return;
117 }
118
119 if (!S_IsActorPlayingSomething (m_Spot, CHAN_BODY, m_QuakeSFX))
120 {
121 S_Sound (m_Spot, CHAN_BODY | CHAN_LOOP, m_QuakeSFX, 1, ATTN_NORM);
122 }
123 if (m_DamageRadius > 0)
124 {
125 for (i = 0; i < MAXPLAYERS; i++)
126 {
127 if (playeringame[i] && !(players[i].cheats & CF_NOCLIP))
128 {
129 AActor *victim = players[i].mo;
130 fixed_t dist;
131
132 dist = m_Spot->AproxDistance (victim, true);
133 // Check if in damage radius
134 if (dist < m_DamageRadius && victim->Z() <= victim->floorz)
135 {
136 if (pr_quake() < 50)
137 {
138 P_DamageMobj (victim, NULL, NULL, pr_quake.HitDice (1), NAME_None);
139 }
140 // Thrust player around
141 angle_t an = victim->angle + ANGLE_1*pr_quake();
142 if (m_IntensityX == m_IntensityY)
143 { // Thrust in a circle
144 P_ThrustMobj (victim, an, m_IntensityX << (FRACBITS-1));
145 }
146 else
147 { // Thrust in an ellipse
148 an >>= ANGLETOFINESHIFT;
149 // So this is actually completely wrong, but it ought to be good
150 // enough. Otherwise, I'd have to use tangents and square roots.
151 victim->velx += FixedMul(m_IntensityX << (FRACBITS-1), finecosine[an]);
152 victim->vely += FixedMul(m_IntensityY << (FRACBITS-1), finesine[an]);
153 }
154 }
155 }
156 }
157 }
158
159 if (--m_Countdown == 0)
160 {
161 if (S_IsActorPlayingSomething(m_Spot, CHAN_BODY, m_QuakeSFX))
162 {
163 S_StopSound(m_Spot, CHAN_BODY);
164 }
165 Destroy();
166 }
167 }
168
GetModWave(double waveMultiplier) const169 fixed_t DEarthquake::GetModWave(double waveMultiplier) const
170 {
171 //QF_WAVE converts intensity into amplitude and unlocks a new property, the wave length.
172 //This is, in short, waves per second (full cycles, mind you, from 0 to 360.)
173 //Named waveMultiplier because that's as the name implies: adds more waves per second.
174
175 double time = m_Countdown - FIXED2DBL(r_TicFrac);
176 return FLOAT2FIXED(sin(waveMultiplier * time * (M_PI * 2 / TICRATE)));
177 }
178
179 //==========================================================================
180 //
181 // DEarthquake :: GetModIntensity
182 //
183 // Given a base intensity, modify it according to the quake's flags.
184 //
185 //==========================================================================
186
GetModIntensity(fixed_t intensity) const187 fixed_t DEarthquake::GetModIntensity(fixed_t intensity) const
188 {
189 assert(m_CountdownStart >= m_Countdown);
190 intensity += intensity; // always doubled
191
192 if (m_Flags & (QF_SCALEDOWN | QF_SCALEUP))
193 {
194 int scalar;
195 if ((m_Flags & (QF_SCALEDOWN | QF_SCALEUP)) == (QF_SCALEDOWN | QF_SCALEUP))
196 {
197 scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown)
198 : MIN(m_Countdown, m_CountdownStart - m_Countdown);
199
200 if (m_Flags & QF_FULLINTENSITY)
201 {
202 scalar *= 2;
203 }
204 }
205 else if (m_Flags & QF_SCALEDOWN)
206 {
207 scalar = m_Countdown;
208 }
209 else // QF_SCALEUP
210 {
211 scalar = m_CountdownStart - m_Countdown;
212 }
213 assert(m_CountdownStart > 0);
214 intensity = Scale(intensity, scalar, m_CountdownStart);
215 }
216 return intensity;
217 }
218
219 //==========================================================================
220 //
221 // DEarthquake::StaticGetQuakeIntensity
222 //
223 // Searches for all quakes near the victim and returns their combined
224 // intensity.
225 //
226 // Pre: jiggers was pre-zeroed by the caller.
227 //
228 //==========================================================================
229
StaticGetQuakeIntensities(AActor * victim,FQuakeJiggers & jiggers)230 int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jiggers)
231 {
232 if (victim->player != NULL && (victim->player->cheats & CF_NOCLIP))
233 {
234 return 0;
235 }
236
237 TThinkerIterator<DEarthquake> iterator(STAT_EARTHQUAKE);
238 DEarthquake *quake;
239 int count = 0;
240
241 while ( (quake = iterator.Next()) != NULL)
242 {
243 if (quake->m_Spot != NULL)
244 {
245 fixed_t dist = quake->m_Spot->AproxDistance (victim, true);
246 if (dist < quake->m_TremorRadius)
247 {
248 ++count;
249 fixed_t x = quake->GetModIntensity(quake->m_IntensityX);
250 fixed_t y = quake->GetModIntensity(quake->m_IntensityY);
251 fixed_t z = quake->GetModIntensity(quake->m_IntensityZ);
252 if (!(quake->m_Flags & QF_WAVE))
253 {
254 if (quake->m_Flags & QF_RELATIVE)
255 {
256 jiggers.RelIntensityX = MAX(x, jiggers.RelIntensityX);
257 jiggers.RelIntensityY = MAX(y, jiggers.RelIntensityY);
258 jiggers.RelIntensityZ = MAX(z, jiggers.RelIntensityZ);
259 }
260 else
261 {
262 jiggers.IntensityX = MAX(x, jiggers.IntensityX);
263 jiggers.IntensityY = MAX(y, jiggers.IntensityY);
264 jiggers.IntensityZ = MAX(z, jiggers.IntensityZ);
265 }
266 }
267 else
268 {
269 fixed_t mx = FixedMul(x, quake->GetModWave(quake->m_WaveSpeedX));
270 fixed_t my = FixedMul(y, quake->GetModWave(quake->m_WaveSpeedY));
271 fixed_t mz = FixedMul(z, quake->GetModWave(quake->m_WaveSpeedZ));
272
273 // [RH] This only gives effect to the last sine quake. I would
274 // prefer if some way was found to make multiples coexist
275 // peacefully, but just summing them together is undesirable
276 // because they could cancel each other out depending on their
277 // relative phases.
278 if (quake->m_Flags & QF_RELATIVE)
279 {
280 jiggers.RelOffsetX = mx;
281 jiggers.RelOffsetY = my;
282 jiggers.RelOffsetZ = mz;
283 }
284 else
285 {
286 jiggers.OffsetX = mx;
287 jiggers.OffsetY = my;
288 jiggers.OffsetZ = mz;
289 }
290 }
291 }
292 }
293 }
294 return count;
295 }
296
297 //==========================================================================
298 //
299 // P_StartQuake
300 //
301 //==========================================================================
302
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)303 bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration,
304 int damrad, int tremrad, FSoundID quakesfx, int flags,
305 double waveSpeedX, double waveSpeedY, double waveSpeedZ)
306 {
307 AActor *center;
308 bool res = false;
309
310 if (intensityX) intensityX = clamp(intensityX, 1, 9);
311 if (intensityY) intensityY = clamp(intensityY, 1, 9);
312 if (intensityZ) intensityZ = clamp(intensityZ, 1, 9);
313
314 if (tid == 0)
315 {
316 if (activator != NULL)
317 {
318 new DEarthquake(activator, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
319 quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ);
320 return true;
321 }
322 }
323 else
324 {
325 FActorIterator iterator (tid);
326 while ( (center = iterator.Next ()) )
327 {
328 res = true;
329 new DEarthquake(center, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
330 quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ);
331 }
332 }
333
334 return res;
335 }
336
P_StartQuake(AActor * activator,int tid,int intensity,int duration,int damrad,int tremrad,FSoundID quakesfx)337 bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx)
338 { //Maintains original behavior by passing 0 to intensityZ, and flags.
339 return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0);
340 }
341