//------------------------------------------------------------------------- /* 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 "names.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 "render.h" #include "glbackend/glbackend.h" EXTERN_CVAR(Bool, testnewrenderer) 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]; bool mirrorinview; uint32_t oscilationclock; // Voxel stuff //bool bVoxelsOn = true; // Turn voxels on by default bool bSpinBobVoxels = false; // Do twizzly stuff to voxels, but // not by default bool 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; int SpriteNum; USERp u; short i; StatIterator it(STAT_DEFAULT); while ((SpriteNum = it.NextIndex()) >= 0) { 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; int SpriteNum; bool 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 = I_GetBuildTime(); 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) { int ii; SPRITEp sp; Found_Cam = false; StatIterator it(STAT_ST1); while ((ii = it.NextIndex()) >= 0) { 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 variables SP_TAG5(sp) = sp->ang; // Set current angle to // sprite angle Found_Cam = true; } } it.Reset(STAT_SPAWN_SPOT); while ((ii = it.NextIndex()) >= 0) { 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 variables 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", wall[i].hitag); Printf("Map Coordinates: x = %d, y = %d\n", wall[i].x, wall[i].y); break; } mirror[mirrorcnt].ismagic = true; Found_Cam = false; if (TEST_BOOL1(&sprite[mirror[mirrorcnt].camera])) { StatIterator it(STAT_DEFAULT); while ((SpriteNum = it.NextIndex()) >= 0) { 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, wall[i].hitag); Printf("Map Coordinates: x = %d, y = %d\n", wall[i].x, 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, binangle ang, fixedhoriz horiz, short dacursectnum, short tilenume, double smoothratio) { TileFiles.MakeCanvas(tilenume, tileWidth(tilenume), tileHeight(tilenume)); auto canvas = renderSetTarget(tilenume); if (!canvas) return; screen->RenderTextureView(canvas, [=](IntRect& rect) { if (!testnewrenderer) { renderDrawRoomsQ16(daposx, daposy, daposz, ang.asq16(), horiz.asq16(), dacursectnum); analyzesprites(pm_tsprite, pm_spritesortcnt, daposx, daposy, daposz, ang.asbuild()); renderDrawMasks(); } else { render_camtex(nullptr, { daposx, daposy, daposz }, dacursectnum, ang, horiz, buildlook(0), tileGetTexture(tilenume), rect, smoothratio); } }); renderRestoreTarget(); } void JS_ProcessEchoSpot() { int i; SPRITEp tp; int j,dist; PLAYERp pp = Player+screenpeek; int16_t reverb; bool reverb_set = false; // Process echo sprites StatIterator it(STAT_ECHO); while ((i = it.NextIndex()) >= 0) { dist = 0x7fffffff; tp = &sprite[i]; j = abs(tp->x - pp->posx); j += abs(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 int lastcamclock; // 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, double smoothratio) { int j, cnt; int dist; int tposx, tposy; // Camera int* longptr; bool bIsWallMirror = false; int camclock = I_GetBuildTime(); camloopcnt += camclock - lastcamclock; if (camloopcnt > (60 * 5)) // 5 seconds per player view { camloopcnt = 0; camplayerview++; if (camplayerview >= numplayers) camplayerview = 1; } lastcamclock = camclock; // 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 = camclock - 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 = abs(wall[mirror[cnt].mirrorwall].x - tx); j += abs(wall[mirror[cnt].mirrorwall].y - ty); if (j < dist) dist = j; } else { SPRITEp tp; tp = &sprite[mirror[cnt].camsprite]; j = abs(tp->x - tx); j += abs(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 = abs(midx - tx); tdy = abs(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 = abs(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) { bool 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 (abs(getincangle(sp->ang, SP_TAG5(sp))) >= 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 (abs(getincangle(sp->ang, SP_TAG5(sp))) >= 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 = clamp(SP_TAG7(sp), gi->playerHorizMin(), gi->playerHorizMax()); } else camhoriz = 0; // 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->angle.ang, cp->horizon.horiz, cp->cursectnum, mirror[cnt].campic, smoothratio); } else { drawroomstotile(sp->x, sp->y, sp->z, buildang(SP_TAG5(sp)), buildhoriz(camhoriz), sp->sectnum, mirror[cnt].campic, smoothratio); } } } } } } } } // Workaround until the camera code can be refactored to process all camera textures that were visible last frame. // Need to stash the parameters for later use. This is only used to find the nearest camera. static PLAYERp cam_pp; static int cam_tx, cam_ty, cam_tz; static int oldstat; void JS_CameraParms(PLAYERp pp, int tx, int ty, int tz) { cam_pp = pp; cam_tx = tx; cam_ty = ty; cam_tz = tz; } void GameInterface::UpdateCameras(double smoothratio) { JS_DrawCameras(cam_pp, cam_tx, cam_ty, cam_tz, smoothratio); } void GameInterface::EnterPortal(spritetype* viewer, int type) { if (type == PORTAL_WALL_MIRROR) display_mirror++; } void GameInterface::LeavePortal(spritetype* viewer, int type) { if (type == PORTAL_WALL_MIRROR) display_mirror--; } 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 (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, wall[wall_num].x, wall[wall_num].y, wall[wall_num].picnum); break; case 2: Printf("wall %d, x %d, y %d, OVERpic %d\n", wall_num, wall[wall_num].x, wall[wall_num].y, wall[wall_num].overpicnum); break; case 3: Printf("sector %d, ceiling %d\n", wall_num, sector[wall_num].ceilingpicnum); break; case 4: Printf("sector %d, floor %d\n", wall_num, 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, 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; } StatIterator it(STAT_DEFAULT); while ((SpriteNum = it.NextIndex()) >= 0) { 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