1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_snapshot.cpp 2785 2012-02-18 23:22:07Z dr_sean $
5 //
6 // Copyright (C) 2000-2006 by Sergey Makovkin (CSDoom .62).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	Stores a limited view of actor, player, and sector objects at a particular
21 //  point in time.  Used with Unlagged, client-side prediction, and positional
22 //  interpolation
23 //
24 //-----------------------------------------------------------------------------
25 
26 
27 #include <math.h>
28 #include "actor.h"
29 #include "d_player.h"
30 #include "p_local.h"
31 #include "p_spec.h"
32 #include "m_vectors.h"
33 
34 #include "p_snapshot.h"
35 
36 static const int MAX_EXTRAPOLATION = 4;
37 
38 static const fixed_t POS_LERP_THRESHOLD = 2 * FRACUNIT;
39 static const fixed_t SECTOR_LERP_THRESHOLD = 2 * FRACUNIT;
40 
41 extern bool predicting;
42 
43 // ============================================================================
44 //
45 // Snapshot implementation
46 //
47 // ============================================================================
48 
Snapshot(int time)49 Snapshot::Snapshot(int time) :
50 		mTime(time), mValid(time > 0),
51 		mAuthoritative(false), mContinuous(true),
52 		mInterpolated(false), mExtrapolated(false)
53 {
54 }
55 
operator ==(const Snapshot & other) const56 bool Snapshot::operator==(const Snapshot &other) const
57 {
58 	return	mTime == other.mTime &&
59 			mValid == other.mValid &&
60 			mAuthoritative == other.mAuthoritative &&
61 			mContinuous == other.mContinuous &&
62 			mInterpolated == other.mInterpolated &&
63 			mExtrapolated == other.mExtrapolated;
64 }
65 
66 // ============================================================================
67 //
68 // ActorSnapshot implementation
69 //
70 // ============================================================================
71 
ActorSnapshot(int time)72 ActorSnapshot::ActorSnapshot(int time) :
73 		Snapshot(time), mFields(0),
74 		mX(0), mY(0), mZ(0),
75 		mMomX(0), mMomY(0), mMomZ(0), mAngle(0), mPitch(0), mOnGround(true),
76 		mCeilingZ(0), mFloorZ(0), mReactionTime(0), mWaterLevel(0),
77 		mFlags(0), mFlags2(0), mFrame(0)
78 {
79 }
80 
ActorSnapshot(int time,const AActor * mo)81 ActorSnapshot::ActorSnapshot(int time, const AActor *mo) :
82 		Snapshot(time), mFields(0xFFFFFFFF),
83 		mX(mo->x), mY(mo->y), mZ(mo->z),
84 		mMomX(mo->momx), mMomY(mo->momy), mMomZ(mo->momz),
85 		mAngle(mo->angle), mPitch(mo->pitch), mOnGround(mo->onground),
86 		mCeilingZ(mo->ceilingz), mFloorZ(mo->floorz),
87 		mReactionTime(mo->reactiontime), mWaterLevel(mo->waterlevel),
88 		mFlags(mo->flags), mFlags2(mo->flags2), mFrame(mo->frame)
89 {
90 }
91 
operator ==(const ActorSnapshot & other) const92 bool ActorSnapshot::operator==(const ActorSnapshot &other) const
93 {
94 	return	Snapshot::operator==(other) &&
95 			mFields == other.mFields &&
96 			mX == other.mX &&
97 			mY == other.mY &&
98 			mZ == other.mZ &&
99 			mMomX == other.mMomX &&
100 			mMomY == other.mMomY &&
101 			mMomZ == other.mMomZ &&
102 			mAngle == other.mAngle &&
103 			mPitch == other.mPitch &&
104 			mOnGround == other.mOnGround &&
105 			mCeilingZ == other.mCeilingZ &&
106 			mFloorZ == other.mFloorZ &&
107 			mReactionTime == other.mReactionTime &&
108 			mWaterLevel == other.mWaterLevel &&
109 			mFlags == other.mFlags &&
110 			mFlags2 == other.mFlags2 &&
111 			mFrame == other.mFrame;
112 }
113 
merge(const ActorSnapshot & other)114 void ActorSnapshot::merge(const ActorSnapshot& other)
115 {
116 	if (other.mFields & ACT_POSITIONX)
117 		setX(other.mX);
118 	if (other.mFields & ACT_POSITIONY)
119 		setY(other.mY);
120 	if (other.mFields & ACT_POSITIONZ)
121 		setZ(other.mZ);
122 	if (other.mFields & ACT_MOMENTUMX)
123 		setMomX(other.mMomX);
124 	if (other.mFields & ACT_MOMENTUMY)
125 		setMomY(other.mMomY);
126 	if (other.mFields & ACT_MOMENTUMZ)
127 		setMomZ(other.mMomZ);
128 	if (other.mFields & ACT_ANGLE)
129 		setAngle(other.mAngle);
130 	if (other.mFields & ACT_PITCH)
131 		setPitch(other.mPitch);
132 	if (other.mFields & ACT_ONGROUND)
133 		setOnGround(other.mOnGround);
134 	if (other.mFields & ACT_CEILINGZ)
135 		setCeilingZ(other.mCeilingZ);
136 	if (other.mFields & ACT_FLOORZ)
137 		setFloorZ(other.mFloorZ);
138 	if (other.mFields & ACT_REACTIONTIME)
139 		setReactionTime(other.mReactionTime);
140 	if (other.mFields & ACT_WATERLEVEL)
141 		setWaterLevel(other.mWaterLevel);
142 	if (other.mFields & ACT_FLAGS)
143 		setFlags(other.mFlags);
144 	if (other.mFields & ACT_FLAGS2)
145 		setFlags2(other.mFlags2);
146 	if (other.mFields & ACT_FRAME)
147 		setFrame(other.mFrame);
148 
149 	if (other.isContinuous())
150 		setContinuous(true);
151 	if (other.isAuthoritative())
152 		setAuthoritative(true);
153 }
154 
toActor(AActor * mo) const155 void ActorSnapshot::toActor(AActor *mo) const
156 {
157 	if (!mo)
158 		return;
159 
160 	if (mFields & (ACT_POSITIONX | ACT_POSITIONY | ACT_POSITIONZ))
161 	{
162 		fixed_t destx = mX, desty = mY, destz = mZ;
163 		if (isInterpolated() || isExtrapolated())
164 		{
165 			// need to check for collisions before moving
166 			P_TestActorMovement(mo, mX, mY, mZ, destx, desty, destz);
167 
168 			#ifdef _SNAPSHOT_DEBUG_
169 			if (mX != destx || mY != desty || mZ != destz)
170 				Printf(PRINT_HIGH, "Snapshot %i: ActorSnapshot::toActor() clipping movement.\n", getTime());
171 			#endif // _SNAPSHOT_DEBUG_
172 		}
173 
174 		// [SL] 2011-11-06 - Avoid setting the actor's floorz value if it hasn't moved.
175 		// This ensures the floorz value is correct for actors that have spawned too
176 		// close to a ledge but have not yet moved.
177 		if (mo->x != destx || mo->y != desty || mo->z != destz)
178 		{
179 			P_CheckPosition(mo, destx, desty);
180 
181 			mo->UnlinkFromWorld();
182 			mo->x = destx;
183 			mo->y = desty;
184 			mo->z = destz;
185 
186 			mo->ceilingz = tmceilingz;
187 			mo->floorz = tmfloorz;
188 			mo->dropoffz = tmdropoffz;
189 			mo->floorsector = tmfloorsector;
190 
191 			mo->LinkToWorld();
192 		}
193 	}
194 
195 	if (mFields & (ACT_MOMENTUMX | ACT_MOMENTUMY | ACT_MOMENTUMZ))
196 	{
197 		mo->momx = mMomX;
198 		mo->momy = mMomY;
199 		mo->momz = mMomZ;
200 	}
201 
202 	// Only set a player's angle if he is alive.  Otherwise it will
203 	// interfere with the deathcam
204 	if (mFields & ACT_ANGLE && (!mo->player || mo->player->playerstate != PST_DEAD))
205 		mo->angle = mAngle;
206 
207 	if (mFields & ACT_PITCH)
208 		mo->pitch = mPitch;
209 	if (mFields & ACT_ONGROUND)
210 		mo->onground = mOnGround;
211 	if (mFields & ACT_CEILINGZ)
212 		mo->ceilingz = mCeilingZ;
213 	if (mFields & ACT_FLOORZ)
214 		mo->floorz = mFloorZ;
215 	if (mFields & ACT_REACTIONTIME)
216 		mo->reactiontime = mReactionTime;
217 	if (mFields & ACT_WATERLEVEL)
218 		mo->waterlevel = mWaterLevel;
219 	if (mFields & ACT_FLAGS)
220 		mo->flags = mFlags;
221 	if (mFields & ACT_FLAGS2)
222 		mo->flags2 = mFlags2;
223 	if (mFields & ACT_FRAME)
224 		mo->frame = mFrame;
225 }
226 
227 // ============================================================================
228 //
229 // PlayerSnapshot implementation
230 //
231 // ============================================================================
232 
PlayerSnapshot(int time)233 PlayerSnapshot::PlayerSnapshot(int time) :
234 		Snapshot(time), mFields(0),
235 		mActorSnap(time),
236 		mViewHeight(0), mDeltaViewHeight(0), mJumpTime(0)
237 {
238 }
239 
PlayerSnapshot(int time,player_t * player)240 PlayerSnapshot::PlayerSnapshot(int time, player_t *player) :
241 		Snapshot(time), mFields(0xFFFFFFFF),
242 		mActorSnap(time, player->mo),
243 		mViewHeight(player->viewheight), mDeltaViewHeight(player->deltaviewheight),
244 		mJumpTime(player->jumpTics)
245 {
246 }
247 
operator ==(const PlayerSnapshot & other) const248 bool PlayerSnapshot::operator==(const PlayerSnapshot &other) const
249 {
250 	return	Snapshot::operator==(other) &&
251 			mFields == other.mFields &&
252 			mActorSnap == other.mActorSnap &&
253 			mViewHeight == other.mViewHeight &&
254 			mDeltaViewHeight == other.mDeltaViewHeight &&
255 			mJumpTime == other.mJumpTime;
256 }
257 
merge(const PlayerSnapshot & other)258 void PlayerSnapshot::merge(const PlayerSnapshot& other)
259 {
260 	mActorSnap.merge(other.mActorSnap);
261 
262 	if (other.mFields & PLY_VIEWHEIGHT)
263 		setViewHeight(other.mViewHeight);
264 	if (other.mFields & PLY_DELTAVIEWHEIGHT)
265 		setDeltaViewHeight(other.mDeltaViewHeight);
266 	if (other.mFields & PLY_JUMPTIME)
267 		setJumpTime(other.mJumpTime);
268 }
269 
toPlayer(player_t * player) const270 void PlayerSnapshot::toPlayer(player_t *player) const
271 {
272 	if (!player || !player->mo)
273 		return;
274 
275 	mActorSnap.toActor(player->mo);
276 
277 	if (mFields & PLY_VIEWHEIGHT)
278 		player->viewheight = mViewHeight;
279 	if (mFields & PLY_DELTAVIEWHEIGHT)
280 		player->deltaviewheight = mDeltaViewHeight;
281 	if (mFields & PLY_JUMPTIME)
282 		player->jumpTics = mJumpTime;
283 }
284 
285 
286 // ============================================================================
287 //
288 // PlayerSnapshotManager implementation
289 //
290 // ============================================================================
291 
PlayerSnapshotManager()292 PlayerSnapshotManager::PlayerSnapshotManager() :
293 	mMostRecent(0)
294 {
295 	clearSnapshots();
296 }
297 
298 //
299 // PlayerSnapshotManager::clearSnapshots()
300 //
301 // Marks all of the snapshots in the container invalid, effectively
302 // clearing the container.
303 //
clearSnapshots()304 void PlayerSnapshotManager::clearSnapshots()
305 {
306 	// Set the time for all snapshots to an invalid value
307 	for (int i = 0; i < NUM_SNAPSHOTS; i++)
308 		mSnaps[i].setTime(-1);
309 
310 	mMostRecent = 0;
311 }
312 
313 //
314 // PlayerSnapshotManager::mValidSnapshot()
315 //
316 // Returns true if a snapshot at the given time is present in the container
317 //
mValidSnapshot(int time) const318 bool PlayerSnapshotManager::mValidSnapshot(int time) const
319 {
320 
321 	return ((time <= mMostRecent) && (mMostRecent - time <= NUM_SNAPSHOTS) &&
322 			(time > 0) && (mSnaps[time % NUM_SNAPSHOTS].isValid()) &&
323 			(mSnaps[time % NUM_SNAPSHOTS].getTime() == time));
324 }
325 
326 //
327 // PlayerSnapshotManager::mInterpolateSnapshots()
328 //
329 // Returns a snapshot based on the snapshot at the time "to" with a position
330 // linearly interpolated between snapshots "from" and "to".
331 //
332 // Note: The caller of this function has the responsibility for verifying
333 // that the container has valid snapshots at the times "from" and "to".
334 //
mInterpolateSnapshots(int from,int to,int time) const335 PlayerSnapshot PlayerSnapshotManager::mInterpolateSnapshots(int from, int to, int time) const
336 {
337 	// Assumes that range checking from and to has been performed by the caller
338 	const PlayerSnapshot *snapfrom = &mSnaps[from % NUM_SNAPSHOTS];
339 	const PlayerSnapshot *snapto = &mSnaps[to % NUM_SNAPSHOTS];
340 
341 	if (to == from || !snapto->isContinuous())
342 		return *snapto;
343 
344 	float amount = float(time - from) / float(to - from);
345 
346 	return P_LerpPlayerPosition(*snapfrom, *snapto, amount);
347 }
348 
349 //
350 // PlayerSnapshotManager::mExtrapolateSnapshots()
351 //
352 // Returns a snapshot based on the snapshot at the time "from" with an
353 // estimated position calculated using the player's velocity.
354 //
355 // Note: The caller of this function has the responsibility for verifying
356 // that the container has valid snapshots at the time "from".
357 //
mExtrapolateSnapshot(int from,int time) const358 PlayerSnapshot PlayerSnapshotManager::mExtrapolateSnapshot(int from, int time) const
359 {
360 	// Assumes that range checking from has been performed by the caller
361 	const PlayerSnapshot *snapfrom = &mSnaps[from % NUM_SNAPSHOTS];
362 
363 	float amount = time - from;
364 
365 	return P_ExtrapolatePlayerPosition(*snapfrom, amount);
366 }
367 
368 //
369 // PlayerSnapshotManager::addSnapshot()
370 //
371 // Inserts a new snapshot into the container, provided it is valid and not
372 // too old
373 //
addSnapshot(const PlayerSnapshot & snap)374 void PlayerSnapshotManager::addSnapshot(const PlayerSnapshot &snap)
375 {
376 	int time = snap.getTime();
377 
378 	if (!snap.isValid())
379 	{
380 		#ifdef _SNAPSHOT_DEBUG_
381 		Printf(PRINT_HIGH, "Snapshot %i: Not adding invalid player snapshot\n", time);
382 		#endif // _SNAPSHOT_DEBUG_
383 		return;
384 	}
385 
386 	if (mMostRecent > snap.getTime() + NUM_SNAPSHOTS)
387 	{
388 		#ifdef _SNAPSHOT_DEBUG_
389 		Printf(PRINT_HIGH, "Snapshot %i: Not adding expired player snapshot\n", time);
390 		#endif // _SNAPSHOT_DEBUG_
391 		return;
392 	}
393 
394 	PlayerSnapshot& dest = mSnaps[time % NUM_SNAPSHOTS];
395 	if (dest.getTime() == time)
396 	{
397 		// A valid snapshot for this time already exists. Merge it with this one.
398 		dest.merge(snap);
399 	}
400 	else
401 	{
402 		dest = snap;
403 	}
404 
405 	if (time > mMostRecent)
406 		mMostRecent = time;
407 }
408 
mFindValidSnapshot(int starttime,int endtime) const409 int PlayerSnapshotManager::mFindValidSnapshot(int starttime, int endtime) const
410 {
411 	if (starttime < mMostRecent - NUM_SNAPSHOTS ||
412 		endtime < mMostRecent - NUM_SNAPSHOTS ||
413 		starttime > mMostRecent || endtime > mMostRecent)
414 		return -1;
415 
416 	if (endtime >= starttime)
417 	{
418 		for (int t = starttime; t <= endtime; t++)
419 			if (mValidSnapshot(t))
420 				return t;
421 	}
422 	else
423 	{
424 		for (int t = starttime; t >= endtime; t--)
425 			if (mValidSnapshot(t))
426 				return t;
427 	}
428 
429 	// Did not find any valid snapshots
430 	return -1;
431 }
432 
433 //
434 // PlayerSnapshotManager::getSnapshot()
435 //
436 // Returns a snapshot from the container at a specified time.
437 // If there is not a snapshot matching the time, one is generated using
438 // interpolation or extrapolation.
439 //
getSnapshot(int time) const440 PlayerSnapshot PlayerSnapshotManager::getSnapshot(int time) const
441 {
442 	if (time <= 0 || mMostRecent <= 0)
443 		return PlayerSnapshot();
444 
445 	// Return the requested snapshot if availible
446 	if (mValidSnapshot(time))
447 		return mSnaps[time % NUM_SNAPSHOTS];
448 
449 	// Should we extrapolate?
450 	if (time > mMostRecent)
451 	{
452 		int amount = time - mMostRecent;
453 		if (amount > MAX_EXTRAPOLATION)		// cap extrapolation
454 		{
455 			#ifdef _SNAPSHOT_DEBUG_
456 			Printf(PRINT_HIGH, "Extrap %i: PlayerSnapshotManager::getSnapshot() capping extrapolation past %i\n",
457 					time, mMostRecent);
458 			#endif // _SNAPSHOT_DEBUG_
459 
460 			amount = MAX_EXTRAPOLATION;
461 		}
462 
463 		#ifdef _SNAPSHOT_DEBUG_
464 		Printf(PRINT_HIGH, "Extrap %i: PlayerSnapshotManager::getSnapshot() extrapolating past %i\n",
465 				time, mMostRecent);
466 		#endif // _SNAPSHOT_DEBUG_
467 
468 		return mExtrapolateSnapshot(mMostRecent, amount + mMostRecent);
469 	}
470 
471 	// find the snapshot that precedes the desired time
472 	int pretime = mFindValidSnapshot(time, mMostRecent - NUM_SNAPSHOTS);
473 
474 	// find the snapshot that follows the desired time
475 	int posttime = mFindValidSnapshot(time, mMostRecent);
476 
477 	// Can we interpolate?
478 	if (pretime > 0 && posttime > 0 && time < posttime && time > pretime)
479 	{
480 		#ifdef _SNAPSHOT_DEBUG_
481 		Printf(PRINT_HIGH, "Lerp %i: PlayerSnapshotManager::getSnapshot() interpolating between %i and %i.\n",
482 					time, pretime, posttime);
483 		#endif // _SNAPSHOT_DEBUG_
484 
485 		return mInterpolateSnapshots(pretime, posttime, time);
486 	}
487 
488 	// No snapshots before the desired time?  Return the closest one to it
489 	if (pretime <= 0 && posttime > 0)
490 		return mSnaps[posttime % NUM_SNAPSHOTS];
491 
492 	// Could not find a valid snapshot so return a blank (invalid) one
493 	return PlayerSnapshot();
494 }
495 
496 
497 // ============================================================================
498 //
499 // Actor and Player Helper functions
500 //
501 // ============================================================================
502 
P_PositionDifference(const v3fixed_t & a,const v3fixed_t & b)503 static fixed_t P_PositionDifference(const v3fixed_t &a, const v3fixed_t &b)
504 {
505 	v3fixed_t diff;
506 	M_SubVec3Fixed(&diff, &b, &a);
507 
508 	return M_LengthVec3Fixed(&diff);
509 }
510 
511 //
512 // P_ExtrapolateActorPosition
513 //
514 //
P_ExtrapolateActorPosition(const ActorSnapshot & from,float amount)515 ActorSnapshot P_ExtrapolateActorPosition(const ActorSnapshot &from, float amount)
516 {
517 	fixed_t amount_fixed = FLOAT2FIXED(amount);
518 
519 	if (amount_fixed <= 0)
520 		return from;
521 
522 	v3fixed_t velocity, pos_new;
523 	M_SetVec3Fixed(&pos_new, from.getX(), from.getY(), from.getZ());
524 	M_SetVec3Fixed(&velocity, from.getMomX(), from.getMomY(), from.getMomZ());
525 	M_ScaleVec3Fixed(&velocity, &velocity, amount_fixed);
526 	M_AddVec3Fixed(&pos_new, &pos_new, &velocity);
527 
528 	ActorSnapshot newsnapshot(from);
529 	newsnapshot.setAuthoritative(false);
530 	newsnapshot.setExtrapolated(true);
531 	newsnapshot.setX(pos_new.x);
532 	newsnapshot.setY(pos_new.y);
533 	newsnapshot.setZ(pos_new.z);
534 
535 	return newsnapshot;
536 }
537 
538 
P_ExtrapolatePlayerPosition(const PlayerSnapshot & from,float amount)539 PlayerSnapshot P_ExtrapolatePlayerPosition(const PlayerSnapshot &from, float amount)
540 {
541 	PlayerSnapshot newsnap(from);
542 	newsnap.mActorSnap = P_ExtrapolateActorPosition(from.mActorSnap, amount);
543 	newsnap.setExtrapolated(newsnap.mActorSnap.isExtrapolated());
544 
545 	return newsnap;
546 }
547 
548 
549 //
550 // P_LerpActorPosition
551 //
552 // Returns a snapshot containing a position that is a percentage of the distance
553 // between the positions contained in from and to.  Besides the position, the
554 // returned snapshot will be identical to the snapshot to.
555 //
556 // Note that amount will most often be between 0.0 and 1.0, however it can be
557 // larger than 1.0 as an alternative method of extrapolating a position.
558 //
P_LerpActorPosition(const ActorSnapshot & from,const ActorSnapshot & to,float amount)559 ActorSnapshot P_LerpActorPosition(const ActorSnapshot &from, const ActorSnapshot &to, float amount)
560 {
561 	fixed_t amount_fixed = FLOAT2FIXED(amount);
562 
563 	if (amount_fixed <= 0)
564 		return from;
565 
566 	// Calculate the distance between the positions
567 	v3fixed_t pos_from, pos_to;
568 	M_SetVec3Fixed(&pos_from, from.getX(), from.getY(), from.getZ());
569 	M_SetVec3Fixed(&pos_to, to.getX(), to.getY(), to.getZ());
570 	fixed_t pos_delta = P_PositionDifference(pos_from, pos_to);
571 
572 	#ifdef _SNAPSHOT_DEBUG_
573 	if (pos_delta)
574 		Printf(PRINT_HIGH, "Lerp: MF2_ONMOBJ = %s\n", from.getFlags2() & MF2_ONMOBJ ? "yes" : "no");
575 	#endif // _SNAPSHOT_DEBUG_
576 
577 	if (pos_delta <= POS_LERP_THRESHOLD || !to.isContinuous())
578 	{
579 		// snap directly to the new position
580 		#ifdef _SNAPSHOT_DEBUG_
581 		if (pos_delta)
582 			Printf(PRINT_HIGH, "Lerp: %d Snapping to position (delta %d)\n",
583 								gametic, pos_delta >> FRACBITS);
584 		#endif // _SNAPSHOT_DEBUG_
585 		return to;
586 	}
587 
588 	// Calculate the interpolated position between pos_from and pos_to
589 	v3fixed_t pos_new;
590 	M_SubVec3Fixed(&pos_new, &pos_to, &pos_from);
591 	M_ScaleVec3Fixed(&pos_new, &pos_new, amount_fixed);
592 	M_AddVec3Fixed(&pos_new, &pos_new, &pos_from);
593 
594 	#ifdef _SNAPSHOT_DEBUG_
595 	Printf(PRINT_HIGH, "Lerp: %d, Lerping to position (delta %d)\n",
596 						gametic, pos_delta >> FRACBITS);
597 	#endif // _SNAPSHOT_DEBUG_
598 
599 	// lerp the angle
600 	int anglediff = int(to.getAngle()) - int(from.getAngle());
601 	angle_t angle = from.getAngle() + FixedMul(anglediff, amount_fixed);
602 
603 	#ifdef _SNAPSHOT_DEBUG_
604 	if (anglediff)
605 		Printf(PRINT_HIGH, "Lerp: %d, Lerping to angle (delta %d)\n",
606 							gametic, anglediff >> ANGLETOFINESHIFT);
607 	#endif // _SNAPSHOT_DEBUG_
608 
609 	ActorSnapshot newsnapshot(to);
610 	newsnapshot.setAuthoritative(false);
611 	newsnapshot.setInterpolated(true);
612 	newsnapshot.setX(pos_new.x);
613 	newsnapshot.setY(pos_new.y);
614 	newsnapshot.setZ(pos_new.z);
615 	newsnapshot.setAngle(angle);
616 
617 	return newsnapshot;
618 }
619 
620 
P_LerpPlayerPosition(const PlayerSnapshot & from,const PlayerSnapshot & to,float amount)621 PlayerSnapshot P_LerpPlayerPosition(const PlayerSnapshot &from, const PlayerSnapshot &to, float amount)
622 {
623 	PlayerSnapshot newsnap(to);
624 	newsnap.mActorSnap = P_LerpActorPosition(from.mActorSnap, to.mActorSnap, amount);
625 	newsnap.setInterpolated(newsnap.mActorSnap.isInterpolated());
626 
627 	return newsnap;
628 }
629 
630 
631 //
632 // P_SetPlayerSnapshotNoPosition
633 //
634 // Handles the special case where we want the player's position to not change
635 // but would like to apply all of the other player characteristics from the
636 // snapshot to the player.
637 //
P_SetPlayerSnapshotNoPosition(player_t * player,const PlayerSnapshot & snap)638 void P_SetPlayerSnapshotNoPosition(player_t *player, const PlayerSnapshot &snap)
639 {
640 	if (!player || !player->mo)
641 		return;
642 
643 	fixed_t x = player->mo->x;
644 	fixed_t y = player->mo->y;
645 	fixed_t z = player->mo->z;
646 	fixed_t ceilingz = player->mo->ceilingz;
647 	fixed_t floorz = player->mo->floorz;
648 	fixed_t momx = player->mo->momx;
649 	fixed_t momy = player->mo->momy;
650 	fixed_t momz = player->mo->momz;
651 
652 	snap.toPlayer(player);
653 
654 	player->mo->UnlinkFromWorld();
655 	player->mo->x = x;
656 	player->mo->y = y;
657 	player->mo->z = z;
658 	player->mo->ceilingz = ceilingz;
659 	player->mo->floorz = floorz;
660 	player->mo->momx = momx;
661 	player->mo->momy = momy;
662 	player->mo->momz = momz;
663 	player->mo->LinkToWorld();
664 }
665 
666 
667 // ============================================================================
668 //
669 // SectorSnapshot implementation
670 //
671 // ============================================================================
672 
673 
SectorSnapshot(int time)674 SectorSnapshot::SectorSnapshot(int time) :
675 	Snapshot(time),	mCeilingMoverType(SEC_INVALID), mFloorMoverType(SEC_INVALID),
676 	mSector(NULL), mCeilingType(0), mFloorType(0), mCeilingTag(0), mFloorTag(0),
677 	mCeilingLine(NULL), mFloorLine(NULL), mCeilingHeight(0), mFloorHeight(0),
678 	mCeilingSpeed(0), mFloorSpeed(0), mCeilingDestination(0), mFloorDestination(0),
679 	mCeilingDirection(0), mFloorDirection(0), mCeilingOldDirection(0), mFloorOldDirection(0),
680 	mCeilingTexture(0), mFloorTexture(0),
681 	mNewCeilingSpecial(0), mNewFloorSpecial(0), mCeilingLow(0), mCeilingHigh(0),
682 	mFloorLow(0), mFloorHigh(0), mCeilingCrush(false), mFloorCrush(false), mSilent(false),
683 	mCeilingWait(0), mFloorWait(0), mCeilingCounter(0), mFloorCounter(0), mResetCounter(0),
684 	mCeilingStatus(0), mFloorStatus(0), mOldFloorStatus(0),
685 	mCrusherSpeed1(0), mCrusherSpeed2(0), mStepTime(0), mPerStepTime(0), mPauseTime(0),
686 	mOrgHeight(0), mDelay(0), mFloorLip(0), mFloorOffset(0), mCeilingChange(0), mFloorChange(0)
687 {
688 }
689 
SectorSnapshot(int time,sector_t * sector)690 SectorSnapshot::SectorSnapshot(int time, sector_t *sector) :
691 	Snapshot(time), mCeilingMoverType(SEC_INVALID), mFloorMoverType(SEC_INVALID),
692 	mSector(sector)
693 {
694 	if (!sector)
695 	{
696 		clear();
697 		return;
698 	}
699 
700 	mCeilingHeight		= P_CeilingHeight(sector);
701 	mFloorHeight		= P_FloorHeight(sector);
702 
703 	if (sector->floordata)
704 	{
705 		if (sector->floordata->IsA(RUNTIME_CLASS(DElevator)))
706 		{
707 			DElevator *elevator = static_cast<DElevator *>(sector->floordata);
708 			mCeilingMoverType	= SEC_ELEVATOR;
709 			mFloorMoverType		= SEC_ELEVATOR;
710 			mCeilingType		= elevator->m_Type;
711 			mFloorType			= elevator->m_Type;
712 			mCeilingStatus		= elevator->m_Status;
713 			mFloorStatus		= elevator->m_Status;
714 			mCeilingDirection	= elevator->m_Direction;
715 			mFloorDirection		= elevator->m_Direction;
716 			mCeilingDestination	= elevator->m_CeilingDestHeight;
717 			mFloorDestination	= elevator->m_FloorDestHeight;
718 			mCeilingSpeed		= elevator->m_Speed;
719 			mFloorSpeed			= elevator->m_Speed;
720 		}
721 		else if (sector->floordata->IsA(RUNTIME_CLASS(DPillar)))
722 		{
723 			DPillar *pillar		= static_cast<DPillar *>(sector->floordata);
724 			mCeilingMoverType	= SEC_PILLAR;
725 			mFloorMoverType		= SEC_PILLAR;
726 			mCeilingType		= pillar->m_Type;
727 			mFloorType			= pillar->m_Type;
728 			mCeilingStatus		= pillar->m_Status;
729 			mFloorStatus		= pillar->m_Status;
730 			mCeilingSpeed		= pillar->m_CeilingSpeed;
731 			mFloorSpeed			= pillar->m_FloorSpeed;
732 			mCeilingDestination	= pillar->m_CeilingTarget;
733 			mFloorDestination	= pillar->m_FloorTarget;
734 			mCeilingCrush		= pillar->m_Crush;
735 			mFloorCrush			= pillar->m_Crush;
736 		}
737 		else if (sector->floordata->IsA(RUNTIME_CLASS(DFloor)))
738 		{
739 			DFloor *floor		= static_cast<DFloor *>(sector->floordata);
740 			mFloorMoverType		= SEC_FLOOR;
741 			mFloorType			= floor->m_Type;
742 			mFloorStatus		= floor->m_Status;
743 			mFloorCrush			= floor->m_Crush;
744 			mFloorDirection		= floor->m_Direction;
745 			mNewFloorSpecial	= floor->m_NewSpecial;
746 			mFloorTexture		= floor->m_Texture;
747 			mFloorDestination	= floor->m_FloorDestHeight;
748 			mFloorSpeed			= floor->m_Speed;
749 			mStepTime			= floor->m_StepTime;
750 			mPerStepTime		= floor->m_PerStepTime;
751 			mResetCounter		= floor->m_ResetCount;
752 			mPauseTime			= floor->m_PauseTime;
753 			mDelay				= floor->m_Delay;
754 			mOrgHeight			= floor->m_OrgHeight;
755 			mFloorLine			= floor->m_Line;
756 			mFloorOffset		= floor->m_Height;
757 			mFloorChange		= floor->m_Change;
758 		}
759 		else if (sector->floordata->IsA(RUNTIME_CLASS(DPlat)))
760 		{
761 			DPlat *plat			= static_cast<DPlat *>(sector->floordata);
762 			mFloorMoverType		= SEC_PLAT;
763 			mFloorType			= plat->m_Type;
764 			mFloorTag			= plat->m_Tag;
765 			mFloorCrush			= plat->m_Crush;
766 			mFloorSpeed			= plat->m_Speed;
767 			mFloorLow			= plat->m_Low;
768 			mFloorHigh			= plat->m_High;
769 			mFloorWait			= plat->m_Wait;
770 			mFloorCounter		= plat->m_Count;
771 			mFloorStatus		= plat->m_Status;
772 			mOldFloorStatus		= plat->m_OldStatus;
773 			mFloorOffset		= plat->m_Height;
774 			mFloorLip			= plat->m_Lip;
775 		}
776 	}
777 
778 	if (sector->ceilingdata)
779 	{
780 		if (sector->ceilingdata->IsA(RUNTIME_CLASS(DCeiling)))
781 		{
782 			DCeiling *ceiling	= static_cast<DCeiling *>(sector->ceilingdata);
783 			mCeilingMoverType	= SEC_CEILING;
784 			mCeilingType		= ceiling->m_Type;
785 			mCeilingStatus		= ceiling->m_Status;
786 			mCeilingTag			= ceiling->m_Tag;
787 			mCeilingCrush		= ceiling->m_Crush;
788 			mSilent				= (ceiling->m_Silent != 0);
789 			mCeilingLow			= ceiling->m_BottomHeight;
790 			mCeilingHigh		= ceiling->m_TopHeight;
791 			mCeilingSpeed		= ceiling->m_Speed;
792 			mCeilingDirection	= ceiling->m_Direction;
793 			mCeilingTexture		= ceiling->m_Texture;
794 			mNewCeilingSpecial	= ceiling->m_NewSpecial;
795 			mCrusherSpeed1		= ceiling->m_Speed1;
796 			mCrusherSpeed2		= ceiling->m_Speed2;
797 			mCeilingOldDirection = ceiling->m_OldDirection;
798 		}
799 		else if (sector->ceilingdata->IsA(RUNTIME_CLASS(DDoor)))
800 		{
801 			DDoor *door			= static_cast<DDoor *>(sector->ceilingdata);
802 			mCeilingMoverType	= SEC_DOOR;
803 			mCeilingType		= door->m_Type;
804 			mCeilingHigh		= door->m_TopHeight;
805 			mCeilingSpeed		= door->m_Speed;
806 			mCeilingWait		= door->m_TopWait;
807 			mCeilingCounter		= door->m_TopCountdown;
808 			mCeilingStatus		= door->m_Status;
809 			mCeilingLine		= door->m_Line;
810 		}
811 	}
812 }
813 
clear()814 void SectorSnapshot::clear()
815 {
816 	setTime(-1);
817 	mCeilingMoverType = SEC_INVALID;
818 	mFloorMoverType = SEC_INVALID;
819 }
820 
toSector(sector_t * sector) const821 void SectorSnapshot::toSector(sector_t *sector) const
822 {
823 	if (!sector)
824 		return;
825 
826 	P_SetCeilingHeight(sector, mCeilingHeight);
827 	P_SetFloorHeight(sector, mFloorHeight);
828 	P_ChangeSector(sector, false);
829 
830 	if (mCeilingMoverType == SEC_PILLAR && mCeilingStatus != DPillar::destroy)
831 	{
832 		if (sector->ceilingdata && !sector->ceilingdata->IsA(RUNTIME_CLASS(DPillar)))
833 		{
834 			sector->ceilingdata->Destroy();
835 			sector->ceilingdata = NULL;
836 
837 		}
838 		if (sector->floordata && !sector->ceilingdata->IsA(RUNTIME_CLASS(DPillar)))
839 		{
840 			sector->floordata->Destroy();
841 			sector->floordata = NULL;
842 		}
843 
844 		if (!sector->ceilingdata)
845 		{
846 			sector->ceilingdata = new DPillar();
847 			sector->floordata = sector->ceilingdata;
848 		}
849 
850 		DPillar *pillar				= static_cast<DPillar *>(sector->ceilingdata);
851 		pillar->m_Type				= static_cast<DPillar::EPillar>(mCeilingType);
852 		pillar->m_Status			= static_cast<DPillar::EPillarState>(mCeilingStatus);
853 		pillar->m_CeilingSpeed		= mCeilingSpeed;
854 		pillar->m_FloorSpeed		= mFloorSpeed;
855 		pillar->m_CeilingTarget		= mCeilingDestination;
856 		pillar->m_FloorTarget		= mFloorDestination;
857 		pillar->m_Crush				= mCeilingCrush;
858 	}
859 
860 	if (mCeilingMoverType == SEC_ELEVATOR && mCeilingStatus != DElevator::destroy)
861 	{
862 		if (sector->ceilingdata && !sector->ceilingdata->IsA(RUNTIME_CLASS(DElevator)))
863 		{
864 			sector->ceilingdata->Destroy();
865 			sector->ceilingdata = NULL;
866 		}
867 		if (sector->floordata && !sector->floordata->IsA(RUNTIME_CLASS(DElevator)))
868 		{
869 				sector->floordata->Destroy();
870 				sector->floordata = NULL;
871 		}
872 
873 		if (!sector->ceilingdata)
874 		{
875 			sector->ceilingdata = new DElevator(sector);
876 			sector->floordata = sector->ceilingdata;
877 		}
878 
879 		DElevator *elevator			= static_cast<DElevator *>(sector->ceilingdata);
880 		elevator->m_Type			= static_cast<DElevator::EElevator>(mCeilingType);
881 		elevator->m_Status			= static_cast<DElevator::EElevatorState>(mCeilingStatus);
882 		elevator->m_Direction		= mCeilingDirection;
883 		elevator->m_CeilingDestHeight = mCeilingDestination;
884 		elevator->m_FloorDestHeight	= mFloorDestination;
885 		elevator->m_Speed			= mCeilingSpeed;
886 	}
887 
888 	if (mCeilingMoverType == SEC_CEILING && mCeilingStatus != DCeiling::destroy)
889 	{
890 		if (sector->ceilingdata && !sector->ceilingdata->IsA(RUNTIME_CLASS(DCeiling)))
891 		{
892 			sector->ceilingdata->Destroy();
893 			sector->ceilingdata = NULL;
894 		}
895 
896 		if (!sector->ceilingdata)
897 			sector->ceilingdata = new DCeiling(sector);
898 
899 		DCeiling *ceiling			= static_cast<DCeiling *>(sector->ceilingdata);
900 		ceiling->m_Type				= static_cast<DCeiling::ECeiling>(mCeilingType);
901 		ceiling->m_Status			= static_cast<DCeiling::ECeilingState>(mCeilingStatus);
902 		ceiling->m_Tag				= mCeilingTag;
903 		ceiling->m_BottomHeight		= mCeilingLow;
904 		ceiling->m_TopHeight		= mCeilingHigh;
905 		ceiling->m_Speed			= mCeilingSpeed;
906 		ceiling->m_Speed1			= mCrusherSpeed1;
907 		ceiling->m_Speed2			= mCrusherSpeed2;
908 		ceiling->m_Crush			= mCeilingCrush;
909 		ceiling->m_Silent			= mSilent;
910 		ceiling->m_Direction		= mCeilingDirection;
911 		ceiling->m_OldDirection		= mCeilingOldDirection;
912 		ceiling->m_Texture			= mCeilingTexture;
913 		ceiling->m_NewSpecial		= mNewCeilingSpecial;
914 	}
915 
916 	if (mCeilingMoverType == SEC_DOOR && mCeilingStatus != DDoor::destroy)
917 	{
918 		if (sector->ceilingdata && !sector->ceilingdata->IsA(RUNTIME_CLASS(DDoor)))
919 		{
920 			sector->ceilingdata->Destroy();
921 			sector->ceilingdata = NULL;
922 		}
923 
924 		if (!sector->ceilingdata)
925 		{
926 			sector->ceilingdata =
927 				new DDoor(sector, mCeilingLine,
928 						  static_cast<DDoor::EVlDoor>(mCeilingType),
929 						  mCeilingSpeed, mCeilingWait);
930 		}
931 
932 		DDoor *door					= static_cast<DDoor *>(sector->ceilingdata);
933 		door->m_Type				= static_cast<DDoor::EVlDoor>(mCeilingType);
934 		door->m_Status				= static_cast<DDoor::EDoorState>(mCeilingStatus);
935 		door->m_TopHeight			= mCeilingHigh;
936 		door->m_Speed				= mCeilingSpeed;
937 		door->m_TopWait				= mCeilingWait;
938 		door->m_TopCountdown		= mCeilingCounter;
939 		door->m_Line				= mCeilingLine;
940 	}
941 
942 	if (mFloorMoverType == SEC_FLOOR && mFloorStatus != DFloor::destroy)
943 	{
944 		if (sector->floordata && !sector->floordata->IsA(RUNTIME_CLASS(DFloor)))
945 		{
946 			sector->floordata->Destroy();
947 			sector->floordata = NULL;
948 		}
949 
950 		if (!sector->floordata)
951 		{
952 			sector->floordata =
953 				new DFloor(sector, static_cast<DFloor::EFloor>(mFloorType),
954 						   mFloorLine, mFloorSpeed, mFloorOffset,
955 						   mFloorCrush, mFloorChange);
956 		}
957 
958 		DFloor *floor				= static_cast<DFloor *>(sector->floordata);
959 		floor->m_Type				= static_cast<DFloor::EFloor>(mFloorType);
960 		floor->m_Status				= static_cast<DFloor::EFloorState>(mFloorStatus);
961 		floor->m_Crush				= mFloorCrush;
962 		floor->m_Direction			= mFloorDirection;
963 		floor->m_NewSpecial			= mNewFloorSpecial;
964 		floor->m_FloorDestHeight	= mFloorDestination;
965 		floor->m_Speed				= mFloorSpeed;
966 		floor->m_ResetCount			= mResetCounter;
967 		floor->m_OrgHeight			= mOrgHeight;
968 		floor->m_Delay				= mDelay;
969 		floor->m_PauseTime			= mPauseTime;
970 		floor->m_StepTime			= mStepTime;
971 		floor->m_PerStepTime		= mPerStepTime;
972 		floor->m_Line				= mFloorLine;
973 		floor->m_Height				= mFloorOffset;
974 		floor->m_Change				= mFloorChange;
975 	}
976 
977 	if (mFloorMoverType == SEC_PLAT && mFloorStatus != DPlat::destroy)
978 	{
979 		if (sector->floordata && !sector->floordata->IsA(RUNTIME_CLASS(DPlat)))
980 		{
981 			sector->floordata->Destroy();
982 			sector->floordata = NULL;
983 		}
984 
985 		if (!sector->floordata)
986 		{
987 			sector->floordata =
988 				new DPlat(sector, static_cast<DPlat::EPlatType>(mFloorType),
989 						  mFloorOffset, mFloorSpeed, mFloorWait, mFloorLip);
990 		}
991 
992 		DPlat *plat					= static_cast<DPlat *>(sector->floordata);
993 		plat->m_Type				= static_cast<DPlat::EPlatType>(mFloorType);
994 		plat->m_Tag					= mFloorTag;
995 		plat->m_Status				= static_cast<DPlat::EPlatState>(mFloorStatus);
996 		plat->m_OldStatus			= static_cast<DPlat::EPlatState>(mOldFloorStatus);
997 		plat->m_Crush				= mFloorCrush;
998 		plat->m_Low					= mFloorLow;
999 		plat->m_High				= mFloorHigh;
1000 		plat->m_Speed				= mFloorSpeed;
1001 		plat->m_Wait				= mFloorWait;
1002 		plat->m_Count				= mFloorCounter;
1003 		plat->m_Height				= mFloorOffset;
1004 		plat->m_Lip					= mFloorLip;
1005 	}
1006 }
1007 
1008 // ============================================================================
1009 //
1010 // SectorrSnapshotManager implementation
1011 //
1012 // ============================================================================
1013 
SectorSnapshotManager()1014 SectorSnapshotManager::SectorSnapshotManager() :
1015 	mMostRecent(0)
1016 {
1017 	clearSnapshots();
1018 }
1019 
1020 //
1021 // SectorSnapshotManager::clearSnapshots()
1022 //
1023 // Marks all of the snapshots in the container invalid, effectively
1024 // clearing the container.
1025 //
clearSnapshots()1026 void SectorSnapshotManager::clearSnapshots()
1027 {
1028 	// Set the time for all snapshots to an invalid value
1029 	for (int i = 0; i < NUM_SNAPSHOTS; i++)
1030 		mSnaps[i].clear();
1031 
1032 	mMostRecent = 0;
1033 }
1034 
1035 //
1036 // SectorSnapshotManager::mValidSnapshot()
1037 //
1038 // Returns true if a snapshot at the given time is present in the container
1039 //
mValidSnapshot(int time) const1040 bool SectorSnapshotManager::mValidSnapshot(int time) const
1041 {
1042 	return ((time <= mMostRecent) && (mMostRecent - time <= NUM_SNAPSHOTS) &&
1043 			(time > 0) && (mSnaps[time % NUM_SNAPSHOTS].isValid()) &&
1044 			(mSnaps[time % NUM_SNAPSHOTS].getTime() == time) &&
1045 			(mSnaps[time % NUM_SNAPSHOTS].getCeilingMoverType() != SEC_INVALID ||
1046 			 mSnaps[time % NUM_SNAPSHOTS].getFloorMoverType() != SEC_INVALID));
1047 }
1048 
1049 //
1050 // SectorSnapshotManager::empty()
1051 //
1052 // Returns true if the container does not contain any valid snapshots
1053 //
empty()1054 bool SectorSnapshotManager::empty()
1055 {
1056 	return (!mValidSnapshot(mMostRecent));
1057 }
1058 
1059 //
1060 // SectorSnapshotManager::addSnapshot()
1061 //
1062 // Inserts a new snapshot into the container, provided it is valid and not
1063 // too old
1064 //
addSnapshot(const SectorSnapshot & newsnap)1065 void SectorSnapshotManager::addSnapshot(const SectorSnapshot &newsnap)
1066 {
1067 	int time = newsnap.getTime();
1068 
1069 	if (!newsnap.isValid())
1070 	{
1071 		#ifdef _SNAPSHOT_DEBUG_
1072 		Printf(PRINT_HIGH, "Snapshot %i: Not adding invalid sector snapshot\n", time);
1073 		#endif // _SNAPSHOT_DEBUG_
1074 		return;
1075 	}
1076 
1077 	if (mMostRecent > newsnap.getTime() + NUM_SNAPSHOTS)
1078 	{
1079 		#ifdef _SNAPSHOT_DEBUG_
1080 		Printf(PRINT_HIGH, "Snapshot %i: Not adding expired sector snapshot\n", time);
1081 		#endif // _SNAPSHOT_DEBUG_
1082 		return;
1083 	}
1084 
1085 	mSnaps[time % NUM_SNAPSHOTS] = newsnap;
1086 
1087 	if (time > mMostRecent)
1088 		mMostRecent = time;
1089 }
1090 
1091 
1092 //
1093 // SectorSnapshotManager::getSnapshot()
1094 //
1095 // Returns a snapshot from the container at a specified time.
1096 // If there is not a snapshot matching the time, one is generated by
1097 // running the moving sector's thinker function.
1098 //
getSnapshot(int time) const1099 SectorSnapshot SectorSnapshotManager::getSnapshot(int time) const
1100 {
1101 	if (time <= 0 || mMostRecent <= 0)
1102 		return SectorSnapshot();
1103 
1104 	// Return the requested snapshot if availible
1105 	if (mValidSnapshot(time))
1106 		return mSnaps[time % NUM_SNAPSHOTS];
1107 
1108 	// Find the snapshot in the container that preceeds the desired time
1109 	int prevsnaptime = time;
1110 	while (--prevsnaptime > mMostRecent - NUM_SNAPSHOTS)
1111 	{
1112 		if (mValidSnapshot(prevsnaptime))
1113 		{
1114 			const SectorSnapshot *snap = &mSnaps[prevsnaptime % NUM_SNAPSHOTS];
1115 
1116 			// turn off any sector movement sounds from RunThink()
1117 			bool oldpredicting = predicting;
1118 			predicting = true;
1119 
1120 			// create a temporary sector for the snapshot and run the
1121 			// sector movement til we get to the desired time
1122 			sector_t tempsector;
1123 			P_CopySector(&tempsector, snap->getSector());
1124 
1125 			// set values for the Z parameter of the sector's planes so that
1126 			// P_SetCeilingHeight/P_SetFloorHeight will work properly
1127 			tempsector.floorplane.c = tempsector.floorplane.invc = FRACUNIT;
1128 			tempsector.ceilingplane.c = tempsector.ceilingplane.invc = -FRACUNIT;
1129 
1130 			snap->toSector(&tempsector);
1131 
1132 			for (int i = 0; i < time - prevsnaptime; i++)
1133 			{
1134 				if (tempsector.ceilingdata)
1135 					tempsector.ceilingdata->RunThink();
1136 				if (tempsector.floordata &&
1137 					tempsector.floordata != tempsector.ceilingdata)
1138 					tempsector.floordata->RunThink();
1139 			}
1140 
1141 			SectorSnapshot newsnap(time, &tempsector);
1142 
1143 			// clean up allocated memory
1144 			if (tempsector.ceilingdata)
1145 				tempsector.ceilingdata->Destroy();
1146 			if (tempsector.floordata)
1147 				tempsector.floordata->Destroy();
1148 
1149 			// restore sector movement sounds
1150 			predicting = oldpredicting;
1151 
1152 			return newsnap;
1153 		}
1154 	}
1155 
1156 	// Could not find a valid snapshot so return a blank (invalid) one
1157 	return SectorSnapshot();
1158 }
1159 
1160 
P_CeilingSnapshotDone(SectorSnapshot * snap)1161 bool P_CeilingSnapshotDone(SectorSnapshot *snap)
1162 {
1163 	if (!snap || !snap->isValid() || snap->getCeilingMoverType() == SEC_INVALID)
1164 		return true;
1165 
1166 	if ((snap->getCeilingMoverType() == SEC_CEILING &&
1167 		 snap->getCeilingStatus() == DCeiling::destroy) ||
1168 		(snap->getCeilingMoverType() == SEC_DOOR &&
1169 		 snap->getCeilingStatus() == DDoor::destroy) ||
1170 		(snap->getCeilingMoverType() == SEC_PILLAR &&
1171 		 snap->getCeilingStatus() == DPillar::destroy) ||
1172 		(snap->getCeilingMoverType() == SEC_ELEVATOR &&
1173 		 snap->getCeilingStatus() == DElevator::destroy))
1174 		return true;
1175 
1176 	return false;
1177 }
1178 
P_FloorSnapshotDone(SectorSnapshot * snap)1179 bool P_FloorSnapshotDone(SectorSnapshot *snap)
1180 {
1181 	if (!snap || !snap->isValid() || snap->getFloorMoverType() == SEC_INVALID)
1182 		return true;
1183 
1184 	if ((snap->getFloorMoverType() == SEC_FLOOR &&
1185 		 snap->getFloorStatus() == DFloor::destroy) ||
1186 		(snap->getFloorMoverType() == SEC_PLAT &&
1187 		 snap->getFloorStatus() == DPlat::destroy) ||
1188 		(snap->getFloorMoverType() == SEC_PILLAR &&
1189 		 snap->getFloorStatus() == DPillar::destroy) ||
1190 		(snap->getFloorMoverType() == SEC_ELEVATOR &&
1191 		 snap->getFloorStatus() == DElevator::destroy))
1192 		return true;
1193 
1194 	return false;
1195 }
1196 
1197 VERSION_CONTROL (p_snapshot_cpp, "$Id: p_snapshot.cpp 2785 2012-02-18 23:22:07Z dr_sean $")
1198 
1199 
1200