1153 lines
29 KiB
C
1153 lines
29 KiB
C
|
|
||
|
//**************************************************************************
|
||
|
//**
|
||
|
//** p_spec.c : Heretic 2 : Raven Software, Corp.
|
||
|
//**
|
||
|
//** $RCSfile: p_spec.c,v $
|
||
|
//** $Revision: 1.67 $
|
||
|
//** $Date: 96/01/06 18:37:33 $
|
||
|
//** $Author: bgokey $
|
||
|
//**
|
||
|
//**************************************************************************
|
||
|
|
||
|
// HEADER FILES ------------------------------------------------------------
|
||
|
|
||
|
#include "h2def.h"
|
||
|
#include "p_local.h"
|
||
|
#include "soundst.h"
|
||
|
|
||
|
// MACROS ------------------------------------------------------------------
|
||
|
|
||
|
#define MAX_TAGGED_LINES 64
|
||
|
|
||
|
// TYPES -------------------------------------------------------------------
|
||
|
|
||
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||
|
|
||
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||
|
|
||
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||
|
|
||
|
static boolean CheckedLockedDoor(mobj_t *mo, byte lock);
|
||
|
|
||
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||
|
|
||
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||
|
|
||
|
int *TerrainTypes;
|
||
|
struct
|
||
|
{
|
||
|
char *name;
|
||
|
int type;
|
||
|
} TerrainTypeDefs[] =
|
||
|
{
|
||
|
{ "X_005", FLOOR_WATER },
|
||
|
{ "X_001", FLOOR_LAVA },
|
||
|
{ "X_009", FLOOR_SLUDGE },
|
||
|
{ "F_033", FLOOR_ICE },
|
||
|
{ "END", -1 }
|
||
|
};
|
||
|
|
||
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||
|
|
||
|
static struct
|
||
|
{
|
||
|
line_t *line;
|
||
|
int lineTag;
|
||
|
} TaggedLines[MAX_TAGGED_LINES];
|
||
|
static int TaggedLineCount;
|
||
|
|
||
|
mobj_t LavaInflictor;
|
||
|
|
||
|
// CODE --------------------------------------------------------------------
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// P_InitLava
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void P_InitLava(void)
|
||
|
{
|
||
|
memset(&LavaInflictor, 0, sizeof(mobj_t));
|
||
|
LavaInflictor.type = MT_CIRCLEFLAME;
|
||
|
LavaInflictor.flags2 = MF2_FIREDAMAGE|MF2_NODMGTHRUST;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// P_InitTerrainTypes
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void P_InitTerrainTypes(void)
|
||
|
{
|
||
|
int i;
|
||
|
int lump;
|
||
|
int size;
|
||
|
|
||
|
size = (numflats+1)*sizeof(int);
|
||
|
TerrainTypes = Z_Malloc(size, PU_STATIC, 0);
|
||
|
memset(TerrainTypes, 0, size);
|
||
|
for(i = 0; TerrainTypeDefs[i].type != -1; i++)
|
||
|
{
|
||
|
lump = W_CheckNumForName(TerrainTypeDefs[i].name);
|
||
|
if(lump != -1)
|
||
|
{
|
||
|
TerrainTypes[lump-firstflat] = TerrainTypeDefs[i].type;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// getSide
|
||
|
//
|
||
|
// Will return a side_t* given the number of the current sector, the
|
||
|
// line number, and the side (0/1) that you want.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
/*
|
||
|
side_t *getSide(int currentSector, int line, int side)
|
||
|
{
|
||
|
return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ];
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// getSector
|
||
|
//
|
||
|
// Will return a sector_t* given the number of the current sector, the
|
||
|
// line number, and the side (0/1) that you want.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
/*
|
||
|
sector_t *getSector(int currentSector, int line, int side)
|
||
|
{
|
||
|
return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// twoSided
|
||
|
//
|
||
|
// Given the sector number and the line number, will tell you whether
|
||
|
// the line is two-sided or not.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
/*
|
||
|
int twoSided(int sector, int line)
|
||
|
{
|
||
|
return (sectors[sector].lines[line])->flags & ML_TWOSIDED;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// Return sector_t * of sector next to current. NULL if not two-sided line
|
||
|
//
|
||
|
//==================================================================
|
||
|
sector_t *getNextSector(line_t *line,sector_t *sec)
|
||
|
{
|
||
|
if (!(line->flags & ML_TWOSIDED))
|
||
|
return NULL;
|
||
|
|
||
|
if (line->frontsector == sec)
|
||
|
return line->backsector;
|
||
|
|
||
|
return line->frontsector;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
|
||
|
//
|
||
|
//==================================================================
|
||
|
fixed_t P_FindLowestFloorSurrounding(sector_t *sec)
|
||
|
{
|
||
|
int i;
|
||
|
line_t *check;
|
||
|
sector_t *other;
|
||
|
fixed_t floor = sec->floorheight;
|
||
|
|
||
|
for (i=0 ;i < sec->linecount ; i++)
|
||
|
{
|
||
|
check = sec->lines[i];
|
||
|
other = getNextSector(check,sec);
|
||
|
if (!other)
|
||
|
continue;
|
||
|
if (other->floorheight < floor)
|
||
|
floor = other->floorheight;
|
||
|
}
|
||
|
return floor;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
|
||
|
//
|
||
|
//==================================================================
|
||
|
fixed_t P_FindHighestFloorSurrounding(sector_t *sec)
|
||
|
{
|
||
|
int i;
|
||
|
line_t *check;
|
||
|
sector_t *other;
|
||
|
fixed_t floor = -500*FRACUNIT;
|
||
|
|
||
|
for (i=0 ;i < sec->linecount ; i++)
|
||
|
{
|
||
|
check = sec->lines[i];
|
||
|
other = getNextSector(check,sec);
|
||
|
if (!other)
|
||
|
continue;
|
||
|
if (other->floorheight > floor)
|
||
|
floor = other->floorheight;
|
||
|
}
|
||
|
return floor;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
|
||
|
//
|
||
|
//==================================================================
|
||
|
fixed_t P_FindNextHighestFloor(sector_t *sec,int currentheight)
|
||
|
{
|
||
|
int i;
|
||
|
int h;
|
||
|
int min;
|
||
|
line_t *check;
|
||
|
sector_t *other;
|
||
|
fixed_t height = currentheight;
|
||
|
fixed_t heightlist[20]; // 20 adjoining sectors max!
|
||
|
|
||
|
for (i =0,h = 0 ;i < sec->linecount ; i++)
|
||
|
{
|
||
|
check = sec->lines[i];
|
||
|
other = getNextSector(check,sec);
|
||
|
if (!other)
|
||
|
continue;
|
||
|
if (other->floorheight > height)
|
||
|
heightlist[h++] = other->floorheight;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Find lowest height in list
|
||
|
//
|
||
|
min = heightlist[0];
|
||
|
for (i = 1;i < h;i++)
|
||
|
if (heightlist[i] < min)
|
||
|
min = heightlist[i];
|
||
|
|
||
|
return min;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// FIND LOWEST CEILING IN THE SURROUNDING SECTORS
|
||
|
//
|
||
|
//==================================================================
|
||
|
fixed_t P_FindLowestCeilingSurrounding(sector_t *sec)
|
||
|
{
|
||
|
int i;
|
||
|
line_t *check;
|
||
|
sector_t *other;
|
||
|
fixed_t height = MAXINT;
|
||
|
|
||
|
for (i=0 ;i < sec->linecount ; i++)
|
||
|
{
|
||
|
check = sec->lines[i];
|
||
|
other = getNextSector(check,sec);
|
||
|
if (!other)
|
||
|
continue;
|
||
|
if (other->ceilingheight < height)
|
||
|
height = other->ceilingheight;
|
||
|
}
|
||
|
return height;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
|
||
|
//
|
||
|
//==================================================================
|
||
|
fixed_t P_FindHighestCeilingSurrounding(sector_t *sec)
|
||
|
{
|
||
|
int i;
|
||
|
line_t *check;
|
||
|
sector_t *other;
|
||
|
fixed_t height = 0;
|
||
|
|
||
|
for (i=0 ;i < sec->linecount ; i++)
|
||
|
{
|
||
|
check = sec->lines[i];
|
||
|
other = getNextSector(check,sec);
|
||
|
if (!other)
|
||
|
continue;
|
||
|
if (other->ceilingheight > height)
|
||
|
height = other->ceilingheight;
|
||
|
}
|
||
|
return height;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
|
||
|
//
|
||
|
//==================================================================
|
||
|
|
||
|
/*
|
||
|
int P_FindSectorFromLineTag(line_t *line,int start)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i=start+1;i<numsectors;i++)
|
||
|
if (sectors[i].tag == line->arg1)
|
||
|
return i;
|
||
|
return -1;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//=========================================================================
|
||
|
//
|
||
|
// P_FindSectorFromTag
|
||
|
//
|
||
|
//=========================================================================
|
||
|
|
||
|
int P_FindSectorFromTag(int tag, int start)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = start+1; i < numsectors; i++)
|
||
|
{
|
||
|
if(sectors[i].tag == tag)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//==================================================================
|
||
|
//
|
||
|
// Find minimum light from an adjacent sector
|
||
|
//
|
||
|
//==================================================================
|
||
|
|
||
|
/*
|
||
|
int P_FindMinSurroundingLight(sector_t *sector,int max)
|
||
|
{
|
||
|
int i;
|
||
|
int min;
|
||
|
line_t *line;
|
||
|
sector_t *check;
|
||
|
|
||
|
min = max;
|
||
|
for (i=0 ; i < sector->linecount ; i++)
|
||
|
{
|
||
|
line = sector->lines[i];
|
||
|
check = getNextSector(line,sector);
|
||
|
if (!check)
|
||
|
continue;
|
||
|
if (check->lightlevel < min)
|
||
|
min = check->lightlevel;
|
||
|
}
|
||
|
return min;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//=========================================================================
|
||
|
//
|
||
|
// EV_SectorSoundChange
|
||
|
//
|
||
|
//=========================================================================
|
||
|
|
||
|
boolean EV_SectorSoundChange(byte *args)
|
||
|
{
|
||
|
int secNum;
|
||
|
boolean rtn;
|
||
|
|
||
|
if(!args[0])
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
secNum = -1;
|
||
|
rtn = false;
|
||
|
while((secNum = P_FindSectorFromTag(args[0], secNum)) >= 0)
|
||
|
{
|
||
|
sectors[secNum].seqType = args[1];
|
||
|
rtn = true;
|
||
|
}
|
||
|
return rtn;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// CheckedLockedDoor
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
static boolean CheckedLockedDoor(mobj_t *mo, byte lock)
|
||
|
{
|
||
|
extern char *TextKeyMessages[11];
|
||
|
char LockedBuffer[80];
|
||
|
|
||
|
if(!mo->player)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if(!lock)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if(!(mo->player->keys&(1<<(lock-1))))
|
||
|
{
|
||
|
sprintf(LockedBuffer, "YOU NEED THE %s\n",
|
||
|
TextKeyMessages[lock-1]);
|
||
|
P_SetMessage(mo->player, LockedBuffer, true);
|
||
|
S_StartSound(mo, SFX_DOOR_LOCKED);
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// EV_LineSearchForPuzzleItem
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
boolean EV_LineSearchForPuzzleItem(line_t *line, byte *args, mobj_t *mo)
|
||
|
{
|
||
|
player_t *player;
|
||
|
int i;
|
||
|
artitype_t type,arti;
|
||
|
|
||
|
if (!mo) return false;
|
||
|
player = mo->player;
|
||
|
if (!player) return false;
|
||
|
|
||
|
// Search player's inventory for puzzle items
|
||
|
for (i=0; i<player->artifactCount; i++)
|
||
|
{
|
||
|
arti = player->inventory[i].type;
|
||
|
type = arti - arti_firstpuzzitem;
|
||
|
if (type < 0) continue;
|
||
|
if (type == line->arg1)
|
||
|
{
|
||
|
// A puzzle item was found for the line
|
||
|
if (P_UseArtifact(player, arti))
|
||
|
{
|
||
|
// A puzzle item was found for the line
|
||
|
P_PlayerRemoveArtifact(player, i);
|
||
|
if(player == &players[consoleplayer])
|
||
|
{
|
||
|
if(arti < arti_firstpuzzitem)
|
||
|
{
|
||
|
S_StartSound(NULL, SFX_ARTIFACT_USE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
S_StartSound(NULL, SFX_PUZZLE_SUCCESS);
|
||
|
}
|
||
|
ArtifactFlash = 4;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
EVENTS
|
||
|
|
||
|
Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
//============================================================================
|
||
|
//
|
||
|
// P_ExecuteLineSpecial
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
boolean P_ExecuteLineSpecial(int special, byte *args, line_t *line, int side,
|
||
|
mobj_t *mo)
|
||
|
{
|
||
|
boolean buttonSuccess;
|
||
|
|
||
|
buttonSuccess = false;
|
||
|
switch(special)
|
||
|
{
|
||
|
case 1: // Poly Start Line
|
||
|
break;
|
||
|
case 2: // Poly Rotate Left
|
||
|
buttonSuccess = EV_RotatePoly(line, args, 1, false);
|
||
|
break;
|
||
|
case 3: // Poly Rotate Right
|
||
|
buttonSuccess = EV_RotatePoly(line, args, -1, false);
|
||
|
break;
|
||
|
case 4: // Poly Move
|
||
|
buttonSuccess = EV_MovePoly(line, args, false, false);
|
||
|
break;
|
||
|
case 5: // Poly Explicit Line: Only used in initialization
|
||
|
break;
|
||
|
case 6: // Poly Move Times 8
|
||
|
buttonSuccess = EV_MovePoly(line, args, true, false);
|
||
|
break;
|
||
|
case 7: // Poly Door Swing
|
||
|
buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SWING);
|
||
|
break;
|
||
|
case 8: // Poly Door Slide
|
||
|
buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SLIDE);
|
||
|
break;
|
||
|
case 10: // Door Close
|
||
|
buttonSuccess = EV_DoDoor(line, args, DREV_CLOSE);
|
||
|
break;
|
||
|
case 11: // Door Open
|
||
|
if(!args[0])
|
||
|
{
|
||
|
buttonSuccess = EV_VerticalDoor(line, mo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
buttonSuccess = EV_DoDoor(line, args, DREV_OPEN);
|
||
|
}
|
||
|
break;
|
||
|
case 12: // Door Raise
|
||
|
if(!args[0])
|
||
|
{
|
||
|
buttonSuccess = EV_VerticalDoor(line, mo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL);
|
||
|
}
|
||
|
break;
|
||
|
case 13: // Door Locked_Raise
|
||
|
if(CheckedLockedDoor(mo, args[3]))
|
||
|
{
|
||
|
if(!args[0])
|
||
|
{
|
||
|
buttonSuccess = EV_VerticalDoor(line, mo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 20: // Floor Lower by Value
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE);
|
||
|
break;
|
||
|
case 21: // Floor Lower to Lowest
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORTOLOWEST);
|
||
|
break;
|
||
|
case 22: // Floor Lower to Nearest
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOOR);
|
||
|
break;
|
||
|
case 23: // Floor Raise by Value
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE);
|
||
|
break;
|
||
|
case 24: // Floor Raise to Highest
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOOR);
|
||
|
break;
|
||
|
case 25: // Floor Raise to Nearest
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORTONEAREST);
|
||
|
break;
|
||
|
case 26: // Stairs Build Down Normal
|
||
|
buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_NORMAL);
|
||
|
break;
|
||
|
case 27: // Build Stairs Up Normal
|
||
|
buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_NORMAL);
|
||
|
break;
|
||
|
case 28: // Floor Raise and Crush
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORCRUSH);
|
||
|
break;
|
||
|
case 29: // Build Pillar (no crushing)
|
||
|
buttonSuccess = EV_BuildPillar(line, args, false);
|
||
|
break;
|
||
|
case 30: // Open Pillar
|
||
|
buttonSuccess = EV_OpenPillar(line, args);
|
||
|
break;
|
||
|
case 31: // Stairs Build Down Sync
|
||
|
buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_SYNC);
|
||
|
break;
|
||
|
case 32: // Build Stairs Up Sync
|
||
|
buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_SYNC);
|
||
|
break;
|
||
|
case 35: // Raise Floor by Value Times 8
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEBYVALUETIMES8);
|
||
|
break;
|
||
|
case 36: // Lower Floor by Value Times 8
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERBYVALUETIMES8);
|
||
|
break;
|
||
|
case 40: // Ceiling Lower by Value
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE);
|
||
|
break;
|
||
|
case 41: // Ceiling Raise by Value
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE);
|
||
|
break;
|
||
|
case 42: // Ceiling Crush and Raise
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHANDRAISE);
|
||
|
break;
|
||
|
case 43: // Ceiling Lower and Crush
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERANDCRUSH);
|
||
|
break;
|
||
|
case 44: // Ceiling Crush Stop
|
||
|
buttonSuccess = EV_CeilingCrushStop(line, args);
|
||
|
break;
|
||
|
case 45: // Ceiling Crush Raise and Stay
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHRAISEANDSTAY);
|
||
|
break;
|
||
|
case 46: // Floor Crush Stop
|
||
|
buttonSuccess = EV_FloorCrushStop(line, args);
|
||
|
break;
|
||
|
case 60: // Plat Perpetual Raise
|
||
|
buttonSuccess = EV_DoPlat(line, args, PLAT_PERPETUALRAISE, 0);
|
||
|
break;
|
||
|
case 61: // Plat Stop
|
||
|
EV_StopPlat(line, args);
|
||
|
break;
|
||
|
case 62: // Plat Down-Wait-Up-Stay
|
||
|
buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNWAITUPSTAY, 0);
|
||
|
break;
|
||
|
case 63: // Plat Down-by-Value*8-Wait-Up-Stay
|
||
|
buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNBYVALUEWAITUPSTAY,
|
||
|
0);
|
||
|
break;
|
||
|
case 64: // Plat Up-Wait-Down-Stay
|
||
|
buttonSuccess = EV_DoPlat(line, args, PLAT_UPWAITDOWNSTAY, 0);
|
||
|
break;
|
||
|
case 65: // Plat Up-by-Value*8-Wait-Down-Stay
|
||
|
buttonSuccess = EV_DoPlat(line, args, PLAT_UPBYVALUEWAITDOWNSTAY,
|
||
|
0);
|
||
|
break;
|
||
|
case 66: // Floor Lower Instant * 8
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERTIMES8INSTANT);
|
||
|
break;
|
||
|
case 67: // Floor Raise Instant * 8
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_RAISETIMES8INSTANT);
|
||
|
break;
|
||
|
case 68: // Floor Move to Value * 8
|
||
|
buttonSuccess = EV_DoFloor(line, args, FLEV_MOVETOVALUETIMES8);
|
||
|
break;
|
||
|
case 69: // Ceiling Move to Value * 8
|
||
|
buttonSuccess = EV_DoCeiling(line, args, CLEV_MOVETOVALUETIMES8);
|
||
|
break;
|
||
|
case 70: // Teleport
|
||
|
if(side == 0)
|
||
|
{ // Only teleport when crossing the front side of a line
|
||
|
buttonSuccess = EV_Teleport(args[0], mo, true);
|
||
|
}
|
||
|
break;
|
||
|
case 71: // Teleport, no fog
|
||
|
if(side == 0)
|
||
|
{ // Only teleport when crossing the front side of a line
|
||
|
buttonSuccess = EV_Teleport(args[0], mo, false);
|
||
|
}
|
||
|
break;
|
||
|
case 72: // Thrust Mobj
|
||
|
if(!side) // Only thrust on side 0
|
||
|
{
|
||
|
P_ThrustMobj(mo, args[0]*(ANGLE_90/64), args[1]<<FRACBITS);
|
||
|
buttonSuccess = 1;
|
||
|
}
|
||
|
break;
|
||
|
case 73: // Damage Mobj
|
||
|
if(args[0])
|
||
|
{
|
||
|
P_DamageMobj(mo, NULL, NULL, args[0]);
|
||
|
}
|
||
|
else
|
||
|
{ // If arg1 is zero, then guarantee a kill
|
||
|
P_DamageMobj(mo, NULL, NULL, 10000);
|
||
|
}
|
||
|
buttonSuccess = 1;
|
||
|
break;
|
||
|
case 74: // Teleport_NewMap
|
||
|
if(side == 0)
|
||
|
{ // Only teleport when crossing the front side of a line
|
||
|
if(!(mo && mo->player && mo->player->playerstate
|
||
|
== PST_DEAD)) // Players must be alive to teleport
|
||
|
{
|
||
|
G_Completed(args[0], args[1]);
|
||
|
buttonSuccess = true;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 75: // Teleport_EndGame
|
||
|
if(side == 0)
|
||
|
{ // Only teleport when crossing the front side of a line
|
||
|
if(!(mo && mo->player && mo->player->playerstate
|
||
|
== PST_DEAD)) // Players must be alive to teleport
|
||
|
{
|
||
|
buttonSuccess = true;
|
||
|
if(deathmatch)
|
||
|
{ // Winning in deathmatch just goes back to map 1
|
||
|
G_Completed(1, 0);
|
||
|
}
|
||
|
else
|
||
|
{ // Passing -1, -1 to G_Completed() starts the Finale
|
||
|
G_Completed(-1, -1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 80: // ACS_Execute
|
||
|
buttonSuccess =
|
||
|
P_StartACS(args[0], args[1], &args[2], mo, line, side);
|
||
|
break;
|
||
|
case 81: // ACS_Suspend
|
||
|
buttonSuccess = P_SuspendACS(args[0], args[1]);
|
||
|
break;
|
||
|
case 82: // ACS_Terminate
|
||
|
buttonSuccess = P_TerminateACS(args[0], args[1]);
|
||
|
break;
|
||
|
case 83: // ACS_LockedExecute
|
||
|
buttonSuccess = P_StartLockedACS(line, args, mo, side);
|
||
|
break;
|
||
|
case 90: // Poly Rotate Left Override
|
||
|
buttonSuccess = EV_RotatePoly(line, args, 1, true);
|
||
|
break;
|
||
|
case 91: // Poly Rotate Right Override
|
||
|
buttonSuccess = EV_RotatePoly(line, args, -1, true);
|
||
|
break;
|
||
|
case 92: // Poly Move Override
|
||
|
buttonSuccess = EV_MovePoly(line, args, false, true);
|
||
|
break;
|
||
|
case 93: // Poly Move Times 8 Override
|
||
|
buttonSuccess = EV_MovePoly(line, args, true, true);
|
||
|
break;
|
||
|
case 94: // Build Pillar Crush
|
||
|
buttonSuccess = EV_BuildPillar(line, args, true);
|
||
|
break;
|
||
|
case 95: // Lower Floor and Ceiling
|
||
|
buttonSuccess = EV_DoFloorAndCeiling(line, args, false);
|
||
|
break;
|
||
|
case 96: // Raise Floor and Ceiling
|
||
|
buttonSuccess = EV_DoFloorAndCeiling(line, args, true);
|
||
|
break;
|
||
|
case 109: // Force Lightning
|
||
|
buttonSuccess = true;
|
||
|
P_ForceLightning();
|
||
|
break;
|
||
|
case 110: // Light Raise by Value
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_RAISEBYVALUE);
|
||
|
break;
|
||
|
case 111: // Light Lower by Value
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_LOWERBYVALUE);
|
||
|
break;
|
||
|
case 112: // Light Change to Value
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_CHANGETOVALUE);
|
||
|
break;
|
||
|
case 113: // Light Fade
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_FADE);
|
||
|
break;
|
||
|
case 114: // Light Glow
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_GLOW);
|
||
|
break;
|
||
|
case 115: // Light Flicker
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_FLICKER);
|
||
|
break;
|
||
|
case 116: // Light Strobe
|
||
|
buttonSuccess = EV_SpawnLight(line, args, LITE_STROBE);
|
||
|
break;
|
||
|
case 120: // Quake Tremor
|
||
|
buttonSuccess = A_LocalQuake(args, mo);
|
||
|
break;
|
||
|
case 129: // UsePuzzleItem
|
||
|
buttonSuccess = EV_LineSearchForPuzzleItem(line, args, mo);
|
||
|
break;
|
||
|
case 130: // Thing_Activate
|
||
|
buttonSuccess = EV_ThingActivate(args[0]);
|
||
|
break;
|
||
|
case 131: // Thing_Deactivate
|
||
|
buttonSuccess = EV_ThingDeactivate(args[0]);
|
||
|
break;
|
||
|
case 132: // Thing_Remove
|
||
|
buttonSuccess = EV_ThingRemove(args[0]);
|
||
|
break;
|
||
|
case 133: // Thing_Destroy
|
||
|
buttonSuccess = EV_ThingDestroy(args[0]);
|
||
|
break;
|
||
|
case 134: // Thing_Projectile
|
||
|
buttonSuccess = EV_ThingProjectile(args, 0);
|
||
|
break;
|
||
|
case 135: // Thing_Spawn
|
||
|
buttonSuccess = EV_ThingSpawn(args, 1);
|
||
|
break;
|
||
|
case 136: // Thing_ProjectileGravity
|
||
|
buttonSuccess = EV_ThingProjectile(args, 1);
|
||
|
break;
|
||
|
case 137: // Thing_SpawnNoFog
|
||
|
buttonSuccess = EV_ThingSpawn(args, 0);
|
||
|
break;
|
||
|
case 138: // Floor_Waggle
|
||
|
buttonSuccess = EV_StartFloorWaggle(args[0], args[1],
|
||
|
args[2], args[3], args[4]);
|
||
|
break;
|
||
|
case 140: // Sector_SoundChange
|
||
|
buttonSuccess = EV_SectorSoundChange(args);
|
||
|
break;
|
||
|
|
||
|
// Line specials only processed during level initialization
|
||
|
// 100: Scroll_Texture_Left
|
||
|
// 101: Scroll_Texture_Right
|
||
|
// 102: Scroll_Texture_Up
|
||
|
// 103: Scroll_Texture_Down
|
||
|
// 121: Line_SetIdentification
|
||
|
|
||
|
// Inert Line specials
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return buttonSuccess;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// P_ActivateLine
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
boolean P_ActivateLine(line_t *line, mobj_t *mo, int side, int activationType)
|
||
|
{
|
||
|
int lineActivation;
|
||
|
boolean repeat;
|
||
|
boolean buttonSuccess;
|
||
|
|
||
|
lineActivation = GET_SPAC(line->flags);
|
||
|
if(lineActivation != activationType)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if(!mo->player && !(mo->flags&MF_MISSILE))
|
||
|
{
|
||
|
if(lineActivation != SPAC_MCROSS)
|
||
|
{ // currently, monsters can only activate the MCROSS activation type
|
||
|
return false;
|
||
|
}
|
||
|
if(line->flags & ML_SECRET)
|
||
|
return false; // never open secret doors
|
||
|
}
|
||
|
repeat = line->flags&ML_REPEAT_SPECIAL;
|
||
|
buttonSuccess = false;
|
||
|
|
||
|
buttonSuccess = P_ExecuteLineSpecial(line->special, &line->arg1, line,
|
||
|
side, mo);
|
||
|
if(!repeat && buttonSuccess)
|
||
|
{ // clear the special on non-retriggerable lines
|
||
|
line->special = 0;
|
||
|
}
|
||
|
if((lineActivation == SPAC_USE || lineActivation == SPAC_IMPACT)
|
||
|
&& buttonSuccess)
|
||
|
{
|
||
|
P_ChangeSwitchTexture(line, repeat);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC P_PlayerInSpecialSector
|
||
|
//
|
||
|
// Called every tic frame that the player origin is in a special sector.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void P_PlayerInSpecialSector(player_t *player)
|
||
|
{
|
||
|
sector_t *sector;
|
||
|
static int pushTab[3] =
|
||
|
{
|
||
|
2048*5,
|
||
|
2048*10,
|
||
|
2048*25
|
||
|
};
|
||
|
|
||
|
sector = player->mo->subsector->sector;
|
||
|
if(player->mo->z != sector->floorheight)
|
||
|
{ // Player is not touching the floor
|
||
|
return;
|
||
|
}
|
||
|
switch(sector->special)
|
||
|
{
|
||
|
case 9: // SecretArea
|
||
|
player->secretcount++;
|
||
|
sector->special = 0;
|
||
|
break;
|
||
|
|
||
|
case 201: case 202: case 203: // Scroll_North_xxx
|
||
|
P_Thrust(player, ANG90, pushTab[sector->special-201]);
|
||
|
break;
|
||
|
case 204: case 205: case 206: // Scroll_East_xxx
|
||
|
P_Thrust(player, 0, pushTab[sector->special-204]);
|
||
|
break;
|
||
|
case 207: case 208: case 209: // Scroll_South_xxx
|
||
|
P_Thrust(player, ANG270, pushTab[sector->special-207]);
|
||
|
break;
|
||
|
case 210: case 211: case 212: // Scroll_West_xxx
|
||
|
P_Thrust(player, ANG180, pushTab[sector->special-210]);
|
||
|
break;
|
||
|
case 213: case 214: case 215: // Scroll_NorthWest_xxx
|
||
|
P_Thrust(player, ANG90+ANG45, pushTab[sector->special-213]);
|
||
|
break;
|
||
|
case 216: case 217: case 218: // Scroll_NorthEast_xxx
|
||
|
P_Thrust(player, ANG45, pushTab[sector->special-216]);
|
||
|
break;
|
||
|
case 219: case 220: case 221: // Scroll_SouthEast_xxx
|
||
|
P_Thrust(player, ANG270+ANG45, pushTab[sector->special-219]);
|
||
|
break;
|
||
|
case 222: case 223: case 224: // Scroll_SouthWest_xxx
|
||
|
P_Thrust(player, ANG180+ANG45, pushTab[sector->special-222]);
|
||
|
break;
|
||
|
|
||
|
case 40: case 41: case 42: case 43: case 44: case 45:
|
||
|
case 46: case 47: case 48: case 49: case 50: case 51:
|
||
|
// Wind specials are handled in (P_mobj):P_XYMovement
|
||
|
break;
|
||
|
|
||
|
case 26: // Stairs_Special1
|
||
|
case 27: // Stairs_Special2
|
||
|
// Used in (P_floor):ProcessStairSector
|
||
|
break;
|
||
|
|
||
|
case 198: // Lightning Special
|
||
|
case 199: // Lightning Flash special
|
||
|
case 200: // Sky2
|
||
|
// Used in (R_plane):R_Drawplanes
|
||
|
break;
|
||
|
default:
|
||
|
I_Error("P_PlayerInSpecialSector: "
|
||
|
"unknown special %i", sector->special);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// P_PlayerOnSpecialFlat
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void P_PlayerOnSpecialFlat(player_t *player, int floorType)
|
||
|
{
|
||
|
if(player->mo->z != player->mo->floorz)
|
||
|
{ // Player is not touching the floor
|
||
|
return;
|
||
|
}
|
||
|
switch(floorType)
|
||
|
{
|
||
|
case FLOOR_LAVA:
|
||
|
if(!(leveltime&31))
|
||
|
{
|
||
|
P_DamageMobj(player->mo, &LavaInflictor, NULL, 10);
|
||
|
S_StartSound(player->mo, SFX_LAVA_SIZZLE);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC P_UpdateSpecials
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void P_UpdateSpecials(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
// Handle buttons
|
||
|
for(i = 0; i < MAXBUTTONS; i++)
|
||
|
{
|
||
|
if(buttonlist[i].btimer)
|
||
|
{
|
||
|
buttonlist[i].btimer--;
|
||
|
if(!buttonlist[i].btimer)
|
||
|
{
|
||
|
switch(buttonlist[i].where)
|
||
|
{
|
||
|
case SWTCH_TOP:
|
||
|
sides[buttonlist[i].line->sidenum[0]].toptexture =
|
||
|
buttonlist[i].btexture;
|
||
|
break;
|
||
|
case SWTCH_MIDDLE:
|
||
|
sides[buttonlist[i].line->sidenum[0]].midtexture =
|
||
|
buttonlist[i].btexture;
|
||
|
break;
|
||
|
case SWTCH_BOTTOM:
|
||
|
sides[buttonlist[i].line->sidenum[0]].bottomtexture =
|
||
|
buttonlist[i].btexture;
|
||
|
break;
|
||
|
}
|
||
|
//S_StartSound((mobj_t *)&buttonlist[i].soundorg, sfx_switch);
|
||
|
memset(&buttonlist[i], 0, sizeof(button_t));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
SPECIAL SPAWNING
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
/*
|
||
|
================================================================================
|
||
|
= P_SpawnSpecials
|
||
|
=
|
||
|
= After the map has been loaded, scan for specials that
|
||
|
= spawn thinkers
|
||
|
=
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
short numlinespecials;
|
||
|
line_t *linespeciallist[MAXLINEANIMS];
|
||
|
|
||
|
void P_SpawnSpecials (void)
|
||
|
{
|
||
|
sector_t *sector;
|
||
|
int i;
|
||
|
|
||
|
//
|
||
|
// Init special SECTORs
|
||
|
//
|
||
|
sector = sectors;
|
||
|
for (i=0 ; i<numsectors ; i++, sector++)
|
||
|
{
|
||
|
if (!sector->special)
|
||
|
continue;
|
||
|
switch (sector->special)
|
||
|
{
|
||
|
case 1: // Phased light
|
||
|
// Hardcoded base, use sector->lightlevel as the index
|
||
|
P_SpawnPhasedLight(sector, 80, -1);
|
||
|
break;
|
||
|
case 2: // Phased light sequence start
|
||
|
P_SpawnLightSequence(sector, 1);
|
||
|
break;
|
||
|
// Specials 3 & 4 are used by the phased light sequences
|
||
|
|
||
|
/*
|
||
|
case 1: // FLICKERING LIGHTS
|
||
|
P_SpawnLightFlash (sector);
|
||
|
break;
|
||
|
case 2: // STROBE FAST
|
||
|
P_SpawnStrobeFlash(sector,FASTDARK,0);
|
||
|
break;
|
||
|
case 3: // STROBE SLOW
|
||
|
P_SpawnStrobeFlash(sector,SLOWDARK,0);
|
||
|
break;
|
||
|
case 4: // STROBE FAST/DEATH SLIME
|
||
|
P_SpawnStrobeFlash(sector,FASTDARK,0);
|
||
|
sector->special = 4;
|
||
|
break;
|
||
|
case 8: // GLOWING LIGHT
|
||
|
P_SpawnGlowingLight(sector);
|
||
|
break;
|
||
|
case 9: // SECRET SECTOR
|
||
|
totalsecret++;
|
||
|
break;
|
||
|
case 10: // DOOR CLOSE IN 30 SECONDS
|
||
|
P_SpawnDoorCloseIn30 (sector);
|
||
|
break;
|
||
|
case 12: // SYNC STROBE SLOW
|
||
|
P_SpawnStrobeFlash (sector, SLOWDARK, 1);
|
||
|
break;
|
||
|
case 13: // SYNC STROBE FAST
|
||
|
P_SpawnStrobeFlash (sector, FASTDARK, 1);
|
||
|
break;
|
||
|
case 14: // DOOR RAISE IN 5 MINUTES
|
||
|
P_SpawnDoorRaiseIn5Mins (sector, i);
|
||
|
break;
|
||
|
*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Init line EFFECTs
|
||
|
//
|
||
|
numlinespecials = 0;
|
||
|
TaggedLineCount = 0;
|
||
|
for(i = 0; i < numlines; i++)
|
||
|
{
|
||
|
switch(lines[i].special)
|
||
|
{
|
||
|
case 100: // Scroll_Texture_Left
|
||
|
case 101: // Scroll_Texture_Right
|
||
|
case 102: // Scroll_Texture_Up
|
||
|
case 103: // Scroll_Texture_Down
|
||
|
linespeciallist[numlinespecials] = &lines[i];
|
||
|
numlinespecials++;
|
||
|
break;
|
||
|
case 121: // Line_SetIdentification
|
||
|
if(lines[i].arg1)
|
||
|
{
|
||
|
if(TaggedLineCount == MAX_TAGGED_LINES)
|
||
|
{
|
||
|
I_Error("P_SpawnSpecials: MAX_TAGGED_LINES "
|
||
|
"(%d) exceeded.", MAX_TAGGED_LINES);
|
||
|
}
|
||
|
TaggedLines[TaggedLineCount].line = &lines[i];
|
||
|
TaggedLines[TaggedLineCount++].lineTag
|
||
|
= lines[i].arg1;
|
||
|
}
|
||
|
lines[i].special = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Init other misc stuff
|
||
|
//
|
||
|
for (i = 0;i < MAXCEILINGS;i++)
|
||
|
activeceilings[i] = NULL;
|
||
|
for (i = 0;i < MAXPLATS;i++)
|
||
|
activeplats[i] = NULL;
|
||
|
for (i = 0;i < MAXBUTTONS;i++)
|
||
|
memset(&buttonlist[i],0,sizeof(button_t));
|
||
|
|
||
|
// Initialize flat and texture animations
|
||
|
P_InitFTAnims();
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// P_FindLine
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
line_t *P_FindLine(int lineTag, int *searchPosition)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = *searchPosition+1; i < TaggedLineCount; i++)
|
||
|
{
|
||
|
if(TaggedLines[i].lineTag == lineTag)
|
||
|
{
|
||
|
*searchPosition = i;
|
||
|
return TaggedLines[i].line;
|
||
|
}
|
||
|
}
|
||
|
*searchPosition = -1;
|
||
|
return NULL;
|
||
|
}
|