1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_spec.cpp 4532 2014-02-09 03:52:19Z mike $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
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 //	*Implements special effects:
21 //	*Texture animation, height or lighting changes
22 //	*according to adjacent sectors, respective
23 //	*utility functions, etc.
24 //	*Line Tag handling. Line and Sector triggers.
25 //	*Implements donut linedef triggers
26 //	*Initializes and implements BOOM linedef triggers for
27 //	*Scrollers/Conveyors
28 //	*Friction
29 //	*Wind/Current
30 //
31 //-----------------------------------------------------------------------------
32 
33 
34 #include "m_alloc.h"
35 #include "doomdef.h"
36 #include "doomstat.h"
37 #include "gstrings.h"
38 
39 #include "i_system.h"
40 #include "z_zone.h"
41 #include "m_argv.h"
42 #include "m_random.h"
43 #include "m_bbox.h"
44 #include "w_wad.h"
45 
46 #include "r_local.h"
47 #include "p_local.h"
48 #include "p_lnspec.h"
49 #include "p_spec.h"
50 #include "p_acs.h"
51 
52 #include "g_game.h"
53 #include "p_unlag.h"
54 
55 #include "s_sound.h"
56 #include "sc_man.h"
57 
58 // State.
59 #include "r_state.h"
60 
61 #include "c_console.h"
62 
63 // [RH] Needed for sky scrolling
64 #include "r_sky.h"
65 
66 EXTERN_CVAR(sv_allowexit)
67 EXTERN_CVAR(sv_fragexitswitch)
68 
69 std::list<movingsector_t> movingsectors;
70 
71 //
72 // P_FindMovingSector
73 //
P_FindMovingSector(sector_t * sector)74 std::list<movingsector_t>::iterator P_FindMovingSector(sector_t *sector)
75 {
76 	std::list<movingsector_t>::iterator itr;
77 	for (itr = movingsectors.begin(); itr != movingsectors.end(); ++itr)
78 		if (sector == itr->sector)
79 			return itr;
80 
81 	// not found
82 	return movingsectors.end();
83 }
84 
85 //
86 // P_AddMovingCeiling
87 //
88 // Updates the movingsectors list to include the passed sector, which
89 // tracks which sectors currently have a moving ceiling/floor
90 //
P_AddMovingCeiling(sector_t * sector)91 void P_AddMovingCeiling(sector_t *sector)
92 {
93 	if (!sector)
94 		return;
95 
96 	movingsector_t *movesec;
97 
98 	// Check if this already exists
99 	std::list<movingsector_t>::iterator itr = P_FindMovingSector(sector);
100 	if (itr != movingsectors.end())
101 	{
102 		// this sector already is moving
103 		movesec = &(*itr);
104 	}
105 	else
106 	{
107 		movingsectors.push_back(movingsector_t());
108 		movesec = &(movingsectors.back());
109 	}
110 
111 	movesec->sector = sector;
112 	movesec->moving_ceiling = true;
113 
114 	sector->moveable = true;
115 	// [SL] 2012-05-04 - Register this sector as a moveable sector with the
116 	// reconciliation system for unlagging
117 	Unlag::getInstance().registerSector(sector);
118 }
119 
120 //
121 // P_AddMovingFloor
122 //
123 // Updates the movingsectors list to include the passed sector, which
124 // tracks which sectors currently have a moving ceiling/floor
125 //
P_AddMovingFloor(sector_t * sector)126 void P_AddMovingFloor(sector_t *sector)
127 {
128 	if (!sector)
129 		return;
130 
131 	movingsector_t *movesec;
132 
133 	// Check if this already exists
134 	std::list<movingsector_t>::iterator itr = P_FindMovingSector(sector);
135 	if (itr != movingsectors.end())
136 	{
137 		// this sector already is moving
138 		movesec = &(*itr);
139 	}
140 	else
141 	{
142 		movingsectors.push_back(movingsector_t());
143 		movesec = &(movingsectors.back());
144 	}
145 
146 	movesec->sector = sector;
147 	movesec->moving_floor = true;
148 
149 	sector->moveable = true;
150 	// [SL] 2012-05-04 - Register this sector as a moveable sector with the
151 	// reconciliation system for unlagging
152 	Unlag::getInstance().registerSector(sector);
153 }
154 
155 //
156 // P_RemoveMovingCeiling
157 //
158 // Removes the passed sector from the movingsectors list, which tracks
159 // which sectors currently have a moving ceiling/floor
160 //
P_RemoveMovingCeiling(sector_t * sector)161 void P_RemoveMovingCeiling(sector_t *sector)
162 {
163 	if (!sector)
164 		return;
165 
166 	std::list<movingsector_t>::iterator itr = P_FindMovingSector(sector);
167 	if (itr != movingsectors.end())
168 	{
169 		itr->moving_ceiling = false;
170 
171 		// Does this sector have a moving floor as well?  If so, just
172 		// mark the ceiling as invalid but don't remove from the list
173 		if (!itr->moving_floor)
174 			movingsectors.erase(itr);
175 
176 		return;
177 	}
178 }
179 
180 //
181 // P_RemoveMovingFloor
182 //
183 // Removes the passed sector from the movingsectors list, which tracks
184 // which sectors currently have a moving ceiling/floor
185 //
P_RemoveMovingFloor(sector_t * sector)186 void P_RemoveMovingFloor(sector_t *sector)
187 {
188 	if (!sector)
189 		return;
190 
191 	std::list<movingsector_t>::iterator itr = P_FindMovingSector(sector);
192 	if (itr != movingsectors.end())
193 	{
194 		itr->moving_floor = false;
195 
196 		// Does this sector have a moving ceiling as well?  If so, just
197 		// mark the floor as invalid but don't remove from the list
198 		if (!itr->moving_ceiling)
199 			movingsectors.erase(itr);
200 
201 		return;
202 	}
203 }
204 
P_MovingCeilingCompleted(sector_t * sector)205 bool P_MovingCeilingCompleted(sector_t *sector)
206 {
207 	if (!sector || !sector->ceilingdata)
208 		return true;
209 
210 	if (sector->ceilingdata->IsA(RUNTIME_CLASS(DDoor)))
211 	{
212 		DDoor *door = static_cast<DDoor *>(sector->ceilingdata);
213 		return (door->m_Status == DDoor::destroy);
214 	}
215 	if (sector->ceilingdata->IsA(RUNTIME_CLASS(DCeiling)))
216 	{
217 		DCeiling *ceiling = static_cast<DCeiling *>(sector->ceilingdata);
218 		return (ceiling->m_Status == DCeiling::destroy);
219 	}
220 	if (sector->ceilingdata->IsA(RUNTIME_CLASS(DPillar)))
221 	{
222 		DPillar *pillar = static_cast<DPillar *>(sector->ceilingdata);
223 		return (pillar->m_Status == DPillar::destroy);
224 	}
225 	if (sector->ceilingdata->IsA(RUNTIME_CLASS(DElevator)))
226 	{
227 		DElevator *elevator = static_cast<DElevator *>(sector->ceilingdata);
228 		return (elevator->m_Status == DElevator::destroy);
229 	}
230 
231 	return false;
232 }
233 
P_MovingFloorCompleted(sector_t * sector)234 bool P_MovingFloorCompleted(sector_t *sector)
235 {
236 	if (!sector || !sector->floordata)
237 		return true;
238 
239 	if (sector->floordata->IsA(RUNTIME_CLASS(DPlat)))
240 	{
241 		DPlat *plat = static_cast<DPlat *>(sector->floordata);
242 		return (plat->m_Status == DPlat::destroy);
243 	}
244 	if (sector->floordata->IsA(RUNTIME_CLASS(DFloor)))
245 	{
246 		DFloor *floor = static_cast<DFloor *>(sector->floordata);
247 		return (floor->m_Status == DFloor::destroy);
248 	}
249 
250 	return false;
251 }
252 
253 
254 EXTERN_CVAR (sv_allowexit)
255 extern bool	HasBehavior;
256 
IMPLEMENT_SERIAL(DScroller,DThinker)257 IMPLEMENT_SERIAL (DScroller, DThinker)
258 IMPLEMENT_SERIAL (DPusher, DThinker)
259 
260 DScroller::DScroller ()
261 {
262 }
263 
Serialize(FArchive & arc)264 void DScroller::Serialize (FArchive &arc)
265 {
266 	Super::Serialize (arc);
267 	if (arc.IsStoring ())
268 	{
269 		arc << m_Type
270 			<< m_dx << m_dy
271 			<< m_Affectee
272 			<< m_Control
273 			<< m_LastHeight
274 			<< m_vdx << m_vdy
275 			<< m_Accel;
276 	}
277 	else
278 	{
279 		arc >> m_Type
280 			>> m_dx >> m_dy
281 			>> m_Affectee
282 			>> m_Control
283 			>> m_LastHeight
284 			>> m_vdx >> m_vdy
285 			>> m_Accel;
286 	}
287 }
288 
DPusher()289 DPusher::DPusher () : m_Type(p_push), m_Xmag(0), m_Ymag(0), m_Magnitude(0),
290     m_Radius(0), m_X(0), m_Y(0), m_Affectee(0)
291 {
292 }
293 
Serialize(FArchive & arc)294 void DPusher::Serialize (FArchive &arc)
295 {
296 	Super::Serialize (arc);
297 	if (arc.IsStoring ())
298 	{
299 		arc << m_Type
300 			<< m_Source
301 			<< m_Xmag
302 			<< m_Ymag
303 			<< m_Magnitude
304 			<< m_Radius
305 			<< m_X
306 			<< m_Y
307 			<< m_Affectee;
308 	}
309 	else
310 	{
311 		arc >> m_Type
312 			>> m_Source->netid
313 			>> m_Xmag
314 			>> m_Ymag
315 			>> m_Magnitude
316 			>> m_Radius
317 			>> m_X
318 			>> m_Y
319 			>> m_Affectee;
320 	}
321 }
322 
323 //
324 // Animating textures and planes
325 // There is another anim_t used in wi_stuff, unrelated.
326 //
327 // [RH] Expanded to work with a Hexen ANIMDEFS lump
328 //
329 #define MAX_ANIM_FRAMES	32
330 
331 typedef struct
332 {
333 	short 	basepic;
334 	short	numframes;
335 	byte 	istexture;
336 	byte	uniqueframes;
337 	byte	countdown;
338 	byte	curframe;
339 	byte 	speedmin[MAX_ANIM_FRAMES];
340 	byte	speedmax[MAX_ANIM_FRAMES];
341 	short	framepic[MAX_ANIM_FRAMES];
342 } anim_t;
343 
344 
345 
346 #define MAXANIMS	32		// Really just a starting point
347 
348 static anim_t*  lastanim;
349 static anim_t*  anims;
350 static size_t	maxanims;
351 
352 
353 // Factor to scale scrolling effect into mobj-carrying properties = 3/32.
354 // (This is so scrolling floors and objects on them can move at same speed.)
355 #define CARRYFACTOR ((fixed_t)(FRACUNIT*.09375))
356 
357 // killough 3/7/98: Initialize generalized scrolling
358 static void P_SpawnScrollers(void);
359 
360 static void P_SpawnFriction(void);		// phares 3/16/98
361 static void P_SpawnPushers(void);		// phares 3/20/98
362 
363 static void ParseAnim (byte istex);
364 
365 //
366 //		Animating line specials
367 //
368 //#define MAXLINEANIMS			64
369 
370 //extern	short	numlinespecials;
371 //extern	line_t* linespeciallist[MAXLINEANIMS];
372 
373 //
374 // [RH] P_InitAnimDefs
375 //
376 // This uses a Hexen ANIMDEFS lump to define the animation sequences
377 //
P_InitAnimDefs()378 static void P_InitAnimDefs ()
379 {
380 	int lump = -1;
381 
382 	while ((lump = W_FindLump ("ANIMDEFS", lump)) != -1)
383 	{
384 		SC_OpenLumpNum (lump, "ANIMDEFS");
385 
386 		while (SC_GetString ())
387 		{
388 			if (SC_Compare ("flat"))
389 			{
390 				ParseAnim (false);
391 			}
392 			else if (SC_Compare ("texture"))
393 			{
394 				ParseAnim (true);
395 			}
396 			else if (SC_Compare ("switch"))   // Don't support switchdef yet...
397 			{
398 				//P_ProcessSwitchDef ();
399 				SC_ScriptError("switchdef not supported.");
400 			}
401 			else if (SC_Compare ("warp"))
402 			{
403 				SC_MustGetString ();
404 				if (SC_Compare ("flat"))
405 				{
406 					SC_MustGetString ();
407 					flatwarp[R_FlatNumForName (sc_String)] = true;
408 				}
409 				else if (SC_Compare ("texture"))
410 				{
411 					// TODO: Make texture warping work with wall textures
412 					SC_MustGetString ();
413 					R_TextureNumForName (sc_String);
414 				}
415 				else
416 				{
417 					SC_ScriptError (NULL, NULL);
418 				}
419 			}
420 		}
421 		SC_Close ();
422 	}
423 }
424 
ParseAnim(byte istex)425 static void ParseAnim (byte istex)
426 {
427 	anim_t sink;
428 	short picnum;
429 	anim_t *place;
430 	byte min, max;
431 	int frame;
432 
433 	SC_MustGetString ();
434 	picnum = istex ? R_CheckTextureNumForName (sc_String)
435 		: W_CheckNumForName (sc_String, ns_flats) - firstflat;
436 
437 	if (picnum == -1)
438 	{ // Base pic is not present, so skip this definition
439 		place = &sink;
440 	}
441 	else
442 	{
443 		for (place = anims; place < lastanim; place++)
444 		{
445 			if (place->basepic == picnum && place->istexture == istex)
446 			{
447 				break;
448 			}
449 		}
450 		if (place == lastanim)
451 		{
452 			lastanim++;
453 			if (lastanim > anims + maxanims)
454 			{
455 				size_t newmax = maxanims ? maxanims*2 : MAXANIMS;
456 				anims = (anim_t *)Realloc (anims, newmax*sizeof(*anims));
457 				place = anims + maxanims;
458 				lastanim = place + 1;
459 				maxanims = newmax;
460 			}
461 		}
462 		// no decals on animating textures by default
463 		//if (istex)
464 		//{
465 		//	texturenodecals[picnum] = 1;
466 		//}
467 	}
468 
469 	place->uniqueframes = true;
470 	place->curframe = 0;
471 	place->numframes = 0;
472 	place->basepic = picnum;
473 	place->istexture = istex;
474 	memset (place->speedmin, 1, MAX_ANIM_FRAMES * sizeof(*place->speedmin));
475 	memset (place->speedmax, 1, MAX_ANIM_FRAMES * sizeof(*place->speedmax));
476 
477 	while (SC_GetString ())
478 	{
479 		/*if (SC_Compare ("allowdecals"))
480 		{
481 			if (istex && picnum >= 0)
482 			{
483 				texturenodecals[picnum] = 0;
484 			}
485 			continue;
486 		}
487 		else*/ if (!SC_Compare ("pic"))
488 		{
489 			SC_UnGet ();
490 			break;
491 		}
492 
493 		if (place->numframes == MAX_ANIM_FRAMES)
494 		{
495 			SC_ScriptError ("Animation has too many frames");
496 		}
497 
498 		min = max = 1;	// Shut up, GCC
499 
500 		SC_MustGetNumber ();
501 		frame = sc_Number;
502 		SC_MustGetString ();
503 		if (SC_Compare ("tics"))
504 		{
505 			SC_MustGetNumber ();
506 			if (sc_Number < 0)
507 				sc_Number = 0;
508 			else if (sc_Number > 255)
509 				sc_Number = 255;
510 			min = max = sc_Number;
511 		}
512 		else if (SC_Compare ("rand"))
513 		{
514 			SC_MustGetNumber ();
515 			min = sc_Number >= 0 ? sc_Number : 0;
516 			SC_MustGetNumber ();
517 			max = sc_Number <= 255 ? sc_Number : 255;
518 		}
519 		else
520 		{
521 			SC_ScriptError ("Must specify a duration for animation frame");
522 		}
523 
524 		place->speedmin[place->numframes] = min;
525 		place->speedmax[place->numframes] = max;
526 		place->framepic[place->numframes] = frame + picnum - 1;
527 		place->numframes++;
528 	}
529 
530 	if (place->numframes < 2)
531 	{
532 		SC_ScriptError ("Animation needs at least 2 frames");
533 	}
534 
535 	place->countdown = place->speedmin[0];
536 }
537 
538 /*
539  *P_InitPicAnims
540  *
541  *Load the table of animation definitions, checking for existence of
542  *the start and end of each frame. If the start doesn't exist the sequence
543  *is skipped, if the last doesn't exist, BOOM exits.
544  *
545  *Wall/Flat animation sequences, defined by name of first and last frame,
546  *The full animation sequence is given using all lumps between the start
547  *and end entry, in the order found in the WAD file.
548  *
549  *This routine modified to read its data from a predefined lump or
550  *PWAD lump called ANIMATED rather than a static table in this module to
551  *allow wad designers to insert or modify animation sequences.
552  *
553  *Lump format is an array of byte packed animdef_t structures, terminated
554  *by a structure with istexture == -1. The lump can be generated from a
555  *text source file using SWANTBLS.EXE, distributed with the BOOM utils.
556  *The standard list of switches and animations is contained in the example
557  *source text file DEFSWANI.DAT also in the BOOM util distribution.
558  *
559  *[RH] Rewritten to support BOOM ANIMATED lump but also make absolutely
560  *no assumptions about how the compiler packs the animdefs array.
561  *
562  */
P_InitPicAnims(void)563 void P_InitPicAnims (void)
564 {
565 	byte *animdefs, *anim_p;
566 
567 	// denis - allow reinitialisation
568 	if(anims)
569 	{
570 		M_Free(anims);
571 		lastanim = 0;
572 		maxanims = 0;
573 	}
574 
575 	// [RH] Load an ANIMDEFS lump first
576 	P_InitAnimDefs ();
577 
578 	if (W_CheckNumForName ("ANIMATED") == -1)
579 		return;
580 
581 	animdefs = (byte *)W_CacheLumpName ("ANIMATED", PU_STATIC);
582 
583 	// Init animation
584 
585 		for (anim_p = animdefs; *anim_p != 255; anim_p += 23)
586 		{
587 			// 1/11/98 killough -- removed limit by array-doubling
588 			if (lastanim >= anims + maxanims)
589 			{
590 				size_t newmax = maxanims ? maxanims*2 : MAXANIMS;
591 				anims = (anim_t *)Realloc(anims, newmax*sizeof(*anims));   // killough
592 				lastanim = anims + maxanims;
593 				maxanims = newmax;
594 			}
595 
596 			if (*anim_p /* .istexture */ & 1)
597 			{
598 				// different episode ?
599 				if (R_CheckTextureNumForName (anim_p + 10 /* .startname */) == -1 ||
600 					R_CheckTextureNumForName (anim_p + 1 /* .endname */) == -1)
601 					continue;
602 
603 				lastanim->basepic = R_TextureNumForName (anim_p + 10 /* .startname */);
604 				lastanim->numframes = R_TextureNumForName (anim_p + 1 /* .endname */)
605 									  - lastanim->basepic + 1;
606 				/*if (*anim_p & 2)
607 				{ // [RH] Bit 1 set means allow decals on walls with this texture
608 					texturenodecals[lastanim->basepic] = 0;
609 				}
610 				else
611 				{
612 					texturenodecals[lastanim->basepic] = 1;
613 				}*/
614 			}
615 			else
616 			{
617 				if (W_CheckNumForName ((char *)anim_p + 10 /* .startname */, ns_flats) == -1 ||
618 					W_CheckNumForName ((char *)anim_p + 1 /* .startname */, ns_flats) == -1)
619 					continue;
620 
621 				lastanim->basepic = R_FlatNumForName (anim_p + 10 /* .startname */);
622 				lastanim->numframes = R_FlatNumForName (anim_p + 1 /* .endname */)
623 									  - lastanim->basepic + 1;
624 			}
625 
626 			lastanim->istexture = *anim_p /* .istexture */;
627 			lastanim->uniqueframes = false;
628 			lastanim->curframe = 0;
629 
630 			if (lastanim->numframes < 2)
631 				Printf (PRINT_HIGH,"P_InitPicAnims: bad cycle from %s to %s",
632 						 anim_p + 10 /* .startname */,
633 						 anim_p + 1 /* .endname */);
634 
635 			lastanim->speedmin[0] = lastanim->speedmax[0] = lastanim->countdown =
636 						/* .speed */
637 						(anim_p[19] << 0) |
638 						(anim_p[20] << 8) |
639 						(anim_p[21] << 16) |
640 						(anim_p[22] << 24);
641 
642 			lastanim->countdown--;
643 
644 			lastanim++;
645 		}
646 	Z_Free (animdefs);
647 }
648 
649 
650 //
651 // UTILITIES
652 //
653 
654 
655 
656 // [RH]
657 // P_NextSpecialSector()
658 //
659 // Returns the next special sector attached to this sector
660 // with a certain special.
P_NextSpecialSector(sector_t * sec,int type,sector_t * nogood)661 sector_t *P_NextSpecialSector (sector_t *sec, int type, sector_t *nogood)
662 {
663 	sector_t *tsec;
664 	int i;
665 
666 	for (i = 0; i < sec->linecount; i++)
667 	{
668 		line_t *ln = sec->lines[i];
669 
670 		if (!(ln->flags & ML_TWOSIDED) ||
671 			!(tsec = ln->frontsector))
672 			continue;
673 
674 		if (sec == tsec)
675 		{
676 			tsec = ln->backsector;
677 			if (sec == tsec)
678 				continue;
679 		}
680 
681 		if (tsec == nogood)
682 			continue;
683 
684 		if ((tsec->special & 0x00ff) == type)
685 		{
686 			return tsec;
687 		}
688 	}
689 	return NULL;
690 }
691 
692 //
693 // P_FindLowestFloorSurrounding()
694 // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
695 //
P_FindLowestFloorSurrounding(sector_t * sec)696 fixed_t P_FindLowestFloorSurrounding (sector_t* sec)
697 {
698 	int i;
699 	line_t *check;
700 	sector_t *other;
701 	fixed_t height = P_FloorHeight(sec);
702 
703 	for (i = 0; i < sec->linecount; i++)
704 	{
705 		check = sec->lines[i];
706 		other = getNextSector (check,sec);
707 
708 		if (!other)
709 			continue;
710 
711 		fixed_t v1height =
712 			P_FloorHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
713 		fixed_t v2height =
714 			P_FloorHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
715 
716 		if (v1height < height)
717 			height = v1height;
718 		if (v2height < height)
719 			height = v2height;
720 	}
721 	return height;
722 }
723 
724 
725 
726 //
727 // P_FindHighestFloorSurrounding()
728 // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
729 //
P_FindHighestFloorSurrounding(sector_t * sec)730 fixed_t P_FindHighestFloorSurrounding (sector_t *sec)
731 {
732 	int i;
733 	line_t *check;
734 	sector_t *other;
735 	fixed_t height = MININT;
736 
737 	for (i = 0; i < sec->linecount; i++)
738 	{
739 		check = sec->lines[i];
740 		other = getNextSector(check,sec);
741 
742 		if (!other)
743 			continue;
744 
745 		fixed_t v1height =
746 			P_FloorHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
747 		fixed_t v2height =
748 			P_FloorHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
749 
750 		if (v1height > height)
751 			height = v1height;
752 		if (v2height > height)
753 			height = v2height;
754 	}
755 	return height;
756 }
757 
758 //
759 // P_FindNextHighestFloor()
760 //
761 // Passed a sector and a floor height, returns the fixed point value
762 // of the smallest floor height in a surrounding sector larger than
763 // the floor height passed. If no such height exists the floorheight
764 // passed is returned.
765 //
766 // [SL] Changed to use ZDoom 1.23's version of this function to account
767 // for sloped sectors.
768 //
P_FindNextHighestFloor(sector_t * sec)769 fixed_t P_FindNextHighestFloor (sector_t *sec)
770 {
771 	sector_t *other;
772 	fixed_t ogheight = P_FloorHeight(sec);
773 	fixed_t height = MAXINT;
774 
775     for (int i = 0; i < sec->linecount; i++)
776     {
777 		if (NULL != (other = getNextSector(sec->lines[i], sec)))
778         {
779         	vertex_t *v = sec->lines[i]->v1;
780 			fixed_t ofloor = P_FloorHeight(v->x, v->y, other);
781 
782 			if (ofloor < height && ofloor > ogheight)
783 				height = ofloor;
784 
785 			v = sec->lines[i]->v2;
786 			ofloor = P_FloorHeight(v->x, v->y, other);
787 
788 			if (ofloor < height && ofloor > ogheight)
789                 height = ofloor;
790         }
791     }
792 
793     if (height == MAXINT)
794     	height = ogheight;
795 
796     return height;
797 }
798 
799 
800 //
801 // P_FindNextLowestFloor()
802 //
803 // Passed a sector and a floor height, returns the fixed point value
804 // of the largest floor height in a surrounding sector smaller than
805 // the floor height passed. If no such height exists the floorheight
806 // passed is returned.
807 //
808 // jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this
809 // [SL] Changed to use ZDoom 1.23's version of this function to account
810 // for sloped sectors.
811 //
P_FindNextLowestFloor(sector_t * sec)812 fixed_t P_FindNextLowestFloor(sector_t *sec)
813 {
814 	sector_t *other;
815 	fixed_t ogheight = P_FloorHeight(sec);
816 	fixed_t height = MININT;
817 
818     for (int i = 0; i < sec->linecount; i++)
819     {
820 		if (NULL != (other = getNextSector(sec->lines[i], sec)))
821         {
822         	vertex_t *v = sec->lines[i]->v1;
823 			fixed_t ofloor = P_FloorHeight(v->x, v->y, other);
824 
825 			if (ofloor > height && ofloor < ogheight)
826 				height = ofloor;
827 
828 			v = sec->lines[i]->v2;
829 			ofloor = P_FloorHeight(v->x, v->y, other);
830 
831 			if (ofloor > height && ofloor < ogheight)
832                 height = ofloor;
833         }
834     }
835 
836     if (height == MININT)
837     	height = ogheight;
838 
839     return height;
840 }
841 
842 //
843 // P_FindNextLowestCeiling()
844 //
845 // Passed a sector and a ceiling height, returns the fixed point value
846 // of the largest ceiling height in a surrounding sector smaller than
847 // the ceiling height passed. If no such height exists the ceiling height
848 // passed is returned.
849 //
850 // jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this
851 // [SL] Changed to use ZDoom 1.23's version of this function to account
852 // for sloped sectors.
853 //
P_FindNextLowestCeiling(sector_t * sec)854 fixed_t P_FindNextLowestCeiling (sector_t *sec)
855 {
856 	sector_t *other;
857 	fixed_t ogheight = P_CeilingHeight(sec);
858 	fixed_t height = MININT;
859 
860     for (int i = 0; i < sec->linecount; i++)
861     {
862 		if (NULL != (other = getNextSector(sec->lines[i], sec)))
863         {
864         	vertex_t *v = sec->lines[i]->v1;
865 			fixed_t oceiling = P_CeilingHeight(v->x, v->y, other);
866 
867 			if (oceiling > height && oceiling < ogheight)
868 				height = oceiling;
869 
870 			v = sec->lines[i]->v2;
871 			oceiling = P_CeilingHeight(v->x, v->y, other);
872 
873 			if (oceiling > height && oceiling < ogheight)
874                 height = oceiling;
875         }
876     }
877 
878     if (height == MININT)
879     	height = ogheight;
880 
881     return height;
882 }
883 
884 
885 //
886 // P_FindNextHighestCeiling()
887 //
888 // Passed a sector and a ceiling height, returns the fixed point value
889 // of the smallest ceiling height in a surrounding sector larger than
890 // the ceiling height passed. If no such height exists the ceiling height
891 // passed is returned.
892 //
893 // jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this
894 // [SL] Changed to use ZDoom 1.23's version of this function to account
895 // for sloped sectors.
896 //
P_FindNextHighestCeiling(sector_t * sec)897 fixed_t P_FindNextHighestCeiling (sector_t *sec)
898 {
899 	sector_t *other;
900 	fixed_t ogheight = P_CeilingHeight(sec);
901 	fixed_t height = MAXINT;
902 
903     for (int i = 0; i < sec->linecount; i++)
904     {
905 		if (NULL != (other = getNextSector(sec->lines[i], sec)))
906         {
907         	vertex_t *v = sec->lines[i]->v1;
908 			fixed_t oceiling = P_CeilingHeight(v->x, v->y, other);
909 
910 			if (oceiling < height && oceiling > ogheight)
911 				height = oceiling;
912 
913 			v = sec->lines[i]->v2;
914 			oceiling = P_CeilingHeight(v->x, v->y, other);
915 
916 			if (oceiling < height && oceiling > ogheight)
917                 height = oceiling;
918         }
919     }
920 
921     if (height == MAXINT)
922     	height = ogheight;
923 
924     return height;
925 }
926 
927 //
928 // FIND LOWEST CEILING IN THE SURROUNDING SECTORS
929 //
P_FindLowestCeilingSurrounding(sector_t * sec)930 fixed_t P_FindLowestCeilingSurrounding (sector_t *sec)
931 {
932 	int i;
933 	line_t *check;
934 	sector_t *other;
935 	fixed_t height = MAXINT;
936 
937 	for (i = 0; i < sec->linecount; i++)
938 	{
939 		check = sec->lines[i];
940 		other = getNextSector(check,sec);
941 
942 		if (!other)
943 			continue;
944 
945 		fixed_t v1height =
946 			P_CeilingHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
947 		fixed_t v2height =
948 			P_CeilingHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
949 
950 		if (v1height < height)
951 			height = v1height;
952 		if (v2height < height)
953 			height = v2height;
954 	}
955 	return height;
956 }
957 
958 
959 //
960 // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
961 //
P_FindHighestCeilingSurrounding(sector_t * sec)962 fixed_t P_FindHighestCeilingSurrounding (sector_t *sec)
963 {
964 	int i;
965 	line_t *check;
966 	sector_t *other;
967 	fixed_t height = MININT;
968 
969 	for (i = 0; i < sec->linecount; i++)
970 	{
971 		check = sec->lines[i];
972 		other = getNextSector (check,sec);
973 
974 		if (!other)
975 			continue;
976 
977 		fixed_t v1height =
978 			P_CeilingHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
979 		fixed_t v2height =
980 			P_CeilingHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other);
981 
982 		if (v1height > height)
983 			height = v1height;
984 		if (v2height > height)
985 			height = v2height;
986 	}
987 	return height;
988 }
989 
990 
991 //
992 // P_FindShortestTextureAround()
993 //
994 // Passed a sector number, returns the shortest lower texture on a
995 // linedef bounding the sector.
996 //
997 // jff 02/03/98 Add routine to find shortest lower texture
998 //
P_FindShortestTextureAround(int secnum)999 fixed_t P_FindShortestTextureAround (int secnum)
1000 {
1001 	int minsize = MAXINT;
1002 	side_t *side;
1003 	int i;
1004 	sector_t *sec = &sectors[secnum];
1005 
1006 	for (i = 0; i < sec->linecount; i++)
1007 	{
1008 		if (twoSided (secnum, i))
1009 		{
1010 			side = getSide (secnum, i, 0);
1011 			if (side->bottomtexture >= 0)
1012 				if (textureheight[side->bottomtexture] < minsize)
1013 					minsize = textureheight[side->bottomtexture];
1014 			side = getSide (secnum, i, 1);
1015 			if (side->bottomtexture >= 0)
1016 				if (textureheight[side->bottomtexture] < minsize)
1017 					minsize = textureheight[side->bottomtexture];
1018 		}
1019 	}
1020 	return minsize;
1021 }
1022 
1023 
1024 //
1025 // P_FindShortestUpperAround()
1026 //
1027 // Passed a sector number, returns the shortest upper texture on a
1028 // linedef bounding the sector.
1029 //
1030 // Note: If no upper texture exists 32000*FRACUNIT is returned.
1031 //       but if compatibility then MAXINT is returned
1032 //
1033 // jff 03/20/98 Add routine to find shortest upper texture
1034 //
P_FindShortestUpperAround(int secnum)1035 fixed_t P_FindShortestUpperAround (int secnum)
1036 {
1037 	int minsize = MAXINT;
1038 	side_t *side;
1039 	int i;
1040 	sector_t *sec = &sectors[secnum];
1041 
1042 	for (i = 0; i < sec->linecount; i++)
1043 	{
1044 		if (twoSided (secnum, i))
1045 		{
1046 			side = getSide (secnum,i,0);
1047 			if (side->toptexture >= 0)
1048 				if (textureheight[side->toptexture] < minsize)
1049 					minsize = textureheight[side->toptexture];
1050 			side = getSide (secnum,i,1);
1051 			if (side->toptexture >= 0)
1052 				if (textureheight[side->toptexture] < minsize)
1053 					minsize = textureheight[side->toptexture];
1054 		}
1055 	}
1056 	return minsize;
1057 }
1058 
1059 
1060 //
1061 // P_FindModelFloorSector()
1062 //
1063 // Passed a floor height and a sector number, return a pointer to a
1064 // a sector with that floor height across the lowest numbered two sided
1065 // line surrounding the sector.
1066 //
1067 // Note: If no sector at that height bounds the sector passed, return NULL
1068 //
1069 // jff 02/03/98 Add routine to find numeric model floor
1070 //  around a sector specified by sector number
1071 // jff 3/14/98 change first parameter to plain height to allow call
1072 //  from routine not using floormove_t
1073 // [SL] Changed to use ZDoom 1.23's version of this function to account
1074 // for sloped sectors.
1075 //
P_FindModelFloorSector(fixed_t floordestheight,int secnum)1076 sector_t *P_FindModelFloorSector (fixed_t floordestheight, int secnum)
1077 {
1078 	sector_t *other, *sec = &sectors[secnum];
1079 
1080     //jff 5/23/98 don't disturb sec->linecount while searching
1081     // but allow early exit in old demos
1082     for (int i = 0; i < sec->linecount; i++)
1083     {
1084         other = getNextSector(sec->lines[i], sec);
1085         if (other != NULL &&
1086         	(P_FloorHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other) == floordestheight ||
1087         	 P_FloorHeight(sec->lines[i]->v2->x, sec->lines[i]->v2->y, other) == floordestheight))
1088         {
1089             return other;
1090         }
1091     }
1092     return NULL;
1093 }
1094 
1095 
1096 //
1097 // P_FindModelCeilingSector()
1098 //
1099 // Passed a ceiling height and a sector number, return a pointer to a
1100 // a sector with that ceiling height across the lowest numbered two sided
1101 // line surrounding the sector.
1102 //
1103 // Note: If no sector at that height bounds the sector passed, return NULL
1104 //
1105 // jff 02/03/98 Add routine to find numeric model ceiling
1106 //  around a sector specified by sector number
1107 //  used only from generalized ceiling types
1108 // jff 3/14/98 change first parameter to plain height to allow call
1109 //  from routine not using ceiling_t
1110 // [SL] Changed to use ZDoom 1.23's version of this function to account
1111 // for sloped sectors.
1112 //
P_FindModelCeilingSector(fixed_t ceildestheight,int secnum)1113 sector_t *P_FindModelCeilingSector (fixed_t ceildestheight, int secnum)
1114 {
1115 	sector_t *other, *sec = &sectors[secnum];
1116 
1117     //jff 5/23/98 don't disturb sec->linecount while searching
1118     // but allow early exit in old demos
1119     for (int i = 0; i < sec->linecount; i++)
1120     {
1121         other = getNextSector(sec->lines[i], sec);
1122         if (other != NULL &&
1123         	(P_CeilingHeight(sec->lines[i]->v1->x, sec->lines[i]->v1->y, other) == ceildestheight ||
1124         	 P_CeilingHeight(sec->lines[i]->v2->x, sec->lines[i]->v2->y, other) == ceildestheight))
1125         {
1126             return other;
1127         }
1128     }
1129     return NULL;
1130 }
1131 
1132 
1133 //
1134 // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
1135 //
1136 
1137 // Find the next sector with a specified tag.
1138 // Rewritten by Lee Killough to use chained hashing to improve speed
1139 
P_FindSectorFromTag(int tag,int start)1140 int P_FindSectorFromTag (int tag, int start)
1141 {
1142 	start = start >= 0 ? sectors[start].nexttag :
1143 		sectors[(unsigned) tag % (unsigned) numsectors].firsttag;
1144 	while (start >= 0 && sectors[start].tag != tag)
1145 		start = sectors[start].nexttag;
1146 	return start;
1147 }
1148 
1149 // killough 4/16/98: Same thing, only for linedefs
1150 
P_FindLineFromID(int id,int start)1151 int P_FindLineFromID (int id, int start)
1152 {
1153 	start = start >= 0 ? lines[start].nextid :
1154 		lines[(unsigned) id % (unsigned) numlines].firstid;
1155 	while (start >= 0 && lines[start].id != id)
1156 		start = lines[start].nextid;
1157 	return start;
1158 }
1159 
1160 // Hash the sector tags across the sectors and linedefs.
P_InitTagLists(void)1161 static void P_InitTagLists (void)
1162 {
1163 	register int i;
1164 
1165 	for (i=numsectors; --i>=0; )		// Initially make all slots empty.
1166 		sectors[i].firsttag = -1;
1167 	for (i=numsectors; --i>=0; )		// Proceed from last to first sector
1168 	{									// so that lower sectors appear first
1169 		int j = (unsigned) sectors[i].tag % (unsigned) numsectors;	// Hash func
1170 		sectors[i].nexttag = sectors[j].firsttag;	// Prepend sector to chain
1171 		sectors[j].firsttag = i;
1172 	}
1173 
1174 	// killough 4/17/98: same thing, only for linedefs
1175 
1176 	for (i=numlines; --i>=0; )			// Initially make all slots empty.
1177 		lines[i].firstid = -1;
1178 	for (i=numlines; --i>=0; )        // Proceed from last to first linedef
1179 	{									// so that lower linedefs appear first
1180 		int j = (unsigned) lines[i].id % (unsigned) numlines;	// Hash func
1181 		lines[i].nextid = lines[j].firstid;	// Prepend linedef to chain
1182 		lines[j].firstid = i;
1183 	}
1184 }
1185 
1186 
1187 //
1188 // Find minimum light from an adjacent sector
1189 //
P_FindMinSurroundingLight(sector_t * sector,int max)1190 int P_FindMinSurroundingLight (sector_t *sector, int max)
1191 {
1192 	int 		i;
1193 	int 		min;
1194 	line_t* 	line;
1195 	sector_t*	check;
1196 
1197 	min = max;
1198 	for (i=0 ; i < sector->linecount ; i++)
1199 	{
1200 		line = sector->lines[i];
1201 		check = getNextSector(line,sector);
1202 
1203 		if (!check)
1204 			continue;
1205 
1206 		if (check->lightlevel < min)
1207 			min = check->lightlevel;
1208 	}
1209 	return min;
1210 }
1211 
1212 // [RH] P_CheckKeys
1213 //
1214 //	Returns true if the player has the desired key,
1215 //	false otherwise.
1216 
P_CheckKeys(player_t * p,card_t lock,BOOL remote)1217 BOOL P_CheckKeys (player_t *p, card_t lock, BOOL remote)
1218 {
1219 	if ((lock & 0x7f) == NoKey)
1220 		return true;
1221 
1222 	if (!p)
1223 		return false;
1224 
1225 	int msg = 0;
1226 	BOOL bc, rc, yc, bs, rs, ys;
1227 	BOOL equiv = lock & 0x80;
1228 
1229         lock = (card_t)(lock & 0x7f);
1230 
1231 	bc = p->cards[it_bluecard];
1232 	rc = p->cards[it_redcard];
1233 	yc = p->cards[it_yellowcard];
1234 	bs = p->cards[it_blueskull];
1235 	rs = p->cards[it_redskull];
1236 	ys = p->cards[it_yellowskull];
1237 
1238 	if (equiv) {
1239 		bc = bs = (bc || bs);
1240 		rc = rs = (rc || rs);
1241 		yc = ys = (yc || ys);
1242 	}
1243 
1244 	switch (lock) {
1245 		default:		// Unknown key; assume we have it
1246 			return true;
1247 
1248 		case AnyKey:
1249 			if (bc || bs || rc || rs || yc || ys)
1250 				return true;
1251 			msg = PD_ANY;
1252 			break;
1253 
1254 		case AllKeys:
1255 			if (bc && bs && rc && rs && yc && ys)
1256 				return true;
1257 			msg = equiv ? PD_ALL3 : PD_ALL6;
1258 			break;
1259 
1260 		case RCard:
1261 			if (rc)
1262 				return true;
1263 			msg = equiv ? (remote ? PD_REDO : PD_REDK) : PD_REDC;
1264 			break;
1265 
1266 		case BCard:
1267 			if (bc)
1268 				return true;
1269 			msg = equiv ? (remote ? PD_BLUEO : PD_BLUEK) : PD_BLUEC;
1270 			break;
1271 
1272 		case YCard:
1273 			if (yc)
1274 				return true;
1275 			msg = equiv ? (remote ? PD_YELLOWO : PD_YELLOWK) : PD_YELLOWC;
1276 			break;
1277 
1278 		case RSkull:
1279 			if (rs)
1280 				return true;
1281 			msg = equiv ? (remote ? PD_REDO : PD_REDK) : PD_REDS;
1282 			break;
1283 
1284 		case BSkull:
1285 			if (bs)
1286 				return true;
1287 			msg = equiv ? (remote ? PD_BLUEO : PD_BLUEK) : PD_BLUES;
1288 			break;
1289 
1290 		case YSkull:
1291 			if (ys)
1292 				return true;
1293 			msg = equiv ? (remote ? PD_YELLOWO : PD_YELLOWK) : PD_YELLOWS;
1294 			break;
1295 	}
1296 
1297 	// If we get here, we don't have the right key,
1298 	// so print an appropriate message and grunt.
1299 	if (p->mo == consoleplayer().camera)
1300 	{
1301 		int keytrysound = S_FindSound ("misc/keytry");
1302 		if (keytrysound > -1)
1303 			UV_SoundAvoidPlayer (p->mo, CHAN_VOICE, "misc/keytry", ATTN_NORM);
1304 		else
1305 			UV_SoundAvoidPlayer (p->mo, CHAN_VOICE, "player/male/grunt1", ATTN_NORM);
1306 		C_MidPrint (GStrings(msg), p);
1307 	}
1308 
1309 	if (serverside && network_game && msg)
1310 	{
1311 		C_MidPrint (GStrings(msg), p);
1312 	}
1313 
1314 	return false;
1315 }
1316 
1317 void OnChangedSwitchTexture (line_t *line, int useAgain);
1318 void OnActivatedLine (line_t *line, AActor *mo, int side, int activationType);
1319 
1320 //
1321 // EVENTS
1322 // Events are operations triggered by using, crossing,
1323 // or shooting special lines, or by timed thinkers.
1324 //
1325 
1326 //
1327 // P_HandleSpecialRepeat
1328 //
1329 // If a line's special function is not supposed to be repeatable,
1330 // remove the line special function from the line. This takes
1331 // into account special circumstances like exit specials that are
1332 // supposed to frag the triggering player during online play.
1333 //
P_HandleSpecialRepeat(line_t * line)1334 void P_HandleSpecialRepeat(line_t* line)
1335 {
1336 	// [SL] Don't remove specials from fragging exit line specials
1337 	if ((line->special == Exit_Normal || line->special == Exit_Secret ||
1338 		 line->special == Teleport_EndGame || line->special == Teleport_NewMap) &&
1339 		(!sv_allowexit && sv_fragexitswitch))
1340 		return;
1341 
1342 	if (!(line->flags & ML_REPEAT_SPECIAL))
1343 		line->special = 0;
1344 }
1345 
1346 
1347 //
1348 // P_CrossSpecialLine - TRIGGER
1349 // Called every time a thing origin is about
1350 //  to cross a line with a non 0 special.
1351 //
1352 void
P_CrossSpecialLine(int linenum,int side,AActor * thing,bool FromServer)1353 P_CrossSpecialLine
1354 ( int		linenum,
1355   int		side,
1356   AActor*	thing,
1357   bool      FromServer)
1358 {
1359     line_t*	line = &lines[linenum];
1360 
1361 	if (!P_CanActivateSpecials(line))
1362 		return;
1363 
1364 	if(thing)
1365 	{
1366 		//	Triggers that other things can activate
1367 		if (!thing->player)
1368 		{
1369 		    if (!(GET_SPAC(line->flags) == SPAC_CROSS)
1370                 && !(GET_SPAC(line->flags) == SPAC_MCROSS))
1371                 return;
1372 
1373 			// Things that should NOT trigger specials...
1374 			switch(thing->type)
1375 			{
1376 				case MT_ROCKET:
1377 				case MT_PLASMA:
1378 				case MT_BFG:
1379 				case MT_TROOPSHOT:
1380 				case MT_HEADSHOT:
1381 				case MT_BRUISERSHOT:
1382 					return;
1383 					break;
1384 
1385 				default: break;
1386 			}
1387 
1388             // This breaks the ability for the eyes to activate the silent teleporter lines
1389             // in boomedit.wad, but without it vanilla demos break.
1390             switch (line->special)
1391             {
1392 				case Teleport:
1393 				case Teleport_NoFog:
1394 				case Teleport_Line:
1395 				break;
1396 
1397                 default:
1398                     if(!(line->flags & ML_MONSTERSCANACTIVATE))
1399                         return;
1400                 break;
1401             }
1402 
1403 		}
1404 		else
1405 		{
1406 		    if (!(GET_SPAC(line->flags) == SPAC_CROSS) &&
1407                 !(GET_SPAC(line->flags) == SPAC_CROSSTHROUGH))
1408                 return;
1409 
1410 			// Likewise, player should not trigger monster lines
1411 			if(GET_SPAC(line->flags) == SPAC_MCROSS)
1412 				return;
1413 
1414 			// And spectators should only trigger teleporters
1415 			if (thing->player->spectator)
1416 			{
1417 				switch (line->special)
1418 				{
1419 					case Teleport:
1420 					case Teleport_NoFog:
1421 					case Teleport_NewMap:
1422 					case Teleport_EndGame:
1423 					case Teleport_Line:
1424 						break;
1425 					default:
1426 						return;
1427 						break;
1428 				}
1429 			}
1430 		}
1431 
1432 		// Do not teleport on the wrong side
1433 		if(side)
1434 		{
1435 			switch(line->special)
1436 			{
1437 				case Teleport:
1438 				case Teleport_NoFog:
1439 				case Teleport_NewMap:
1440 				case Teleport_EndGame:
1441 				case Teleport_Line:
1442 					return;
1443 					break;
1444 				default: break;
1445 			}
1446 		}
1447 	}
1448 
1449 	TeleportSide = side;
1450 
1451 	LineSpecials[line->special] (line, thing, line->args[0],
1452 					line->args[1], line->args[2],
1453 					line->args[3], line->args[4]);
1454 
1455 	P_HandleSpecialRepeat(line);
1456 
1457 	OnActivatedLine(line, thing, side, 0);
1458 }
1459 
1460 //
1461 // P_ShootSpecialLine - IMPACT SPECIALS
1462 // Called when a thing shoots a special line.
1463 //
1464 void
P_ShootSpecialLine(AActor * thing,line_t * line,bool FromServer)1465 P_ShootSpecialLine
1466 ( AActor*	thing,
1467   line_t*	line,
1468   bool      FromServer)
1469 {
1470 	if (!P_CanActivateSpecials(line))
1471 		return;
1472 
1473 	if(thing)
1474 	{
1475 		if (!(GET_SPAC(line->flags) == SPAC_IMPACT))
1476 			return;
1477 
1478 		if (thing->flags & MF_MISSILE)
1479 			return;
1480 
1481 		if (!thing->player && !(line->flags & ML_MONSTERSCANACTIVATE))
1482 			return;
1483 	}
1484 
1485 	//TeleportSide = side;
1486 
1487 	LineSpecials[line->special] (line, thing, line->args[0],
1488 					line->args[1], line->args[2],
1489 					line->args[3], line->args[4]);
1490 
1491 	P_HandleSpecialRepeat(line);
1492 
1493 	OnActivatedLine(line, thing, 0, 2);
1494 
1495 	if(serverside)
1496 	{
1497 		P_ChangeSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1498 		OnChangedSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1499 	}
1500 }
1501 
1502 
1503 //
1504 // P_UseSpecialLine
1505 // Called when a thing uses a special line.
1506 // Only the front sides of lines are usable.
1507 //
1508 bool
P_UseSpecialLine(AActor * thing,line_t * line,int side,bool FromServer)1509 P_UseSpecialLine
1510 ( AActor*	thing,
1511   line_t*	line,
1512   int		side,
1513   bool      FromServer)
1514 {
1515 	if (!P_CanActivateSpecials(line))
1516 		return false;
1517 
1518 	// Err...
1519 	// Use the back sides of VERY SPECIAL lines...
1520 	if (side)
1521 	{
1522 		switch(line->special)
1523 		{
1524 		case Exit_Secret:
1525 			// Sliding door open&close
1526 			// UNUSED?
1527 			break;
1528 
1529 		default:
1530 			return false;
1531 			break;
1532 		}
1533 	}
1534 
1535 	if(thing)
1536 	{
1537 		if ((GET_SPAC(line->flags) != SPAC_USE) &&
1538 			(GET_SPAC(line->flags) != SPAC_PUSH) &&
1539             (GET_SPAC(line->flags) != SPAC_USETHROUGH))
1540 			return false;
1541 
1542 		// Switches that other things can activate.
1543 		if (!thing->player)
1544 		{
1545 			// not for monsters?
1546 			if (!(line->flags & ML_MONSTERSCANACTIVATE))
1547 				return false;
1548 
1549 			// never open secret doors
1550 			if (line->flags & ML_SECRET)
1551 				return false;
1552 		}
1553 		else
1554 		{
1555 			// spectators and dead players can't use switches
1556 			if(thing->player->spectator ||
1557                            thing->player->playerstate != PST_LIVE)
1558 				return false;
1559 		}
1560 	}
1561 
1562     TeleportSide = side;
1563 
1564 	if(LineSpecials[line->special] (line, thing, line->args[0],
1565 					line->args[1], line->args[2],
1566 					line->args[3], line->args[4]))
1567 	{
1568 		P_HandleSpecialRepeat(line);
1569 
1570 		OnActivatedLine(line, thing, side, 1);
1571 
1572 		if(serverside && GET_SPAC(line->flags) != SPAC_PUSH)
1573 		{
1574 			P_ChangeSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1575 			OnChangedSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1576 		}
1577 	}
1578 
1579     return true;
1580 }
1581 
1582 
1583 //
1584 // P_PushSpecialLine
1585 // Called when a thing pushes a special line, only in advanced map format
1586 // Only the front sides of lines are pushable.
1587 //
1588 bool
P_PushSpecialLine(AActor * thing,line_t * line,int side,bool FromServer)1589 P_PushSpecialLine
1590 ( AActor*	thing,
1591   line_t*	line,
1592   int		side,
1593   bool      FromServer)
1594 {
1595 	if (!P_CanActivateSpecials(line))
1596 		return false;
1597 
1598 	// Err...
1599 	// Use the back sides of VERY SPECIAL lines...
1600 	if (side)
1601 		return false;
1602 
1603 	if(thing)
1604 	{
1605 		if (GET_SPAC(line->flags) != SPAC_PUSH)
1606 			return false;
1607 
1608 		// Switches that other things can activate.
1609 		if (!thing->player)
1610 		{
1611 			// not for monsters?
1612 			if (!(line->flags & ML_MONSTERSCANACTIVATE))
1613 				return false;
1614 
1615 			// never open secret doors
1616 			if (line->flags & ML_SECRET)
1617 				return false;
1618 		}
1619 		else
1620 		{
1621 			// spectators and dead players can't push walls
1622 			if(thing->player->spectator ||
1623                            thing->player->playerstate != PST_LIVE)
1624 				return false;
1625 		}
1626 	}
1627 
1628     TeleportSide = side;
1629 
1630 	if(LineSpecials[line->special] (line, thing, line->args[0],
1631 					line->args[1], line->args[2],
1632 					line->args[3], line->args[4]))
1633 	{
1634 		P_HandleSpecialRepeat(line);
1635 
1636 		OnActivatedLine(line, thing, side, 3);
1637 
1638 		if(serverside)
1639 		{
1640 			P_ChangeSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1641 			OnChangedSwitchTexture (line, line->flags & ML_REPEAT_SPECIAL);
1642 		}
1643 	}
1644 
1645     return true;
1646 }
1647 
1648 
1649 
1650 //
1651 // P_PlayerInSpecialSector
1652 // Called every tic frame
1653 //	that the player origin is in a special sector
1654 //
P_PlayerInSpecialSector(player_t * player)1655 void P_PlayerInSpecialSector (player_t *player)
1656 {
1657 	// Spectators should not be affected by special sectors
1658 	if (player->spectator)
1659 		return;
1660 
1661 	sector_t *sector = player->mo->subsector->sector;
1662 	int special = sector->special & ~SECRET_MASK;
1663 
1664 	// Falling, not all the way down yet?
1665 	if (player->mo->z != P_FloorHeight(player->mo) && !player->mo->waterlevel)
1666 		return;
1667 
1668 	// Has hitten ground.
1669 	// [RH] Normal DOOM special or BOOM specialized?
1670 	if (special >= dLight_Flicker && special <= 255)
1671 	{
1672 		switch (special)
1673 		{
1674 		  case Damage_InstantDeath:
1675 			P_DamageMobj (player->mo, NULL, NULL, 999, MOD_UNKNOWN);
1676 			break;
1677 
1678 		  case dDamage_Hellslime:
1679 			// HELLSLIME DAMAGE
1680 			if (!player->powers[pw_ironfeet])
1681 				if (!(level.time&0x1f))
1682 					P_DamageMobj (player->mo, NULL, NULL, 10, MOD_SLIME);
1683 			break;
1684 
1685 		  case dDamage_Nukage:
1686 			// NUKAGE DAMAGE
1687 			if (!player->powers[pw_ironfeet])
1688 				if (!(level.time&0x1f))
1689 					P_DamageMobj (player->mo, NULL, NULL, 5, MOD_LAVA);
1690 			break;
1691 
1692 		  case hDamage_Sludge:
1693 			if (!player->powers[pw_ironfeet] && !(level.time&0x1f))
1694 				P_DamageMobj (player->mo, NULL, NULL, 4, MOD_SLIME);
1695 			break;
1696 
1697 		  case dDamage_SuperHellslime:
1698 			// SUPER HELLSLIME DAMAGE
1699 		  case dLight_Strobe_Hurt:
1700 			// STROBE HURT
1701 			if (!player->powers[pw_ironfeet]
1702 				|| (P_Random ()<5) )
1703 			{
1704 				if (!(level.time&0x1f))
1705 					P_DamageMobj (player->mo, NULL, NULL, 20, MOD_SLIME);
1706 			}
1707 			break;
1708 
1709 		  case dDamage_End:
1710 			// EXIT SUPER DAMAGE! (for E1M8 finale)
1711 			player->cheats &= ~CF_GODMODE;
1712 
1713 			if (!(level.time & 0x1f))
1714 				P_DamageMobj (player->mo, NULL, NULL, 20, MOD_UNKNOWN);
1715 
1716 			if(sv_gametype == GM_COOP || sv_allowexit)
1717 			{
1718 				if (gamestate == GS_LEVEL && player->health <= 10)
1719 					G_ExitLevel(0, 1);
1720 			}
1721 			break;
1722 
1723 		  case dDamage_LavaWimpy:
1724 		  case dScroll_EastLavaDamage:
1725 			if (!(level.time & 15))
1726 				P_DamageMobj(player->mo, NULL, NULL, 5, MOD_LAVA);
1727 
1728 			break;
1729 
1730 		  case dDamage_LavaHefty:
1731 			if(!(level.time & 15))
1732 				P_DamageMobj(player->mo, NULL, NULL, 8, MOD_LAVA);
1733 
1734 			break;
1735 
1736 		  default:
1737 			// [RH] Ignore unknown specials
1738 			break;
1739 		}
1740 	}
1741 	else
1742 	{
1743 		//jff 3/14/98 handle extended sector types for secrets and damage
1744 		switch (special & DAMAGE_MASK) {
1745 			case 0x000: // no damage
1746 				break;
1747 			case 0x100: // 2/5 damage per 31 ticks
1748 				if (!player->powers[pw_ironfeet])
1749 					if (!(level.time&0x1f))
1750 						P_DamageMobj (player->mo, NULL, NULL, 5, MOD_LAVA);
1751 				break;
1752 			case 0x200: // 5/10 damage per 31 ticks
1753 				if (!player->powers[pw_ironfeet])
1754 					if (!(level.time&0x1f))
1755 						P_DamageMobj (player->mo, NULL, NULL, 10, MOD_SLIME);
1756 				break;
1757 			case 0x300: // 10/20 damage per 31 ticks
1758 				if (!player->powers[pw_ironfeet]
1759 					|| (P_Random(player->mo)<5))	// take damage even with suit
1760 				{
1761 					if (!(level.time&0x1f))
1762 						P_DamageMobj (player->mo, NULL, NULL, 20, MOD_SLIME);
1763 				}
1764 				break;
1765 		}
1766 
1767 		// [RH] Apply any customizable damage
1768 		if (sector->damage) {
1769 			if (sector->damage < 20) {
1770 				if (!player->powers[pw_ironfeet] && !(level.time&0x1f))
1771 					P_DamageMobj (player->mo, NULL, NULL, sector->damage, sector->mod);
1772 			} else if (sector->damage < 50) {
1773 				if ((!player->powers[pw_ironfeet] || (P_Random(player->mo)<5))
1774 					 && !(level.time&0x1f))
1775 					P_DamageMobj (player->mo, NULL, NULL, sector->damage, sector->mod);
1776 			} else {
1777 				P_DamageMobj (player->mo, NULL, NULL, sector->damage, sector->mod);
1778 			}
1779 		}
1780 
1781 		if (sector->special & SECRET_MASK) {
1782 			player->secretcount++;
1783 			level.found_secrets++;
1784 			sector->special &= ~SECRET_MASK;
1785 			if (player->mo == consoleplayer().camera)
1786 				C_RevealSecret();
1787 		}
1788 	}
1789 }
1790 
1791 //
1792 // P_UpdateSpecials
1793 // Animate planes, scroll walls, etc.
1794 //
1795 
P_UpdateSpecials(void)1796 void P_UpdateSpecials (void)
1797 {
1798 	anim_t *anim;
1799 	int i;
1800 
1801 	// ANIMATE FLATS AND TEXTURES GLOBALLY
1802 	// [RH] Changed significantly to work with ANIMDEFS lumps
1803 	for (anim = anims; anim < lastanim; anim++)
1804 	{
1805 		if (--anim->countdown == 0)
1806 		{
1807 			int speedframe;
1808 
1809 			anim->curframe = (anim->numframes) ?
1810 					(anim->curframe + 1) % anim->numframes : 0;
1811 
1812 			speedframe = (anim->uniqueframes) ? anim->curframe : 0;
1813 
1814 			if (anim->speedmin[speedframe] == anim->speedmax[speedframe])
1815 				anim->countdown = anim->speedmin[speedframe];
1816 			else
1817 				anim->countdown = M_Random() %
1818 					(anim->speedmax[speedframe] - anim->speedmin[speedframe]) +
1819 					anim->speedmin[speedframe];
1820 		}
1821 
1822 		if (anim->uniqueframes)
1823 		{
1824 			int pic = anim->framepic[anim->curframe];
1825 
1826 			if (anim->istexture)
1827 				for (i = 0; i < anim->numframes; i++)
1828 					texturetranslation[anim->framepic[i]] = pic;
1829 			else
1830 				for (i = 0; i < anim->numframes; i++)
1831 					flattranslation[anim->framepic[i]] = pic;
1832 		}
1833 		else
1834 		{
1835 			for (i = anim->basepic; i < anim->basepic + anim->numframes; i++)
1836 			{
1837 				int pic = anim->basepic + (anim->curframe + i) % anim->numframes;
1838 
1839 				if (anim->istexture)
1840 					texturetranslation[i] = pic;
1841 				else
1842 					flattranslation[i] = pic;
1843 			}
1844 		}
1845 	}
1846 
1847 	// [ML] 5/11/06 - Remove sky scrolling ability
1848 }
1849 
1850 
1851 
1852 //
1853 // SPECIAL SPAWNING
1854 //
1855 
CVAR_FUNC_IMPL(sv_forcewater)1856 CVAR_FUNC_IMPL (sv_forcewater)
1857 {
1858 	if (gamestate == GS_LEVEL)
1859 	{
1860 		int i;
1861 		byte set = var ? 2 : 0;
1862 
1863 		for (i = 0; i < numsectors; i++)
1864 		{
1865 			if (sectors[i].heightsec &&
1866 				!(sectors[i].heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) &&
1867 				sectors[i].heightsec->waterzone != 1)
1868 
1869 				sectors[i].heightsec->waterzone = set;
1870 		}
1871 	}
1872 }
1873 
1874 //
1875 // P_SpawnSpecials
1876 // After the map has been loaded, scan for specials
1877 //	that spawn thinkers
1878 //
1879 
P_SpawnSpecials(void)1880 void P_SpawnSpecials (void)
1881 {
1882 	sector_t*	sector;
1883 	int 		i;
1884 
1885 	//	Init special SECTORs.
1886 	sector = sectors;
1887 	for (i = 0; i < numsectors; i++, sector++)
1888 	{
1889 		if (!sector->special)
1890 			continue;
1891 
1892 		// [RH] All secret sectors are marked with a BOOM-ish bitfield
1893 		if (sector->special & SECRET_MASK)
1894 			level.total_secrets++;
1895 
1896 		switch (sector->special & 0xff)
1897 		{
1898 			// [RH] Normal DOOM/Hexen specials. We clear off the special for lights
1899 			//	  here instead of inside the spawners.
1900 
1901 		case dLight_Flicker:
1902 			// FLICKERING LIGHTS
1903 			new DLightFlash (sector);
1904 			sector->special &= 0xff00;
1905 			break;
1906 
1907 		case dLight_StrobeFast:
1908 			// STROBE FAST
1909 			new DStrobe (sector, STROBEBRIGHT, FASTDARK, false);
1910 			sector->special &= 0xff00;
1911 			break;
1912 
1913 		case dLight_StrobeSlow:
1914 			// STROBE SLOW
1915 			new DStrobe (sector, STROBEBRIGHT, SLOWDARK, false);
1916 			sector->special &= 0xff00;
1917 			break;
1918 
1919 		case dLight_Strobe_Hurt:
1920 			// STROBE FAST/DEATH SLIME
1921 			new DStrobe (sector, STROBEBRIGHT, FASTDARK, false);
1922 			break;
1923 
1924 		case dLight_Glow:
1925 			// GLOWING LIGHT
1926 			new DGlow (sector);
1927 			sector->special &= 0xff00;
1928 			break;
1929 
1930 		case dSector_DoorCloseIn30:
1931 			// DOOR CLOSE IN 30 SECONDS
1932 			P_SpawnDoorCloseIn30 (sector);
1933 			break;
1934 
1935 		case dLight_StrobeSlowSync:
1936 			// SYNC STROBE SLOW
1937 			new DStrobe (sector, STROBEBRIGHT, SLOWDARK, true);
1938 			sector->special &= 0xff00;
1939 			break;
1940 
1941 		case dLight_StrobeFastSync:
1942 			// SYNC STROBE FAST
1943 			new DStrobe (sector, STROBEBRIGHT, FASTDARK, true);
1944 			sector->special &= 0xff00;
1945 			break;
1946 
1947 		case dSector_DoorRaiseIn5Mins:
1948 			// DOOR RAISE IN 5 MINUTES
1949 			P_SpawnDoorRaiseIn5Mins (sector);
1950 			break;
1951 
1952 		case dLight_FireFlicker:
1953 			// fire flickering
1954 			new DFireFlicker (sector);
1955 			sector->special &= 0xff00;
1956 			break;
1957 
1958 		  // [RH] Hexen-like phased lighting
1959 		case LightSequenceStart:
1960 			new DPhased (sector);
1961 			break;
1962 
1963 		case Light_Phased:
1964 			new DPhased (sector, 48, 63 - (sector->lightlevel & 63));
1965 			break;
1966 
1967 		case Sky2:
1968 			sector->sky = PL_SKYFLAT;
1969 			break;
1970 
1971 		default:
1972 			// [RH] Try for normal Hexen scroller
1973 			if ((sector->special & 0xff) >= Scroll_North_Slow &&
1974 				(sector->special & 0xff) <= Scroll_SouthWest_Fast)
1975 			{
1976 				static char hexenScrollies[24][2] =
1977 				{
1978 					{  0,  1 }, {  0,  2 }, {  0,  4 },
1979 					{ -1,  0 }, { -2,  0 }, { -4,  0 },
1980 					{  0, -1 }, {  0, -2 }, {  0, -4 },
1981 					{  1,  0 }, {  2,  0 }, {  4,  0 },
1982 					{  1,  1 }, {  2,  2 }, {  4,  4 },
1983 					{ -1,  1 }, { -2,  2 }, { -4,  4 },
1984 					{ -1, -1 }, { -2, -2 }, { -4, -4 },
1985 					{  1, -1 }, {  2, -2 }, {  4, -4 }
1986 				};
1987 				int i = (sector->special & 0xff) - Scroll_North_Slow;
1988 				int dx = hexenScrollies[i][0] * (FRACUNIT/2);
1989 				int dy = hexenScrollies[i][1] * (FRACUNIT/2);
1990 
1991 				new DScroller (DScroller::sc_floor, dx, dy, -1, sector-sectors, 0);
1992 				// Hexen scrolling floors cause the player to move
1993 				// faster than they scroll. I do this just for compatibility
1994 				// with Hexen and recommend using Killough's more-versatile
1995 				// scrollers instead.
1996 				dx = FixedMul (-dx, CARRYFACTOR*2);
1997 				dy = FixedMul (dy, CARRYFACTOR*2);
1998 				new DScroller (DScroller::sc_carry, dx, dy, -1, sector-sectors, 0);
1999 				sector->special &= 0xff00;
2000 			}
2001 		break;
2002 		}
2003 	}
2004 
2005 	// Init other misc stuff
2006 
2007 	// P_InitTagLists() must be called before P_FindSectorFromTag()
2008 	// or P_FindLineFromID() can be called.
2009 
2010 	P_InitTagLists();   // killough 1/30/98: Create xref tables for tags
2011 	P_SpawnScrollers(); // killough 3/7/98: Add generalized scrollers
2012 	P_SpawnFriction();	// phares 3/12/98: New friction model using linedefs
2013 	P_SpawnPushers();	// phares 3/20/98: New pusher model using linedefs
2014 
2015 	for (i=0; i<numlines; i++)
2016 		switch (lines[i].special)
2017 		{
2018 			int s;
2019 			sector_t *sec;
2020 
2021 		// killough 3/7/98:
2022 		// support for drawn heights coming from different sector
2023 		case Transfer_Heights:
2024 			sec = sides[*lines[i].sidenum].sector;
2025 			DPrintf("Sector tagged %d: TransferHeights \n",sec->tag);
2026 			if (sv_forcewater)
2027 			{
2028 				sec->waterzone = 2;
2029 			}
2030 			if (lines[i].args[1] & 2)
2031 			{
2032 				sec->MoreFlags |= SECF_FAKEFLOORONLY;
2033 			}
2034 			if (lines[i].args[1] & 4)
2035 			{
2036 				sec->MoreFlags |= SECF_CLIPFAKEPLANES;
2037 				DPrintf("Sector tagged %d: CLIPFAKEPLANES \n",sec->tag);
2038 			}
2039 			if (lines[i].args[1] & 8)
2040 			{
2041 				sec->waterzone = 1;
2042 				DPrintf("Sector tagged %d: Sets waterzone=1 \n",sec->tag);
2043 			}
2044 			if (lines[i].args[1] & 16)
2045 			{
2046 				sec->MoreFlags |= SECF_IGNOREHEIGHTSEC;
2047 				DPrintf("Sector tagged %d: IGNOREHEIGHTSEC \n",sec->tag);
2048 			}
2049 			if (lines[i].args[1] & 32)
2050 			{
2051 				sec->MoreFlags |= SECF_NOFAKELIGHT;
2052 				DPrintf("Sector tagged %d: NOFAKELIGHTS \n",sec->tag);
2053 			}
2054 			for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2055 			{
2056 				sectors[s].heightsec = sec;
2057 			}
2058 
2059 			DPrintf("Sector tagged %d: MoreFlags: %u \n",sec->tag,sec->MoreFlags);
2060 			break;
2061 
2062 		// killough 3/16/98: Add support for setting
2063 		// floor lighting independently (e.g. lava)
2064 		case Transfer_FloorLight:
2065 			sec = sides[*lines[i].sidenum].sector;
2066 			for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2067 				sectors[s].floorlightsec = sec;
2068 			break;
2069 
2070 		// killough 4/11/98: Add support for setting
2071 		// ceiling lighting independently
2072 		case Transfer_CeilingLight:
2073 			sec = sides[*lines[i].sidenum].sector;
2074 			for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2075 				sectors[s].ceilinglightsec = sec;
2076 			break;
2077 
2078 		// [RH] ZDoom Static_Init settings
2079 		case Static_Init:
2080 			switch (lines[i].args[1])
2081 			{
2082 			case Init_Gravity:
2083 				{
2084 				float grav = ((float)P_AproxDistance (lines[i].dx, lines[i].dy)) / (FRACUNIT * 100.0f);
2085 				for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2086 					sectors[s].gravity = grav;
2087 				}
2088 				break;
2089 
2090 			//case Init_Color:
2091 			// handled in P_LoadSideDefs2()
2092 
2093 			case Init_Damage:
2094 				{
2095 					int damage = P_AproxDistance (lines[i].dx, lines[i].dy) >> FRACBITS;
2096 					for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2097 					{
2098 						sectors[s].damage = damage;
2099 						sectors[s].mod = MOD_UNKNOWN;
2100 					}
2101 				}
2102 				break;
2103 
2104 			// killough 10/98:
2105 			//
2106 			// Support for sky textures being transferred from sidedefs.
2107 			// Allows scrolling and other effects (but if scrolling is
2108 			// used, then the same sector tag needs to be used for the
2109 			// sky sector, the sky-transfer linedef, and the scroll-effect
2110 			// linedef). Still requires user to use F_SKY1 for the floor
2111 			// or ceiling texture, to distinguish floor and ceiling sky.
2112 
2113 			case Init_TransferSky:
2114 				for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0],s)) >= 0;)
2115 					sectors[s].sky = (i+1) | PL_SKYFLAT;
2116 				break;
2117 			}
2118 			break;
2119 		}
2120 
2121 
2122 	// [RH] Start running any open scripts on this map
2123 	if (level.behavior != NULL)
2124 	{
2125 		level.behavior->StartTypedScripts (SCRIPT_Open, NULL);
2126 	}
2127 }
2128 
2129 // killough 2/28/98:
2130 //
2131 // This function, with the help of r_plane.c and r_bsp.c, supports generalized
2132 // scrolling floors and walls, with optional mobj-carrying properties, e.g.
2133 // conveyor belts, rivers, etc. A linedef with a special type affects all
2134 // tagged sectors the same way, by creating scrolling and/or object-carrying
2135 // properties. Multiple linedefs may be used on the same sector and are
2136 // cumulative, although the special case of scrolling a floor and carrying
2137 // things on it, requires only one linedef. The linedef's direction determines
2138 // the scrolling direction, and the linedef's length determines the scrolling
2139 // speed. This was designed so that an edge around the sector could be used to
2140 // control the direction of the sector's scrolling, which is usually what is
2141 // desired.
2142 //
2143 // Process the active scrollers.
2144 //
2145 // This is the main scrolling code
2146 // killough 3/7/98
2147 
RunThink()2148 void DScroller::RunThink ()
2149 {
2150 	fixed_t dx = m_dx, dy = m_dy;
2151 
2152 	if (m_Control != -1)
2153 	{	// compute scroll amounts based on a sector's height changes
2154 		sector_t *sector = &sectors[m_Control];
2155 		fixed_t centerfloor = P_FloorHeight(sector->soundorg[0], sector->soundorg[1], sector);
2156 		fixed_t centerceiling = P_FloorHeight(sector->soundorg[0], sector->soundorg[1], sector);
2157 
2158 		fixed_t height = centerfloor + centerceiling;
2159 		fixed_t delta = height - m_LastHeight;
2160 		m_LastHeight = height;
2161 		dx = FixedMul(dx, delta);
2162 		dy = FixedMul(dy, delta);
2163 	}
2164 
2165 	// killough 3/14/98: Add acceleration
2166 	if (m_Accel)
2167 	{
2168 		m_vdx = (dx += m_vdx);
2169 		m_vdy = (dy += m_vdy);
2170 	}
2171 
2172 	if (!(dx | dy))			// no-op if both (x,y) offsets 0
2173 		return;
2174 
2175 	switch (m_Type)
2176 	{
2177 		side_t *side;
2178 		sector_t *sec;
2179 		fixed_t height, waterheight;	// killough 4/4/98: add waterheight
2180 		msecnode_t *node;
2181 		AActor *thing;
2182 
2183 		case sc_side:				// killough 3/7/98: Scroll wall texture
2184 			side = sides + m_Affectee;
2185 			side->textureoffset += dx;
2186 			side->rowoffset += dy;
2187 			break;
2188 
2189 		case sc_floor:				// killough 3/7/98: Scroll floor texture
2190 			sec = sectors + m_Affectee;
2191 			sec->floor_xoffs += dx;
2192 			sec->floor_yoffs += dy;
2193 			break;
2194 
2195 		case sc_ceiling:			// killough 3/7/98: Scroll ceiling texture
2196 			sec = sectors + m_Affectee;
2197 			sec->ceiling_xoffs += dx;
2198 			sec->ceiling_yoffs += dy;
2199 			break;
2200 
2201 		case sc_carry:
2202 		{
2203 			// killough 3/7/98: Carry things on floor
2204 			// killough 3/20/98: use new sector list which reflects true members
2205 			// killough 3/27/98: fix carrier bug
2206 			// killough 4/4/98: Underwater, carry things even w/o gravity
2207 			sec = sectors + m_Affectee;
2208 			height = P_HighestHeightOfFloor(sec);
2209 			waterheight = sec->heightsec &&
2210 				P_HighestHeightOfFloor(sec->heightsec) > height ?
2211 				P_HighestHeightOfFloor(sec->heightsec) : MININT;
2212 
2213 			for (node = sec->touching_thinglist; node; node = node->m_snext)
2214 				if (!((thing = node->m_thing)->flags & MF_NOCLIP) &&
2215 					(!(thing->flags & MF_NOGRAVITY || thing->z > height) ||
2216 					 thing->z < waterheight))
2217 				  {
2218 					// Move objects only if on floor or underwater,
2219 					// non-floating, and clipped.
2220 					thing->momx += dx;
2221 					thing->momy += dy;
2222 				  }
2223 			break;
2224 		}
2225 
2226 		case sc_carry_ceiling:       // to be added later
2227 			break;
2228 	}
2229 }
2230 
2231 //
2232 // Add_Scroller()
2233 //
2234 // Add a generalized scroller to the thinker list.
2235 //
2236 // type: the enumerated type of scrolling: floor, ceiling, floor carrier,
2237 //   wall, floor carrier & scroller
2238 //
2239 // (dx,dy): the direction and speed of the scrolling or its acceleration
2240 //
2241 // control: the sector whose heights control this scroller's effect
2242 //   remotely, or -1 if no control sector
2243 //
2244 // affectee: the index of the affected object (sector or sidedef)
2245 //
2246 // accel: non-zero if this is an accelerative effect
2247 //
2248 
DScroller(EScrollType type,fixed_t dx,fixed_t dy,int control,int affectee,int accel)2249 DScroller::DScroller (EScrollType type, fixed_t dx, fixed_t dy,
2250 					  int control, int affectee, int accel)
2251 {
2252 	m_Type = type;
2253 	m_dx = dx;
2254 	m_dy = dy;
2255         m_vdx = 0;
2256         m_vdy = 0;
2257         m_Accel = accel;
2258 	if ((m_Control = control) != -1)
2259 	{
2260 		sector_t *sector = &sectors[control];
2261 		fixed_t centerfloor =
2262 			P_FloorHeight(sector->soundorg[0], sector->soundorg[1], sector);
2263 		fixed_t centerceiling =
2264 			P_CeilingHeight(sector->soundorg[0], sector->soundorg[1], sector);
2265 
2266 		m_LastHeight = centerfloor + centerceiling;
2267 	}
2268 	m_Affectee = affectee;
2269 }
2270 
2271 // Adds wall scroller. Scroll amount is rotated with respect to wall's
2272 // linedef first, so that scrolling towards the wall in a perpendicular
2273 // direction is translated into vertical motion, while scrolling along
2274 // the wall in a parallel direction is translated into horizontal motion.
2275 //
2276 // killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff
2277 
DScroller(fixed_t dx,fixed_t dy,const line_t * l,int control,int accel)2278 DScroller::DScroller (fixed_t dx, fixed_t dy, const line_t *l,
2279 					 int control, int accel)
2280 {
2281 	fixed_t x = abs(l->dx), y = abs(l->dy), d;
2282 	if (y > x)
2283 		d = x, x = y, y = d;
2284 	d = FixedDiv (x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90)
2285 						  >> ANGLETOFINESHIFT]);
2286 	x = -FixedDiv (FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d);
2287 	y = -FixedDiv (FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d);
2288 
2289 	m_Type = sc_side;
2290 	m_dx = x;
2291 	m_dy = y;
2292 	m_vdx = m_vdy = 0;
2293 	m_Accel = accel;
2294 
2295 	if ((m_Control = control) != -1)
2296 	{
2297 		sector_t *sector = &sectors[control];
2298 		fixed_t centerfloor =
2299 			P_FloorHeight(sector->soundorg[0], sector->soundorg[1], sector);
2300 		fixed_t centerceiling =
2301 			P_CeilingHeight(sector->soundorg[0], sector->soundorg[1], sector);
2302 
2303 		m_LastHeight = centerfloor + centerceiling;
2304 	}
2305 	m_Affectee = *l->sidenum;
2306 }
2307 
2308 // Amount (dx,dy) vector linedef is shifted right to get scroll amount
2309 #define SCROLL_SHIFT 5
2310 
2311 // Initialize the scrollers
P_SpawnScrollers(void)2312 static void P_SpawnScrollers(void)
2313 {
2314 	int i;
2315 	line_t *l = lines;
2316 
2317 	for (i = 0; i < numlines; i++, l++)
2318 	{
2319 		fixed_t dx = 0;	// direction and speed of scrolling
2320 		fixed_t dy = 0;
2321 		int control = -1, accel = 0;		// no control sector or acceleration
2322 		int special = l->special;
2323 
2324 		// killough 3/7/98: Types 245-249 are same as 250-254 except that the
2325 		// first side's sector's heights cause scrolling when they change, and
2326 		// this linedef controls the direction and speed of the scrolling. The
2327 		// most complicated linedef since donuts, but powerful :)
2328 		//
2329 		// killough 3/15/98: Add acceleration. Types 214-218 are the same but
2330 		// are accelerative.
2331 
2332 		if (special == Scroll_Ceiling ||
2333 			special == Scroll_Floor ||
2334 			special == Scroll_Texture_Model)
2335 		{
2336 			if (l->args[1] & 3)
2337 			{
2338 				// if 1, then displacement
2339 				// if 2, then accelerative (also if 3)
2340 				control = sides[*l->sidenum].sector - sectors;
2341 				if (l->args[1] & 2)
2342 					accel = 1;
2343 			}
2344 			if (special == Scroll_Texture_Model || l->args[1] & 4)
2345 			{
2346 				// The line housing the special controls the
2347 				// direction and speed of scrolling.
2348 				dx = l->dx >> SCROLL_SHIFT;
2349 				dy = l->dy >> SCROLL_SHIFT;
2350 			}
2351 			else
2352 			{
2353 				// The speed and direction are parameters to the special.
2354 				dx = (l->args[3] - 128) * (FRACUNIT / 32);
2355 				dy = (l->args[4] - 128) * (FRACUNIT / 32);
2356 			}
2357 		}
2358 
2359 		switch (special)
2360 		{
2361 			register int s;
2362 
2363 			case Scroll_Ceiling:
2364 				for (s=-1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0;)
2365 					new DScroller (DScroller::sc_ceiling, -dx, dy, control, s, accel);
2366 				break;
2367 
2368 			case Scroll_Floor:
2369 				if (l->args[2] != 1)
2370 					// scroll the floor
2371 					for (s=-1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0;)
2372 						new DScroller (DScroller::sc_floor, -dx, dy, control, s, accel);
2373 
2374 				if (l->args[2] > 0) {
2375 					// carry objects on the floor
2376 					dx = FixedMul(dx,CARRYFACTOR);
2377 					dy = FixedMul(dy,CARRYFACTOR);
2378 					for (s=-1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0;)
2379 						new DScroller (DScroller::sc_carry, dx, dy, control, s, accel);
2380 				}
2381 				break;
2382 
2383 			// killough 3/1/98: scroll wall according to linedef
2384 			// (same direction and speed as scrolling floors)
2385 			case Scroll_Texture_Model:
2386 				for (s=-1; (s = P_FindLineFromID (l->args[0],s)) >= 0;)
2387 					if (s != i)
2388 						new DScroller (dx, dy, lines+s, control, accel);
2389 				break;
2390 
2391 			case Scroll_Texture_Offsets:
2392 				// killough 3/2/98: scroll according to sidedef offsets
2393 				s = lines[i].sidenum[0];
2394 				new DScroller (DScroller::sc_side, -sides[s].textureoffset,
2395 							   sides[s].rowoffset, -1, s, accel);
2396 				break;
2397 
2398 			case Scroll_Texture_Left:
2399 				new DScroller (DScroller::sc_side, l->args[0] * (FRACUNIT/64), 0,
2400 							   -1, lines[i].sidenum[0], accel);
2401 				break;
2402 
2403 			case Scroll_Texture_Right:
2404 				new DScroller (DScroller::sc_side, l->args[0] * (-FRACUNIT/64), 0,
2405 							   -1, lines[i].sidenum[0], accel);
2406 				break;
2407 
2408 			case Scroll_Texture_Up:
2409 				new DScroller (DScroller::sc_side, 0, l->args[0] * (FRACUNIT/64),
2410 							   -1, lines[i].sidenum[0], accel);
2411 				break;
2412 
2413 			case Scroll_Texture_Down:
2414 				new DScroller (DScroller::sc_side, 0, l->args[0] * (-FRACUNIT/64),
2415 							   -1, lines[i].sidenum[0], accel);
2416 				break;
2417 
2418 			case Scroll_Texture_Both:
2419 				if (l->args[0] == 0) {
2420 					dx = (l->args[1] - l->args[2]) * (FRACUNIT/64);
2421 					dy = (l->args[4] - l->args[3]) * (FRACUNIT/64);
2422 					new DScroller (DScroller::sc_side, dx, dy, -1, lines[i].sidenum[0], accel);
2423 				}
2424 				break;
2425 
2426 			default:
2427 				break;
2428 		}
2429 	}
2430 }
2431 
2432 // killough 3/7/98 -- end generalized scroll effects
2433 
2434 ////////////////////////////////////////////////////////////////////////////
2435 //
2436 // FRICTION EFFECTS
2437 //
2438 // phares 3/12/98: Start of friction effects
2439 
2440 // As the player moves, friction is applied by decreasing the x and y
2441 // momentum values on each tic. By varying the percentage of decrease,
2442 // we can simulate muddy or icy conditions. In mud, the player slows
2443 // down faster. In ice, the player slows down more slowly.
2444 //
2445 // The amount of friction change is controlled by the length of a linedef
2446 // with type 223. A length < 100 gives you mud. A length > 100 gives you ice.
2447 //
2448 // Also, each sector where these effects are to take place is given a
2449 // new special type _______. Changing the type value at runtime allows
2450 // these effects to be turned on or off.
2451 //
2452 // Sector boundaries present problems. The player should experience these
2453 // friction changes only when his feet are touching the sector floor. At
2454 // sector boundaries where floor height changes, the player can find
2455 // himself still 'in' one sector, but with his feet at the floor level
2456 // of the next sector (steps up or down). To handle this, Thinkers are used
2457 // in icy/muddy sectors. These thinkers examine each object that is touching
2458 // their sectors, looking for players whose feet are at the same level as
2459 // their floors. Players satisfying this condition are given new friction
2460 // values that are applied by the player movement code later.
2461 
2462 //
2463 // killough 8/28/98:
2464 //
2465 // Completely redid code, which did not need thinkers, and which put a heavy
2466 // drag on CPU. Friction is now a property of sectors, NOT objects inside
2467 // them. All objects, not just players, are affected by it, if they touch
2468 // the sector's floor. Code simpler and faster, only calling on friction
2469 // calculations when an object needs friction considered, instead of doing
2470 // friction calculations on every sector during every tic.
2471 //
2472 // Although this -might- ruin Boom demo sync involving friction, it's the only
2473 // way, short of code explosion, to fix the original design bug. Fixing the
2474 // design bug in Boom's original friction code, while maintaining demo sync
2475 // under every conceivable circumstance, would double or triple code size, and
2476 // would require maintenance of buggy legacy code which is only useful for old
2477 // demos. Doom demos, which are more important IMO, are not affected by this
2478 // change.
2479 //
2480 // [RH] On the other hand, since I've given up on trying to maintain demo
2481 //		sync between versions, these considerations aren't a big deal to me.
2482 //
2483 /////////////////////////////
2484 //
2485 // Initialize the sectors where friction is increased or decreased
2486 
P_SpawnFriction(void)2487 static void P_SpawnFriction(void)
2488 {
2489 	int i;
2490 	line_t *l = lines;
2491 
2492 	for (i = 0 ; i < numlines ; i++,l++)
2493 	{
2494 		if (l->special == Sector_SetFriction)
2495 		{
2496 			int length, s;
2497 			fixed_t friction, movefactor;
2498 
2499 			if (l->args[1])
2500 			{	// [RH] Allow setting friction amount from parameter
2501 				length = l->args[1] <= 200 ? l->args[1] : 200;
2502 			}
2503 			else
2504 			{
2505 				length = P_AproxDistance(l->dx,l->dy)>>FRACBITS;
2506 			}
2507 			friction = (0x1EB8*length)/0x80 + 0xD000;
2508 
2509 			// killough 8/28/98: prevent odd situations
2510 			if (friction > FRACUNIT)
2511 				friction = FRACUNIT;
2512 			if (friction < 0)
2513 				friction = 0;
2514 
2515 			// The following check might seem odd. At the time of movement,
2516 			// the move distance is multiplied by 'friction/0x10000', so a
2517 			// higher friction value actually means 'less friction'.
2518 
2519 			// [RH] Twiddled these values so that momentum on ice (with
2520 			//		friction 0xf900) is the same as in Heretic/Hexen.
2521 			if (friction > ORIG_FRICTION)	// ice
2522 //				movefactor = ((0x10092 - friction)*(0x70))/0x158;
2523 				movefactor = ((0x10092 - friction) * 1024) / 4352 + 568;
2524 			else
2525 				movefactor = ((friction - 0xDB34)*(0xA))/0x80;
2526 
2527 			// killough 8/28/98: prevent odd situations
2528 			if (movefactor < 32)
2529 				movefactor = 32;
2530 
2531 			for (s = -1; (s = P_FindSectorFromTag(l->args[0],s)) >= 0 ; )
2532 			{
2533 				// killough 8/28/98:
2534 				//
2535 				// Instead of spawning thinkers, which are slow and expensive,
2536 				// modify the sector's own friction values. Friction should be
2537 				// a property of sectors, not objects which reside inside them.
2538 				// Original code scanned every object in every friction sector
2539 				// on every tic, adjusting its friction, putting unnecessary
2540 				// drag on CPU. New code adjusts friction of sector only once
2541 				// at level startup, and then uses this friction value.
2542 
2543 				sectors[s].friction = friction;
2544 				sectors[s].movefactor = movefactor;
2545 			}
2546 		}
2547 	}
2548 }
2549 
2550 //
2551 // phares 3/12/98: End of friction effects
2552 //
2553 ////////////////////////////////////////////////////////////////////////////
2554 
2555 ////////////////////////////////////////////////////////////////////////////
2556 //
2557 // PUSH/PULL EFFECT
2558 //
2559 // phares 3/20/98: Start of push/pull effects
2560 //
2561 // This is where push/pull effects are applied to objects in the sectors.
2562 //
2563 // There are four kinds of push effects
2564 //
2565 // 1) Pushing Away
2566 //
2567 //    Pushes you away from a point source defined by the location of an
2568 //    MT_PUSH Thing. The force decreases linearly with distance from the
2569 //    source. This force crosses sector boundaries and is felt w/in a circle
2570 //    whose center is at the MT_PUSH. The force is felt only if the point
2571 //    MT_PUSH can see the target object.
2572 //
2573 // 2) Pulling toward
2574 //
2575 //    Same as Pushing Away except you're pulled toward an MT_PULL point
2576 //    source. This force crosses sector boundaries and is felt w/in a circle
2577 //    whose center is at the MT_PULL. The force is felt only if the point
2578 //    MT_PULL can see the target object.
2579 //
2580 // 3) Wind
2581 //
2582 //    Pushes you in a constant direction. Full force above ground, half
2583 //    force on the ground, nothing if you're below it (water).
2584 //
2585 // 4) Current
2586 //
2587 //    Pushes you in a constant direction. No force above ground, full
2588 //    force if on the ground or below it (water).
2589 //
2590 // The magnitude of the force is controlled by the length of a controlling
2591 // linedef. The force vector for types 3 & 4 is determined by the angle
2592 // of the linedef, and is constant.
2593 //
2594 // For each sector where these effects occur, the sector special type has
2595 // to have the PUSH_MASK bit set. If this bit is turned off by a switch
2596 // at run-time, the effect will not occur. The controlling sector for
2597 // types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing.
2598 
2599 
2600 #define PUSH_FACTOR 7
2601 
2602 /////////////////////////////
2603 //
2604 // Add a push thinker to the thinker list
2605 
DPusher(DPusher::EPusher type,line_t * l,int magnitude,int angle,AActor * source,int affectee)2606 DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle,
2607 				  AActor *source, int affectee)
2608 {
2609 	m_Source = source ? source->ptr() : AActor::AActorPtr();
2610 	m_Type = type;
2611 	if (l)
2612 	{
2613 		m_Xmag = l->dx>>FRACBITS;
2614 		m_Ymag = l->dy>>FRACBITS;
2615 		m_Magnitude = P_AproxDistance (m_Xmag, m_Ymag);
2616 	}
2617 	else
2618 	{ // [RH] Allow setting magnitude and angle with parameters
2619 		ChangeValues (magnitude, angle);
2620 	}
2621 	if (source) // point source exist?
2622 	{
2623 		m_Radius = (m_Magnitude) << (FRACBITS+1); // where force goes to zero
2624 		m_X = m_Source->x;
2625 		m_Y = m_Source->y;
2626 	}
2627 	m_Affectee = affectee;
2628 }
2629 
2630 /////////////////////////////
2631 //
2632 // PIT_PushThing determines the angle and magnitude of the effect.
2633 // The object's x and y momentum values are changed.
2634 //
2635 // tmpusher belongs to the point source (MT_PUSH/MT_PULL).
2636 //
2637 
2638 DPusher *tmpusher; // pusher structure for blockmap searches
2639 
PIT_PushThing(AActor * thing)2640 BOOL PIT_PushThing (AActor *thing)
2641 {
2642 	if (thing->player &&
2643 		!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)))
2644 	{
2645 		int sx = tmpusher->m_X;
2646 		int sy = tmpusher->m_Y;
2647 		int dist = P_AproxDistance (thing->x - sx,thing->y - sy);
2648 		int speed = (tmpusher->m_Magnitude -
2649 					((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1);
2650 
2651 		// If speed <= 0, you're outside the effective radius. You also have
2652 		// to be able to see the push/pull source point.
2653 
2654 		if (speed > 0 && P_CheckSight(thing, tmpusher->m_Source))
2655 		{
2656 			angle_t pushangle = P_PointToAngle (thing->x, thing->y, sx, sy);
2657 			if (tmpusher->m_Source->type == MT_PUSH)
2658 				pushangle += ANG180;    // away
2659 			pushangle >>= ANGLETOFINESHIFT;
2660 			thing->momx += FixedMul (speed, finecosine[pushangle]);
2661 			thing->momy += FixedMul (speed, finesine[pushangle]);
2662 		}
2663 	}
2664 	return true;
2665 }
2666 
2667 /////////////////////////////
2668 //
2669 // T_Pusher looks for all objects that are inside the radius of
2670 // the effect.
2671 //
2672 extern fixed_t tmbbox[4];
2673 
RunThink()2674 void DPusher::RunThink ()
2675 {
2676 	sector_t *sec;
2677 	AActor *thing;
2678 	msecnode_t *node;
2679 	int xspeed,yspeed;
2680 	int xl,xh,yl,yh,bx,by;
2681 	int radius;
2682 	int ht = 0;
2683 
2684 	sec = sectors + m_Affectee;
2685 
2686 	// Be sure the special sector type is still turned on. If so, proceed.
2687 	// Else, bail out; the sector type has been changed on us.
2688 
2689 	if (!(sec->special & PUSH_MASK))
2690 		return;
2691 
2692 	// For constant pushers (wind/current) there are 3 situations:
2693 	//
2694 	// 1) Affected Thing is above the floor.
2695 	//
2696 	//    Apply the full force if wind, no force if current.
2697 	//
2698 	// 2) Affected Thing is on the ground.
2699 	//
2700 	//    Apply half force if wind, full force if current.
2701 	//
2702 	// 3) Affected Thing is below the ground (underwater effect).
2703 	//
2704 	//    Apply no force if wind, full force if current.
2705 	//
2706 	// Apply the effect to clipped players only for now.
2707 	//
2708 	// In Phase II, you can apply these effects to Things other than players.
2709 
2710 	if (m_Type == p_push)
2711 	{
2712 		// Seek out all pushable things within the force radius of this
2713 		// point pusher. Crosses sectors, so use blockmap.
2714 
2715 		tmpusher = this; // MT_PUSH/MT_PULL point source
2716 		radius = m_Radius; // where force goes to zero
2717 		tmbbox[BOXTOP]    = m_Y + radius;
2718 		tmbbox[BOXBOTTOM] = m_Y - radius;
2719 		tmbbox[BOXRIGHT]  = m_X + radius;
2720 		tmbbox[BOXLEFT]   = m_X - radius;
2721 
2722 		xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
2723 		xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
2724 		yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
2725 		yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
2726 		for (bx=xl ; bx<=xh ; bx++)
2727 			for (by=yl ; by<=yh ; by++)
2728 				P_BlockThingsIterator (bx, by, PIT_PushThing);
2729 		return;
2730 	}
2731 
2732 	// constant pushers p_wind and p_current
2733 
2734 	if (sec->heightsec) // special water sector?
2735 		ht = P_FloorHeight(sec->heightsec);
2736 	node = sec->touching_thinglist; // things touching this sector
2737 	for ( ; node ; node = node->m_snext)
2738 	{
2739 		thing = node->m_thing;
2740 		if (!thing->player || (thing->flags & (MF_NOGRAVITY | MF_NOCLIP)))
2741 			continue;
2742 		if (m_Type == p_wind)
2743 		{
2744 			if (sec->heightsec == NULL ||
2745 				sec->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) // NOT special water sector
2746 			{
2747 				if (thing->z > thing->floorz) // above ground
2748 				{
2749 					xspeed = m_Xmag; // full force
2750 					yspeed = m_Ymag;
2751 				}
2752 				else // on ground
2753 				{
2754 					xspeed = (m_Xmag)>>1; // half force
2755 					yspeed = (m_Ymag)>>1;
2756 				}
2757 			}
2758 			else // special water sector
2759 			{
2760 				if (thing->z > ht) // above ground
2761 				{
2762 					xspeed = m_Xmag; // full force
2763 					yspeed = m_Ymag;
2764 				}
2765 				else if (thing->player->viewz < ht) // underwater
2766 					xspeed = yspeed = 0; // no force
2767 				else // wading in water
2768 				{
2769 					xspeed = (m_Xmag)>>1; // half force
2770 					yspeed = (m_Ymag)>>1;
2771 				}
2772 			}
2773 		}
2774 		else // p_current
2775 		{
2776 			if (sec->heightsec == NULL ||
2777 				sec->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)
2778 			{ // NOT special water sector
2779 				if (thing->z > P_FloorHeight(sec)) // above ground
2780 					xspeed = yspeed = 0; // no force
2781 				else // on ground
2782 				{
2783 					xspeed = m_Xmag; // full force
2784 					yspeed = m_Ymag;
2785 				}
2786 			}
2787 			else
2788 			{ // special water sector
2789 				if (thing->z > ht) // above ground
2790 					xspeed = yspeed = 0; // no force
2791 				else // underwater
2792 				{
2793 					xspeed = m_Xmag; // full force
2794 					yspeed = m_Ymag;
2795 				}
2796 			}
2797 		}
2798 		thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR);
2799 		thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR);
2800 	}
2801 }
2802 
2803 /////////////////////////////
2804 //
2805 // P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing,
2806 // NULL otherwise.
2807 
P_GetPushThing(int s)2808 AActor *P_GetPushThing (int s)
2809 {
2810 	AActor* thing;
2811 	sector_t* sec;
2812 
2813 	sec = sectors + s;
2814 	thing = sec->thinglist;
2815 	while (thing)
2816 	{
2817 		switch (thing->type)
2818 		{
2819 		  case MT_PUSH:
2820 		  case MT_PULL:
2821 			return thing;
2822 		  default:
2823 			break;
2824 		}
2825 		thing = thing->snext;
2826 	}
2827 	return NULL;
2828 }
2829 
2830 /////////////////////////////
2831 //
2832 // Initialize the sectors where pushers are present
2833 //
2834 
P_SpawnPushers(void)2835 static void P_SpawnPushers(void)
2836 {
2837 	int i;
2838 	line_t *l = lines;
2839 	register int s;
2840 
2841 	for (i = 0; i < numlines; i++, l++)
2842 		switch (l->special)
2843 		{
2844 		  case Sector_SetWind: // wind
2845 			for (s = -1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0 ; )
2846 				new DPusher (DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
2847 			break;
2848 		  case Sector_SetCurrent: // current
2849 			for (s = -1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0 ; )
2850 				new DPusher (DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
2851 			break;
2852 		  case PointPush_SetForce: // push/pull
2853 			if (l->args[0]) {	// [RH] Find thing by sector
2854 				for (s = -1; (s = P_FindSectorFromTag (l->args[0], s)) >= 0 ; )
2855 				{
2856 					AActor *thing = P_GetPushThing (s);
2857 					if (thing) {	// No MT_P* means no effect
2858 						// [RH] Allow narrowing it down by tid
2859 						if (!l->args[1] || l->args[1] == thing->tid)
2860 							new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
2861 										 0, thing, s);
2862 					}
2863 				}
2864 			} else {	// [RH] Find thing by tid
2865 				AActor *thing = NULL;
2866 
2867 				while ( (thing = AActor::FindByTID (thing, l->args[1])) )
2868 				{
2869 					if (thing->type == MT_PUSH || thing->type == MT_PULL)
2870 						new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
2871 									 0, thing, thing->subsector->sector - sectors);
2872 				}
2873 			}
2874 			break;
2875 		}
2876 }
2877 
2878 //
2879 // phares 3/20/98: End of Pusher effects
2880 //
2881 ////////////////////////////////////////////////////////////////////////////
2882 
2883 // [AM] Trigger a special associated with an actor.
A_CheckTrigger(AActor * mo,AActor * triggerer)2884 bool A_CheckTrigger(AActor *mo, AActor *triggerer) {
2885 	if (mo->special &&
2886 		(triggerer->player ||
2887 		 ((mo->flags & MF_AMBUSH) && (triggerer->flags2 & MF2_MCROSS)) ||
2888 		 ((mo->flags2 & MF2_DORMANT) && (triggerer->flags2 & MF2_PCROSS)))) {
2889 		int savedSide = TeleportSide;
2890 		TeleportSide = 0;
2891 		bool res = (LineSpecials[mo->special](NULL, triggerer, mo->args[0],
2892 											 mo->args[1], mo->args[2],
2893 											 mo->args[3], mo->args[4]) != 0);
2894 		TeleportSide = savedSide;
2895 		return res;
2896 	}
2897 	return false;
2898 }
2899 
2900 // [AM] Selectively trigger a list of sector action specials that are linked by
2901 //      their tracer fields based on the passed activation type.
A_TriggerAction(AActor * mo,AActor * triggerer,int activationType)2902 bool A_TriggerAction(AActor *mo, AActor *triggerer, int activationType) {
2903 	bool trigger_action = false;
2904 
2905 	// The mobj type must agree with the activation type.
2906 	switch (mo->type) {
2907 	case MT_SECACTENTER:
2908 		trigger_action = ((activationType & SECSPAC_Enter) != 0);
2909 		break;
2910 	case MT_SECACTEXIT:
2911 		trigger_action = ((activationType & SECSPAC_Exit) != 0);
2912 		break;
2913 	case MT_SECACTHITFLOOR:
2914 		trigger_action = ((activationType & SECSPAC_HitFloor) != 0);
2915 		break;
2916 	case MT_SECACTHITCEIL:
2917 		trigger_action = ((activationType & SECSPAC_HitCeiling) != 0);
2918 		break;
2919 	case MT_SECACTUSE:
2920 		trigger_action = ((activationType & SECSPAC_Use) != 0);
2921 		break;
2922 	case MT_SECACTUSEWALL:
2923 		trigger_action = ((activationType & SECSPAC_UseWall) != 0);
2924 		break;
2925 	case MT_SECACTEYESDIVE:
2926 		trigger_action = ((activationType & SECSPAC_EyesDive) != 0);
2927 		break;
2928 	case MT_SECACTEYESSURFACE:
2929 		trigger_action = ((activationType & SECSPAC_EyesSurface) != 0);
2930 		break;
2931 	case MT_SECACTEYESBELOWC:
2932 		trigger_action = ((activationType & SECSPAC_EyesBelowC) != 0);
2933 		break;
2934 	case MT_SECACTEYESABOVEC:
2935 		trigger_action = ((activationType & SECSPAC_EyesAboveC) != 0);
2936 		break;
2937 	default:
2938 		// This isn't a sector action mobj.
2939 		break;
2940 	}
2941 
2942 	if (trigger_action) {
2943 		trigger_action = A_CheckTrigger(mo, triggerer);
2944 	}
2945 
2946 	// The tracer field could potentially contain a pointer to another
2947 	// actor special.
2948 	if (mo->tracer != NULL) {
2949 		return trigger_action | A_TriggerAction(mo->tracer, triggerer, activationType);
2950 	}
2951 
2952 	return trigger_action;
2953 }
2954 
2955 VERSION_CONTROL (p_spec_cpp, "$Id: p_spec.cpp 4532 2014-02-09 03:52:19Z mike $")
2956 
2957