1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //		Teleportation.
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include "templates.h"
26 #include "doomtype.h"
27 #include "doomdef.h"
28 #include "s_sound.h"
29 #include "p_local.h"
30 #include "p_terrain.h"
31 #include "r_state.h"
32 #include "gi.h"
33 #include "a_sharedglobal.h"
34 #include "m_random.h"
35 #include "i_system.h"
36 #include "doomstat.h"
37 
38 #define FUDGEFACTOR		10
39 
40 static FRandom pr_teleport ("Teleport");
41 
42 extern void P_CalcHeight (player_t *player);
43 
44 CVAR (Bool, telezoom, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
45 
IMPLEMENT_CLASS(ATeleportFog)46 IMPLEMENT_CLASS (ATeleportFog)
47 
48 void ATeleportFog::PostBeginPlay ()
49 {
50 	Super::PostBeginPlay ();
51 	S_Sound (this, CHAN_BODY, "misc/teleport", 1, ATTN_NORM);
52 	switch (gameinfo.gametype)
53 	{
54 	case GAME_Hexen:
55 	case GAME_Heretic:
56 		SetState(FindState(NAME_Raven));
57 		break;
58 
59 	case GAME_Strife:
60 		SetState(FindState(NAME_Strife));
61 		break;
62 
63 	default:
64 		break;
65 	}
66 }
67 
68 //==========================================================================
69 //
70 // P_SpawnTeleportFog
71 //
72 // The beginning of customizable teleport fog
73 // (not active yet)
74 //
75 //==========================================================================
76 
P_SpawnTeleportFog(AActor * mobj,fixed_t x,fixed_t y,fixed_t z,bool beforeTele,bool setTarget)77 void P_SpawnTeleportFog(AActor *mobj, fixed_t x, fixed_t y, fixed_t z, bool beforeTele, bool setTarget)
78 {
79 	AActor *mo;
80 	if ((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType) == NULL)
81 	{
82 		//Do nothing.
83 		mo = NULL;
84 	}
85 	else
86 	{
87 		mo = Spawn((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType), x, y, z, ALLOW_REPLACE);
88 	}
89 
90 	if (mo != NULL && setTarget)
91 		mo->target = mobj;
92 
93 }
94 
95 //
96 // TELEPORTATION
97 //
98 
P_Teleport(AActor * thing,fixed_t x,fixed_t y,fixed_t z,angle_t angle,int flags)99 bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int flags)
100 {
101 	bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING));
102 
103 	fixedvec3 old;
104 	fixed_t aboveFloor;
105 	player_t *player;
106 	angle_t an;
107 	sector_t *destsect;
108 	bool resetpitch = false;
109 	fixed_t floorheight, ceilingheight;
110 	fixed_t missilespeed = 0;
111 
112 	old = thing->Pos();
113 	aboveFloor = thing->Z() - thing->floorz;
114 	destsect = P_PointInSector (x, y);
115 	// killough 5/12/98: exclude voodoo dolls:
116 	player = thing->player;
117 	if (player && player->mo != thing)
118 		player = NULL;
119 	floorheight = destsect->floorplane.ZatPoint (x, y);
120 	ceilingheight = destsect->ceilingplane.ZatPoint (x, y);
121 	if (thing->flags & MF_MISSILE)
122 	{ // We don't measure z velocity, because it doesn't change.
123 		missilespeed = xs_CRoundToInt(TVector2<double>(thing->velx, thing->vely).Length());
124 	}
125 	if (flags & TELF_KEEPHEIGHT)
126 	{
127 		z = floorheight + aboveFloor;
128 	}
129 	else if (z == ONFLOORZ)
130 	{
131 		if (player)
132 		{
133 			if (thing->flags & MF_NOGRAVITY && aboveFloor)
134 			{
135 				z = floorheight + aboveFloor;
136 				if (z + thing->height > ceilingheight)
137 				{
138 					z = ceilingheight - thing->height;
139 				}
140 			}
141 			else
142 			{
143 				z = floorheight;
144 				if (!(flags & TELF_KEEPORIENTATION))
145 				{
146 					resetpitch = false;
147 				}
148 			}
149 		}
150 		else if (thing->flags & MF_MISSILE)
151 		{
152 			z = floorheight + aboveFloor;
153 			if (z + thing->height > ceilingheight)
154 			{
155 				z = ceilingheight - thing->height;
156 			}
157 		}
158 		else
159 		{
160 			z = floorheight;
161 		}
162 	}
163 	if (!P_TeleportMove (thing, x, y, z, false))
164 	{
165 		return false;
166 	}
167 	if (player)
168 	{
169 		player->viewz = thing->Z() + player->viewheight;
170 		if (resetpitch)
171 		{
172 			player->mo->pitch = 0;
173 		}
174 	}
175 	if (!(flags & TELF_KEEPORIENTATION))
176 	{
177 		thing->angle = angle;
178 	}
179 	else
180 	{
181 		angle = thing->angle;
182 	}
183 	// Spawn teleport fog at source and destination
184 	if ((flags & TELF_SOURCEFOG) && !predicting)
185 	{
186 		P_SpawnTeleportFog(thing, old, true, true); //Passes the actor through which then pulls the TeleFog metadata types based on properties.
187 	}
188 	if (flags & TELF_DESTFOG)
189 	{
190 		if (!predicting)
191 		{
192 			fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
193 			an = angle >> ANGLETOFINESHIFT;
194 			P_SpawnTeleportFog(thing, x + 20 * finecosine[an], y + 20 * finesine[an], thing->Z() + fogDelta, false, true);
195 
196 		}
197 		if (thing->player)
198 		{
199 			// [RH] Zoom player's field of vision
200 			// [BC] && bHaltVelocity.
201 			if (telezoom && thing->player->mo == thing && !(flags & TELF_KEEPVELOCITY))
202 				thing->player->FOV = MIN (175.f, thing->player->DesiredFOV + 45.f);
203 		}
204 	}
205 	// [BC] && bHaltVelocity.
206 	if (thing->player && ((flags & TELF_DESTFOG) || !(flags & TELF_KEEPORIENTATION)) && !(flags & TELF_KEEPVELOCITY))
207 	{
208 		// Freeze player for about .5 sec
209 		if (thing->Inventory == NULL || !thing->Inventory->GetNoTeleportFreeze())
210 			thing->reactiontime = 18;
211 	}
212 	if (thing->flags & MF_MISSILE)
213 	{
214 		angle >>= ANGLETOFINESHIFT;
215 		thing->velx = FixedMul (missilespeed, finecosine[angle]);
216 		thing->vely = FixedMul (missilespeed, finesine[angle]);
217 	}
218 	// [BC] && bHaltVelocity.
219 	else if (!(flags & TELF_KEEPORIENTATION) && !(flags & TELF_KEEPVELOCITY))
220 	{ // no fog doesn't alter the player's momentum
221 		thing->velx = thing->vely = thing->velz = 0;
222 		// killough 10/98: kill all bobbing velocity too
223 		if (player)
224 			player->velx = player->vely = 0;
225 	}
226 	return true;
227 }
228 
SelectTeleDest(int tid,int tag,bool norandom)229 static AActor *SelectTeleDest (int tid, int tag, bool norandom)
230 {
231 	AActor *searcher;
232 
233 	// If tid is non-zero, select a destination from a matching actor at random.
234 	// If tag is also non-zero, the selection is restricted to actors in sectors
235 	// with a matching tag. If tid is zero and tag is non-zero, then the old Doom
236 	// behavior is used instead (return the first teleport dest found in a tagged
237 	// sector).
238 
239 	// Compatibility hack for some maps that fell victim to a bug in the teleport code in 2.0.9x
240 	if (ib_compatflags & BCOMPATF_BADTELEPORTERS) tag = 0;
241 
242 	if (tid != 0)
243 	{
244 		NActorIterator iterator (NAME_TeleportDest, tid);
245 		int count = 0;
246 		while ( (searcher = iterator.Next ()) )
247 		{
248 			if (tag == 0 || tagManager.SectorHasTag(searcher->Sector, tag))
249 			{
250 				count++;
251 			}
252 		}
253 
254 		// If teleport dests were not found, the sector tag is ignored for the
255 		// following compatibility searches.
256 		// Do this only when tag is 0 because this is the only case that was defined in Hexen.
257 		if (count == 0)
258 		{
259 			if (tag == 0)
260 			{
261 				// Try to find a matching map spot (fixes Hexen MAP10)
262 				NActorIterator it2 (NAME_MapSpot, tid);
263 				searcher = it2.Next ();
264 				if (searcher == NULL)
265 				{
266 					// Try to find a matching non-blocking spot of any type (fixes Caldera MAP13)
267 					FActorIterator it3 (tid);
268 					searcher = it3.Next ();
269 					while (searcher != NULL && (searcher->flags & MF_SOLID))
270 					{
271 						searcher = it3.Next ();
272 					}
273 					return searcher;
274 				}
275 			}
276 		}
277 		else
278 		{
279 			if (count != 1 && !norandom)
280 			{
281 				count = 1 + (pr_teleport() % count);
282 			}
283 			searcher = NULL;
284 			while (count > 0)
285 			{
286 				searcher = iterator.Next ();
287 				if (tag == 0 || tagManager.SectorHasTag(searcher->Sector, tag))
288 				{
289 					count--;
290 				}
291 			}
292 		}
293 		return searcher;
294 	}
295 
296 	if (tag != 0)
297 	{
298 		int secnum;
299 
300 		FSectorTagIterator itr(tag);
301 		while ((secnum = itr.Next()) >= 0)
302 		{
303 			// Scanning the snext links of things in the sector will not work, because
304 			// TeleportDests have MF_NOSECTOR set. So you have to search *everything*.
305 			// If there is more than one sector with a matching tag, then the destination
306 			// in the lowest-numbered sector is returned, rather than the earliest placed
307 			// teleport destination. This means if 50 sectors have a matching tag and
308 			// only the last one has a destination, *every* actor is scanned at least 49
309 			// times. Yuck.
310 			TThinkerIterator<AActor> it2(NAME_TeleportDest);
311 			while ((searcher = it2.Next()) != NULL)
312 			{
313 				if (searcher->Sector == sectors + secnum)
314 				{
315 					return searcher;
316 				}
317 			}
318 		}
319 	}
320 
321 	return NULL;
322 }
323 
EV_Teleport(int tid,int tag,line_t * line,int side,AActor * thing,int flags)324 bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, int flags)
325 {
326 	AActor *searcher;
327 	fixed_t z;
328 	angle_t angle = 0;
329 	fixed_t s = 0, c = 0;
330 	fixed_t velx = 0, vely = 0;
331 	angle_t badangle = 0;
332 
333 	if (thing == NULL)
334 	{ // Teleport function called with an invalid actor
335 		return false;
336 	}
337 	bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING));
338 	if (thing->flags2 & MF2_NOTELEPORT)
339 	{
340 		return false;
341 	}
342 	if (side != 0)
343 	{ // Don't teleport if hit back of line, so you can get out of teleporter.
344 		return 0;
345 	}
346 	searcher = SelectTeleDest(tid, tag, predicting);
347 	if (searcher == NULL)
348 	{
349 		return false;
350 	}
351 	// [RH] Lee Killough's changes for silent teleporters from BOOM
352 	if ((flags & TELF_KEEPORIENTATION) && line)
353 	{
354 		// Get the angle between the exit thing and source linedef.
355 		// Rotate 90 degrees, so that walking perpendicularly across
356 		// teleporter linedef causes thing to exit in the direction
357 		// indicated by the exit thing.
358 		angle = R_PointToAngle2 (0, 0, line->dx, line->dy) - searcher->angle + ANG90;
359 
360 		// Sine, cosine of angle adjustment
361 		s = finesine[angle>>ANGLETOFINESHIFT];
362 		c = finecosine[angle>>ANGLETOFINESHIFT];
363 
364 		// Velocity of thing crossing teleporter linedef
365 		velx = thing->velx;
366 		vely = thing->vely;
367 
368 		z = searcher->Z();
369 	}
370 	else if (searcher->IsKindOf (PClass::FindClass(NAME_TeleportDest2)))
371 	{
372 		z = searcher->Z();
373 	}
374 	else
375 	{
376 		z = ONFLOORZ;
377 	}
378 	if ((i_compatflags2 & COMPATF2_BADANGLES) && (thing->player != NULL))
379 	{
380 		badangle = 1 << ANGLETOFINESHIFT;
381 	}
382 	if (P_Teleport (thing, searcher->X(), searcher->Y(), z, searcher->angle + badangle, flags))
383 	{
384 		// [RH] Lee Killough's changes for silent teleporters from BOOM
385 		if (!(flags & TELF_DESTFOG) && line && (flags & TELF_KEEPORIENTATION))
386 		{
387 			// Rotate thing according to difference in angles
388 			thing->angle += angle;
389 
390 			// Rotate thing's velocity to come out of exit just like it entered
391 			thing->velx = FixedMul(velx, c) - FixedMul(vely, s);
392 			thing->vely = FixedMul(vely, c) + FixedMul(velx, s);
393 		}
394 		if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing && !predicting)
395 		{
396 			thing->player->mo->PlayIdle ();
397 		}
398 		return true;
399 	}
400 	return false;
401 }
402 
403 //
404 // Silent linedef-based TELEPORTATION, by Lee Killough
405 // Primarily for rooms-over-rooms etc.
406 // This is the complete player-preserving kind of teleporter.
407 // It has advantages over the teleporter with thing exits.
408 //
409 
410 // [RH] Modified to support different source and destination ids.
411 // [RH] Modified some more to be accurate.
EV_SilentLineTeleport(line_t * line,int side,AActor * thing,int id,INTBOOL reverse)412 bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBOOL reverse)
413 {
414 	int i;
415 	line_t *l;
416 
417 	if (side || thing->flags2 & MF2_NOTELEPORT || !line || line->sidedef[1] == NULL)
418 		return false;
419 
420 	FLineIdIterator itr(id);
421 	while ((i = itr.Next()) >= 0)
422 	{
423 		if (line-lines == i)
424 			continue;
425 
426 		if ((l=lines+i) != line && l->backsector)
427 		{
428 			// Get the thing's position along the source linedef
429 			SDWORD pos;				// 30.2 fixed
430 			fixed_t nposx, nposy;	// offsets from line
431 			{
432 				SQWORD den;
433 
434 				den = (SQWORD)line->dx*line->dx + (SQWORD)line->dy*line->dy;
435 				if (den == 0)
436 				{
437 					pos = 0;
438 					nposx = 0;
439 					nposy = 0;
440 				}
441 				else
442 				{
443 					SQWORD num = (SQWORD)(thing->X()-line->v1->x)*line->dx +
444 								 (SQWORD)(thing->Y()-line->v1->y)*line->dy;
445 					if (num <= 0)
446 					{
447 						pos = 0;
448 					}
449 					else if (num >= den)
450 					{
451 						pos = 1<<30;
452 					}
453 					else
454 					{
455 						pos = (SDWORD)(num / (den>>30));
456 					}
457 					nposx = thing->X() - line->v1->x - MulScale30 (line->dx, pos);
458 					nposy = thing->Y() - line->v1->y - MulScale30 (line->dy, pos);
459 				}
460 			}
461 
462 			// Get the angle between the two linedefs, for rotating
463 			// orientation and velocity. Rotate 180 degrees, and flip
464 			// the position across the exit linedef, if reversed.
465 			angle_t angle =
466 				R_PointToAngle2(0, 0, l->dx, l->dy) -
467 				R_PointToAngle2(0, 0, line->dx, line->dy);
468 
469 			if (!reverse)
470 			{
471 				angle += ANGLE_180;
472 				pos = (1<<30) - pos;
473 			}
474 
475 			// Sine, cosine of angle adjustment
476 			fixed_t s = finesine[angle>>ANGLETOFINESHIFT];
477 			fixed_t c = finecosine[angle>>ANGLETOFINESHIFT];
478 
479 			fixed_t x, y;
480 
481 			// Rotate position along normal to match exit linedef
482 			x = DMulScale16 (nposx, c, -nposy, s);
483 			y = DMulScale16 (nposy, c,  nposx, s);
484 
485 			// Interpolate position across the exit linedef
486 			x += l->v1->x + MulScale30 (pos, l->dx);
487 			y += l->v1->y + MulScale30 (pos, l->dy);
488 
489 			// Whether this is a player, and if so, a pointer to its player_t.
490 			// Voodoo dolls are excluded by making sure thing->player->mo==thing.
491 			player_t *player = thing->player && thing->player->mo == thing ?
492 				thing->player : NULL;
493 
494 			// Whether walking towards first side of exit linedef steps down
495 			bool stepdown = l->frontsector->floorplane.ZatPoint(x, y) < l->backsector->floorplane.ZatPoint(x, y);
496 
497 			// Height of thing above ground
498 			fixed_t z = thing->Z() - thing->floorz;
499 
500 			// Side to exit the linedef on positionally.
501 			//
502 			// Notes:
503 			//
504 			// This flag concerns exit position, not momentum. Due to
505 			// roundoff error, the thing can land on either the left or
506 			// the right side of the exit linedef, and steps must be
507 			// taken to make sure it does not end up on the wrong side.
508 			//
509 			// Exit momentum is always towards side 1 in a reversed
510 			// teleporter, and always towards side 0 otherwise.
511 			//
512 			// Exiting positionally on side 1 is always safe, as far
513 			// as avoiding oscillations and stuck-in-wall problems,
514 			// but may not be optimum for non-reversed teleporters.
515 			//
516 			// Exiting on side 0 can cause oscillations if momentum
517 			// is towards side 1, as it is with reversed teleporters.
518 			//
519 			// Exiting on side 1 slightly improves player viewing
520 			// when going down a step on a non-reversed teleporter.
521 
522 			int side = reverse || (player && stepdown);
523 			int fudge = FUDGEFACTOR;
524 
525 			// Make sure we are on correct side of exit linedef.
526 			while (P_PointOnLineSidePrecise(x, y, l) != side && --fudge >= 0)
527 			{
528 				if (abs(l->dx) > abs(l->dy))
529 					y -= (l->dx < 0) != side ? -1 : 1;
530 				else
531 					x += (l->dy < 0) != side ? -1 : 1;
532 			}
533 
534 			// Adjust z position to be same height above ground as before.
535 			// Ground level at the exit is measured as the higher of the
536 			// two floor heights at the exit linedef.
537 			z = z + l->sidedef[stepdown]->sector->floorplane.ZatPoint(x, y);
538 
539 			// Attempt to teleport, aborting if blocked
540 			if (!P_TeleportMove (thing, x, y, z, false))
541 			{
542 				return false;
543 			}
544 
545 			if (thing == players[consoleplayer].camera)
546 			{
547 				R_ResetViewInterpolation ();
548 			}
549 
550 			// Rotate thing's orientation according to difference in linedef angles
551 			thing->angle += angle;
552 
553 			// Velocity of thing crossing teleporter linedef
554 			x = thing->velx;
555 			y = thing->vely;
556 
557 			// Rotate thing's velocity to come out of exit just like it entered
558 			thing->velx = DMulScale16 (x, c, -y, s);
559 			thing->vely = DMulScale16 (y, c,  x, s);
560 
561 			// Adjust a player's view, in case there has been a height change
562 			if (player && player->mo == thing)
563 			{
564 				// Adjust player's local copy of velocity
565 				x = player->velx;
566 				y = player->vely;
567 				player->velx = DMulScale16 (x, c, -y, s);
568 				player->vely = DMulScale16 (y, c,  x, s);
569 
570 				// Save the current deltaviewheight, used in stepping
571 				fixed_t deltaviewheight = player->deltaviewheight;
572 
573 				// Clear deltaviewheight, since we don't want any changes now
574 				player->deltaviewheight = 0;
575 
576 				// Set player's view according to the newly set parameters
577 				P_CalcHeight(player);
578 
579 				// Reset the delta to have the same dynamics as before
580 				player->deltaviewheight = deltaviewheight;
581 			}
582 
583 			return true;
584 		}
585 	}
586 	return false;
587 }
588 
589 // [RH] Teleport anything matching other_tid to dest_tid
EV_TeleportOther(int other_tid,int dest_tid,bool fog)590 bool EV_TeleportOther (int other_tid, int dest_tid, bool fog)
591 {
592 	bool didSomething = false;
593 
594 	if (other_tid != 0 && dest_tid != 0)
595 	{
596 		AActor *victim;
597 		FActorIterator iterator (other_tid);
598 
599 		while ( (victim = iterator.Next ()) )
600 		{
601 			didSomething |= EV_Teleport (dest_tid, 0, NULL, 0, victim,
602 				fog ? (TELF_DESTFOG | TELF_SOURCEFOG) : TELF_KEEPORIENTATION);
603 		}
604 	}
605 
606 	return didSomething;
607 }
608 
DoGroupForOne(AActor * victim,AActor * source,AActor * dest,bool floorz,bool fog)609 static bool DoGroupForOne (AActor *victim, AActor *source, AActor *dest, bool floorz, bool fog)
610 {
611 	int an = (dest->angle - source->angle) >> ANGLETOFINESHIFT;
612 	fixed_t offX = victim->X() - source->X();
613 	fixed_t offY = victim->Y() - source->Y();
614 	angle_t offAngle = victim->angle - source->angle;
615 	fixed_t newX = DMulScale16 (offX, finecosine[an], -offY, finesine[an]);
616 	fixed_t newY = DMulScale16 (offX, finesine[an], offY, finecosine[an]);
617 
618 	bool res =
619 		P_Teleport (victim, dest->X() + newX,
620 							dest->Y() + newY,
621 							floorz ? ONFLOORZ : dest->Z() + victim->Z() - source->Z(),
622 							0, fog ? (TELF_DESTFOG | TELF_SOURCEFOG) : TELF_KEEPORIENTATION);
623 	// P_Teleport only changes angle if fog is true
624 	victim->angle = dest->angle + offAngle;
625 
626 	return res;
627 }
628 
629 #if 0
630 static void MoveTheDecal (DBaseDecal *decal, fixed_t z, AActor *source, AActor *dest)
631 {
632 	int an = (dest->angle - source->angle) >> ANGLETOFINESHIFT;
633 	fixed_t offX = decal->x - source->x;
634 	fixed_t offY = decal->y - source->y;
635 	fixed_t newX = DMulScale16 (offX, finecosine[an], -offY, finesine[an]);
636 	fixed_t newY = DMulScale16 (offX, finesine[an], offY, finecosine[an]);
637 
638 	decal->Relocate (dest->x + newX, dest->y + newY, dest->z + z - source->z);
639 }
640 #endif
641 
642 // [RH] Teleport a group of actors centered around source_tid so
643 // that they become centered around dest_tid instead.
EV_TeleportGroup(int group_tid,AActor * victim,int source_tid,int dest_tid,bool moveSource,bool fog)644 bool EV_TeleportGroup (int group_tid, AActor *victim, int source_tid, int dest_tid, bool moveSource, bool fog)
645 {
646 	AActor *sourceOrigin, *destOrigin;
647 	{
648 		FActorIterator iterator (source_tid);
649 		sourceOrigin = iterator.Next ();
650 	}
651 	if (sourceOrigin == NULL)
652 	{ // If there is no source origin, behave like TeleportOther
653 		return EV_TeleportOther (group_tid, dest_tid, fog);
654 	}
655 
656 	{
657 		NActorIterator iterator (NAME_TeleportDest, dest_tid);
658 		destOrigin = iterator.Next ();
659 	}
660 	if (destOrigin == NULL)
661 	{
662 		return false;
663 	}
664 
665 	bool didSomething = false;
666 	bool floorz = !destOrigin->IsKindOf (PClass::FindClass("TeleportDest2"));
667 
668 	// Use the passed victim if group_tid is 0
669 	if (group_tid == 0 && victim != NULL)
670 	{
671 		didSomething = DoGroupForOne (victim, sourceOrigin, destOrigin, floorz, fog);
672 	}
673 	else
674 	{
675 		FActorIterator iterator (group_tid);
676 
677 		// For each actor with tid matching arg0, move it to the same
678 		// position relative to destOrigin as it is relative to sourceOrigin
679 		// before the teleport.
680 		while ( (victim = iterator.Next ()) )
681 		{
682 			didSomething |= DoGroupForOne (victim, sourceOrigin, destOrigin, floorz, fog);
683 		}
684 	}
685 
686 	if (moveSource && didSomething)
687 	{
688 		didSomething |=
689 			P_Teleport (sourceOrigin, destOrigin->X(), destOrigin->Y(),
690 				floorz ? ONFLOORZ : destOrigin->Z(), 0, TELF_KEEPORIENTATION);
691 		sourceOrigin->angle = destOrigin->angle;
692 	}
693 
694 	return didSomething;
695 }
696 
697 // [RH] Teleport a group of actors in a sector. Source_tid is used as a
698 // reference point so that they end up in the same position relative to
699 // dest_tid. Group_tid can be used to not teleport all actors in the sector.
EV_TeleportSector(int tag,int source_tid,int dest_tid,bool fog,int group_tid)700 bool EV_TeleportSector (int tag, int source_tid, int dest_tid, bool fog, int group_tid)
701 {
702 	AActor *sourceOrigin, *destOrigin;
703 	{
704 		FActorIterator iterator (source_tid);
705 		sourceOrigin = iterator.Next ();
706 	}
707 	if (sourceOrigin == NULL)
708 	{
709 		return false;
710 	}
711 	{
712 		NActorIterator iterator (NAME_TeleportDest, dest_tid);
713 		destOrigin = iterator.Next ();
714 	}
715 	if (destOrigin == NULL)
716 	{
717 		return false;
718 	}
719 
720 	bool didSomething = false;
721 	bool floorz = !destOrigin->IsKindOf (PClass::FindClass("TeleportDest2"));
722 	int secnum;
723 
724 	secnum = -1;
725 	FSectorTagIterator itr(tag);
726 	while ((secnum = itr.Next()) >= 0)
727 	{
728 		msecnode_t *node;
729 		const sector_t * const sec = &sectors[secnum];
730 
731 		for (node = sec->touching_thinglist; node; )
732 		{
733 			AActor *actor = node->m_thing;
734 			msecnode_t *next = node->m_snext;
735 
736 			// possibly limit actors by group
737 			if (actor != NULL && (group_tid == 0 || actor->tid == group_tid))
738 			{
739 				didSomething |= DoGroupForOne (actor, sourceOrigin, destOrigin, floorz, fog);
740 			}
741 			node = next;
742 		}
743 
744 #if 0
745 		if (group_tid == 0 && !fog)
746 		{
747 			int lineindex;
748 			for (lineindex = sec->linecount-1; lineindex >= 0; --lineindex)
749 			{
750 				line_t *line = sec->lines[lineindex];
751 				int wallnum;
752 
753 				wallnum = line->sidenum[(line->backsector == sec)];
754 				if (wallnum != -1)
755 				{
756 					side_t *wall = &sides[wallnum];
757 					ADecal *decal = wall->BoundActors;
758 
759 					while (decal != NULL)
760 					{
761 						ADecal *next = (ADecal *)decal->snext;
762 						MoveTheDecal (decal, decal->GetRealZ (wall), sourceOrigin, destOrigin);
763 						decal = next;
764 					}
765 				}
766 			}
767 		}
768 #endif
769 	}
770 	return didSomething;
771 }
772