ref: aa5529bc6cbd5424c33f624d8a33fc482d18c489
dir: /src/heretic/p_spec.c/
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2008 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------
// P_Spec.c
#include "doomdef.h"
#include "deh_str.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_random.h"
#include "p_local.h"
#include "s_sound.h"
#include "v_video.h"
// Macros
#define MAX_AMBIENT_SFX 8 // Per level
// Types
typedef enum
{
afxcmd_play, // (sound)
afxcmd_playabsvol, // (sound, volume)
afxcmd_playrelvol, // (sound, volume)
afxcmd_delay, // (ticks)
afxcmd_delayrand, // (andbits)
afxcmd_end // ()
} afxcmd_t;
// Data
int *LevelAmbientSfx[MAX_AMBIENT_SFX];
int *AmbSfxPtr;
int AmbSfxCount;
int AmbSfxTics;
int AmbSfxVolume;
int AmbSndSeqInit[] = { // Startup
afxcmd_end
};
int AmbSndSeq1[] = { // Scream
afxcmd_play, sfx_amb1,
afxcmd_end
};
int AmbSndSeq2[] = { // Squish
afxcmd_play, sfx_amb2,
afxcmd_end
};
int AmbSndSeq3[] = { // Drops
afxcmd_play, sfx_amb3,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_play, sfx_amb7,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_play, sfx_amb3,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_play, sfx_amb7,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_play, sfx_amb3,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_play, sfx_amb7,
afxcmd_delay, 16,
afxcmd_delayrand, 31,
afxcmd_end
};
int AmbSndSeq4[] = { // SlowFootSteps
afxcmd_play, sfx_amb4,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 15,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_end
};
int AmbSndSeq5[] = { // Heartbeat
afxcmd_play, sfx_amb5,
afxcmd_delay, 35,
afxcmd_play, sfx_amb5,
afxcmd_delay, 35,
afxcmd_play, sfx_amb5,
afxcmd_delay, 35,
afxcmd_play, sfx_amb5,
afxcmd_end
};
int AmbSndSeq6[] = { // Bells
afxcmd_play, sfx_amb6,
afxcmd_delay, 17,
afxcmd_playrelvol, sfx_amb6, -8,
afxcmd_delay, 17,
afxcmd_playrelvol, sfx_amb6, -8,
afxcmd_delay, 17,
afxcmd_playrelvol, sfx_amb6, -8,
afxcmd_end
};
int AmbSndSeq7[] = { // Growl
afxcmd_play, sfx_bstsit,
afxcmd_end
};
int AmbSndSeq8[] = { // Magic
afxcmd_play, sfx_amb8,
afxcmd_end
};
int AmbSndSeq9[] = { // Laughter
afxcmd_play, sfx_amb9,
afxcmd_delay, 16,
afxcmd_playrelvol, sfx_amb9, -4,
afxcmd_delay, 16,
afxcmd_playrelvol, sfx_amb9, -4,
afxcmd_delay, 16,
afxcmd_playrelvol, sfx_amb10, -4,
afxcmd_delay, 16,
afxcmd_playrelvol, sfx_amb10, -4,
afxcmd_delay, 16,
afxcmd_playrelvol, sfx_amb10, -4,
afxcmd_end
};
int AmbSndSeq10[] = { // FastFootsteps
afxcmd_play, sfx_amb4,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb4, -3,
afxcmd_delay, 8,
afxcmd_playrelvol, sfx_amb11, -3,
afxcmd_end
};
int *AmbientSfx[] = {
AmbSndSeq1, // Scream
AmbSndSeq2, // Squish
AmbSndSeq3, // Drops
AmbSndSeq4, // SlowFootsteps
AmbSndSeq5, // Heartbeat
AmbSndSeq6, // Bells
AmbSndSeq7, // Growl
AmbSndSeq8, // Magic
AmbSndSeq9, // Laughter
AmbSndSeq10 // FastFootsteps
};
animdef_t animdefs[] = {
// false = flat
// true = texture
{false, "FLTWAWA3", "FLTWAWA1", 8}, // Water
{false, "FLTSLUD3", "FLTSLUD1", 8}, // Sludge
{false, "FLTTELE4", "FLTTELE1", 6}, // Teleport
{false, "FLTFLWW3", "FLTFLWW1", 9}, // River - West
{false, "FLTLAVA4", "FLTLAVA1", 8}, // Lava
{false, "FLATHUH4", "FLATHUH1", 8}, // Super Lava
{true, "LAVAFL3", "LAVAFL1", 6}, // Texture: Lavaflow
{true, "WATRWAL3", "WATRWAL1", 4}, // Texture: Waterfall
{-1}
};
anim_t anims[MAXANIMS];
anim_t *lastanim;
int *TerrainTypes;
struct
{
char *name;
int type;
} TerrainTypeDefs[] =
{
{ "FLTWAWA1", FLOOR_WATER },
{ "FLTFLWW1", FLOOR_WATER },
{ "FLTLAVA1", FLOOR_LAVA },
{ "FLATHUH1", FLOOR_LAVA },
{ "FLTSLUD1", FLOOR_SLUDGE },
{ "END", -1 }
};
mobj_t LavaInflictor;
//----------------------------------------------------------------------------
//
// PROC P_InitLava
//
//----------------------------------------------------------------------------
void P_InitLava(void)
{
memset(&LavaInflictor, 0, sizeof(mobj_t));
LavaInflictor.type = MT_PHOENIXFX2;
LavaInflictor.flags2 = MF2_FIREDAMAGE | MF2_NODMGTHRUST;
}
//----------------------------------------------------------------------------
//
// PROC 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;
}
}
}
//----------------------------------------------------------------------------
//
// PROC P_InitPicAnims
//
//----------------------------------------------------------------------------
void P_InitPicAnims(void)
{
char *startname;
char *endname;
int i;
lastanim = anims;
for (i = 0; animdefs[i].istexture != -1; i++)
{
startname = DEH_String(animdefs[i].startname);
endname = DEH_String(animdefs[i].endname);
if (animdefs[i].istexture)
{ // Texture animation
if (R_CheckTextureNumForName(startname) == -1)
{ // Texture doesn't exist
continue;
}
lastanim->picnum = R_TextureNumForName(endname);
lastanim->basepic = R_TextureNumForName(startname);
}
else
{ // Flat animation
if (W_CheckNumForName(startname) == -1)
{ // Flat doesn't exist
continue;
}
lastanim->picnum = R_FlatNumForName(endname);
lastanim->basepic = R_FlatNumForName(startname);
}
lastanim->istexture = animdefs[i].istexture;
lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
if (lastanim->numpics < 2)
{
I_Error("P_InitPicAnims: bad cycle from %s to %s",
startname, endname);
}
lastanim->speed = animdefs[i].speed;
lastanim++;
}
}
/*
==============================================================================
UTILITIES
==============================================================================
*/
//
// 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]];
}
//
// 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;
}
//
// 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;
fixed_t min;
line_t *check;
sector_t *other;
fixed_t height = currentheight;
min = INT_MAX;
for (i = 0, h = 0; i < sec->linecount; i++)
{
check = sec->lines[i];
other = getNextSector(check, sec);
if (other != NULL && other->floorheight > height)
{
if (other->floorheight < min)
{
min = other->floorheight;
}
++h;
}
}
// Compatibility note, in case of demo desyncs.
if (h > 20)
{
fprintf(stderr, "P_FindNextHighestFloor: exceeded Vanilla limit\n");
}
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 = INT_MAX;
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->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;
}
/*
==============================================================================
EVENTS
Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers
==============================================================================
*/
/*
===============================================================================
=
= P_CrossSpecialLine - TRIGGER
=
= Called every time a thing origin is about to cross
= a line with a non 0 special
=
===============================================================================
*/
void P_CrossSpecialLine(int linenum, int side, mobj_t * thing)
{
line_t *line;
line = &lines[linenum];
if (!thing->player)
{ // Check if trigger allowed by non-player mobj
switch (line->special)
{
case 39: // Trigger_TELEPORT
case 97: // Retrigger_TELEPORT
case 4: // Trigger_Raise_Door
//case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER
//case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER
break;
default:
return;
break;
}
}
switch (line->special)
{
//====================================================
// TRIGGERS
//====================================================
case 2: // Open Door
EV_DoDoor(line, open, VDOORSPEED);
line->special = 0;
break;
case 3: // Close Door
EV_DoDoor(line, close, VDOORSPEED);
line->special = 0;
break;
case 4: // Raise Door
EV_DoDoor(line, normal, VDOORSPEED);
line->special = 0;
break;
case 5: // Raise Floor
EV_DoFloor(line, raiseFloor);
line->special = 0;
break;
case 6: // Fast Ceiling Crush & Raise
EV_DoCeiling(line, fastCrushAndRaise);
line->special = 0;
break;
case 8: // Trigger_Build_Stairs (8 pixel steps)
EV_BuildStairs(line, 8 * FRACUNIT);
line->special = 0;
break;
case 106: // Trigger_Build_Stairs_16 (16 pixel steps)
EV_BuildStairs(line, 16 * FRACUNIT);
line->special = 0;
break;
case 10: // PlatDownWaitUp
EV_DoPlat(line, downWaitUpStay, 0);
line->special = 0;
break;
case 12: // Light Turn On - brightest near
EV_LightTurnOn(line, 0);
line->special = 0;
break;
case 13: // Light Turn On 255
EV_LightTurnOn(line, 255);
line->special = 0;
break;
case 16: // Close Door 30
EV_DoDoor(line, close30ThenOpen, VDOORSPEED);
line->special = 0;
break;
case 17: // Start Light Strobing
EV_StartLightStrobing(line);
line->special = 0;
break;
case 19: // Lower Floor
EV_DoFloor(line, lowerFloor);
line->special = 0;
break;
case 22: // Raise floor to nearest height and change texture
EV_DoPlat(line, raiseToNearestAndChange, 0);
line->special = 0;
break;
case 25: // Ceiling Crush and Raise
EV_DoCeiling(line, crushAndRaise);
line->special = 0;
break;
case 30: // Raise floor to shortest texture height
// on either side of lines
EV_DoFloor(line, raiseToTexture);
line->special = 0;
break;
case 35: // Lights Very Dark
EV_LightTurnOn(line, 35);
line->special = 0;
break;
case 36: // Lower Floor (TURBO)
EV_DoFloor(line, turboLower);
line->special = 0;
break;
case 37: // LowerAndChange
EV_DoFloor(line, lowerAndChange);
line->special = 0;
break;
case 38: // Lower Floor To Lowest
EV_DoFloor(line, lowerFloorToLowest);
line->special = 0;
break;
case 39: // TELEPORT!
EV_Teleport(line, side, thing);
line->special = 0;
break;
case 40: // RaiseCeilingLowerFloor
EV_DoCeiling(line, raiseToHighest);
EV_DoFloor(line, lowerFloorToLowest);
line->special = 0;
break;
case 44: // Ceiling Crush
EV_DoCeiling(line, lowerAndCrush);
line->special = 0;
break;
case 52: // EXIT!
G_ExitLevel();
line->special = 0;
break;
case 53: // Perpetual Platform Raise
EV_DoPlat(line, perpetualRaise, 0);
line->special = 0;
break;
case 54: // Platform Stop
EV_StopPlat(line);
line->special = 0;
break;
case 56: // Raise Floor Crush
EV_DoFloor(line, raiseFloorCrush);
line->special = 0;
break;
case 57: // Ceiling Crush Stop
EV_CeilingCrushStop(line);
line->special = 0;
break;
case 58: // Raise Floor 24
EV_DoFloor(line, raiseFloor24);
line->special = 0;
break;
case 59: // Raise Floor 24 And Change
EV_DoFloor(line, raiseFloor24AndChange);
line->special = 0;
break;
case 104: // Turn lights off in sector(tag)
EV_TurnTagLightsOff(line);
line->special = 0;
break;
case 105: // Trigger_SecretExit
G_SecretExitLevel();
line->special = 0;
break;
//====================================================
// RE-DOABLE TRIGGERS
//====================================================
case 72: // Ceiling Crush
EV_DoCeiling(line, lowerAndCrush);
break;
case 73: // Ceiling Crush and Raise
EV_DoCeiling(line, crushAndRaise);
break;
case 74: // Ceiling Crush Stop
EV_CeilingCrushStop(line);
break;
case 75: // Close Door
EV_DoDoor(line, close, VDOORSPEED);
break;
case 76: // Close Door 30
EV_DoDoor(line, close30ThenOpen, VDOORSPEED);
break;
case 77: // Fast Ceiling Crush & Raise
EV_DoCeiling(line, fastCrushAndRaise);
break;
case 79: // Lights Very Dark
EV_LightTurnOn(line, 35);
break;
case 80: // Light Turn On - brightest near
EV_LightTurnOn(line, 0);
break;
case 81: // Light Turn On 255
EV_LightTurnOn(line, 255);
break;
case 82: // Lower Floor To Lowest
EV_DoFloor(line, lowerFloorToLowest);
break;
case 83: // Lower Floor
EV_DoFloor(line, lowerFloor);
break;
case 84: // LowerAndChange
EV_DoFloor(line, lowerAndChange);
break;
case 86: // Open Door
EV_DoDoor(line, open, VDOORSPEED);
break;
case 87: // Perpetual Platform Raise
EV_DoPlat(line, perpetualRaise, 0);
break;
case 88: // PlatDownWaitUp
EV_DoPlat(line, downWaitUpStay, 0);
break;
case 89: // Platform Stop
EV_StopPlat(line);
break;
case 90: // Raise Door
EV_DoDoor(line, normal, VDOORSPEED);
break;
case 100: // Retrigger_Raise_Door_Turbo
EV_DoDoor(line, normal, VDOORSPEED * 3);
break;
case 91: // Raise Floor
EV_DoFloor(line, raiseFloor);
break;
case 92: // Raise Floor 24
EV_DoFloor(line, raiseFloor24);
break;
case 93: // Raise Floor 24 And Change
EV_DoFloor(line, raiseFloor24AndChange);
break;
case 94: // Raise Floor Crush
EV_DoFloor(line, raiseFloorCrush);
break;
case 95: // Raise floor to nearest height and change texture
EV_DoPlat(line, raiseToNearestAndChange, 0);
break;
case 96: // Raise floor to shortest texture height
// on either side of lines
EV_DoFloor(line, raiseToTexture);
break;
case 97: // TELEPORT!
EV_Teleport(line, side, thing);
break;
case 98: // Lower Floor (TURBO)
EV_DoFloor(line, turboLower);
break;
}
}
//----------------------------------------------------------------------------
//
// PROC P_ShootSpecialLine
//
// Called when a thing shoots a special line.
//
//----------------------------------------------------------------------------
void P_ShootSpecialLine(mobj_t * thing, line_t * line)
{
if (!thing->player)
{ // Check if trigger allowed by non-player mobj
switch (line->special)
{
case 46: // Impact_OpenDoor
break;
default:
return;
break;
}
}
switch (line->special)
{
case 24: // Impact_RaiseFloor
EV_DoFloor(line, raiseFloor);
P_ChangeSwitchTexture(line, 0);
break;
case 46: // Impact_OpenDoor
EV_DoDoor(line, open, VDOORSPEED);
P_ChangeSwitchTexture(line, 1);
break;
case 47: // Impact_RaiseFloorNear&Change
EV_DoPlat(line, raiseToNearestAndChange, 0);
P_ChangeSwitchTexture(line, 0);
break;
}
}
//----------------------------------------------------------------------------
//
// 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[5] = {
2048 * 5,
2048 * 10,
2048 * 25,
2048 * 30,
2048 * 35
};
sector = player->mo->subsector->sector;
if (player->mo->z != sector->floorheight)
{ // Player is not touching the floor
return;
}
switch (sector->special)
{
case 7: // Damage_Sludge
if (!(leveltime & 31))
{
P_DamageMobj(player->mo, NULL, NULL, 4);
}
break;
case 5: // Damage_LavaWimpy
if (!(leveltime & 15))
{
P_DamageMobj(player->mo, &LavaInflictor, NULL, 5);
P_HitFloor(player->mo);
}
break;
case 16: // Damage_LavaHefty
if (!(leveltime & 15))
{
P_DamageMobj(player->mo, &LavaInflictor, NULL, 8);
P_HitFloor(player->mo);
}
break;
case 4: // Scroll_EastLavaDamage
P_Thrust(player, 0, 2048 * 28);
if (!(leveltime & 15))
{
P_DamageMobj(player->mo, &LavaInflictor, NULL, 5);
P_HitFloor(player->mo);
}
break;
case 9: // SecretArea
player->secretcount++;
sector->special = 0;
break;
case 11: // Exit_SuperDamage (DOOM E1M8 finale)
/*
player->cheats &= ~CF_GODMODE;
if(!(leveltime&0x1f))
{
P_DamageMobj(player->mo, NULL, NULL, 20);
}
if(player->health <= 10)
{
G_ExitLevel();
}
*/
break;
case 25:
case 26:
case 27:
case 28:
case 29: // Scroll_North
P_Thrust(player, ANG90, pushTab[sector->special - 25]);
break;
case 20:
case 21:
case 22:
case 23:
case 24: // Scroll_East
P_Thrust(player, 0, pushTab[sector->special - 20]);
break;
case 30:
case 31:
case 32:
case 33:
case 34: // Scroll_South
P_Thrust(player, ANG270, pushTab[sector->special - 30]);
break;
case 35:
case 36:
case 37:
case 38:
case 39: // Scroll_West
P_Thrust(player, ANG180, pushTab[sector->special - 35]);
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 15: // Friction_Low
// Only used in (P_mobj):P_XYMovement and (P_user):P_Thrust
break;
default:
I_Error("P_PlayerInSpecialSector: "
"unknown special %i", sector->special);
}
}
//----------------------------------------------------------------------------
//
// PROC P_UpdateSpecials
//
// Animate planes, scroll walls, etc.
//
//----------------------------------------------------------------------------
void P_UpdateSpecials(void)
{
int i;
int pic;
anim_t *anim;
line_t *line;
// Animate flats and textures
for (anim = anims; anim < lastanim; anim++)
{
for (i = anim->basepic; i < anim->basepic + anim->numpics; i++)
{
pic =
anim->basepic +
((leveltime / anim->speed + i) % anim->numpics);
if (anim->istexture)
{
texturetranslation[i] = pic;
}
else
{
flattranslation[i] = pic;
}
}
}
// Update scrolling texture offsets
for (i = 0; i < numlinespecials; i++)
{
line = linespeciallist[i];
switch (line->special)
{
case 48: // Effect_Scroll_Left
sides[line->sidenum[0]].textureoffset += FRACUNIT;
break;
case 99: // Effect_Scroll_Right
sides[line->sidenum[0]].textureoffset -= FRACUNIT;
break;
}
}
// Handle buttons
for (i = 0; i < MAXBUTTONS; i++)
{
if (buttonlist[i].btimer)
{
buttonlist[i].btimer--;
if (!buttonlist[i].btimer)
{
switch (buttonlist[i].where)
{
case top:
sides[buttonlist[i].line->sidenum[0]].toptexture =
buttonlist[i].btexture;
break;
case middle:
sides[buttonlist[i].line->sidenum[0]].midtexture =
buttonlist[i].btexture;
break;
case bottom:
sides[buttonlist[i].line->sidenum[0]].bottomtexture =
buttonlist[i].btexture;
break;
}
S_StartSound(buttonlist[i].soundorg, sfx_switch);
memset(&buttonlist[i], 0, sizeof(button_t));
}
}
}
}
//============================================================
//
// Special Stuff that can't be categorized
//
//============================================================
int EV_DoDonut(line_t * line)
{
sector_t *s1;
sector_t *s2;
sector_t *s3;
int secnum;
int rtn;
int i;
floormove_t *floor;
secnum = -1;
rtn = 0;
while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
{
s1 = §ors[secnum];
// ALREADY MOVING? IF SO, KEEP GOING...
if (s1->specialdata)
continue;
rtn = 1;
s2 = getNextSector(s1->lines[0], s1);
for (i = 0; i < s2->linecount; i++)
{
if ((!s2->lines[i]->flags & ML_TWOSIDED) ||
(s2->lines[i]->backsector == s1))
continue;
s3 = s2->lines[i]->backsector;
//
// Spawn rising slime
//
floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
P_AddThinker(&floor->thinker);
s2->specialdata = floor;
floor->thinker.function = T_MoveFloor;
floor->type = donutRaise;
floor->crush = false;
floor->direction = 1;
floor->sector = s2;
floor->speed = FLOORSPEED / 2;
floor->texture = s3->floorpic;
floor->newspecial = 0;
floor->floordestheight = s3->floorheight;
//
// Spawn lowering donut-hole
//
floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
P_AddThinker(&floor->thinker);
s1->specialdata = floor;
floor->thinker.function = T_MoveFloor;
floor->type = lowerFloor;
floor->crush = false;
floor->direction = -1;
floor->sector = s1;
floor->speed = FLOORSPEED / 2;
floor->floordestheight = s3->floorheight;
break;
}
}
return rtn;
}
/*
==============================================================================
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;
int episode;
episode = 1;
if (W_CheckNumForName(DEH_String("texture2")) >= 0)
episode = 2;
//
// Init special SECTORs
//
sector = sectors;
for (i = 0; i < numsectors; i++, sector++)
{
if (!sector->special)
continue;
switch (sector->special)
{
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;
for (i = 0; i < numlines; i++)
switch (lines[i].special)
{
case 48: // Effect_Scroll_Left
case 99: // Effect_Scroll_Right
linespeciallist[numlinespecials] = &lines[i];
numlinespecials++;
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));
}
//----------------------------------------------------------------------------
//
// PROC P_InitAmbientSound
//
//----------------------------------------------------------------------------
void P_InitAmbientSound(void)
{
AmbSfxCount = 0;
AmbSfxVolume = 0;
AmbSfxTics = 10 * TICRATE;
AmbSfxPtr = AmbSndSeqInit;
}
//----------------------------------------------------------------------------
//
// PROC P_AddAmbientSfx
//
// Called by (P_mobj):P_SpawnMapThing during (P_setup):P_SetupLevel.
//
//----------------------------------------------------------------------------
void P_AddAmbientSfx(int sequence)
{
if (AmbSfxCount == MAX_AMBIENT_SFX)
{
I_Error("Too many ambient sound sequences");
}
LevelAmbientSfx[AmbSfxCount++] = AmbientSfx[sequence];
}
//----------------------------------------------------------------------------
//
// PROC P_AmbientSound
//
// Called every tic by (P_tick):P_Ticker.
//
//----------------------------------------------------------------------------
void P_AmbientSound(void)
{
afxcmd_t cmd;
int sound;
boolean done;
if (!AmbSfxCount)
{ // No ambient sound sequences on current level
return;
}
if (--AmbSfxTics)
{
return;
}
done = false;
do
{
cmd = *AmbSfxPtr++;
switch (cmd)
{
case afxcmd_play:
AmbSfxVolume = P_Random() >> 2;
S_StartSoundAtVolume(NULL, *AmbSfxPtr++, AmbSfxVolume);
break;
case afxcmd_playabsvol:
sound = *AmbSfxPtr++;
AmbSfxVolume = *AmbSfxPtr++;
S_StartSoundAtVolume(NULL, sound, AmbSfxVolume);
break;
case afxcmd_playrelvol:
sound = *AmbSfxPtr++;
AmbSfxVolume += *AmbSfxPtr++;
if (AmbSfxVolume < 0)
{
AmbSfxVolume = 0;
}
else if (AmbSfxVolume > 127)
{
AmbSfxVolume = 127;
}
S_StartSoundAtVolume(NULL, sound, AmbSfxVolume);
break;
case afxcmd_delay:
AmbSfxTics = *AmbSfxPtr++;
done = true;
break;
case afxcmd_delayrand:
AmbSfxTics = P_Random() & (*AmbSfxPtr++);
done = true;
break;
case afxcmd_end:
AmbSfxTics = 6 * TICRATE + P_Random();
AmbSfxPtr = LevelAmbientSfx[P_Random() % AmbSfxCount];
done = true;
break;
default:
I_Error("P_AmbientSound: Unknown afxcmd %d", cmd);
break;
}
}
while (done == false);
}