1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
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.
26 //-----------------------------------------------------------------------------
29 rcsid[] = "$Id: p_spec.c,v 1.6 1997/02/03 22:45:12 b1 Exp $";
55 // Animating textures and planes
56 // There is another anim_t used in wi_stuff, unrelated.
69 // source animation definition
73 boolean istexture; // if false, it is a flat
83 extern anim_t anims[MAXANIMS];
84 extern anim_t* lastanim;
90 // Floor/ceiling animation sequences,
91 // defined by first and last frame,
92 // i.e. the flat (64x64 tile) name to
94 // The full animation sequence is given
95 // using all the flats between the start
96 // and end entry, in the order found in
99 animdef_t animdefs[] =
101 {false, "NUKAGE3", "NUKAGE1", 8},
102 {false, "FWATER4", "FWATER1", 8},
103 {false, "SWATER4", "SWATER1", 8},
104 {false, "LAVA4", "LAVA1", 8},
105 {false, "BLOOD3", "BLOOD1", 8},
107 // DOOM II flat animations.
108 {false, "RROCK08", "RROCK05", 8},
109 {false, "SLIME04", "SLIME01", 8},
110 {false, "SLIME08", "SLIME05", 8},
111 {false, "SLIME12", "SLIME09", 8},
113 {true, "BLODGR4", "BLODGR1", 8},
114 {true, "SLADRIP3", "SLADRIP1", 8},
116 {true, "BLODRIP4", "BLODRIP1", 8},
117 {true, "FIREWALL", "FIREWALA", 8},
118 {true, "GSTFONT3", "GSTFONT1", 8},
119 {true, "FIRELAVA", "FIRELAV3", 8},
120 {true, "FIREMAG3", "FIREMAG1", 8},
121 {true, "FIREBLU2", "FIREBLU1", 8},
122 {true, "ROCKRED3", "ROCKRED1", 8},
124 {true, "BFALL4", "BFALL1", 8},
125 {true, "SFALL4", "SFALL1", 8},
126 {true, "WFALL4", "WFALL1", 8},
127 {true, "DBRAIN4", "DBRAIN1", 8},
132 anim_t anims[MAXANIMS];
137 // Animating line specials
139 #define MAXLINEANIMS 64
141 extern short numlinespecials;
142 extern line_t* linespeciallist[MAXLINEANIMS];
146 void P_InitPicAnims (void)
153 for (i=0 ; animdefs[i].istexture != -1 ; i++)
155 if (animdefs[i].istexture)
157 // different episode ?
158 if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
161 lastanim->picnum = R_TextureNumForName (animdefs[i].endname);
162 lastanim->basepic = R_TextureNumForName (animdefs[i].startname);
166 if (W_CheckNumForName(animdefs[i].startname) == -1)
169 lastanim->picnum = R_FlatNumForName (animdefs[i].endname);
170 lastanim->basepic = R_FlatNumForName (animdefs[i].startname);
173 lastanim->istexture = animdefs[i].istexture;
174 lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
176 if (lastanim->numpics < 2)
177 I_Error ("P_InitPicAnims: bad cycle from %s to %s",
178 animdefs[i].startname,
179 animdefs[i].endname);
181 lastanim->speed = animdefs[i].speed;
197 // Will return a side_t*
198 // given the number of the current sector,
199 // the line number, and the side (0/1) that you want.
207 return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ];
213 // Will return a sector_t*
214 // given the number of the current sector,
215 // the line number and the side (0/1) that you want.
223 return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector;
229 // Given the sector number and the line number,
230 // it will tell you whether the line is two-sided or not.
237 return (sectors[sector].lines[line])->flags & ML_TWOSIDED;
245 // Return sector_t * of sector next to current.
246 // NULL if not two-sided line
253 if (!(line->flags & ML_TWOSIDED))
256 if (line->frontsector == sec)
257 return line->backsector;
259 return line->frontsector;
265 // P_FindLowestFloorSurrounding()
266 // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
268 fixed_t P_FindLowestFloorSurrounding(sector_t* sec)
273 fixed_t floor = sec->floorheight;
275 for (i=0 ;i < sec->linecount ; i++)
277 check = sec->lines[i];
278 other = getNextSector(check,sec);
283 if (other->floorheight < floor)
284 floor = other->floorheight;
292 // P_FindHighestFloorSurrounding()
293 // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
295 fixed_t P_FindHighestFloorSurrounding(sector_t *sec)
300 fixed_t floor = -500*FRACUNIT;
302 for (i=0 ;i < sec->linecount ; i++)
304 check = sec->lines[i];
305 other = getNextSector(check,sec);
310 if (other->floorheight > floor)
311 floor = other->floorheight;
319 // P_FindNextHighestFloor
320 // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
321 // Note: this should be doable w/o a fixed array.
323 // 20 adjoining sectors max!
324 #define MAX_ADJOINING_SECTORS 20
327 P_FindNextHighestFloor
336 fixed_t height = currentheight;
339 fixed_t heightlist[MAX_ADJOINING_SECTORS];
341 for (i=0, h=0 ;i < sec->linecount ; i++)
343 check = sec->lines[i];
344 other = getNextSector(check,sec);
349 if (other->floorheight > height)
350 heightlist[h++] = other->floorheight;
352 // Check for overflow. Exit.
353 if ( h >= MAX_ADJOINING_SECTORS )
356 "Sector with more than 20 adjoining sectors\n" );
361 // Find lowest height in list
363 return currentheight;
368 for (i = 1;i < h;i++)
369 if (heightlist[i] < min)
377 // FIND LOWEST CEILING IN THE SURROUNDING SECTORS
380 P_FindLowestCeilingSurrounding(sector_t* sec)
385 fixed_t height = MAXINT;
387 for (i=0 ;i < sec->linecount ; i++)
389 check = sec->lines[i];
390 other = getNextSector(check,sec);
395 if (other->ceilingheight < height)
396 height = other->ceilingheight;
403 // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
405 fixed_t P_FindHighestCeilingSurrounding(sector_t* sec)
412 for (i=0 ;i < sec->linecount ; i++)
414 check = sec->lines[i];
415 other = getNextSector(check,sec);
420 if (other->ceilingheight > height)
421 height = other->ceilingheight;
429 // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
432 P_FindSectorFromLineTag
438 for (i=start+1;i<numsectors;i++)
439 if (sectors[i].tag == line->tag)
449 // Find minimum light from an adjacent sector
452 P_FindMinSurroundingLight
462 for (i=0 ; i < sector->linecount ; i++)
464 line = sector->lines[i];
465 check = getNextSector(line,sector);
470 if (check->lightlevel < min)
471 min = check->lightlevel;
480 // Events are operations triggered by using, crossing,
481 // or shooting special lines, or by timed thinkers.
485 // P_CrossSpecialLine - TRIGGER
486 // Called every time a thing origin is about
487 // to cross a line with a non 0 special.
498 line = &lines[linenum];
500 // Triggers that other things can activate
503 // Things that should NOT trigger specials...
519 switch(line->special)
521 case 39: // TELEPORT TRIGGER
522 case 97: // TELEPORT RETRIGGER
523 case 125: // TELEPORT MONSTERONLY TRIGGER
524 case 126: // TELEPORT MONSTERONLY RETRIGGER
525 case 4: // RAISE DOOR
526 case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER
527 case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER
536 // Note: could use some const's here.
537 switch (line->special)
540 // All from here to RETRIGGERS.
543 EV_DoDoor(line,Open);
549 EV_DoDoor(line,Close);
555 EV_DoDoor(line,Normal);
561 EV_DoFloor(line,raiseFloor);
566 // Fast Ceiling Crush & Raise
567 EV_DoCeiling(line,fastCrushAndRaise);
573 EV_BuildStairs(line,build8);
579 EV_DoPlat(line,downWaitUpStay,0);
584 // Light Turn On - brightest near
585 EV_LightTurnOn(line,0);
591 EV_LightTurnOn(line,255);
597 EV_DoDoor(line,Close30ThenOpen);
602 // Start Light Strobing
603 EV_StartLightStrobing(line);
609 EV_DoFloor(line,lowerFloor);
614 // Raise floor to nearest height and change texture
615 EV_DoPlat(line,raiseToNearestAndChange,0);
620 // Ceiling Crush and Raise
621 EV_DoCeiling(line,crushAndRaise);
626 // Raise floor to shortest texture height
627 // on either side of lines.
628 EV_DoFloor(line,raiseToTexture);
634 EV_LightTurnOn(line,35);
639 // Lower Floor (TURBO)
640 EV_DoFloor(line,turboLower);
646 EV_DoFloor(line,lowerAndChange);
651 // Lower Floor To Lowest
652 EV_DoFloor( line, lowerFloorToLowest );
658 EV_Teleport( line, side, thing );
663 // RaiseCeilingLowerFloor
664 EV_DoCeiling( line, raiseToHighest );
665 EV_DoFloor( line, lowerFloorToLowest );
671 EV_DoCeiling( line, lowerAndCrush );
681 // Perpetual Platform Raise
682 EV_DoPlat(line,perpetualRaise,0);
694 EV_DoFloor(line,raiseFloorCrush);
699 // Ceiling Crush Stop
700 EV_CeilingCrushStop(line);
706 EV_DoFloor(line,raiseFloor24);
711 // Raise Floor 24 And Change
712 EV_DoFloor(line,raiseFloor24AndChange);
717 // Turn lights off in sector(tag)
718 EV_TurnTagLightsOff(line);
723 // Blazing Door Raise (faster than TURBO!)
724 EV_DoDoor (line,BlazeRaise);
729 // Blazing Door Open (faster than TURBO!)
730 EV_DoDoor (line,BlazeOpen);
735 // Build Stairs Turbo 16
736 EV_BuildStairs(line,turbo16);
741 // Blazing Door Close (faster than TURBO!)
742 EV_DoDoor (line,BlazeClose);
747 // Raise floor to nearest surr. floor
748 EV_DoFloor(line,raiseFloorToNearest);
753 // Blazing PlatDownWaitUpStay
754 EV_DoPlat(line,blazeDWUS,0);
760 G_SecretExitLevel ();
764 // TELEPORT MonsterONLY
767 EV_Teleport( line, side, thing );
774 EV_DoFloor(line,raiseFloorTurbo);
779 // Silent Ceiling Crush & Raise
780 EV_DoCeiling(line,silentCrushAndRaise);
784 // RETRIGGERS. All from here till end.
787 EV_DoCeiling( line, lowerAndCrush );
791 // Ceiling Crush and Raise
792 EV_DoCeiling(line,crushAndRaise);
796 // Ceiling Crush Stop
797 EV_CeilingCrushStop(line);
802 EV_DoDoor(line,Close);
807 EV_DoDoor(line,Close30ThenOpen);
811 // Fast Ceiling Crush & Raise
812 EV_DoCeiling(line,fastCrushAndRaise);
817 EV_LightTurnOn(line,35);
821 // Light Turn On - brightest near
822 EV_LightTurnOn(line,0);
827 EV_LightTurnOn(line,255);
831 // Lower Floor To Lowest
832 EV_DoFloor( line, lowerFloorToLowest );
837 EV_DoFloor(line,lowerFloor);
842 EV_DoFloor(line,lowerAndChange);
847 EV_DoDoor(line,Open);
851 // Perpetual Platform Raise
852 EV_DoPlat(line,perpetualRaise,0);
857 EV_DoPlat(line,downWaitUpStay,0);
867 EV_DoDoor(line,Normal);
872 EV_DoFloor(line,raiseFloor);
877 EV_DoFloor(line,raiseFloor24);
881 // Raise Floor 24 And Change
882 EV_DoFloor(line,raiseFloor24AndChange);
887 EV_DoFloor(line,raiseFloorCrush);
891 // Raise floor to nearest height
892 // and change texture.
893 EV_DoPlat(line,raiseToNearestAndChange,0);
897 // Raise floor to shortest texture height
898 // on either side of lines.
899 EV_DoFloor(line,raiseToTexture);
904 EV_Teleport( line, side, thing );
908 // Lower Floor (TURBO)
909 EV_DoFloor(line,turboLower);
913 // Blazing Door Raise (faster than TURBO!)
914 EV_DoDoor (line,BlazeRaise);
918 // Blazing Door Open (faster than TURBO!)
919 EV_DoDoor (line,BlazeOpen);
923 // Blazing Door Close (faster than TURBO!)
924 EV_DoDoor (line,BlazeClose);
928 // Blazing PlatDownWaitUpStay.
929 EV_DoPlat(line,blazeDWUS,0);
933 // TELEPORT MonsterONLY.
935 EV_Teleport( line, side, thing );
939 // Raise To Nearest Floor
940 EV_DoFloor(line,raiseFloorToNearest);
945 EV_DoFloor(line,raiseFloorTurbo);
953 // P_ShootSpecialLine - IMPACT SPECIALS
954 // Called when a thing shoots a special line.
963 // Impacts that other things can activate.
967 switch(line->special)
978 switch(line->special)
982 EV_DoFloor(line,raiseFloor);
983 P_ChangeSwitchTexture(line,0);
988 EV_DoDoor(line,Open);
989 P_ChangeSwitchTexture(line,1);
993 // RAISE FLOOR NEAR AND CHANGE
994 EV_DoPlat(line,raiseToNearestAndChange,0);
995 P_ChangeSwitchTexture(line,0);
1003 // P_PlayerInSpecialSector
1004 // Called every tic frame
1005 // that the player origin is in a special sector
1007 void P_PlayerInSpecialSector (player_t* player)
1011 sector = player->mo->subsector->sector;
1013 // Falling, not all the way down yet?
1014 if (player->mo->z != sector->floorheight)
1017 // Has hitten ground.
1018 switch (sector->special)
1022 if (!player->powers[pw_ironfeet])
1023 if (!(leveltime&0x1f))
1024 P_DamageMobj (player->mo, NULL, NULL, 10);
1029 if (!player->powers[pw_ironfeet])
1030 if (!(leveltime&0x1f))
1031 P_DamageMobj (player->mo, NULL, NULL, 5);
1035 // SUPER HELLSLIME DAMAGE
1038 if (!player->powers[pw_ironfeet]
1041 if (!(leveltime&0x1f))
1042 P_DamageMobj (player->mo, NULL, NULL, 20);
1048 player->secretcount++;
1049 sector->special = 0;
1053 // EXIT SUPER DAMAGE! (for E1M8 finale)
1054 player->cheats &= ~CF_GODMODE;
1056 if (!(leveltime&0x1f))
1057 P_DamageMobj (player->mo, NULL, NULL, 20);
1059 if (player->health <= 10)
1064 I_Error ("P_PlayerInSpecialSector: "
1065 "unknown special %i",
1076 // Animate planes, scroll walls, etc.
1081 void P_UpdateSpecials (void)
1090 if (levelTimer == true)
1093 if (!levelTimeCount)
1097 // ANIMATE FLATS AND TEXTURES GLOBALLY
1098 for (anim = anims ; anim < lastanim ; anim++)
1100 for (i=anim->basepic ; i<anim->basepic+anim->numpics ; i++)
1102 pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics );
1103 if (anim->istexture)
1104 texturetranslation[i] = pic;
1106 flattranslation[i] = pic;
1111 // ANIMATE LINE SPECIALS
1112 for (i = 0; i < numlinespecials; i++)
1114 line = linespeciallist[i];
1115 switch(line->special)
1118 // EFFECT FIRSTCOL SCROLL +
1119 sides[line->sidenum[0]].textureoffset += FRACUNIT;
1126 for (i = 0; i < MAXBUTTONS; i++)
1127 if (buttonlist[i].btimer)
1129 buttonlist[i].btimer--;
1130 if (!buttonlist[i].btimer)
1132 switch(buttonlist[i].where)
1135 sides[buttonlist[i].line->sidenum[0]].toptexture =
1136 buttonlist[i].btexture;
1140 sides[buttonlist[i].line->sidenum[0]].midtexture =
1141 buttonlist[i].btexture;
1145 sides[buttonlist[i].line->sidenum[0]].bottomtexture =
1146 buttonlist[i].btexture;
1149 S_StartSound((mobj_t *)&buttonlist[i].soundorg,sfx_swtchn);
1150 memset(&buttonlist[i],0,sizeof(button_t));
1159 // Special Stuff that can not be categorized
1161 int EV_DoDonut(line_t* line)
1173 while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
1175 s1 = §ors[secnum];
1177 // ALREADY MOVING? IF SO, KEEP GOING...
1178 if (s1->specialdata)
1182 s2 = getNextSector(s1->lines[0],s1);
1183 for (i = 0;i < s2->linecount;i++)
1185 if ((!s2->lines[i]->flags & ML_TWOSIDED) ||
1186 (s2->lines[i]->backsector == s1))
1188 s3 = s2->lines[i]->backsector;
1190 // Spawn rising slime
1191 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
1192 P_AddThinker (&floor->thinker);
1193 s2->specialdata = floor;
1194 floor->thinker.function = T_MoveFloor;
1195 floor->type = donutRaise;
1196 floor->crush = false;
1197 floor->direction = 1;
1199 floor->speed = FLOORSPEED / 2;
1200 floor->texture = s3->floorpic;
1201 floor->newspecial = 0;
1202 floor->floordestheight = s3->floorheight;
1204 // Spawn lowering donut-hole
1205 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
1206 P_AddThinker (&floor->thinker);
1207 s1->specialdata = floor;
1208 floor->thinker.function = T_MoveFloor;
1209 floor->type = lowerFloor;
1210 floor->crush = false;
1211 floor->direction = -1;
1213 floor->speed = FLOORSPEED / 2;
1214 floor->floordestheight = s3->floorheight;
1229 // After the map has been loaded, scan for specials
1230 // that spawn thinkers
1232 short numlinespecials;
1233 line_t* linespeciallist[MAXLINEANIMS];
1236 // Parses command line parameters.
1237 void P_SpawnSpecials (void)
1242 // See if -TIMER needs to be used.
1245 i = M_CheckParm("-avg");
1246 if (i && deathmatch)
1249 levelTimeCount = 20 * 60 * 35;
1252 i = M_CheckParm("-timer");
1253 if (i && deathmatch)
1256 time = atoi(myargv[i+1]) * 60 * 35;
1258 levelTimeCount = time;
1261 // Init special SECTORs.
1263 for (i=0 ; i<numsectors ; i++, sector++)
1265 if (!sector->special)
1268 switch (sector->special)
1271 // FLICKERING LIGHTS
1272 P_SpawnLightFlash (sector);
1277 P_SpawnStrobeFlash(sector,FASTDARK,0);
1282 P_SpawnStrobeFlash(sector,SLOWDARK,0);
1286 // STROBE FAST/DEATH SLIME
1287 P_SpawnStrobeFlash(sector,FASTDARK,0);
1288 sector->special = 4;
1293 P_SpawnGlowingLight(sector);
1301 // DOOR CLOSE IN 30 SECONDS
1302 P_SpawnDoorCloseIn30 (sector);
1307 P_SpawnStrobeFlash (sector, SLOWDARK, 1);
1312 P_SpawnStrobeFlash (sector, FASTDARK, 1);
1316 // DOOR RAISE IN 5 MINUTES
1317 P_SpawnDoorRaiseIn5Mins (sector);
1321 P_SpawnFireFlicker(sector);
1327 // Init line EFFECTs
1328 numlinespecials = 0;
1329 for (i = 0;i < numlines; i++)
1331 switch(lines[i].special)
1334 // EFFECT FIRSTCOL SCROLL+
1335 linespeciallist[numlinespecials] = &lines[i];
1342 // Init other misc stuff
1343 for (i = 0;i < MAXCEILINGS;i++)
1344 activeceilings[i] = NULL;
1346 for (i = 0;i < MAXPLATS;i++)
1347 activeplats[i] = NULL;
1349 for (i = 0;i < MAXBUTTONS;i++)
1350 memset(&buttonlist[i],0,sizeof(button_t));
1352 // UNUSED: no horizonal sliders.
1353 // P_InitSlidingDoorFrames();