raze/source/sw/src/jsector.cpp

1489 lines
45 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
// JSECTOR.C
// This is all Jim's programming having to do with sectors.
#include "ns.h"
#include "build.h"
#include "keys.h"
#include "names2.h"
#include "jnames.h"
#include "panel.h"
#include "game.h"
#include "tags.h"
#include "sector.h"
#include "player.h"
#include "sprite.h"
#include "reserve.h"
#include "jsector.h"
#include "jtags.h"
#include "lists.h"
#include "pal.h"
#include "parent.h"
#include "v_video.h"
BEGIN_SW_NS
// V A R I A B L E D E C L A R A T I O N S //////////////////////////////////////////////////////
MIRRORTYPE mirror[MAXMIRRORS];
short mirrorcnt; //, floormirrorcnt;
//short floormirrorsector[MAXMIRRORS];
SWBOOL mirrorinview;
uint32_t oscilationclock;
// Voxel stuff
//SWBOOL bVoxelsOn = TRUE; // Turn voxels on by default
SWBOOL bSpinBobVoxels = FALSE; // Do twizzly stuff to voxels, but
// not by default
SWBOOL bAutoSize = TRUE; // Autosizing on/off
//extern int chainnumpages;
extern AMB_INFO ambarray[];
extern short NormalVisibility;
extern ParentalStruct aVoxelArray[MAXTILES];
// F U N C T I O N S //////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////
// SpawnWallSound
/////////////////////////////////////////////////////
void SpawnWallSound(short sndnum, short i)
{
short SpriteNum;
vec3_t mid;
SPRITEp sp;
SpriteNum = COVERinsertsprite(0, STAT_DEFAULT);
if (SpriteNum < 0)
return;
sp = &sprite[SpriteNum];
sp->cstat = 0;
sp->extra = 0;
// Get wall midpoint for offset in mirror view
mid.x = (wall[i].x + wall[wall[i].point2].x) / 2;
mid.y = (wall[i].y + wall[wall[i].point2].y) / 2;
mid.z = (sector[wall[i].nextsector].ceilingz + sector[wall[i].nextsector].floorz) / 2;
setspritez(SpriteNum, &mid);
sp = &sprite[SpriteNum];
PlaySound(sndnum, sp, v3df_dontpan | v3df_doppler);
}
short
CheckTileSound(short picnum)
{
short sndnum = -1;
switch (picnum)
{
case 163: // Sizzly Lava
case 167:
sndnum = DIGI_VOLCANOSTEAM1;
break;
case 175: // Flowing Lava
sndnum = DIGI_ERUPTION;
break;
case 179: // Bubbly lava
sndnum = DIGI_LAVAFLOW1;
break;
case 300: // Water fall tile
sndnum = DIGI_WATERFALL1;
break;
case 334: // Teleporter Pad
sndnum = DIGI_ENGROOM1;
break;
case 2690: // Jet engine fan
sndnum = DIGI_JET;
break;
case 2672: // X-Ray Machine engine
sndnum = DIGI_ENGROOM5;
break;
case 768: // Electricity
// sndnum = DIGI_;
break;
case 2714: // Pachinko Machine
// sndnum = DIGI_;
break;
case 2782: // Telepad
sndnum = DIGI_ENGROOM4;
break;
case 3382: // Gears
sndnum = DIGI_ENGROOM5;
break;
case 2801: // Computers
case 2804:
case 2807:
case 3352:
case 3385:
case 3389:
case 3393:
case 3397:
case 3401:
case 3405:
// sndnum = DIGI_;
break;
case 3478: // Radar screen
// sndnum = DIGI_;
break;
default:
sndnum = -1;
break;
}
return sndnum;
}
ANIMATOR GenerateDrips;
/////////////////////////////////////////////////////
// Initialize any of my special use sprites
/////////////////////////////////////////////////////
void
JS_SpriteSetup(void)
{
SPRITEp sp;
short SpriteNum = 0, NextSprite;
USERp u;
short i;
TRAVERSE_SPRITE_STAT(headspritestat[0], SpriteNum, NextSprite)
{
short tag;
sp = &sprite[SpriteNum];
tag = sp->hitag;
// Non static camera. Camera sprite will be drawn!
if (tag == MIRROR_CAM && sprite[SpriteNum].picnum != ST1)
{
// Just change it to static, sprite has all the info I need
// u = SpawnUser(SpriteNum, sp->picnum, NULL);
// RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
// SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
change_sprite_stat(SpriteNum, STAT_SPAWN_SPOT);
}
switch (sprite[SpriteNum].picnum)
{
case ST1:
if (tag == MIRROR_CAM)
{
// Just change it to static, sprite has all the info I need
// ST1 cameras won't move with SOBJ's!
change_sprite_stat(SpriteNum, STAT_ST1);
}
else if (tag == MIRROR_SPAWNSPOT)
{
// Just change it to static, sprite has all the info I need
change_sprite_stat(SpriteNum, STAT_ST1);
}
else if (tag == AMBIENT_SOUND)
{
change_sprite_stat(SpriteNum, STAT_AMBIENT);
}
else if (tag == TAG_ECHO_SOUND)
{
change_sprite_stat(SpriteNum, STAT_ECHO);
}
else if (tag == TAG_DRIPGEN)
{
u = SpawnUser(SpriteNum, 0, NULL);
ASSERT(u != NULL);
u->RotNum = 0;
u->WaitTics = sp->lotag * 120;
u->ActorActionFunc = GenerateDrips;
change_sprite_stat(SpriteNum, STAT_NO_STATE);
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
}
break;
// Sprites in editart that should play ambient sounds
// automatically
case 380:
case 396:
case 430:
case 443:
case 512:
case 521:
case 541:
case 2720:
case 3143:
case 3157:
PlaySound(DIGI_FIRE1, sp, v3df_follow|v3df_dontpan|v3df_doppler);
break;
case 795:
case 880:
PlaySound(DIGI_WATERFLOW1, sp, v3df_follow|v3df_dontpan|v3df_doppler);
break;
case 460: // Wind Chimes
InitAmbient(79, sp);
break;
}
}
// Check for certain walls to make sounds
for (i = 0; i < numwalls; i++)
{
short picnum;
picnum = wall[i].picnum;
// Set the don't stick bit for liquid tiles
switch (picnum)
{
case 175:
case 179:
case 300:
case 320:
case 330:
case 352:
case 780:
case 890:
case 2608:
case 2616:
//case 3834:
SET(wall[i].extra, WALLFX_DONT_STICK);
break;
}
#if 0
short sndnum;
if ((sndnum = CheckTileSound(picnum)) != -1)
{
SpawnWallSound(sndnum, i);
}
picnum = wall[i].overpicnum;
if ((sndnum = CheckTileSound(picnum)) != -1)
{
SpawnWallSound(sndnum, i);
}
#endif
}
}
/////////////////////////////////////////////////////
// Initialize the mirrors
/////////////////////////////////////////////////////
void JS_InitMirrors(void)
{
short startwall, endwall;
int i, j, s;
short SpriteNum = 0, NextSprite;
SWBOOL Found_Cam = FALSE;
// Set all the mirror struct values to -1
memset(mirror, 0xFF, sizeof(mirror));
mirrorinview = FALSE; // Initially set global mirror flag
// to no mirrors seen
// Scan wall tags for mirrors
mirrorcnt = 0;
tileDelete(MIRROR);
oscilationclock = ototalclock;
for (i = 0; i < MAXMIRRORS; i++)
{
tileDelete(i + MIRRORLABEL);
mirror[i].campic = -1;
mirror[i].camsprite = -1;
mirror[i].camera = -1;
mirror[i].ismagic = FALSE;
}
for (i = 0; i < numwalls; i++)
{
s = wall[i].nextsector;
if ((s >= 0) && (wall[i].overpicnum == MIRROR) && (wall[i].cstat & 32))
{
if ((sector[s].floorstat & 1) == 0)
{
if (mirrorcnt >= MAXMIRRORS)
{
Printf("MAXMIRRORS reached! Skipping mirror wall[%d]\n", i);
wall[i].overpicnum = sector[s].ceilingpicnum;
continue;
}
wall[i].overpicnum = MIRRORLABEL + mirrorcnt;
wall[i].picnum = MIRRORLABEL + mirrorcnt;
sector[s].ceilingpicnum = MIRRORLABEL + mirrorcnt;
sector[s].floorpicnum = MIRRORLABEL + mirrorcnt;
sector[s].floorstat |= 1;
mirror[mirrorcnt].mirrorwall = i;
mirror[mirrorcnt].mirrorsector = s;
mirror[mirrorcnt].numspawnspots = 0;
mirror[mirrorcnt].ismagic = FALSE;
do if (wall[i].lotag == TAG_WALL_MAGIC_MIRROR)
{
short ii, nextii;
SPRITEp sp;
Found_Cam = FALSE;
TRAVERSE_SPRITE_STAT(headspritestat[STAT_ST1], ii, nextii)
{
sp = &sprite[ii];
// if correct type and matches
if (sp->hitag == MIRROR_CAM && sp->lotag == wall[i].hitag)
{
mirror[mirrorcnt].camera = ii;
// Set up camera varialbes
SP_TAG5(sp) = sp->ang; // Set current angle to
// sprite angle
Found_Cam = TRUE;
}
}
ii = nextii = 0;
TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPAWN_SPOT], ii, nextii)
{
sp = &sprite[ii];
// if correct type and matches
if (sp->hitag == MIRROR_CAM && sp->lotag == wall[i].hitag)
{
mirror[mirrorcnt].camera = ii;
// Set up camera varialbes
SP_TAG5(sp) = sp->ang; // Set current angle to
// sprite angle
Found_Cam = TRUE;
}
}
if (!Found_Cam)
{
Printf("Cound not find the camera view sprite for match %d\n",TrackerCast(wall[i].hitag));
Printf("Map Coordinates: x = %d, y = %d\n",TrackerCast(wall[i].x),TrackerCast(wall[i].y));
break;
}
mirror[mirrorcnt].ismagic = TRUE;
Found_Cam = FALSE;
if (TEST_BOOL1(&sprite[mirror[mirrorcnt].camera]))
{
TRAVERSE_SPRITE_STAT(headspritestat[0], SpriteNum, NextSprite)
{
sp = &sprite[SpriteNum];
if (sp->picnum >= CAMSPRITE && sp->picnum < CAMSPRITE + 8 &&
sp->hitag == wall[i].hitag)
{
mirror[mirrorcnt].campic = sp->picnum;
mirror[mirrorcnt].camsprite = SpriteNum;
// JBF: commenting out this line results in the screen in $BULLET being visible
tileDelete(mirror[mirrorcnt].campic);
Found_Cam = TRUE;
}
}
if (!Found_Cam)
{
Printf("Did not find drawtotile for camera number %d\n",mirrorcnt);
Printf("wall[%d].hitag == %d\n",i,TrackerCast(wall[i].hitag));
Printf("Map Coordinates: x = %d, y = %d\n", TrackerCast(wall[i].x), TrackerCast(wall[i].y));
RESET_BOOL1(&sprite[mirror[mirrorcnt].camera]);
}
}
// For magic mirrors, set allowable viewing time to 30
// secs
// Base rate is supposed to be 120, but time is double
// what I expect
mirror[mirrorcnt].maxtics = 60 * 30;
}
while (0);
mirror[mirrorcnt].mstate = m_normal;
// Set tics used to none
mirror[mirrorcnt].tics = 0;
if (mirror[mirrorcnt].ismagic)
{
//DSPRINTF(ds, "mirror.mirrorwall %d", mirror[mirrorcnt].mirrorwall);
MONO_PRINT(ds);
//DSPRINTF(ds, "mirror.mirrorsector %d", mirror[mirrorcnt].mirrorsector);
MONO_PRINT(ds);
//DSPRINTF(ds, "mirror.camera %d", mirror[mirrorcnt].camera);
MONO_PRINT(ds);
}
mirrorcnt++;
}
else
wall[i].overpicnum = sector[s].ceilingpicnum;
}
}
// Invalidate textures in sector behind mirror
for (i = 0; i < mirrorcnt; i++)
{
startwall = sector[mirror[i].mirrorsector].wallptr;
endwall = startwall + sector[mirror[i].mirrorsector].wallnum;
for (j = startwall; j < endwall; j++)
{
wall[j].picnum = MIRROR;
wall[j].overpicnum = MIRROR;
}
}
} // InitMirrors
/////////////////////////////////////////////////////
// Draw a 3d screen to a specific tile
/////////////////////////////////////////////////////
void drawroomstotile(int daposx, int daposy, int daposz,
fix16_t daq16ang, fix16_t daq16horiz, short dacursectnum, short tilenume)
{
TileFiles.MakeCanvas(tilenume, tilesiz[tilenume].x, tilesiz[tilenume].y);
renderSetTarget(tilenume, tilesiz[tilenume].x, tilesiz[tilenume].y);
screen->BeginScene();
renderDrawRoomsQ16(daposx, daposy, daposz, daq16ang, daq16horiz, dacursectnum);
analyzesprites(daposx, daposy, daposz, FALSE);
renderDrawMasks();
screen->FinishScene();
renderRestoreTarget();
}
void
JS_ProcessEchoSpot()
{
short i,nexti;
SPRITEp tp;
int j,dist;
PLAYERp pp = Player+screenpeek;
int16_t reverb;
SWBOOL reverb_set = FALSE;
// Process echo sprites
TRAVERSE_SPRITE_STAT(headspritestat[STAT_ECHO], i, nexti)
{
dist = 0x7fffffff;
tp = &sprite[i];
j = klabs(tp->x - pp->posx);
j += klabs(tp->y - pp->posy);
if (j < dist)
dist = j;
if (dist <= SP_TAG4(tp)) // tag4 = ang
{
reverb = SP_TAG2(tp);
if (reverb > 200) reverb = 200;
if (reverb < 100) reverb = 100;
COVER_SetReverb(reverb);
reverb_set = TRUE;
}
}
if (!TEST(pp->Flags, PF_DIVING) && !reverb_set && pp->Reverb <= 0)
COVER_SetReverb(0);
}
/////////////////////////////////////////////////////
// Draw one mirror, the one closest to player
// Cams and see to teleporters do NOT support room above room!
/////////////////////////////////////////////////////
#define MAXCAMDIST 8000
int camloopcnt = 0; // Timer to cycle through player
// views
short camplayerview = 1; // Don't show yourself!
// Hack job alert!
// Mirrors and cameras are maintained in the same data structure, but for hardware rendering they cannot be interleaved.
// So this function replicates JS_DrawMirrors to only process the camera textures but not change any global state.
void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz)
{
int j, cnt;
int dist;
int tposx, tposy; // Camera
int* longptr;
SWBOOL bIsWallMirror = FALSE;
camloopcnt += (int32_t) (totalclock - ototalclock);
if (camloopcnt > (60 * 5)) // 5 seconds per player view
{
camloopcnt = 0;
camplayerview++;
if (camplayerview >= numplayers)
camplayerview = 1;
}
// WARNING! Assuming (MIRRORLABEL&31) = 0 and MAXMIRRORS = 64 <-- JBF: wrong
longptr = (int*)&gotpic[MIRRORLABEL >> 3];
if (longptr && (longptr[0] || longptr[1]))
{
uint32_t oscilation_delta = ototalclock - oscilationclock;
oscilation_delta -= oscilation_delta % 4;
oscilationclock += oscilation_delta;
oscilation_delta *= 2;
for (cnt = MAXMIRRORS - 1; cnt >= 0; cnt--)
{
if (!mirror[cnt].ismagic) continue; // these are definitely not camera textures.
//if (TEST_GOTPIC(cnt + MIRRORLABEL) || TEST_GOTPIC(cnt + CAMSPRITE))
if (TEST_GOTPIC(cnt + MIRRORLABEL) || ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic)))
{
// Do not change any global state here!
bIsWallMirror = (TEST_GOTPIC(cnt + MIRRORLABEL));
dist = 0x7fffffff;
if (bIsWallMirror)
{
j = klabs(wall[mirror[cnt].mirrorwall].x - tx);
j += klabs(wall[mirror[cnt].mirrorwall].y - ty);
if (j < dist)
dist = j;
}
else
{
SPRITEp tp;
tp = &sprite[mirror[cnt].camsprite];
j = klabs(tp->x - tx);
j += klabs(tp->y - ty);
if (j < dist)
dist = j;
}
SPRITEp sp = NULL;
int camhoriz;
short w;
int dx, dy, dz, tdx, tdy, tdz, midx, midy;
ASSERT(mirror[cnt].camera != -1);
sp = &sprite[mirror[cnt].camera];
ASSERT(sp);
// Calculate the angle of the mirror wall
w = mirror[cnt].mirrorwall;
// Get wall midpoint for offset in mirror view
midx = (wall[w].x + wall[wall[w].point2].x) / 2;
midy = (wall[w].y + wall[wall[w].point2].y) / 2;
// Finish finding offsets
tdx = klabs(midx - tx);
tdy = klabs(midy - ty);
if (midx >= tx)
dx = sp->x - tdx;
else
dx = sp->x + tdx;
if (midy >= ty)
dy = sp->y - tdy;
else
dy = sp->y + tdy;
tdz = klabs(tz - sp->z);
if (tz >= sp->z)
dz = sp->z + tdz;
else
dz = sp->z - tdz;
// Is it a TV cam or a teleporter that shows destination?
// TRUE = It's a TV cam
mirror[cnt].mstate = m_normal;
if (TEST_BOOL1(sp))
mirror[cnt].mstate = m_viewon;
// Show teleport destination
// NOTE: Adding MAXSECTORS lets you draw a room, even if
// you are outside of it!
if (mirror[cnt].mstate == m_viewon)
{
SWBOOL DoCam = FALSE;
if (mirror[cnt].campic == -1)
{
Printf("Missing campic for mirror %d\n",cnt);
Printf("Map Coordinates: x = %d, y = %d\n",midx,midy);
return;
}
// BOOL2 = Oscilate camera
if (TEST_BOOL2(sp) && MoveSkip2 == 0)
{
if (TEST_BOOL3(sp)) // If true add increment to
// angle else subtract
{
// Store current angle in TAG5
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
// TAG6 = Turn radius
if (klabs(GetDeltaAngle(SP_TAG5(sp), sp->ang)) >= SP_TAG6(sp))
{
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
RESET_BOOL3(sp); // Reverse turn
// direction.
}
}
else
{
// Store current angle in TAG5
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
// TAG6 = Turn radius
if (klabs(GetDeltaAngle(SP_TAG5(sp), sp->ang)) >= SP_TAG6(sp))
{
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
SET_BOOL3(sp); // Reverse turn
// direction.
}
}
}
else if (!TEST_BOOL2(sp))
{
SP_TAG5(sp) = sp->ang; // Copy sprite angle to
// tag5
}
// See if there is a horizon value. 0 defaults to
// 100!
if (SP_TAG7(sp) != 0)
{
camhoriz = SP_TAG7(sp);
if (camhoriz > PLAYER_HORIZ_MAX)
camhoriz = PLAYER_HORIZ_MAX;
else if (camhoriz < PLAYER_HORIZ_MIN)
camhoriz = PLAYER_HORIZ_MIN;
}
else
camhoriz = 100; // Default
// If player is dead still then update at MoveSkip4
// rate.
if (pp->posx == pp->oposx && pp->posy == pp->oposy && pp->posz == pp->oposz)
DoCam = TRUE;
// Set up the tile for drawing
TileFiles.MakeCanvas(mirror[cnt].campic, 128, 128);
{
if (dist < MAXCAMDIST)
{
PLAYERp cp = Player + camplayerview;
if (TEST_BOOL11(sp) && numplayers > 1)
{
drawroomstotile(cp->posx, cp->posy, cp->posz, cp->q16ang, cp->q16horiz, cp->cursectnum, mirror[cnt].campic);
}
else
{
drawroomstotile(sp->x, sp->y, sp->z, fix16_from_int(SP_TAG5(sp)), fix16_from_int(camhoriz), sp->sectnum, mirror[cnt].campic);
}
}
}
}
}
}
}
}
void JS_DrawMirrors(PLAYERp pp, int tx, int ty, int tz, fix16_t tpq16ang, fix16_t tpq16horiz)
{
int j, cnt;
int dist;
int tposx, tposy; // Camera
int *longptr;
fix16_t tang;
// int tx, ty, tz, tpang; // Interpolate so mirror doesn't
// drift!
SWBOOL bIsWallMirror = FALSE;
// WARNING! Assuming (MIRRORLABEL&31) = 0 and MAXMIRRORS = 64 <-- JBF: wrong
longptr = (int *)&gotpic[MIRRORLABEL >> 3];
if (longptr && (longptr[0] || longptr[1]))
{
for (cnt = MAXMIRRORS - 1; cnt >= 0; cnt--)
//if (TEST_GOTPIC(cnt + MIRRORLABEL) || TEST_GOTPIC(cnt + CAMSPRITE))
if (TEST_GOTPIC(cnt + MIRRORLABEL) || ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic)))
{
bIsWallMirror = FALSE;
if (TEST_GOTPIC(cnt + MIRRORLABEL))
{
bIsWallMirror = TRUE;
RESET_GOTPIC(cnt + MIRRORLABEL);
}
//else if (TEST_GOTPIC(cnt + CAMSPRITE))
else if ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic))
{
//RESET_GOTPIC(cnt + CAMSPRITE);
RESET_GOTPIC(mirror[cnt].campic);
}
mirrorinview = TRUE;
// tx = pp->oposx + mulscale16(pp->posx - pp->oposx, smoothratio);
// ty = pp->oposy + mulscale16(pp->posy - pp->oposy, smoothratio);
// tz = pp->oposz + mulscale16(pp->posz - pp->oposz, smoothratio);
// tpq16ang = pp->q16ang;
dist = 0x7fffffff;
if (bIsWallMirror)
{
j = klabs(wall[mirror[cnt].mirrorwall].x - tx);
j += klabs(wall[mirror[cnt].mirrorwall].y - ty);
if (j < dist)
dist = j;
}
else
{
SPRITEp tp;
tp = &sprite[mirror[cnt].camsprite];
j = klabs(tp->x - tx);
j += klabs(tp->y - ty);
if (j < dist)
dist = j;
}
// //DSPRINTF(ds,"mirror.tics == %ul", mirror[i].tics);
// MONO_PRINT(ds);
if (mirror[cnt].ismagic)
{
SPRITEp sp=NULL;
int camhoriz;
short w;
int dx, dy, dz, tdx, tdy, tdz, midx, midy;
ASSERT(mirror[cnt].camera != -1);
sp = &sprite[mirror[cnt].camera];
ASSERT(sp);
// char tvisibility;
// tvisibility = g_visibility;
// g_visibility <<= 1; // Make mirror darker
// Make TV cam style mirror seem to shimmer
// if (mirror[cnt].ismagic && STD_RANDOM_P2(256) > 128)
// g_visibility -= STD_RANDOM_P2(128);
// Calculate the angle of the mirror wall
w = mirror[cnt].mirrorwall;
// Get wall midpoint for offset in mirror view
midx = (wall[w].x + wall[wall[w].point2].x) / 2;
midy = (wall[w].y + wall[wall[w].point2].y) / 2;
// Finish finding offsets
tdx = klabs(midx - tx);
tdy = klabs(midy - ty);
if (midx >= tx)
dx = sp->x - tdx;
else
dx = sp->x + tdx;
if (midy >= ty)
dy = sp->y - tdy;
else
dy = sp->y + tdy;
tdz = klabs(tz - sp->z);
if (tz >= sp->z)
dz = sp->z + tdz;
else
dz = sp->z - tdz;
// Is it a TV cam or a teleporter that shows destination?
// TRUE = It's a TV cam
mirror[cnt].mstate = m_normal;
if (TEST_BOOL1(sp))
mirror[cnt].mstate = m_viewon;
// Show teleport destination
// NOTE: Adding MAXSECTORS lets you draw a room, even if
// you are outside of it!
if (mirror[cnt].mstate != m_viewon)
{
tileDelete(MIRROR);
// Set TV camera sprite size to 0 to show mirror
// behind in this case!
if (mirror[cnt].campic != -1)
tileDelete(mirror[cnt].campic);
2020-03-29 08:03:00 +00:00
renderDrawRoomsQ16(dx, dy, dz, tpq16ang, tpq16horiz, sp->sectnum + MAXSECTORS);
analyzesprites(dx, dy, dz, FALSE);
renderDrawMasks();
}
}
else
{
// It's just a mirror
// Prepare drawrooms for drawing mirror and calculate
// reflected
// position into tposx, tposy, and tang (tposz == cposz)
// Must call preparemirror before drawrooms and
// completemirror after drawrooms
2020-03-29 08:03:00 +00:00
renderPrepareMirror(tx, ty, tz, tpq16ang, tpq16horiz,
mirror[cnt].mirrorwall, /*mirror[cnt].mirrorsector,*/ &tposx, &tposy, &tang);
2020-03-28 11:31:02 +00:00
renderDrawRoomsQ16(tposx, tposy, tz, (tang), tpq16horiz, mirror[cnt].mirrorsector + MAXSECTORS);
analyzesprites(tposx, tposy, tz, TRUE);
renderDrawMasks();
renderCompleteMirror(); // Reverse screen x-wise in this
// function
}
// g_visibility = tvisibility;
// g_visibility = NormalVisibility;
2020-03-29 08:03:00 +00:00
// renderDrawRoomsQ16(tx, ty, tz, tpq16ang, tpq16horiz, pp->cursectnum);
// Clean up anything that the camera view might have done
SetFragBar(pp);
tileDelete(MIRROR);
wall[mirror[cnt].mirrorwall].overpicnum = MIRRORLABEL + cnt;
}
else
mirrorinview = FALSE;
}
}
void
DoAutoSize(tspriteptr_t tspr)
{
if (!bAutoSize)
return;
switch (tspr->picnum)
{
case ICON_STAR: // 1793
break;
case ICON_UZI: // 1797
tspr->xrepeat = 43;
tspr->yrepeat = 40;
break;
case ICON_UZIFLOOR: // 1807
tspr->xrepeat = 43;
tspr->yrepeat = 40;
break;
case ICON_LG_UZI_AMMO: // 1799
break;
case ICON_HEART: // 1824
break;
case ICON_HEART_LG_AMMO: // 1820
break;
case ICON_GUARD_HEAD: // 1814
break;
case ICON_FIREBALL_LG_AMMO: // 3035
break;
case ICON_ROCKET: // 1843
break;
case ICON_SHOTGUN: // 1794
tspr->xrepeat = 57;
tspr->yrepeat = 58;
break;
case ICON_LG_ROCKET: // 1796
break;
case ICON_LG_SHOTSHELL: // 1823
break;
case ICON_MICRO_GUN: // 1818
break;
case ICON_MICRO_BATTERY: // 1800
break;
case ICON_GRENADE_LAUNCHER: // 1817
tspr->xrepeat = 54;
tspr->yrepeat = 52;
break;
case ICON_LG_GRENADE: // 1831
break;
case ICON_LG_MINE: // 1842
break;
case ICON_RAIL_GUN: // 1811
tspr->xrepeat = 50;
tspr->yrepeat = 54;
break;
case ICON_RAIL_AMMO: // 1812
break;
case ICON_SM_MEDKIT: // 1802
break;
case ICON_MEDKIT: // 1803
break;
case ICON_CHEMBOMB:
tspr->xrepeat = 64;
tspr->yrepeat = 47;
break;
case ICON_FLASHBOMB:
tspr->xrepeat = 32;
tspr->yrepeat = 34;
break;
case ICON_NUKE:
break;
case ICON_CALTROPS:
tspr->xrepeat = 37;
tspr->yrepeat = 30;
break;
case ICON_BOOSTER: // 1810
tspr->xrepeat = 30;
tspr->yrepeat = 38;
break;
case ICON_HEAT_CARD: // 1819
tspr->xrepeat = 46;
tspr->yrepeat = 47;
break;
case ICON_REPAIR_KIT: // 1813
break;
case ICON_EXPLOSIVE_BOX: // 1801
break;
case ICON_ENVIRON_SUIT: // 1837
break;
case ICON_FLY: // 1782
break;
case ICON_CLOAK: // 1826
break;
case ICON_NIGHT_VISION: // 3031
tspr->xrepeat = 59;
tspr->yrepeat = 71;
break;
case ICON_NAPALM: // 3046
break;
case ICON_RING: // 3050
break;
case ICON_RINGAMMO: // 3054
break;
case ICON_NAPALMAMMO: // 3058
break;
case ICON_GRENADE: // 3059
break;
case ICON_ARMOR: // 3030
tspr->xrepeat = 82;
tspr->yrepeat = 84;
break;
case BLUE_KEY: // 1766
break;
case RED_KEY: // 1770
break;
case GREEN_KEY: // 1774
break;
case YELLOW_KEY: // 1778
break;
case BLUE_CARD:
case RED_CARD:
case GREEN_CARD:
case YELLOW_CARD:
tspr->xrepeat = 36;
tspr->yrepeat = 33;
break;
case GOLD_SKELKEY:
case SILVER_SKELKEY:
case BRONZE_SKELKEY:
case RED_SKELKEY:
tspr->xrepeat = 39;
tspr->yrepeat = 45;
break;
case SKEL_LOCKED:
case SKEL_UNLOCKED:
tspr->xrepeat = 47;
tspr->yrepeat = 40;
break;
case RAMCARD_LOCKED:
case RAMCARD_UNLOCKED:
case CARD_LOCKED:
case CARD_UNLOCKED:
break;
default:
break;
}
}
// Rotation angles for sprites
short rotang = 0;
void
JAnalyzeSprites(tspriteptr_t tspr)
{
rotang += 4;
if (rotang > 2047)
rotang = 0;
// Take care of autosizing
DoAutoSize(tspr);
2019-10-23 19:11:37 +00:00
if (videoGetRenderMode() >= REND_POLYMOST && md_tilehasmodel(tspr->picnum, 0) >= 0 && hw_models) return;
// Check for voxels
//if (bVoxelsOn)
if (r_voxels)
{
2020-02-02 19:48:40 +00:00
if (aVoxelArray[tspr->picnum].Voxel >= 0 && !(spriteext[tspr->owner].flags & SPREXT_NOTMD))
{
// Turn on voxels
tspr->picnum = aVoxelArray[tspr->picnum].Voxel; // Get the voxel number
tspr->cstat |= 48; // Set stat to voxelize sprite
}
}
else
{
switch (tspr->picnum)
{
case 764: // Gun barrel
2020-02-02 19:48:40 +00:00
if (!r_voxels || (spriteext[tspr->owner].flags & SPREXT_NOTMD))
{
tspr->cstat |= 16;
break;
}
if (aVoxelArray[tspr->picnum].Voxel >= 0)
{
// Turn on voxels
tspr->picnum = aVoxelArray[tspr->picnum].Voxel; // Get the voxel number
tspr->cstat |= 48; // Set stat to voxelize sprite
}
break;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
// Parental Lockout Stuff
//////////////////////////////////////////////////////////////////////////////////////////////
OrgTileList orgwalllist; // The list containing orginal wall
// pics
OrgTileList orgwalloverlist; // The list containing orginal wall
// over pics
OrgTileList orgsectorceilinglist; // The list containing orginal sector
// ceiling pics
OrgTileList orgsectorfloorlist; // The list containing orginal sector
// floor pics
void
InsertOrgTile(OrgTileP tp, OrgTileListP thelist)
{
ASSERT(tp);
ASSERT(ValidPtr(tp));
// if list is empty, insert at front
if (EMPTY(thelist))
{
INSERT(thelist, tp);
return;
}
// Otherwise insert it at end
INSERT_TAIL(thelist, tp);
return;
}
OrgTileP
InitOrgTile(OrgTileListP thelist)
{
OrgTileP tp;
tp = (OrgTileP)CallocMem(sizeof(OrgTile), 1);
ASSERT(tp);
InsertOrgTile(tp, thelist);
return tp;
}
void
KillOrgTile(OrgTileP tp)
{
ASSERT(tp);
ASSERT(ValidPtr(tp));
REMOVE(tp);
FreeMem(tp);
}
OrgTileP
FindOrgTile(short index, OrgTileListP thelist)
{
OrgTileP tp, next_tp;
if (EMPTY(thelist))
return NULL;
TRAVERSE(thelist, tp, next_tp)
{
if (tp->index == index)
return tp;
}
return NULL;
}
// Call this at terminate game time
void
JS_UnInitLockouts(void)
{
OrgTileP tp=NULL, next_tp=NULL;
if (orgwalllist.Next)
{
TRAVERSE(&orgwalllist, tp, next_tp)
{
KillOrgTile(tp);
}
}
if (orgwalloverlist.Next)
{
TRAVERSE(&orgwalloverlist, tp, next_tp)
{
KillOrgTile(tp);
}
}
if (orgsectorceilinglist.Next)
{
TRAVERSE(&orgsectorceilinglist, tp, next_tp)
{
KillOrgTile(tp);
}
}
if (orgsectorfloorlist.Next)
{
TRAVERSE(&orgsectorfloorlist, tp, next_tp)
{
KillOrgTile(tp);
}
}
}
/////////////////////////////////////////////////////
// Initialize the original tiles list
// Creates a list of all orginal tiles and their
// replacements. Several tiles can use the same
// replacement tilenum, so the list is built
// using the original tilenums as a basis for
// memory allocation
// t == 1 - wall
// t == 2 - overpicnum
// t == 3 - ceiling
// t == 4 - floor
/////////////////////////////////////////////////////
void
JS_PlockError(short wall_num, short t)
{
Printf("ERROR: JS_InitLockouts(), out of range tile number\n");
switch (t)
{
case 1:
Printf("wall %d, x %d, y %d, pic %d\n", wall_num, TrackerCast(wall[wall_num].x), TrackerCast(wall[wall_num].y), TrackerCast(wall[wall_num].picnum));
break;
case 2:
Printf("wall %d, x %d, y %d, OVERpic %d\n", wall_num, TrackerCast(wall[wall_num].x), TrackerCast(wall[wall_num].y), TrackerCast(wall[wall_num].overpicnum));
break;
case 3:
Printf("sector %d, ceiling %d\n", wall_num, TrackerCast(sector[wall_num].ceilingpicnum));
break;
case 4:
Printf("sector %d, floor %d\n", wall_num, TrackerCast(sector[wall_num].floorpicnum));
break;
}
}
void
JS_InitLockouts(void)
{
short i;
OrgTileP tp;
INITLIST(&orgwalllist); // The list containing orginal wall
// pics
INITLIST(&orgwalloverlist); // The list containing orginal wall
// over pics
INITLIST(&orgsectorceilinglist); // The list containing orginal sector
// ceiling pics
INITLIST(&orgsectorfloorlist); // The list containing orginal sector
// floor pics
// Check all walls
for (i = 0; i < numwalls; i++)
{
short picnum;
picnum = wall[i].picnum;
if (aVoxelArray[picnum].Parental >= INVISTILE)
{
JS_PlockError(i, 1);
continue;
}
if (aVoxelArray[picnum].Parental >= 0)
{
if ((tp = FindOrgTile(i, &orgwalllist)) == NULL)
tp = InitOrgTile(&orgwalllist);
tp->index = i;
tp->orgpicnum = wall[i].picnum;
}
picnum = wall[i].overpicnum;
if (aVoxelArray[picnum].Parental >= INVISTILE)
{
JS_PlockError(i, 2);
continue;
}
if (aVoxelArray[picnum].Parental >= 0)
{
if ((tp = FindOrgTile(i, &orgwalloverlist)) == NULL)
tp = InitOrgTile(&orgwalloverlist);
tp->index = i;
tp->orgpicnum = wall[i].overpicnum;
}
}
// Check all ceilings and floors
for (i = 0; i < numsectors; i++)
{
short picnum;
picnum = sector[i].ceilingpicnum;
if (aVoxelArray[picnum].Parental >= INVISTILE)
{
JS_PlockError(i, 3);
continue;
}
if (aVoxelArray[picnum].Parental >= 0)
{
if ((tp = FindOrgTile(i, &orgsectorceilinglist)) == NULL)
tp = InitOrgTile(&orgsectorceilinglist);
tp->index = i;
tp->orgpicnum = sector[i].ceilingpicnum;
}
picnum = sector[i].floorpicnum;
if (aVoxelArray[picnum].Parental >= INVISTILE)
{
JS_PlockError(i, 2);
continue;
}
if (aVoxelArray[picnum].Parental >= 0)
{
if ((tp = FindOrgTile(i, &orgsectorfloorlist)) == NULL)
tp = InitOrgTile(&orgsectorfloorlist);
tp->index = i;
tp->orgpicnum = sector[i].floorpicnum;
}
}
}
/////////////////////////////////////////////////////
// Switch back and forth between locked out stuff
/////////////////////////////////////////////////////
void
JS_ToggleLockouts(void)
{
short i;
OrgTileP tp;
// Check all walls
for (i = 0; i < numwalls; i++)
{
short picnum;
if (adult_lockout)
{
picnum = wall[i].picnum;
ASSERT(aVoxelArray[picnum].Parental < INVISTILE); // Invalid, walls can't
// be invisible
if (aVoxelArray[picnum].Parental >= 0)
{
wall[i].picnum = aVoxelArray[picnum].Parental;
}
}
else if ((tp = FindOrgTile(i, &orgwalllist)) != NULL)
wall[i].picnum = tp->orgpicnum; // Restore them
if (adult_lockout)
{
picnum = wall[i].overpicnum;
ASSERT(aVoxelArray[picnum].Parental < INVISTILE); // Invalid, walls can't
// be invisible
if (aVoxelArray[picnum].Parental >= 0)
{
wall[i].overpicnum = aVoxelArray[picnum].Parental;
}
}
else if ((tp = FindOrgTile(i, &orgwalloverlist)) != NULL)
wall[i].overpicnum = tp->orgpicnum; // Restore them
}
// Check all sectors
for (i = 0; i < numsectors; i++)
{
short picnum;
if (adult_lockout)
{
picnum = sector[i].ceilingpicnum;
ASSERT(aVoxelArray[picnum].Parental < INVISTILE); // Invalid, walls can't
// be invisible
if (aVoxelArray[picnum].Parental >= 0)
{
sector[i].ceilingpicnum = aVoxelArray[picnum].Parental;
}
}
else if ((tp = FindOrgTile(i, &orgsectorceilinglist)) != NULL)
sector[i].ceilingpicnum = tp->orgpicnum; // Restore them
if (adult_lockout)
{
picnum = sector[i].floorpicnum;
ASSERT(aVoxelArray[picnum].Parental < INVISTILE); // Invalid, walls can't
// be invisible
if (aVoxelArray[picnum].Parental >= 0)
{
sector[i].floorpicnum = aVoxelArray[picnum].Parental;
}
}
else if ((tp = FindOrgTile(i, &orgsectorfloorlist)) != NULL)
sector[i].floorpicnum = tp->orgpicnum; // Restore them
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void
UnlockKeyLock(short key_num, short hit_sprite)
{
SPRITEp sp;
int SpriteNum = 0, NextSprite = 0, color = 0;
// Get palette by looking at key number
switch (key_num - 1)
{
case 0: // RED_KEY
color = PALETTE_PLAYER9;
break;
case 1: // BLUE_KEY
color = PALETTE_PLAYER7;
break;
case 2: // GREEN_KEY
color = PALETTE_PLAYER6;
break;
case 3: // YELLOW_KEY
color = PALETTE_PLAYER4;
break;
case 4: // SILVER_SKELKEY
color = PALETTE_PLAYER4;
break;
case 5: // GOLD_SKELKEY
color = PALETTE_PLAYER1;
break;
case 6: // BRONZE_SKELKEY
color = PALETTE_PLAYER8;
break;
case 7: // RED_SKELKEY
color = PALETTE_PLAYER9;
break;
}
TRAVERSE_SPRITE_STAT(headspritestat[0], SpriteNum, NextSprite)
{
sp = &sprite[SpriteNum];
switch (sp->picnum)
{
case SKEL_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_UNLOCK, sp, v3df_doppler | v3df_dontpan);
if (SpriteNum == hit_sprite)
sp->picnum = SKEL_UNLOCKED;
}
break;
case RAMCARD_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_CARDUNLOCK, sp, v3df_doppler | v3df_dontpan);
sp->picnum = RAMCARD_UNLOCKED;
}
break;
case CARD_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_RAMUNLOCK, sp, v3df_doppler | v3df_dontpan);
if (SpriteNum == hit_sprite)
sp->picnum = CARD_UNLOCKED;
else
sp->picnum = CARD_UNLOCKED+1;
}
break;
}
}
}
END_SW_NS