]> icculus.org git repositories - theoddone33/hhexen.git/blob - base/p_spec.c
Round 2: Added --help and --version, as well as HHEXEN_DATA environment variable...
[theoddone33/hhexen.git] / base / p_spec.c
1
2 //**************************************************************************
3 //**
4 //** p_spec.c : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile$
7 //** $Revision$
8 //** $Date$
9 //** $Author$
10 //**
11 //**************************************************************************
12
13 // HEADER FILES ------------------------------------------------------------
14
15 #include "h2def.h"
16 #include "p_local.h"
17 #include "soundst.h"
18
19 // MACROS ------------------------------------------------------------------
20
21 #define MAX_TAGGED_LINES 64
22
23 // TYPES -------------------------------------------------------------------
24
25 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
26
27 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
28
29 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
30
31 static boolean CheckedLockedDoor(mobj_t *mo, byte lock);
32
33 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
34
35 // PUBLIC DATA DEFINITIONS -------------------------------------------------
36
37 int *TerrainTypes;
38 struct
39 {
40         char *name;
41         int type;
42 } TerrainTypeDefs[] =
43 {
44         { "X_005", FLOOR_WATER },
45         { "X_001", FLOOR_LAVA },
46         { "X_009", FLOOR_SLUDGE },
47         { "F_033", FLOOR_ICE },
48         { "END", -1 }
49 };
50
51 // PRIVATE DATA DEFINITIONS ------------------------------------------------
52
53 static struct
54 {
55         line_t *line;
56         int lineTag;
57 } TaggedLines[MAX_TAGGED_LINES];
58 static int TaggedLineCount;
59
60 mobj_t LavaInflictor;
61
62 // CODE --------------------------------------------------------------------
63
64 //==========================================================================
65 //
66 // P_InitLava
67 //
68 //==========================================================================
69
70 void P_InitLava(void)
71 {
72         memset(&LavaInflictor, 0, sizeof(mobj_t));
73         LavaInflictor.type = MT_CIRCLEFLAME;
74         LavaInflictor.flags2 = MF2_FIREDAMAGE|MF2_NODMGTHRUST;
75 }
76
77 //==========================================================================
78 //
79 // P_InitTerrainTypes
80 //
81 //==========================================================================
82
83 void P_InitTerrainTypes(void)
84 {
85         int i;
86         int lump;
87         int size;
88
89         size = (numflats+1)*sizeof(int);
90         TerrainTypes = Z_Malloc(size, PU_STATIC, 0);
91         memset(TerrainTypes, 0, size);
92         for(i = 0; TerrainTypeDefs[i].type != -1; i++)
93         {
94                 lump = W_CheckNumForName(TerrainTypeDefs[i].name);
95                 if(lump != -1)
96                 {
97                         TerrainTypes[lump-firstflat] = TerrainTypeDefs[i].type;
98                 }
99         }
100 }
101
102 //==========================================================================
103 //
104 // getSide
105 //
106 // Will return a side_t* given the number of the current sector, the
107 // line number, and the side (0/1) that you want.
108 //
109 //==========================================================================
110
111 /*
112 side_t *getSide(int currentSector, int line, int side)
113 {
114         return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ];
115 }
116 */
117
118 //==========================================================================
119 //
120 // getSector
121 //
122 // Will return a sector_t* given the number of the current sector, the
123 // line number, and the side (0/1) that you want.
124 //
125 //==========================================================================
126
127 /*
128 sector_t *getSector(int currentSector, int line, int side)
129 {
130         return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector;
131 }
132 */
133
134 //==========================================================================
135 //
136 // twoSided
137 //
138 // Given the sector number and the line number, will tell you whether
139 // the line is two-sided or not.
140 //
141 //==========================================================================
142
143 /*
144 int     twoSided(int sector, int line)
145 {
146         return (sectors[sector].lines[line])->flags & ML_TWOSIDED;
147 }
148 */
149
150 //==================================================================
151 //
152 //      Return sector_t * of sector next to current. NULL if not two-sided line
153 //
154 //==================================================================
155 sector_t *getNextSector(line_t *line,sector_t *sec)
156 {
157         if (!(line->flags & ML_TWOSIDED))
158                 return NULL;
159
160         if (line->frontsector == sec)
161                 return line->backsector;
162
163         return line->frontsector;
164 }
165
166 //==================================================================
167 //
168 //      FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
169 //
170 //==================================================================
171 fixed_t P_FindLowestFloorSurrounding(sector_t *sec)
172 {
173         int                     i;
174         line_t          *check;
175         sector_t        *other;
176         fixed_t         floor = sec->floorheight;
177
178         for (i=0 ;i < sec->linecount ; i++)
179         {
180                 check = sec->lines[i];
181                 other = getNextSector(check,sec);
182                 if (!other)
183                         continue;
184                 if (other->floorheight < floor)
185                         floor = other->floorheight;
186         }
187         return floor;
188 }
189
190 //==================================================================
191 //
192 //      FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
193 //
194 //==================================================================
195 fixed_t P_FindHighestFloorSurrounding(sector_t *sec)
196 {
197         int                     i;
198         line_t          *check;
199         sector_t        *other;
200         fixed_t         floor = -500*FRACUNIT;
201
202         for (i=0 ;i < sec->linecount ; i++)
203         {
204                 check = sec->lines[i];
205                 other = getNextSector(check,sec);
206                 if (!other)
207                         continue;
208                 if (other->floorheight > floor)
209                         floor = other->floorheight;
210         }
211         return floor;
212 }
213
214 //==================================================================
215 //
216 //      FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
217 //
218 //==================================================================
219 fixed_t P_FindNextHighestFloor(sector_t *sec,int currentheight)
220 {
221         int                     i;
222         int                     h;
223         int                     min;
224         line_t          *check;
225         sector_t        *other;
226         fixed_t         height = currentheight;
227         fixed_t         heightlist[20];         // 20 adjoining sectors max!
228
229         for (i =0,h = 0 ;i < sec->linecount ; i++)
230         {
231                 check = sec->lines[i];
232                 other = getNextSector(check,sec);
233                 if (!other)
234                         continue;
235                 if (other->floorheight > height)
236                         heightlist[h++] = other->floorheight;
237         }
238
239         //
240         // Find lowest height in list
241         //
242         min = heightlist[0];
243         for (i = 1;i < h;i++)
244                 if (heightlist[i] < min)
245                         min = heightlist[i];
246
247         return min;
248 }
249
250 //==================================================================
251 //
252 //      FIND LOWEST CEILING IN THE SURROUNDING SECTORS
253 //
254 //==================================================================
255 fixed_t P_FindLowestCeilingSurrounding(sector_t *sec)
256 {
257         int                     i;
258         line_t          *check;
259         sector_t        *other;
260         fixed_t         height = MAXINT;
261
262         for (i=0 ;i < sec->linecount ; i++)
263         {
264                 check = sec->lines[i];
265                 other = getNextSector(check,sec);
266                 if (!other)
267                         continue;
268                 if (other->ceilingheight < height)
269                         height = other->ceilingheight;
270         }
271         return height;
272 }
273
274 //==================================================================
275 //
276 //      FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
277 //
278 //==================================================================
279 fixed_t P_FindHighestCeilingSurrounding(sector_t *sec)
280 {
281         int     i;
282         line_t  *check;
283         sector_t        *other;
284         fixed_t height = 0;
285
286         for (i=0 ;i < sec->linecount ; i++)
287         {
288                 check = sec->lines[i];
289                 other = getNextSector(check,sec);
290                 if (!other)
291                         continue;
292                 if (other->ceilingheight > height)
293                         height = other->ceilingheight;
294         }
295         return height;
296 }
297
298 //==================================================================
299 //
300 //      RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
301 //
302 //==================================================================
303
304 /*
305 int     P_FindSectorFromLineTag(line_t  *line,int start)
306 {
307         int     i;
308
309         for (i=start+1;i<numsectors;i++)
310                 if (sectors[i].tag == line->arg1)
311                         return i;
312         return -1;
313 }
314 */
315
316 //=========================================================================
317 //
318 // P_FindSectorFromTag
319 //
320 //=========================================================================
321
322 int P_FindSectorFromTag(int tag, int start)
323 {
324         int i;
325         
326         for(i = start+1; i < numsectors; i++)
327         {
328                 if(sectors[i].tag == tag)
329                 {
330                         return i;
331                 }
332         }
333         return -1;
334 }
335
336 //==================================================================
337 //
338 //      Find minimum light from an adjacent sector
339 //
340 //==================================================================
341
342 /*
343 int     P_FindMinSurroundingLight(sector_t *sector,int max)
344 {
345         int                     i;
346         int                     min;
347         line_t          *line;
348         sector_t        *check;
349
350         min = max;
351         for (i=0 ; i < sector->linecount ; i++)
352         {
353                 line = sector->lines[i];
354                 check = getNextSector(line,sector);
355                 if (!check)
356                         continue;
357                 if (check->lightlevel < min)
358                         min = check->lightlevel;
359         }
360         return min;
361 }
362 */
363
364 //=========================================================================
365 //
366 // EV_SectorSoundChange
367 //
368 //=========================================================================
369
370 boolean EV_SectorSoundChange(byte *args)
371 {
372         int secNum;
373         boolean rtn;
374
375         if(!args[0])
376         {
377                 return false;
378         }
379         secNum = -1;
380         rtn = false;
381         while((secNum = P_FindSectorFromTag(args[0], secNum)) >= 0)
382         {
383                 sectors[secNum].seqType = args[1];
384                 rtn = true;
385         }
386         return rtn;
387 }
388
389 //============================================================================
390 //
391 // CheckedLockedDoor
392 //
393 //============================================================================
394
395 static boolean CheckedLockedDoor(mobj_t *mo, byte lock)
396 {
397         extern char *TextKeyMessages[11];
398         char LockedBuffer[80];
399
400         if(!mo->player)
401         {
402                 return false;
403         }
404         if(!lock)
405         {       
406                 return true;
407         }
408         if(!(mo->player->keys&(1<<(lock-1))))
409         {
410                 sprintf(LockedBuffer, "YOU NEED THE %s\n", 
411                         TextKeyMessages[lock-1]);
412                 P_SetMessage(mo->player, LockedBuffer, true);
413                 S_StartSound(mo, SFX_DOOR_LOCKED);
414                 return false;
415         }
416         return true;
417 }
418
419
420 //==========================================================================
421 //
422 // EV_LineSearchForPuzzleItem
423 //
424 //==========================================================================
425
426 boolean EV_LineSearchForPuzzleItem(line_t *line, byte *args, mobj_t *mo)
427 {
428         player_t *player;
429         int i;
430         artitype_t type,arti;
431
432         if (!mo) return false;
433         player = mo->player;
434         if (!player) return false;
435
436         // Search player's inventory for puzzle items
437         for (i=0; i<player->artifactCount; i++)
438         {
439                 arti = player->inventory[i].type;
440                 type = arti - arti_firstpuzzitem;
441                 if (type < 0) continue;
442                 if (type == line->arg1)
443                 {
444                         // A puzzle item was found for the line
445                         if (P_UseArtifact(player, arti))
446                         {
447                                 // A puzzle item was found for the line
448                                 P_PlayerRemoveArtifact(player, i);
449                                 if(player == &players[consoleplayer])
450                                 {
451                                         if(arti < arti_firstpuzzitem)
452                                         {
453                                                 S_StartSound(NULL, SFX_ARTIFACT_USE);
454                                         }
455                                         else
456                                         {
457                                                 S_StartSound(NULL, SFX_PUZZLE_SUCCESS);
458                                         }
459                                         ArtifactFlash = 4;
460                                 }
461                                 return true;
462                         }
463                 }
464         }
465         return false;
466 }
467
468
469
470 /*
471 ==============================================================================
472
473                                                         EVENTS
474
475 Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers
476
477 ==============================================================================
478 */
479 //============================================================================
480 //
481 // P_ExecuteLineSpecial
482 //
483 //============================================================================
484
485 boolean P_ExecuteLineSpecial(int special, byte *args, line_t *line, int side,
486         mobj_t *mo)
487 {
488         boolean buttonSuccess;
489
490         buttonSuccess = false;
491         switch(special)
492         {
493                 case 1: // Poly Start Line
494                         break;
495                 case 2: // Poly Rotate Left
496                         buttonSuccess = EV_RotatePoly(line, args, 1, false);
497                         break;
498                 case 3: // Poly Rotate Right
499                         buttonSuccess = EV_RotatePoly(line, args, -1, false);
500                         break;
501                 case 4: // Poly Move
502                         buttonSuccess = EV_MovePoly(line, args, false, false);
503                         break;
504                 case 5: // Poly Explicit Line:  Only used in initialization
505                         break;
506                 case 6: // Poly Move Times 8
507                         buttonSuccess = EV_MovePoly(line, args, true, false);
508                         break;
509                 case 7: // Poly Door Swing
510                         buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SWING);
511                         break;
512                 case 8: // Poly Door Slide
513                         buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SLIDE);
514                         break;
515                 case 10: // Door Close
516                         buttonSuccess = EV_DoDoor(line, args, DREV_CLOSE);
517                         break;
518                 case 11: // Door Open
519                         if(!args[0])
520                         {
521                                 buttonSuccess = EV_VerticalDoor(line, mo);
522                         }
523                         else
524                         {
525                                 buttonSuccess = EV_DoDoor(line, args, DREV_OPEN);
526                         }
527                         break;
528                 case 12: // Door Raise
529                         if(!args[0])
530                         {
531                                 buttonSuccess = EV_VerticalDoor(line, mo);
532                         }
533                         else
534                         {
535                                 buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL);
536                         }
537                         break;
538                 case 13: // Door Locked_Raise
539                         if(CheckedLockedDoor(mo, args[3]))
540                         {
541                                 if(!args[0])
542                                 {
543                                         buttonSuccess = EV_VerticalDoor(line, mo);
544                                 }
545                                 else
546                                 {
547                                         buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL);
548                                 }
549                         }
550                         break;
551                 case 20: // Floor Lower by Value
552                         buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE);
553                         break;
554                 case 21: // Floor Lower to Lowest
555                         buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORTOLOWEST);
556                         break;
557                 case 22: // Floor Lower to Nearest
558                         buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOOR);
559                         break;
560                 case 23: // Floor Raise by Value
561                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE);
562                         break;
563                 case 24: // Floor Raise to Highest
564                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOOR);
565                         break;
566                 case 25: // Floor Raise to Nearest
567                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORTONEAREST);
568                         break;
569                 case 26: // Stairs Build Down Normal
570                         buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_NORMAL);
571                         break;
572                 case 27: // Build Stairs Up Normal
573                         buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_NORMAL);
574                         break;
575                 case 28: // Floor Raise and Crush
576                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORCRUSH);
577                         break;
578                 case 29: // Build Pillar (no crushing)
579                         buttonSuccess = EV_BuildPillar(line, args, false);
580                         break;
581                 case 30: // Open Pillar
582                         buttonSuccess = EV_OpenPillar(line, args);
583                         break;
584                 case 31: // Stairs Build Down Sync
585                         buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_SYNC);
586                         break;
587                 case 32: // Build Stairs Up Sync
588                         buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_SYNC);
589                         break;
590                 case 35: // Raise Floor by Value Times 8
591                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEBYVALUETIMES8);
592                         break;
593                 case 36: // Lower Floor by Value Times 8
594                         buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERBYVALUETIMES8);
595                         break;
596                 case 40: // Ceiling Lower by Value
597                         buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE);
598                         break;
599                 case 41: // Ceiling Raise by Value
600                         buttonSuccess = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE);
601                         break;
602                 case 42: // Ceiling Crush and Raise
603                         buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHANDRAISE);
604                         break;
605                 case 43: // Ceiling Lower and Crush
606                         buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERANDCRUSH);
607                         break;
608                 case 44: // Ceiling Crush Stop
609                         buttonSuccess = EV_CeilingCrushStop(line, args);
610                         break;
611                 case 45: // Ceiling Crush Raise and Stay
612                         buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHRAISEANDSTAY);
613                         break;
614                 case 46: // Floor Crush Stop
615                         buttonSuccess = EV_FloorCrushStop(line, args);
616                         break;
617                 case 60: // Plat Perpetual Raise
618                         buttonSuccess = EV_DoPlat(line, args, PLAT_PERPETUALRAISE, 0);
619                         break;
620                 case 61: // Plat Stop
621                         EV_StopPlat(line, args);
622                         break;
623                 case 62: // Plat Down-Wait-Up-Stay
624                         buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNWAITUPSTAY, 0);
625                         break;
626                 case 63: // Plat Down-by-Value*8-Wait-Up-Stay
627                         buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNBYVALUEWAITUPSTAY,
628                                 0);
629                         break;
630                 case 64: // Plat Up-Wait-Down-Stay
631                         buttonSuccess = EV_DoPlat(line, args, PLAT_UPWAITDOWNSTAY, 0);
632                         break;
633                 case 65: // Plat Up-by-Value*8-Wait-Down-Stay
634                         buttonSuccess = EV_DoPlat(line, args, PLAT_UPBYVALUEWAITDOWNSTAY,
635                                 0);
636                         break;
637                 case 66: // Floor Lower Instant * 8
638                         buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERTIMES8INSTANT);
639                         break;
640                 case 67: // Floor Raise Instant * 8
641                         buttonSuccess = EV_DoFloor(line, args, FLEV_RAISETIMES8INSTANT);
642                         break;
643                 case 68: // Floor Move to Value * 8
644                         buttonSuccess = EV_DoFloor(line, args, FLEV_MOVETOVALUETIMES8);
645                         break;
646                 case 69: // Ceiling Move to Value * 8
647                         buttonSuccess = EV_DoCeiling(line, args, CLEV_MOVETOVALUETIMES8);
648                         break;
649                 case 70: // Teleport
650                         if(side == 0)
651                         { // Only teleport when crossing the front side of a line
652                                 buttonSuccess = EV_Teleport(args[0], mo, true);
653                         }
654                         break;
655                 case 71: // Teleport, no fog
656                         if(side == 0)
657                         { // Only teleport when crossing the front side of a line
658                                 buttonSuccess = EV_Teleport(args[0], mo, false);
659                         }
660                         break;
661                 case 72: // Thrust Mobj
662                         if(!side) // Only thrust on side 0
663                         {
664                                 P_ThrustMobj(mo, args[0]*(ANGLE_90/64), args[1]<<FRACBITS);
665                                 buttonSuccess = 1;
666                         }
667                         break;
668                 case 73: // Damage Mobj
669                         if(args[0])
670                         {
671                                 P_DamageMobj(mo, NULL, NULL, args[0]);
672                         }
673                         else
674                         { // If arg1 is zero, then guarantee a kill
675                                 P_DamageMobj(mo, NULL, NULL, 10000);
676                         }
677                         buttonSuccess = 1;
678                         break;
679                 case 74: // Teleport_NewMap
680                         if(side == 0)
681                         { // Only teleport when crossing the front side of a line
682                                 if(!(mo && mo->player && mo->player->playerstate
683                                         == PST_DEAD)) // Players must be alive to teleport
684                                 {
685                                         G_Completed(args[0], args[1]);
686                                         buttonSuccess = true;
687                                 }
688                         }
689                         break;
690                 case 75: // Teleport_EndGame
691                         if(side == 0)
692                         { // Only teleport when crossing the front side of a line
693                                 if(!(mo && mo->player && mo->player->playerstate
694                                         == PST_DEAD)) // Players must be alive to teleport
695                                 {
696                                         buttonSuccess = true;
697                                         if(deathmatch)
698                                         { // Winning in deathmatch just goes back to map 1
699                                                 G_Completed(1, 0);
700                                         }
701                                         else
702                                         { // Passing -1, -1 to G_Completed() starts the Finale
703                                                 G_Completed(-1, -1);
704                                         }
705                                 }
706                         }
707                         break;
708                 case 80: // ACS_Execute
709                         buttonSuccess =
710                                 P_StartACS(args[0], args[1], &args[2], mo, line, side);
711                         break;
712                 case 81: // ACS_Suspend
713                         buttonSuccess = P_SuspendACS(args[0], args[1]);
714                         break;
715                 case 82: // ACS_Terminate
716                         buttonSuccess = P_TerminateACS(args[0], args[1]);
717                         break;
718                 case 83: // ACS_LockedExecute
719                         buttonSuccess = P_StartLockedACS(line, args, mo, side);
720                         break;
721                 case 90: // Poly Rotate Left Override
722                         buttonSuccess = EV_RotatePoly(line, args, 1, true);
723                         break;
724                 case 91: // Poly Rotate Right Override
725                         buttonSuccess = EV_RotatePoly(line, args, -1, true);
726                         break;
727                 case 92: // Poly Move Override
728                         buttonSuccess = EV_MovePoly(line, args, false, true);
729                         break;
730                 case 93: // Poly Move Times 8 Override
731                         buttonSuccess = EV_MovePoly(line, args, true, true);
732                         break;
733                 case 94: // Build Pillar Crush 
734                         buttonSuccess = EV_BuildPillar(line, args, true);
735                         break;
736                 case 95: // Lower Floor and Ceiling
737                         buttonSuccess = EV_DoFloorAndCeiling(line, args, false);
738                         break;
739                 case 96: // Raise Floor and Ceiling
740                         buttonSuccess = EV_DoFloorAndCeiling(line, args, true);
741                         break;
742                 case 109: // Force Lightning
743                         buttonSuccess = true;
744                         P_ForceLightning();
745                         break;
746                 case 110: // Light Raise by Value
747                         buttonSuccess = EV_SpawnLight(line, args, LITE_RAISEBYVALUE);
748                         break; 
749                 case 111: // Light Lower by Value
750                         buttonSuccess = EV_SpawnLight(line, args, LITE_LOWERBYVALUE);
751                         break; 
752                 case 112: // Light Change to Value
753                         buttonSuccess = EV_SpawnLight(line, args, LITE_CHANGETOVALUE);
754                         break; 
755                 case 113: // Light Fade
756                         buttonSuccess = EV_SpawnLight(line, args, LITE_FADE);
757                         break; 
758                 case 114: // Light Glow
759                         buttonSuccess = EV_SpawnLight(line, args, LITE_GLOW);
760                         break; 
761                 case 115: // Light Flicker
762                         buttonSuccess = EV_SpawnLight(line, args, LITE_FLICKER);
763                         break; 
764                 case 116: // Light Strobe
765                         buttonSuccess = EV_SpawnLight(line, args, LITE_STROBE);
766                         break; 
767                 case 120: // Quake Tremor
768                         buttonSuccess = A_LocalQuake(args, mo);
769                         break;
770                 case 129: // UsePuzzleItem
771                         buttonSuccess = EV_LineSearchForPuzzleItem(line, args, mo);
772                         break;
773                 case 130: // Thing_Activate
774                         buttonSuccess = EV_ThingActivate(args[0]);
775                         break;
776                 case 131: // Thing_Deactivate
777                         buttonSuccess = EV_ThingDeactivate(args[0]);
778                         break;
779                 case 132: // Thing_Remove
780                         buttonSuccess = EV_ThingRemove(args[0]);
781                         break;
782                 case 133: // Thing_Destroy
783                         buttonSuccess = EV_ThingDestroy(args[0]);
784                         break;
785                 case 134: // Thing_Projectile
786                         buttonSuccess = EV_ThingProjectile(args, 0);
787                         break;
788                 case 135: // Thing_Spawn
789                         buttonSuccess = EV_ThingSpawn(args, 1);
790                         break;
791                 case 136: // Thing_ProjectileGravity
792                         buttonSuccess = EV_ThingProjectile(args, 1);
793                         break;
794                 case 137: // Thing_SpawnNoFog
795                         buttonSuccess = EV_ThingSpawn(args, 0);
796                         break;
797                 case 138: // Floor_Waggle
798                         buttonSuccess = EV_StartFloorWaggle(args[0], args[1],
799                                 args[2], args[3], args[4]);
800                         break;
801                 case 140: // Sector_SoundChange
802                         buttonSuccess = EV_SectorSoundChange(args);
803                         break;
804
805                 // Line specials only processed during level initialization
806                 // 100: Scroll_Texture_Left
807                 // 101: Scroll_Texture_Right
808                 // 102: Scroll_Texture_Up
809                 // 103: Scroll_Texture_Down
810                 // 121: Line_SetIdentification
811
812                 // Inert Line specials
813                 default:
814                         break;
815         }
816         return buttonSuccess;
817 }
818
819 //============================================================================
820 //
821 // P_ActivateLine
822 //
823 //============================================================================
824
825 boolean P_ActivateLine(line_t *line, mobj_t *mo, int side, int activationType)
826 {
827         int lineActivation;
828         boolean repeat;
829         boolean buttonSuccess;
830
831         lineActivation = GET_SPAC(line->flags);
832         if(lineActivation != activationType)
833         {
834                 return false;
835         }
836         if(!mo->player && !(mo->flags&MF_MISSILE))
837         {
838                 if(lineActivation != SPAC_MCROSS)
839                 { // currently, monsters can only activate the MCROSS activation type
840                         return false;
841                 }
842                 if(line->flags & ML_SECRET)
843                         return false;           // never open secret doors
844         }
845         repeat = line->flags&ML_REPEAT_SPECIAL;
846         buttonSuccess = false;
847
848         buttonSuccess = P_ExecuteLineSpecial(line->special, &line->arg1, line,
849                 side, mo);
850         if(!repeat && buttonSuccess)
851         { // clear the special on non-retriggerable lines
852                 line->special = 0;
853         }
854         if((lineActivation == SPAC_USE || lineActivation == SPAC_IMPACT) 
855                 && buttonSuccess)
856         {
857                 P_ChangeSwitchTexture(line, repeat);
858         }
859         return true;
860 }
861
862 //----------------------------------------------------------------------------
863 //
864 // PROC P_PlayerInSpecialSector
865 //
866 // Called every tic frame that the player origin is in a special sector.
867 //
868 //----------------------------------------------------------------------------
869
870 void P_PlayerInSpecialSector(player_t *player)
871 {
872         sector_t *sector;
873         static int pushTab[3] =
874         {
875                 2048*5,
876                 2048*10,
877                 2048*25
878         };
879
880         sector = player->mo->subsector->sector;
881         if(player->mo->z != sector->floorheight)
882         { // Player is not touching the floor
883                 return;
884         }
885         switch(sector->special)
886         {
887                 case 9: // SecretArea
888                         player->secretcount++;
889                         sector->special = 0;
890                         break;
891
892                 case 201: case 202: case 203: // Scroll_North_xxx
893                         P_Thrust(player, ANG90, pushTab[sector->special-201]);
894                         break;
895                 case 204: case 205: case 206: // Scroll_East_xxx
896                         P_Thrust(player, 0, pushTab[sector->special-204]);
897                         break;
898                 case 207: case 208: case 209: // Scroll_South_xxx
899                         P_Thrust(player, ANG270, pushTab[sector->special-207]);
900                         break;
901                 case 210: case 211: case 212: // Scroll_West_xxx
902                         P_Thrust(player, ANG180, pushTab[sector->special-210]);
903                         break;
904                 case 213: case 214: case 215: // Scroll_NorthWest_xxx
905                         P_Thrust(player, ANG90+ANG45, pushTab[sector->special-213]);
906                         break;
907                 case 216: case 217: case 218: // Scroll_NorthEast_xxx
908                         P_Thrust(player, ANG45, pushTab[sector->special-216]);
909                         break;
910                 case 219: case 220: case 221: // Scroll_SouthEast_xxx
911                         P_Thrust(player, ANG270+ANG45, pushTab[sector->special-219]);
912                         break;
913                 case 222: case 223: case 224: // Scroll_SouthWest_xxx
914                         P_Thrust(player, ANG180+ANG45, pushTab[sector->special-222]);
915                         break;
916
917                 case 40: case 41: case 42: case 43: case 44: case 45:
918                 case 46: case 47: case 48: case 49: case 50: case 51:
919                         // Wind specials are handled in (P_mobj):P_XYMovement
920                         break;
921
922                 case 26: // Stairs_Special1
923                 case 27: // Stairs_Special2
924                         // Used in (P_floor):ProcessStairSector
925                         break;
926
927                 case 198: // Lightning Special
928                 case 199: // Lightning Flash special
929                 case 200: // Sky2
930                         // Used in (R_plane):R_Drawplanes
931                         break;
932                 default:
933                         I_Error("P_PlayerInSpecialSector: "
934                                 "unknown special %i", sector->special);
935         }
936 }
937
938 //============================================================================
939 //
940 // P_PlayerOnSpecialFlat
941 //
942 //============================================================================
943
944 void P_PlayerOnSpecialFlat(player_t *player, int floorType)
945 {
946         if(player->mo->z != player->mo->floorz)
947         { // Player is not touching the floor
948                 return;
949         }
950         switch(floorType)
951         {
952                 case FLOOR_LAVA:
953                         if(!(leveltime&31))
954                         {
955                                 P_DamageMobj(player->mo, &LavaInflictor, NULL, 10);
956                                 S_StartSound(player->mo, SFX_LAVA_SIZZLE);
957                         }
958                         break;
959                 default:
960                         break;
961         }
962 }
963
964 //----------------------------------------------------------------------------
965 //
966 // PROC P_UpdateSpecials
967 //
968 //----------------------------------------------------------------------------
969
970 void P_UpdateSpecials(void)
971 {
972         int i;
973
974         // Handle buttons
975         for(i = 0; i < MAXBUTTONS; i++)
976         {
977                 if(buttonlist[i].btimer)
978                 {
979                         buttonlist[i].btimer--;
980                         if(!buttonlist[i].btimer)
981                         {
982                                 switch(buttonlist[i].where)
983                                 {
984                                         case SWTCH_TOP:
985                                                 sides[buttonlist[i].line->sidenum[0]].toptexture =
986                                                         buttonlist[i].btexture;
987                                                 break;
988                                         case SWTCH_MIDDLE:
989                                                 sides[buttonlist[i].line->sidenum[0]].midtexture =
990                                                         buttonlist[i].btexture;
991                                                 break;
992                                         case SWTCH_BOTTOM:
993                                                 sides[buttonlist[i].line->sidenum[0]].bottomtexture =
994                                                         buttonlist[i].btexture;
995                                                 break;
996                                 }
997                                 //S_StartSound((mobj_t *)&buttonlist[i].soundorg, sfx_switch);
998                                 memset(&buttonlist[i], 0, sizeof(button_t));
999                         }
1000                 }
1001         }
1002 }
1003
1004 /*
1005 ==============================================================================
1006
1007                                                         SPECIAL SPAWNING
1008
1009 ==============================================================================
1010 */
1011 /*
1012 ================================================================================
1013 = P_SpawnSpecials
1014 =
1015 = After the map has been loaded, scan for specials that
1016 = spawn thinkers
1017 =
1018 ===============================================================================
1019 */
1020
1021 short   numlinespecials;
1022 line_t  *linespeciallist[MAXLINEANIMS];
1023
1024 void P_SpawnSpecials (void)
1025 {
1026         sector_t        *sector;
1027         int             i;
1028
1029         //
1030         //      Init special SECTORs
1031         //
1032         sector = sectors;
1033         for (i=0 ; i<numsectors ; i++, sector++)
1034         {
1035                 if (!sector->special)
1036                         continue;
1037                 switch (sector->special)
1038                 {
1039                         case 1: // Phased light
1040                                 // Hardcoded base, use sector->lightlevel as the index
1041                                 P_SpawnPhasedLight(sector, 80, -1);
1042                                 break;
1043                         case 2: // Phased light sequence start
1044                                 P_SpawnLightSequence(sector, 1);
1045                                 break;
1046                         // Specials 3 & 4 are used by the phased light sequences
1047
1048                         /*
1049                         case 1:         // FLICKERING LIGHTS
1050                                 P_SpawnLightFlash (sector);
1051                                 break;
1052                         case 2:         // STROBE FAST
1053                                 P_SpawnStrobeFlash(sector,FASTDARK,0);
1054                                 break;
1055                         case 3:         // STROBE SLOW
1056                                 P_SpawnStrobeFlash(sector,SLOWDARK,0);
1057                                 break;
1058                         case 4:         // STROBE FAST/DEATH SLIME
1059                                 P_SpawnStrobeFlash(sector,FASTDARK,0);
1060                                 sector->special = 4;
1061                                 break;
1062                         case 8:         // GLOWING LIGHT
1063                                 P_SpawnGlowingLight(sector);
1064                                 break;
1065                         case 9:         // SECRET SECTOR
1066                                 totalsecret++;
1067                                 break;
1068                         case 10:        // DOOR CLOSE IN 30 SECONDS
1069                                 P_SpawnDoorCloseIn30 (sector);
1070                                 break;
1071                         case 12:        // SYNC STROBE SLOW
1072                                 P_SpawnStrobeFlash (sector, SLOWDARK, 1);
1073                                 break;
1074                         case 13:        // SYNC STROBE FAST
1075                                 P_SpawnStrobeFlash (sector, FASTDARK, 1);
1076                                 break;
1077                         case 14:        // DOOR RAISE IN 5 MINUTES
1078                                 P_SpawnDoorRaiseIn5Mins (sector, i);
1079                                 break;
1080                         */
1081                 }
1082         }
1083
1084
1085         //
1086         //      Init line EFFECTs
1087         //
1088         numlinespecials = 0;
1089         TaggedLineCount = 0;
1090         for(i = 0; i < numlines; i++)
1091         {
1092                 switch(lines[i].special)
1093                 {
1094                         case 100: // Scroll_Texture_Left
1095                         case 101: // Scroll_Texture_Right
1096                         case 102: // Scroll_Texture_Up
1097                         case 103: // Scroll_Texture_Down
1098                                 linespeciallist[numlinespecials] = &lines[i];
1099                                 numlinespecials++;
1100                                 break;
1101                         case 121: // Line_SetIdentification
1102                                 if(lines[i].arg1)
1103                                 {
1104                                         if(TaggedLineCount == MAX_TAGGED_LINES)
1105                                         {
1106                                                 I_Error("P_SpawnSpecials: MAX_TAGGED_LINES "
1107                                                         "(%d) exceeded.", MAX_TAGGED_LINES);
1108                                         }
1109                                         TaggedLines[TaggedLineCount].line = &lines[i];
1110                                         TaggedLines[TaggedLineCount++].lineTag
1111                                                 = lines[i].arg1;
1112                                 }
1113                                 lines[i].special = 0;
1114                                 break;
1115                 }
1116         }
1117
1118         //
1119         //      Init other misc stuff
1120         //
1121         for (i = 0;i < MAXCEILINGS;i++)
1122                 activeceilings[i] = NULL;
1123         for (i = 0;i < MAXPLATS;i++)
1124                 activeplats[i] = NULL;
1125         for (i = 0;i < MAXBUTTONS;i++)
1126                 memset(&buttonlist[i],0,sizeof(button_t));
1127
1128         // Initialize flat and texture animations
1129         P_InitFTAnims();
1130 }
1131
1132 //==========================================================================
1133 //
1134 // P_FindLine
1135 //
1136 //==========================================================================
1137
1138 line_t *P_FindLine(int lineTag, int *searchPosition)
1139 {
1140         int i;
1141
1142         for(i = *searchPosition+1; i < TaggedLineCount; i++)
1143         {
1144                 if(TaggedLines[i].lineTag == lineTag)
1145                 {
1146                         *searchPosition = i;
1147                         return TaggedLines[i].line;
1148                 }
1149         }
1150         *searchPosition = -1;
1151         return NULL;
1152 }