raze/source/sw/src/jsector.cpp
Mitchell Richters 40739d9176 - SW: Add ogameclock and use in places where ototalclock was used.
* Duke used `ototalclock` only to keep track of when the game ticked. SW uses it in the rendering code for differentials between tics so we should preserve that to prevent issues down the track.
2020-08-26 09:48:51 +10:00

1458 lines
44 KiB
C++

//-------------------------------------------------------------------------
/*
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 "names2.h"
#include "panel.h"
#include "game.h"
#include "tags.h"
#include "sector.h"
#include "player.h"
#include "sprite.h"
#include "jsector.h"
#include "jtags.h"
#include "lists.h"
#include "pal.h"
#include "parent.h"
#include "v_video.h"
#include "glbackend/glbackend.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;
}
}
}
/////////////////////////////////////////////////////
// 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 = ogameclock;
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);
auto canvas = renderSetTarget(tilenume);
if (!canvas) return;
screen->RenderTextureView(canvas, [=](IntRect& rect)
{
renderDrawRoomsQ16(daposx, daposy, daposz, daq16ang, daq16horiz, dacursectnum);
analyzesprites(daposx, daposy, daposz, FALSE);
renderDrawMasks();
});
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 += gameclock - ogameclock;
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 = ogameclock - 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;
}
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);
// 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);
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
renderPrepareMirror(tx, ty, tz, tpq16ang, tpq16horiz,
mirror[cnt].mirrorwall, /*mirror[cnt].mirrorsector,*/ &tposx, &tposy, &tang);
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;
// renderDrawRoomsQ16(tx, ty, tz, tpq16ang, tpq16horiz, pp->cursectnum);
// Clean up anything that the camera view might have done
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);
if (videoGetRenderMode() >= REND_POLYMOST && md_tilehasmodel(tspr->picnum, 0) >= 0 && hw_models) return;
// Check for voxels
//if (bVoxelsOn)
if (r_voxels)
{
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
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);
// 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);
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