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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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