1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_teleport.cpp 4469 2014-01-03 23:38:29Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2012 by Randy Heit (ZDoom).
8 // Copyright (C) 2006-2014 by The Odamex Team.
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // DESCRIPTION:
21 //	Teleportation.
22 //
23 //-----------------------------------------------------------------------------
24 
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include "doomtype.h"
30 #include "doomdef.h"
31 #include "doomstat.h"
32 
33 #include "m_random.h"
34 #include "s_sound.h"
35 
36 #include "p_local.h"
37 
38 
39 // State.
40 #include "r_state.h"
41 
42 
43 extern void P_CalcHeight (player_t *player);
44 
45 // [AM] From ZDoom SVN, modified for use with Odamex and without the
46 //      buggy 2.0.x teleport behavior compatibility fix.  Thanks to both
47 //      Randy and Graf.
SelectTeleDest(int tid,int tag)48 static AActor* SelectTeleDest(int tid, int tag)
49 {
50 	AActor* searcher;
51 
52 	// If tid is non-zero, select a destination from a matching actor at random.
53 	// If tag is also non-zero, the selection is restricted to actors in sectors
54 	// with a matching tag. If tid is zero and tag is non-zero, then the old Doom
55 	// behavior is used instead (return the first teleport dest found in a tagged
56 	// sector).
57 
58 	if (tid != 0)
59 	{
60 		TThinkerIterator<AActor> iterator;
61 		int count = 0;
62 		while ((searcher = iterator.Next()))
63 		{
64 			if (!(searcher->type == MT_TELEPORTMAN || searcher->type == MT_TELEPORTMAN2))
65 				continue;
66 			if (searcher->tid != tid)
67 				continue;
68 
69 			if (tag == 0 || searcher->subsector->sector->tag == tag)
70 				count++;
71 		}
72 
73 		// If teleport dests were not found, the sector tag is ignored for the
74 		// following compatibility searches.
75 		// Do this only when tag is 0 because this is the only case that was defined in Hexen.
76 		if (count == 0)
77 		{
78 			if (tag == 0)
79 			{
80 				// Try to find a matching map spot (fixes Hexen MAP10)
81 				TThinkerIterator<AActor> it2;
82 				searcher = it2.Next();
83 				while ((searcher = it2.Next()) != NULL)
84 				{
85 					if (searcher->type == MT_MAPSPOT || searcher->type == MT_MAPSPOTGRAVITY)
86 						break;
87 				}
88 
89 				if (searcher == NULL)
90 				{
91 					// Try to find a matching non-blocking spot of any type (fixes Caldera MAP13)
92 					FActorIterator it3(tid);
93 					searcher = it3.Next();
94 					while (searcher != NULL && (searcher->flags & MF_SOLID))
95 						searcher = it3.Next();
96 					return searcher;
97 				}
98 			}
99 		}
100 		else
101 		{
102 			if (count != 1)
103 				count = 1 + (P_Random() % count);
104 
105 			searcher = NULL;
106 			while (count > 0)
107 			{
108 				searcher = iterator.Next();
109 				if (!(searcher->type == MT_TELEPORTMAN || searcher->type == MT_TELEPORTMAN2))
110 					continue;
111 				if (searcher->tid != tid)
112 					continue;
113 				if (tag == 0 || searcher->subsector->sector->tag == tag)
114 					count--;
115 			}
116 		}
117 		return searcher;
118 	}
119 
120 	if (tag != 0)
121 	{
122 		int secnum = -1;
123 
124 		while ((secnum = P_FindSectorFromTag(tag, secnum)) >= 0)
125 		{
126 			// Scanning the snext links of things in the sector will not work, because
127 			// TeleportDests have MF_NOSECTOR set. So you have to search *everything*.
128 			// If there is more than one sector with a matching tag, then the destination
129 			// in the lowest-numbered sector is returned, rather than the earliest placed
130 			// teleport destination. This means if 50 sectors have a matching tag and
131 			// only the last one has a destination, *every* actor is scanned at least 49
132 			// times. Yuck.
133 			TThinkerIterator<AActor> it2;
134 			while ((searcher = it2.Next()) != NULL)
135 			{
136 				if (!(searcher->type == MT_TELEPORTMAN || searcher->type == MT_TELEPORTMAN2))
137 					continue;
138 
139 				if (searcher->subsector->sector == sectors + secnum)
140 					return searcher;
141 			}
142 		}
143 	}
144 
145 	return NULL;
146 }
147 
148 //
149 // TELEPORTATION
150 //
EV_Teleport(int tid,int tag,int side,AActor * thing)151 BOOL EV_Teleport(int tid, int tag, int side, AActor *thing)
152 {
153 	AActor *m;
154 	unsigned	an;
155 	fixed_t 	oldx;
156 	fixed_t 	oldy;
157 	fixed_t 	oldz;
158 	player_t	*player;
159 
160 	// don't teleport missiles
161 	if (thing->flags & MF_MISSILE)
162 		return false;
163 
164 	// Don't teleport if hit back of line, so you can get out of teleporter.
165 	if (side == 1)
166 		return false;
167 
168 	// [AM] Use modern ZDoom teleport destination selection.
169 	m = SelectTeleDest(tid, tag);
170 	if (m == NULL)
171 		return false;
172 
173 	// killough 5/12/98: exclude voodoo dolls:
174 	player = thing->player;
175 	if (player && player->mo != thing)
176 		player = NULL;
177 
178 	oldx = thing->x;
179 	oldy = thing->y;
180 	oldz = thing->z;
181 
182 	fixed_t destz = (m->type == MT_TELEPORTMAN) ? P_FloorHeight(m) : m->z;
183 
184 	if (!P_TeleportMove (thing, m->x, m->y, destz, false))
185 		return false;
186 
187 	// fraggle: this was changed in final doom,
188 	// problem between normal doom2 1.9 and final doom
189 	// Note that although chex.exe is based on Final Doom,
190 	// it does not have this quirk.
191 	if (gamemission < pack_tnt || gamemission == chex)
192 		thing->z = thing->floorz;
193 
194 	if (player)
195 		player->viewz = thing->z + thing->player->viewheight;
196 
197 	// spawn teleport fog at source and destination
198 	if(serverside && !(player && player->spectator))
199 	{
200 		S_Sound (new AActor (oldx, oldy, oldz, MT_TFOG), CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
201 		an = m->angle >> ANGLETOFINESHIFT;
202 		// emit sound at new spot
203 		S_Sound (new AActor (m->x+20*finecosine[an], m->y+20*finesine[an], thing->z, MT_TFOG), CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
204 	}
205 
206 	// don't move for a bit
207 	if (player && !player->spectator)
208 		thing->reactiontime = 18;
209 
210 	thing->momx = thing->momy = thing->momz = 0;
211 	thing->angle = m->angle;
212 	thing->pitch = 0;
213 
214 	return true;
215 }
216 
217 // [ML] Original vanilla-style EV_Teleport, based on code from chocolate doom
EV_LineTeleport(line_t * line,int side,AActor * thing)218 BOOL EV_LineTeleport (line_t *line, int side, AActor *thing)
219 {
220 	AActor *m;
221 	unsigned	an;
222 	int		i;
223 	int		tag;
224 	fixed_t 	oldx;
225 	fixed_t 	oldy;
226 	fixed_t 	oldz;
227 	player_t	*player;
228 	sector_t*	sector;
229 	TThinkerIterator<AActor> iterator;
230 
231     // don't teleport missiles
232     if (thing->flags & MF_MISSILE)
233 		return false;
234 
235     // Don't teleport if hit back of line,
236     //  so you can get out of teleporter.
237     if (side == 1)
238 		return false;
239 
240 	tag = line->id;
241 	// Yeah, cycle through all of them...
242 	for (i = 0; i < numsectors; i++)
243 	{
244 		if (sectors[ i ].tag == tag )
245 		{
246 			while ( (m = iterator.Next ()) )
247 			{
248 				// not a teleportman
249 				if (m->type != MT_TELEPORTMAN)
250 					continue;
251 
252 				sector = m->subsector->sector;
253 				// wrong sector
254 				if (sector-sectors != i )
255 					continue;
256 
257 				// killough 5/12/98: exclude voodoo dolls:
258 				player = thing->player;
259 				if (player && player->mo != thing)
260 					player = NULL;
261 
262 				oldx = thing->x;
263 				oldy = thing->y;
264 				oldz = thing->z;
265 
266 				fixed_t destz = (m->type == MT_TELEPORTMAN) ? P_FloorHeight(m) : m->z;
267 
268 				if (!P_TeleportMove (thing, m->x, m->y, destz, false))
269 					return false;
270 
271 				// fraggle: this was changed in final doom,
272 				// problem between normal doom2 1.9 and final doom
273 				// Note that although chex.exe is based on Final Doom,
274 				// it does not have this quirk.
275 				if (gamemission < pack_tnt || gamemission == chex)
276 						thing->z = thing->floorz;
277 
278 				if (player)
279 					player->viewz = thing->z + thing->player->viewheight;
280 
281 				// spawn teleport fog at source and destination
282 				if(serverside && !(player && player->spectator))
283 				{
284 					S_Sound (new AActor (oldx, oldy, oldz, MT_TFOG), CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
285 					an = m->angle >> ANGLETOFINESHIFT;
286 					// emit sound at new spot
287 					S_Sound (new AActor (m->x+20*finecosine[an], m->y+20*finesine[an], thing->z, MT_TFOG), CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
288 				}
289 
290 				// don't move for a bit
291 				if (player && !player->spectator)
292 					thing->reactiontime = 18;
293 
294 				thing->momx = thing->momy = thing->momz = 0;
295 				thing->angle = m->angle;
296 				thing->pitch = 0;
297 
298 				return true;
299 			}
300 		}
301 	}
302 
303 	return false;
304 }
305 
306 
307 //
308 // Silent TELEPORTATION, by Lee Killough
309 // Primarily for rooms-over-rooms etc.
310 // [RH] Changed to find destination by tid rather than sector
311 //
312 
EV_SilentTeleport(int tid,line_t * line,int side,AActor * thing)313 BOOL EV_SilentTeleport (int tid, line_t *line, int side, AActor *thing)
314 {
315 	AActor    *m;
316 
317 	// don't teleport missiles
318 	// Don't teleport if hit back of line,
319 	// so you can get out of teleporter.
320 	// [RH] This could potentially be called without a source linedef
321 
322 	if (thing->flags & MF_MISSILE || !line)
323 		return false;
324 
325 	// [AM] TODO: Change this to use SelectTeleDest.
326 	if (NULL == (m = AActor::FindGoal (NULL, tid, MT_TELEPORTMAN)))
327 		if (NULL == (m = AActor::FindGoal (NULL, tid, MT_TELEPORTMAN2)))
328 			return false;
329 
330 	// Height of thing above ground, in case of mid-air teleports:
331 	fixed_t z = thing->z - thing->floorz;
332 
333 	// Get the angle between the exit thing and source linedef.
334 	// Rotate 90 degrees, so that walking perpendicularly across
335 	// teleporter linedef causes thing to exit in the direction
336 	// indicated by the exit thing.
337 	angle_t angle =
338 		P_PointToAngle(0, 0, line->dx, line->dy) - m->angle + ANG90;
339 
340 	// Sine, cosine of angle adjustment
341 	fixed_t s = finesine[angle>>ANGLETOFINESHIFT];
342 	fixed_t c = finecosine[angle>>ANGLETOFINESHIFT];
343 
344 	// Momentum of thing crossing teleporter linedef
345 	fixed_t momx = thing->momx;
346 	fixed_t momy = thing->momy;
347 
348 	// Whether this is a player, and if so, a pointer to its player_t
349 	player_t *player = thing->player;
350 
351 	// Attempt to teleport, aborting if blocked
352 	if (!P_TeleportMove (thing, m->x, m->y, z + m->floorz, false))
353 		return 0;
354 
355 	// Rotate thing according to difference in angles
356 	thing->angle += angle;
357 
358 	// Rotate thing's momentum to come out of exit just like it entered
359 	thing->momx = FixedMul(momx, c) - FixedMul(momy, s);
360 	thing->momy = FixedMul(momy, c) + FixedMul(momx, s);
361 
362 	// Adjust player's view, in case there has been a height change
363 	// Voodoo dolls are excluded by making sure player->mo == thing.
364 	if (player && player->mo == thing)
365 	{
366 		// Save the current deltaviewheight, used in stepping
367 		fixed_t deltaviewheight = player->deltaviewheight;
368 
369 		// Clear deltaviewheight, since we don't want any changes
370 		player->deltaviewheight = 0;
371 
372 		// Set player's view according to the newly set parameters
373 		P_CalcHeight(player);
374 
375 		// Reset the delta to have the same dynamics as before
376 		player->deltaviewheight = deltaviewheight;
377 	}
378 	return true;
379 }
380 
381 //
382 // Silent linedef-based TELEPORTATION, by Lee Killough
383 // Primarily for rooms-over-rooms etc.
384 // This is the complete player-preserving kind of teleporter.
385 // It has advantages over the teleporter with thing exits.
386 //
387 
388 // maximum fixed_t units to move object to avoid hiccups
389 #define FUDGEFACTOR 10
390 
391 // [RH] Modified to support different source and destination ids.
EV_SilentLineTeleport(line_t * line,int side,AActor * thing,int id,BOOL reverse)392 BOOL EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id,
393 							BOOL reverse)
394 {
395 	int i;
396 	line_t *l;
397 
398 	if (thing->flags & MF_MISSILE || !line)
399 		return false;
400 
401 	for (i = -1; (i = P_FindLineFromID (id, i)) >= 0;)
402 		if ((l=lines+i) != line && l->backsector)
403 		{
404 			// Get the thing's position along the source linedef
405 			fixed_t pos = abs(line->dx) > abs(line->dy) ?
406 				FixedDiv(thing->x - line->v1->x, line->dx) :
407 				FixedDiv(thing->y - line->v1->y, line->dy) ;
408 
409 			// Get the angle between the two linedefs, for rotating
410 			// orientation and momentum. Rotate 180 degrees, and flip
411 			// the position across the exit linedef, if reversed.
412 			angle_t angle = (reverse ? pos = FRACUNIT-pos, 0 : ANG180) +
413 				P_PointToAngle(0, 0, l->dx, l->dy) -
414 				P_PointToAngle(0, 0, line->dx, line->dy);
415 
416 			// Interpolate position across the exit linedef
417 			fixed_t x = l->v2->x - FixedMul(pos, l->dx);
418 			fixed_t y = l->v2->y - FixedMul(pos, l->dy);
419 
420 			// Sine, cosine of angle adjustment
421 			fixed_t s = finesine[angle>>ANGLETOFINESHIFT];
422 			fixed_t c = finecosine[angle>>ANGLETOFINESHIFT];
423 
424 			// Maximum distance thing can be moved away from interpolated
425 			// exit, to ensure that it is on the correct side of exit linedef
426 			int fudge = FUDGEFACTOR;
427 
428 			// Whether this is a player, and if so, a pointer to its player_t.
429 			// Voodoo dolls are excluded by making sure thing->player->mo==thing.
430 			player_t *player = thing->player && thing->player->mo == thing ?
431 				thing->player : NULL;
432 
433 			// Height of thing above ground
434 			fixed_t z = thing->z -
435 						P_FloorHeight(thing->x, thing->y, sides[line->sidenum[1]].sector) +
436 						P_FloorHeight(x, y, sides[l->sidenum[0]].sector);
437 
438 			// Side to exit the linedef on positionally.
439 			//
440 			// Notes:
441 			//
442 			// This flag concerns exit position, not momentum. Due to
443 			// roundoff error, the thing can land on either the left or
444 			// the right side of the exit linedef, and steps must be
445 			// taken to make sure it does not end up on the wrong side.
446 			//
447 			// Exit momentum is always towards side 1 in a reversed
448 			// teleporter, and always towards side 0 otherwise.
449 			//
450 			// Exiting positionally on side 1 is always safe, as far
451 			// as avoiding oscillations and stuck-in-wall problems,
452 			// but may not be optimum for non-reversed teleporters.
453 			//
454 			// Exiting on side 0 can cause oscillations if momentum
455 			// is towards side 1, as it is with reversed teleporters.
456 			//
457 			// Exiting on side 1 slightly improves player viewing
458 			// when going down a step on a non-reversed teleporter.
459 
460 			int side = reverse;
461 
462 			// Make sure we are on correct side of exit linedef.
463 			while (P_PointOnLineSide(x, y, l) != side && --fudge>=0)
464 				if (abs(l->dx) > abs(l->dy))
465 					y -= ((l->dx < 0) != side ? -1 : 1);
466 				else
467 					x += ((l->dy < 0) != side ? -1 : 1);
468 
469 			// Attempt to teleport, aborting if blocked
470 			// Adjust z position to be same height above ground as before.
471 			// Ground level at the exit is measured as the higher of the
472 			// two floor heights at the exit linedef.
473 			if (!P_TeleportMove (thing, x, y, z, false))
474 				return false;
475 
476 			// Rotate thing's orientation according to difference in linedef angles
477 			thing->angle += angle;
478 
479 			// Momentum of thing crossing teleporter linedef
480 			x = thing->momx;
481 			y = thing->momy;
482 
483 			// Rotate thing's momentum to come out of exit just like it entered
484 			thing->momx = FixedMul(x, c) - FixedMul(y, s);
485 			thing->momy = FixedMul(y, c) + FixedMul(x, s);
486 
487 			// Adjust a player's view, in case there has been a height change
488 			if (player)
489 			{
490 				// Save the current deltaviewheight, used in stepping
491 				fixed_t deltaviewheight = player->deltaviewheight;
492 
493 				// Clear deltaviewheight, since we don't want any changes now
494 				player->deltaviewheight = 0;
495 
496 				// Set player's view according to the newly set parameters
497 				P_CalcHeight(player);
498 
499 				// Reset the delta to have the same dynamics as before
500 				player->deltaviewheight = deltaviewheight;
501 			}
502 
503 			return true;
504 		}
505 	return false;
506 }
507 
508 VERSION_CONTROL (p_teleport_cpp, "$Id: p_teleport.cpp 4469 2014-01-03 23:38:29Z dr_sean $")
509 
510