#include #include #include "typedefs.h" #include "engine.h" #include "debug4g.h" #include "bstub.h" #include "misc.h" #include "gameutil.h" #include "build.h" #include "trig.h" #include "names.h" #include "db.h" #include "tile.h" #include "screen.h" #include "gui.h" #include "key.h" #include "globals.h" #include "gfx.h" #include "mouse.h" #include "sectorfx.h" typedef void HSECTORFUNC( short nSector ); typedef void HSPRITEFUNC( SPRITE *pSprite ); /******************************************************************************* Defines *******************************************************************************/ #define kPlayerRadius 200 #define kMaxVisited 8192 // set to larger of kMaxSectors and kMaxWalls /******************************************************************************* Local Variables *******************************************************************************/ static char buffer[256] = ""; static char *gZModeMsg[3] = { "Gravity", "Locked/Step", "Locked/Free" }; static BOOL visited[kMaxVisited]; static int gStep; static ushort WallShadeFrac[kMaxWalls]; static ushort FloorShadeFrac[kMaxSectors]; static ushort CeilShadeFrac[kMaxSectors]; static int WallArea[kMaxWalls]; static int SectorArea[kMaxSectors]; static int WallNx[kMaxWalls], WallNy[kMaxWalls]; static int FloorNx[kMaxSectors], FloorNy[kMaxSectors], FloorNz[kMaxSectors]; static int CeilNx[kMaxSectors], CeilNy[kMaxSectors], CeilNz[kMaxSectors]; /******************************************************************************* These are globals that should be referenced in other parts of build.c. Ken should ADD these to BUILD and make them non-static. *******************************************************************************/ char tempvisibility = 0; /*********************************************************************** * SetCeilingZ() * * Change the z of a sector ceiling and all sprites on the ceiling **********************************************************************/ static void SetCeilingZ( int nSector, long z ) { dassert(nSector >= 0 && nSector < kMaxSectors); // don't allow to go through the floor z = ClipHigh(z, sector[nSector].floorz); for (int i = headspritesect[searchsector]; i != -1; i = nextspritesect[i]) { int zTop, zBot; SPRITE *pSprite = &sprite[i]; GetSpriteExtents(pSprite, &zTop, &zBot); if ( zTop <= getceilzofslope((short)nSector, pSprite->x, pSprite->y) ) pSprite->z += z - sector[nSector].ceilingz; } sector[nSector].ceilingz = z; } /*********************************************************************** * SetFloorZ() * * Change the z of a sector floor and all sprites on the floor **********************************************************************/ static void SetFloorZ( int nSector, long z ) { dassert(nSector >= 0 && nSector < kMaxSectors); // don't allow to go through the ceiling z = ClipLow(z, sector[nSector].ceilingz); for (int i = headspritesect[searchsector]; i != -1; i = nextspritesect[i]) { int zTop, zBot; SPRITE *pSprite = &sprite[i]; GetSpriteExtents(pSprite, &zTop, &zBot); if (zBot >= getflorzofslope((short)nSector, pSprite->x, pSprite->y) ) pSprite->z += z - sector[nSector].floorz; } sector[nSector].floorz = z; } /*********************************************************************** * SetCeilingSlope() * * Change the slope of a sector ceiling and all sprites on the ceiling **********************************************************************/ static void SetCeilingSlope( short nSector, short nSlope ) { dassert(nSector >= 0 && nSector < kMaxSectors); sector[nSector].ceilingslope = nSlope; if ( sector[nSector].ceilingslope == 0 ) sector[nSector].ceilingstat &= ~kSectorSloped; else sector[nSector].ceilingstat |= kSectorSloped; for (int i = headspritesect[searchsector]; i != -1; i = nextspritesect[i]) { SPRITE *pSprite = &sprite[i]; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); long z = getceilzofslope(nSector, pSprite->x, pSprite->y); if ( zTop < z ) sprite[i].z += z - zTop; } } /*********************************************************************** * SetFloorSlope() * * Change the slope of a sector floor and all sprites on the floor **********************************************************************/ static void SetFloorSlope( short nSector, short nSlope ) { dassert(nSector >= 0 && nSector < kMaxSectors); sector[nSector].floorslope = nSlope; if ( sector[nSector].floorslope == 0 ) sector[nSector].floorstat &= ~kSectorSloped; else sector[nSector].floorstat |= kSectorSloped; for (int i = headspritesect[searchsector]; i != -1; i = nextspritesect[i]) { SPRITE *pSprite = &sprite[i]; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); long z = getflorzofslope(nSector, pSprite->x, pSprite->y); if ( zBot > z ) sprite[i].z += z - zBot; } } /*********************************************************************** * IsSectorHighlight() * * Determines if a sector is in the sector highlight list **********************************************************************/ static BOOL IsSectorHighlight( int nSector ) { dassert(nSector >= 0 && nSector < kMaxSectors); for (int i = 0; i < highlightsectorcnt; i++) if (highlightsector[i] == nSector) return TRUE; return FALSE; } inline int NextCCW( int nWall ) { dassert( wall[nWall].nextwall >= 0 ); return wall[wall[nWall].nextwall].point2; } /******************************************************************************* FUNCTION: GetWallZPeg() DESCRIPTION: Calculate the z position that the wall texture is relative to. *******************************************************************************/ inline int GetWallZPeg( int nWall ) { dassert(nWall >= 0 && nWall < kMaxWalls); int z; int nSector = sectorofwall((short)nWall); dassert(nSector >= 0 && nSector < kMaxSectors); int nNextSector = wall[nWall].nextsector; if (nNextSector == -1) { // one sided wall if ( wall[nWall].cstat & kWallBottomOrg ) z = sector[nSector].floorz; else z = sector[nSector].ceilingz; } else { // two sided wall if ( wall[nWall].cstat & kWallOutsideOrg ) z = sector[nSector].ceilingz; else { // top step if (sector[nNextSector].ceilingz > sector[nSector].ceilingz) z = sector[nNextSector].ceilingz; // bottom step if (sector[nNextSector].floorz < sector[nSector].floorz) z = sector[nNextSector].floorz; } } return z; } static void AlignWalls( int nWall0, int z0, int nWall1, int z1, int nTile ) { dassert(nWall0 >= 0 && nWall0 < kMaxWalls); dassert(nWall1 >= 0 && nWall1 < kMaxWalls); dprintf("¯%d", nWall1); // do the x alignment wall[nWall1].cstat &= ~kWallFlipMask; // set to non-flip wall[nWall1].xpanning = (uchar)((wall[nWall0].xpanning + (wall[nWall0].xrepeat << 3)) % tilesizx[nTile]); z1 = GetWallZPeg(nWall1); int n = picsiz[nTile] >> 4; if ( (1 << n) != tilesizy[nTile] ) n++; wall[nWall1].yrepeat = wall[nWall0].yrepeat; wall[nWall1].ypanning = (uchar)(wall[nWall0].ypanning + (((z1 - z0) * wall[nWall0].yrepeat) >> (n + 3))); } #define kMaxAlign 64 static void AutoAlignWalls( int nWall0, int ply = 0 ) { dassert(nWall0 >= 0 && nWall0 < kMaxWalls); int z0, z1; int nTile = wall[nWall0].picnum; int nWall1; int branch = 0; if ( ply == kMaxAlign ) { dprintf("\nAlignment aborted by ply -- possible node problem!\n"); return; } if ( ply == 0 ) { // clear visited bits memset(visited, FALSE, sizeof(visited)); visited[nWall0] = TRUE; dprintf("Aligning walls: %d", nWall0); } z0 = GetWallZPeg(nWall0); nWall1 = wall[nWall0].point2; dassert(nWall1 >= 0 && nWall1 < kMaxWalls); // loop through walls at this vertex in CCW order while (1) { // break if this wall would connect us in a loop if ( visited[nWall1] ) break; visited[nWall1] = TRUE; // break if reached back of left wall if ( wall[nWall1].nextwall == nWall0 ) break; if ( wall[nWall1].picnum == nTile ) { z1 = GetWallZPeg(nWall1); BOOL visible = FALSE; int nNextSector = wall[nWall1].nextsector; if ( nNextSector < 0 ) visible = TRUE; else { // ignore two sided walls that have no visible face int nSector = wall[wall[nWall1].nextwall].nextsector; if ( getceilzofslope((short)nSector, wall[nWall1].x, wall[nWall1].y) < getceilzofslope((short)nNextSector, wall[nWall1].x, wall[nWall1].y) ) visible = TRUE; if ( getflorzofslope((short)nSector, wall[nWall1].x, wall[nWall1].y) > getflorzofslope((short)nNextSector, wall[nWall1].x, wall[nWall1].y) ) visible = TRUE; } if ( visible ) { if ( branch++ ) dprintf(" %d", nWall0); AlignWalls(nWall0, z0, nWall1, z1, nTile); int nNextWall = wall[nWall1].nextwall; // if wall was 1-sided, no need to recurse if ( nNextWall < 0 ) { nWall0 = nWall1; z0 = GetWallZPeg(nWall0); nWall1 = wall[nWall0].point2; branch = 0; continue; } else { if ( wall[nWall1].cstat & kWallBottomSwap && wall[nNextWall].picnum == nTile ) AlignWalls(nWall0, z0, nNextWall, z1, nTile); AutoAlignWalls(nWall1, ply + 1); } } } if (wall[nWall1].nextwall < 0) break; nWall1 = wall[wall[nWall1].nextwall].point2; } if ( ply == 0 ) dprintf("\n"); } static void BuildStairsF( int nSector ) { int i, j; dassert(nSector >= 0 && nSector < kMaxSectors); // mark this sector as visited visited[nSector] = TRUE; for (i = 0; i < sector[nSector].wallnum; i++) { j = wall[sector[nSector].wallptr + i].nextsector; if (j != -1) { if ( IsSectorHighlight(j) && !visited[j] ) { SetFloorZ(j, sector[nSector].floorz - (gStairHeight << 8)); BuildStairsF(j); } } } } static void BuildStairsC( int nSector ) { int i, j; dassert(nSector >= 0 && nSector < kMaxSectors); // mark this sector as visited visited[nSector] = TRUE; for (i = 0; i < sector[nSector].wallnum; i++) { j = wall[sector[nSector].wallptr + i].nextsector; if (j != -1) { if ( IsSectorHighlight(j) && !visited[j] ) { SetCeilingZ(j, sector[nSector].ceilingz - (gStairHeight << 8)); BuildStairsC(j); } } } } static void ShootRay(int x, int y, int z, short nSector, int dx, int dy, int dz, int nIntensity, int nReflect, int dist) { short hitsect = -1, hitwall = -1, hitsprite = -1; long hitx, hity, hitz; int x2, y2, z2; int dotProduct; int E; hitscan(x, y, z, nSector, dx, dy, dz << 4, &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz); x2 = qabs(x - hitx) >> 4; y2 = qabs(y - hity) >> 4; z2 = qabs(z - hitz) >> 8; dist += ksqrt(x2 * x2 + y2 * y2 + z2 * z2); if (hitwall >= 0) { E = divscale16(nIntensity, ClipLow(dist + gLBRampDist, 1)); if (E <= 0) return; // too small to deal with // distribute over area of wall E = divscale16(E, ClipLow(WallArea[hitwall], 1)); int n = (wall[hitwall].shade << 16) | WallShadeFrac[hitwall]; n -= E; wall[hitwall].shade = (schar)ClipLow(n >> 16, gLBMaxBright); WallShadeFrac[hitwall] = (ushort)(n & 0xFFFF); if ( nReflect < gLBReflections ) { int Nx = WallNx[hitwall]; int Ny = WallNy[hitwall]; // dotProduct is cos of angle of intersection dotProduct = dmulscale16(dx, Nx, dy, Ny); if (dotProduct < 0) return; // bogus intersection // reflect vector dx -= mulscale16(2 * dotProduct, Nx); dy -= mulscale16(2 * dotProduct, Ny); hitx += dx >> 12; hity += dy >> 12; hitz += dz >> 8; nIntensity -= mulscale16(nIntensity, gLBAttenuation); ShootRay(hitx, hity, hitz, hitsect, dx, dy, dz, nIntensity, nReflect + 1, dist); } } else if (hitsprite >= 0) { } else if (dz > 0) // hit floor { E = divscale16(nIntensity, ClipLow(dist + gLBRampDist, 1)); if (E <= 0) return; // too small to deal with // distribute over area of floor E = divscale16(E, ClipLow(SectorArea[hitsect], 1)); int n = (sector[hitsect].floorshade << 16) | FloorShadeFrac[hitsect]; n -= E; sector[hitsect].floorshade = (schar)ClipLow(n >> 16, gLBMaxBright); FloorShadeFrac[hitsect] = (ushort)(n & 0xFFFF); if ( nReflect < gLBReflections ) { if ( sector[hitsect].floorstat & kSectorSloped ) { int Nx = FloorNx[hitsect]; int Ny = FloorNy[hitsect]; int Nz = FloorNz[hitsect]; // dotProduct is cos of angle of intersection dotProduct = tmulscale16(dx, Nx, dy, Ny, dz, Nz); if (dotProduct < 0) return; // bogus intersection dx -= mulscale16(2 * dotProduct, Nx); dy -= mulscale16(2 * dotProduct, Ny); dz -= mulscale16(2 * dotProduct, Nz); } else dz = -dz; nIntensity -= mulscale16(nIntensity, gLBAttenuation); hitx += dx >> 12; hity += dy >> 12; hitz += dz >> 8; ShootRay(hitx, hity, hitz, hitsect, dx, dy, dz, nIntensity, nReflect + 1, dist); } } else // hit ceiling { E = divscale16(nIntensity, ClipLow(dist + gLBRampDist, 1)); if (E <= 0) return; // too small to deal with // distribute over area of ceiling E = divscale16(E, ClipLow(SectorArea[hitsect], 1)); int n = (sector[hitsect].ceilingshade << 16) | CeilShadeFrac[hitsect]; n -= E; sector[hitsect].ceilingshade = (schar)ClipLow(n >> 16, gLBMaxBright); CeilShadeFrac[hitsect] = (ushort)(n & 0xFFFF); if ( nReflect < gLBReflections ) { // reflect vector if ( sector[hitsect].ceilingstat & kSectorSloped ) { int Nx = CeilNx[hitsect]; int Ny = CeilNy[hitsect]; int Nz = CeilNz[hitsect]; // dotProduct is cos of angle of intersection dotProduct = tmulscale16(dx, Nx, dy, Ny, dz, Nz); if (dotProduct < 0) return; // bogus intersection dx -= mulscale16(2 * dotProduct, Nx); dy -= mulscale16(2 * dotProduct, Ny); dz -= mulscale16(2 * dotProduct, Nz); } else dz = -dz; nIntensity -= mulscale16(nIntensity, gLBAttenuation); hitx += dx >> 12; hity += dy >> 12; hitz += dz >> 8; ShootRay(hitx, hity, hitz, hitsect, dx, dy, dz, nIntensity, nReflect + 1, dist); } } } static int AreaOfSector( SECTOR *pSector ) { int area = 0; int startwall = pSector->wallptr; int endwall = startwall + pSector->wallnum; for (int i = startwall; i < endwall; i++) { int x1 = wall[i].x >> 4; int y1 = wall[i].y >> 4; int x2 = wall[wall[i].point2].x >> 4; int y2 = wall[wall[i].point2].y >> 4; area += (x1+x2) * (y2-y1); // add area of this trapezoid } area >>= 1; return area; } static void SetupLightBomb( void ) { SECTOR *pSector; WALL *pWall; int nSector, nWall; pSector = §or[0]; for ( nSector = 0; nSector < numsectors; nSector++, pSector++ ) { FloorShadeFrac[nSector] = 0; CeilShadeFrac[nSector] = 0; SectorArea[nSector] = AreaOfSector(pSector); pWall = &wall[pSector->wallptr]; for (nWall = pSector->wallptr; nWall < pSector->wallptr + pSector->wallnum; nWall++, pWall++) { WallShadeFrac[nWall] = 0; int Nx, Ny; // calculate normal for wall Nx = (wall[pWall->point2].y - pWall->y) >> 4; Ny = -(wall[pWall->point2].x - pWall->x) >> 4; int length = ksqrt(Nx * Nx + Ny * Ny); WallNx[nWall] = divscale16(Nx, length); WallNy[nWall] = divscale16(Ny, length); long ceilZ, ceilZ1, ceilZ2, floorZ, floorZ1, floorZ2; getzsofslope((short)nSector, pWall->x, pWall->y, &ceilZ1, &floorZ1); getzsofslope((short)nSector, wall[pWall->point2].x, wall[pWall->point2].y, &ceilZ2, &floorZ2); ceilZ = (ceilZ1 + ceilZ2) >> 1; floorZ = (floorZ1 + floorZ2) >> 1; // calculate the area of the wall int height = floorZ - ceilZ; // red wall? if ( pWall->nextsector >= 0 ) { height = 0; long nextCeilZ, nextCeilZ1, nextCeilZ2, nextFloorZ, nextFloorZ1, nextFloorZ2; getzsofslope(pWall->nextsector, pWall->x, pWall->y, &nextCeilZ1, &nextFloorZ1); getzsofslope(pWall->nextsector, wall[pWall->point2].x, wall[pWall->point2].y, &nextCeilZ2, &nextFloorZ2); nextCeilZ = (nextCeilZ1 + nextCeilZ2) >> 1; nextFloorZ = (nextFloorZ1 + nextFloorZ2) >> 1; // floor step up? if ( nextFloorZ < floorZ) height += floorZ - nextFloorZ; // ceiling step down? if ( nextCeilZ > ceilZ ) height += nextCeilZ - ceilZ; } WallArea[nWall] = length * height >> 8; } FloorNx[nSector] = 0; FloorNy[nSector] = 0; FloorNz[nSector] = -0x10000; CeilNx[nSector] = 0; CeilNy[nSector] = 0; CeilNz[nSector] = 0x10000; } } static void LightBomb( int x, int y, int z, short nSector ) { int dx, dy, dz; for (int a = kAngle90 - kAngle60; a <= kAngle90 + kAngle60; a += kAngle15) { for (int i = 0; i < kAngle360; i += kAngle360 / 256) { dx = mulscale30(Cos(i), Sin(a)) >> 16; dy = mulscale30(Sin(i), Sin(a)) >> 16; dz = Cos(a) >> 16; ShootRay(x, y, z, nSector, dx, dy, dz, gLBIntensity, 0, 0); } } } static void SetFirstWall( int nSector, int nWall ) { int start, length, shift; int i, j, k; WALL tempWall; // rotate the walls using the shift copy algorithm start = sector[nSector].wallptr; length = sector[nSector].wallnum; dassert(nWall >= start && nWall < start + length); shift = nWall - start; if (shift == 0) return; i = k = start; for (int n = length; n > 0; n--) { if (i == k) tempWall = wall[i]; j = i + shift; while (j >= start + length) j -= length; if (j == k) { wall[i] = tempWall; i = ++k; continue; } wall[i] = wall[j]; i = j; } for (i = start; i < start + length; i++) { if ( (wall[i].point2 -= shift) < start ) wall[i].point2 += length; if ( wall[i].nextwall >= 0 ) wall[wall[i].nextwall].nextwall = (short)i; } CleanUp(); } static void TranslateWallToSector( void ) { short hitsect = -1, hitwall = -1, hitsprite = -1; long hitx, hity, hitz; long x, y; x = 1 << 14; y = divscale(searchx - xdim / 2, xdim / 2, 14); RotateVector(&x, &y, ang); hitscan(posx, posy, posz, cursectnum, //Start position x, y, (searchy - horiz) * 2000, //vector of 3D ang &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz); if (hitwall == searchwall) // front of wall? searchsector = wall[searchwall].nextsector; else if (hitwall == wall[searchwall].nextwall) // back of 2 texture wall? searchsector = wall[hitwall].nextsector; else { dprintf("Can't figure out what you are pointing at\n"); return; } if (hitz > getflorzofslope(searchsector, hitx, hity)) { searchstat = SS_FLOOR; } else { searchstat = SS_CEILING; } } /******************************************************************************* FUNCTION: InsertMisc() DESCRIPTION: Displays a tile menu allowing user to insert a miscellaneous game object. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. *******************************************************************************/ static BOOL InsertMisc( int where, int nSector, long x, long y, long z, int nAngle ) { static short miscTiles[] = { kPicSwitch1Off, kPicSwitch2Off, kPicSwitch3Off, kPicSwitch4Off, kPicSwitch5Off, kPicSwitch6Off, kPicSwitch7Off, kPicWallCrack, kPicFluorescent, kPicGlass, kPicBlockWeb, kPicWoodBeam, // kPicMetalGrate1, kPicCrateFace, kPicVase1, kPicVase2, kPicStart1, kPicStart2, kPicStart3, kPicStart4, kPicStart5, kPicStart6, kPicStart7, kPicStart8, }; short nType; short nStat = kStatDefault; tileIndexCount = LENGTH(miscTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = miscTiles[i]; // gMiscInfo[i].picnum; short picnum = tilePick( -1 /*gMiscInfo[0].picnum*/, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kPicSwitch1Off: case kPicSwitch2Off: case kPicSwitch3Off: case kPicSwitch4Off: case kPicSwitch5Off: case kPicSwitch6Off: case kPicSwitch7Off: nType = kSwitchToggle; break; case kPicWallCrack: nType = kThingWallCrack; break; case kPicFluorescent: nType = kThingFluorescent; break; case kPicGlass: nType = kThingClearGlass; break; case kPicBlockWeb: nType = kThingWeb; break; case kPicWoodBeam: nType = kThingWoodBeam; break; // case kPicMetalGrate1: // nType = kThingMetalGrate1; // break; case kPicCrateFace: nType = kThingCrateFace; break; case kPicVase1: nType = kThingBlueVase; break; case kPicVase2: nType = kThingBrownVase; break; case kPicStart1: case kPicStart2: case kPicStart3: case kPicStart4: case kPicStart5: case kPicStart6: case kPicStart7: case kPicStart8: nType = kMarkerPlayerStart; break; } int nSprite = insertsprite( (short)nSector, nStat ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; switch( picnum ) { case kPicStart1: case kPicStart2: case kPicStart3: case kPicStart4: case kPicStart5: case kPicStart6: case kPicStart7: case kPicStart8: { int nXSprite = GetXSprite( nSprite ); dassert(nXSprite > 0 && nXSprite < kMaxXSprites); xsprite[nXSprite].data1 = picnum - kPicStart1; break; } } AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); BeepOkay(); return TRUE; } /******************************************************************************* FUNCTION: InsertHazard() DESCRIPTION: Displays a tile menu allowing user to insert a hazard. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. *******************************************************************************/ static BOOL InsertHazard( int where, int nSector, long x, long y, long z, int nAngle ) { static short hazardTiles[] = { kAnmCircSaw1, kAnmTNTProxArmed, kPicTNTBarrel, kAnmFloorSpike, kAnmPendulum, kPicGuillotine, kAnmElectrify, kPicMachineGun, }; short nType; short nStat = kStatDefault; tileIndexCount = LENGTH(hazardTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = hazardTiles[i]; // gItemInfo[i].picnum; short picnum = tilePick( -1 /*gHazardInfo[0].picnum*/, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kAnmCircSaw1: nType = kTrapSawBlade; break; case kAnmTNTProxArmed: nType = kThingTNTProxArmed; nStat = kStatProximity; break; case kPicTNTBarrel: nType = kThingTNTBarrel; break; // case kAnmRollBarrel: // nType = kThingTNTBarrel; // break; case kAnmFloorSpike: nType = kTrapSpiketrap; break; case kAnmPendulum: nType = kTrapPendulum; break; case kPicGuillotine: nType = kTrapGuillotine; break; case kAnmElectrify: nType = kTrapPoweredZap; break; case kPicMachineGun: nType = kThingMachineGun; break; default: scrSetMessage("Invalid hazard tile selected!"); BeepError(); return FALSE; } int nSprite = insertsprite( (short)nSector, nStat ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); BeepOkay(); return TRUE; } /******************************************************************************* FUNCTION: InsertItem() DESCRIPTION: Displays a tile menu allowing user to insert an item on the floor. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. *******************************************************************************/ static BOOL InsertItem( int where, int nSector, long x, long y, long z, int nAngle ) { static short itemTiles[] = { kPicKey1, //kItemKey1 kPicKey2, //kItemKey2 kPicKey3, //kItemKey3 kPicKey4, //kItemKey4 kPicKey5, //kItemKey5 kPicKey6, //kItemKey6 kPicDocBag, //kItemDoctorBag kPicMedPouch, //kItemMedPouch kPicEssence, //kItemLifeEssence kAnmLifeSeed, //kItemLifeSeed kPicPotion, //kItemPotion1 kAnmFeather, //kItemFeatherFall kAnmInviso, //kItemLtdInvisibility kPicInvulnerable, //kItemInvulnerability kPicJumpBoots, //kItemJumpBoots kPicRavenFlight, //kItemRavenFlight kPicGunsAkimbo, //kItemGunsAkimbo kPicDivingSuit, //kItemDivingSuit kPicGasMask, //kItemGasMask kAnmClone, //kItemClone kPicCrystalBall, //kItemCrystalBall kPicDecoy, //kItemDecoy kAnmDoppleganger, //kItemDoppleganger kAnmReflectShots, //kItemReflectiveShots kPicRoseGlasses, //kItemRoseGlasses kAnmCloakNDagger, //kItemShadowCloak kPicShroom1, //kItemShroomRage kPicShroom2, //kItemShroomDelirium kPicShroom3, //kItemShroomGrow kPicShroom4, //kItemShroomShrink kPicDeathMask, //kItemDeathMask kPicGoblet, //kItemWineGoblet kPicBottle1, //kItemWineBottle kPicSkullGrail, //kItemSkullGrail kPicSilverGrail, //kItemSilverGrail kPicTome1, //kItemTome kPicBlackChest, //kItemBlackChest kPicWoodChest, //kItemWoodenChest kPicAsbestosSuit, //kItemAsbestosArmor kPicRandomUp //kItemRandom }; short nType; tileIndexCount = LENGTH(itemTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = itemTiles[i]; // gItemInfo[i].picnum; short picnum = tilePick( -1 /*gItemInfo[0].picnum*/, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kPicKey1: nType = kItemKey1; break; case kPicKey2: nType = kItemKey2; break; case kPicKey3: nType = kItemKey3; break; case kPicKey4: nType = kItemKey4; break; case kPicKey5: nType = kItemKey5; break; case kPicKey6: nType = kItemKey6; break; case kPicDocBag: nType = kItemDoctorBag; break; case kPicMedPouch: nType = kItemMedPouch; break; case kPicEssence: nType = kItemLifeEssence; break; case kAnmLifeSeed: nType = kItemLifeSeed; break; case kPicPotion: nType = kItemPotion1; break; case kAnmFeather: nType = kItemFeatherFall; break; case kAnmInviso: nType = kItemLtdInvisibility; break; case kPicInvulnerable: nType = kItemInvulnerability; break; case kPicJumpBoots: nType = kItemJumpBoots; break; case kPicRavenFlight: nType = kItemRavenFlight; break; case kPicGunsAkimbo: nType = kItemGunsAkimbo; break; case kPicDivingSuit: nType = kItemDivingSuit; break; case kPicGasMask: nType = kItemGasMask; break; case kAnmClone: nType = kItemClone; break; case kPicCrystalBall: nType = kItemCrystalBall; break; case kPicDecoy: nType = kItemDecoy; break; case kAnmDoppleganger: nType = kItemDoppleganger; break; case kAnmReflectShots: nType = kItemReflectiveShots; break; case kPicRoseGlasses: nType = kItemRoseGlasses; break; case kAnmCloakNDagger: nType = kItemShadowCloak; break; case kPicShroom1: nType = kItemShroomRage; break; case kPicShroom2: nType = kItemShroomDelirium; break; case kPicShroom3: nType = kItemShroomGrow; break; case kPicShroom4: nType = kItemShroomShrink; break; case kPicDeathMask: nType = kItemDeathMask; break; case kPicGoblet: nType = kItemWineGoblet; break; case kPicBottle1: nType = kItemWineBottle; break; case kPicSkullGrail: nType = kItemSkullGrail; break; case kPicSilverGrail: nType = kItemSilverGrail; break; case kPicTome1: nType = kItemTome; break; case kPicBlackChest: nType = kItemBlackChest; break; case kPicWoodChest: nType = kItemWoodenChest; break; case kPicAsbestosSuit: nType = kItemAsbestosArmor; break; case kPicRandomUp: nType = kItemRandom; break; default: scrSetMessage("Invalid item tile selected!"); BeepError(); return FALSE; } int nSprite = insertsprite( (short)nSector, kStatItem ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; // gItemInfo[nItemType].shade; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); BeepOkay(); return TRUE; } /******************************************************************************* FUNCTION: InsertAmmo() DESCRIPTION: Displays a tile menu allowing user to insert an ammo object on the floor. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. *******************************************************************************/ static BOOL InsertAmmo( int where, int nSector, long x, long y, long z, int nAngle ) { static short ammoTiles[] = { kPicSpear, kPicSpears, kPicTNTStick, kPicTNTPak, kPicSprayCan, kPicShotShells, kPicTNTBox, kPicTNTRemote, kPicTNTProx, kPicShellBox, kPicBullets, kPicBulletBox, kPicBulletBoxAP, kPicTommyDrum, kPicFlares, kPicFlareHE, kPicFlareBurst, kPicSpearExplode, kPicRandomUp }; short nType; tileIndexCount = LENGTH(ammoTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = ammoTiles[i]; // gAmmoInfo[i].picnum; short picnum = tilePick( -1 /*gAmmoInfo[0].picnum*/, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kPicSpear: nType = kAmmoItemSpear; break; case kPicSpears: nType = kAmmoItemSpearPack; break; case kPicTNTStick: nType = kAmmoItemTNTStick; break; case kPicTNTPak: nType = kAmmoItemTNTBundle; break; case kPicSprayCan: nType = kAmmoItemSprayCan; break; case kPicShotShells: nType = kAmmoItemShells; break; case kPicTNTBox: nType = kAmmoItemTNTCase; break; case kPicTNTRemote: nType = kAmmoItemTNTRemote; break; case kPicTNTProx: nType = kAmmoItemTNTProximity; break; case kPicShellBox: nType = kAmmoItemShellBox; break; case kPicBullets: nType = kAmmoItemBullets; break; case kPicBulletBox: nType = kAmmoItemBulletBox; break; case kPicBulletBoxAP: nType = kAmmoItemAPBullets; break; case kPicTommyDrum: nType = kAmmoItemTommyDrum; break; case kPicFlareHE: nType = kAmmoItemHEFlares; break; case kPicFlareBurst: nType = kAmmoItemStarFlares; break; case kPicSpearExplode: nType = kAmmoItemHESpears; break; case kPicFlares: nType = kAmmoItemFlares; break; case kPicRandomUp: nType = kAmmoItemRandom; break; default: scrSetMessage("Invalid ammo tile selected!"); BeepError(); return FALSE; } int nSprite = insertsprite( (short)nSector, kStatItem ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; // gAmmoInfo[nAmmoType].shade; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); BeepOkay(); return TRUE; } /******************************************************************************* FUNCTION: InsertWeapon() DESCRIPTION: Displays a tile menu allowing user to insert a weapon on the floor. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. *******************************************************************************/ static BOOL InsertWeapon( int where, int nSector, long x, long y, long z, int nAngle ) { static short weaponTiles[] = { kPicRandomUp, kPicShotgun, kPicTommyGun, kPicFlareGun, kPicVoodooDoll, kPicSpearGun, kPicShadowGun }; short nType; tileIndexCount = LENGTH(weaponTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = weaponTiles[i]; // gWeaponInfo[i].picnum; short picnum = tilePick( -1 /*gWeaponInfo[0].picnum*/, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kPicRandomUp: nType = kWeaponItemRandom; break; case kPicShotgun: nType = kWeaponItemShotgun; break; case kPicTommyGun: nType = kWeaponItemTommyGun; break; case kPicFlareGun: nType = kWeaponItemFlareGun; break; case kPicVoodooDoll: nType = kWeaponItemVoodooDoll; break; case kPicSpearGun: nType = kWeaponItemSpearGun; break; case kPicShadowGun: nType = kWeaponItemShadowGun; break; default: scrSetMessage("Invalid weapon tile selected!"); BeepError(); return FALSE; } int nSprite = insertsprite( (short)nSector, kStatItem ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); BeepOkay(); return TRUE; } /******************************************************************************* FUNCTION: InsertEnemy() DESCRIPTION: Displays a tile menu allowing user to insert an enemy on the ceiling or floor. PARAMETERS: NOTES: This routine should offer a list box rather than tiles. At present, only base enemies may be added, but may be "extended" in 2D mode to other types in the same class. *******************************************************************************/ static BOOL InsertEnemy( int where, int nSector, long x, long y, long z, int nAngle ) { static short dudeTiles[] = { kAnmCultistM1, kPicShotgunIdle, kAnmZomb1M1, kPicEarthIdle, kAnmZomb2M1, kAnmGargoyleM1, kAnmGargStatue, kAnmPhantasmM1, kAnmHellM2, kAnmHandM1, kAnmSpiderM1, kAnmSpiderM1B, kAnmSpiderM1C, kAnmSpiderM1D, kAnmGillM1, kAnmEelM1, kAnmBatM1, kAnmRatM1, kAnmPodM1, kAnmTentacleM1, kAnmCerberusM1, kPicRandomUp, kAnmTchernobogM1, kAnmRachelM1, }; short nType; tileIndexCount = LENGTH(dudeTiles); for ( int i = 0; i < tileIndexCount; i++ ) tileIndex[i] = dudeTiles[i]; short picnum = tilePick( -1, -1, SS_CUSTOM ); if ( picnum == -1 ) return FALSE; switch( picnum ) { case kAnmCultistM1: nType = kDudeTommyCultist; break; case kPicShotgunIdle: nType = kDudeShotgunCultist; break; case kAnmZomb1M1: nType = kDudeAxeZombie; break; case kPicEarthIdle: nType = kDudeEarthZombie; break; case kAnmZomb2M1: nType = kDudeFatZombie; break; case kAnmGargoyleM1: nType = kDudeFleshGargoyle; break; case kAnmGargStatue: nType = kDudeFleshStatue; break; case kAnmPhantasmM1: nType = kDudePhantasm; break; case kAnmHellM2: nType = kDudeHound; break; case kAnmHandM1: nType = kDudeHand; break; case kAnmSpiderM1: nType = kDudeBrownSpider; break; case kAnmSpiderM1B: nType = kDudeRedSpider; break; case kAnmSpiderM1C: nType = kDudeBlackSpider; break; case kAnmSpiderM1D: nType = kDudeMotherSpider; break; case kAnmGillM1: nType = kDudeGillBeast; break; case kAnmEelM1: nType = kDudeEel; break; case kAnmBatM1: nType = kDudeBat; break; case kAnmRatM1: nType = kDudeRat; break; case kAnmPodM1: nType = kDudeGreenPod; break; case kAnmTentacleM1: nType = kDudeGreenTentacle; break; case kAnmCerberusM1: nType = kDudeCerberus; break; case kPicRandomUp: nType = kDudeRandom; break; case kAnmTchernobogM1: nType = kDudeTchernobog; break; case kAnmRachelM1: nType = kDudeRachel; break; default: scrSetMessage("Invalid dude tile selected!"); BeepError(); return FALSE; } int nSprite = insertsprite( (short)nSector, kStatDude ); dassert( nSprite >= 0 && nSprite < kMaxSprites ); sprite[nSprite].type = nType; AutoAdjustSprites(); SPRITE *pSprite = &sprite[nSprite]; pSprite->x = x; pSprite->y = y; pSprite->z = z; pSprite->ang = (short)nAngle; pSprite->shade = -8; int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); if ( where == SS_FLOOR) pSprite->z += getflorzofslope((short)nSector, pSprite->x, pSprite->y) - zBot; else pSprite->z += getceilzofslope((short)nSector, pSprite->x, pSprite->y) - zTop; updatenumsprites(); int nXSprite = GetXSprite(nSprite); dassert(nXSprite > 0 && nXSprite < kMaxXSprites); // handle any special setups here switch( nType ) { case kDudeBrownSpider: case kDudeRedSpider: case kDudeBlackSpider: case kDudeMotherSpider: if (where == SS_CEILING && !(sector[nSector].ceilingstat & kSectorParallax) ) pSprite->cstat |= kSpriteFlipY; // invert ceiling spiders break; case kDudeBat: if ( where == SS_CEILING && !(sector[nSector].ceilingstat & kSectorParallax) ) pSprite->picnum = kAnmBatM2; // make picnum ceiling bat else xsprite[nXSprite].state = 1; // they must be flying, better turn them on break; } BeepOkay(); return TRUE; } // modal results for InsertGameObject enum { mrEnemy = mrUser, mrWeapon, mrAmmo, mrItem, mrHazard, mrMisc, }; /******************************************************************************* FUNCTION: InsertGameObject() DESCRIPTION: Displays a menu allowing user to insert a game-specific object. Only ornaments can be placed as wall sprites. PARAMETERS: NOTES: *******************************************************************************/ static BOOL InsertGameObject( int where, int nSector, long x, long y, long z, int nAngle ) { Window dialog(0, 0, 80, 182, "Insert"); TextButton *pbEnemy = new TextButton( 4, 4, 60, 20, "&Enemy", (MODAL_RESULT)mrEnemy ); TextButton *pbWeapon = new TextButton( 4, 26, 60, 20, "&Weapon", (MODAL_RESULT)mrWeapon ); TextButton *pbAmmo = new TextButton( 4, 48, 60, 20, "&Ammo", (MODAL_RESULT)mrAmmo ); TextButton *pbItem = new TextButton( 4, 70, 60, 20, "&Item", (MODAL_RESULT)mrItem ); TextButton *pbHazard = new TextButton( 4, 92, 60, 20, "&Hazard", (MODAL_RESULT)mrHazard ); TextButton *pbMisc = new TextButton( 4, 114, 60, 20, "&Misc", (MODAL_RESULT)mrMisc ); TextButton *pbCancel = new TextButton( 4, 136, 60, 20, "&Cancel", (MODAL_RESULT)mrCancel ); dialog.Insert(pbEnemy); dialog.Insert(pbWeapon); dialog.Insert(pbAmmo); dialog.Insert(pbItem); dialog.Insert(pbHazard); dialog.Insert(pbMisc); dialog.Insert(pbCancel); ShowModal(&dialog); switch ( dialog.endState ) { case mrEnemy: return InsertEnemy( where, nSector, x, y, z, (nAngle + kAngle180) & kAngleMask ); case mrWeapon: return InsertWeapon( where, nSector, x, y, z, 0 ); case mrAmmo: return InsertAmmo( where, nSector, x, y, z, 0 ); case mrItem: return InsertItem( where, nSector, x, y, z, 0); case mrHazard: return InsertHazard( where, nSector, x, y, z, nAngle ); case mrMisc: return InsertMisc( where, nSector, x, y, z, nAngle ); case mrCancel: // pressed escape return FALSE; } return TRUE; } static BOOL CompareXSectors( XSECTOR *pXModel, XSECTOR *pXSector ) { return memcmp(pXModel, pXSector, sizeof(XSECTOR)) == 0; } static BOOL Confirm( char *zMessage ) { // create the dialog Window dialog(59, 80, 202, 46, zMessage); dialog.Insert(new TextButton(4, 4, 60, 20, "&Yes", mrYes)); dialog.Insert(new TextButton(68, 4, 60, 20, "&No", mrNo)); ShowModal(&dialog); return (dialog.endState == mrYes) ? TRUE : FALSE; } /******************************************************************************* FUNCTION: OptionsMenu() DESCRIPTION: PARAMETERS: NOTES: *******************************************************************************/ static BOOL OptionsMenu( void ) { Window dialog(0, 0, 80, 182, "Options"); TextButton *pbClean = new TextButton( 4, 4, 60, 20, "C&lean", (MODAL_RESULT)mrUser ); TextButton *pbCancel = new TextButton( 4, 136, 60, 20, "&Cancel", (MODAL_RESULT)mrCancel ); dialog.Insert(pbClean); dialog.Insert(pbCancel); ShowModal(&dialog); switch ( dialog.endState ) { case mrUser: { XSECTOR model; if (somethingintab == SS_FLOOR && sector[searchsector].extra >= 0 && xsector[sector[searchsector].extra].reference == searchsector) memcpy(&model, &xsector[sector[searchsector].extra], sizeof(XSECTOR)); int nCount = 1; for (int i = 0; i < kMaxXSectors; i++) { int nSector = xsector[i].reference; // compare with all other valid xsectors except self if ((nSector != -1) && sector[nSector].extra == i && nSector != searchsector) { // fake exceptions in model model.reference = xsector[i].reference; // compare the xsectors if ( CompareXSectors(&model, &xsector[i]) ) nCount++; } } char message[40]; sprintf(message, "Clean %i common sectors?", nCount); if (Confirm(message)) { for (int i = 0; i < kMaxXSectors; i++) { int nSector = xsector[i].reference; // compare with all other valid xsectors if ((nSector != -1) && sector[nSector].extra == i) { // fake exceptions in model model.reference = xsector[i].reference; // compare the xsectors if ( CompareXSectors(&model, &xsector[i]) ) dbDeleteXSector( i ); } } } InitSectorFX(); break; } case mrCancel: // pressed escape return FALSE; } return TRUE; } /*********************************************************************** * SetupSky() * * Setup the sky based on the width of the first sky tile. * Called by ExtCheckKeys, when Alt-1 is pressed. * * Sky Tile 1024 Tile Engine * Width Count Shift Shift pskybits * ----------------------------------------- * 1024 1 10 10 0 * 512 2 10 9 1 * 256 4 10 8 2 * 128 8 10 7 3 * 64 16 10 6 4 * 32 32 10 5 5 * 16 64 10 4 6 * 8 128 10 3 7 **********************************************************************/ static void SetupSky( int nTile ) { dassert(nTile >= 0 && nTile < kMaxTiles); pskybits = (short)(10 - (picsiz[nTile] & 0x0F)); gSkyCount = 1 << pskybits; sector[searchsector].ceilingpicnum = (short)nTile; dprintf("nTile = %i\n",nTile); dprintf("pskybits = %i\n",pskybits); dprintf("gSkyCount = %i\n",gSkyCount); // setup the sky tile offset table for (short i = 0; i < gSkyCount; i++) { pskyoff[i] = i; dprintf("pskyoff[%i] = %i\n",i,pskyoff[i]); } // reset the base tile for all the parallaxed skies (ceiling & floor!) for (i = 0; i < numsectors; i++) { if ( sector[i].ceilingstat & kSectorParallax ) sector[i].ceilingpicnum = (short)nTile; if ( sector[i].floorstat & kSectorParallax ) sector[i].floorpicnum = (short)nTile; } } static void PutSpriteOnCeiling( SPRITE *pSprite ) { int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); pSprite->z += getceilzofslope(pSprite->sectnum, pSprite->x, pSprite->y) - zTop; } static void PutSpriteOnFloor( SPRITE *pSprite ) { int zTop, zBot; GetSpriteExtents(pSprite, &zTop, &zBot); pSprite->z += getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y) - zBot; } static void RaiseSprite( SPRITE *pSprite ) { pSprite->z = DecNext(pSprite->z, gStep); } static void LowerSprite( SPRITE *pSprite ) { pSprite->z = IncNext(pSprite->z, gStep); } static void ProcessHighlightSprites( HSPRITEFUNC SpriteFunc ) { if ( TestBitString(show2dsprite, searchwall) ) // highlighted? { for (int i = 0; i < highlightcnt; i++) { if ( (highlight[i] & 0xC000) == 0x4000 ) { short nSprite = (short)(highlight[i] & 0x3FFF); SpriteFunc(&sprite[nSprite]); } } } else SpriteFunc(&sprite[searchwall]); } static void RaiseCeiling( short nSector) { SetCeilingZ(nSector, DecNext(sector[nSector].ceilingz, gStep)); } static void RaiseFloor( short nSector) { SetFloorZ(nSector, DecNext(sector[nSector].floorz, gStep)); } static void LowerCeiling( short nSector) { SetCeilingZ(nSector, IncNext(sector[nSector].ceilingz, gStep)); } static void LowerFloor( short nSector) { SetFloorZ(nSector, IncNext(sector[nSector].floorz, gStep)); } static void ProcessHighlightSectors( HSECTORFUNC FloorFunc ) { if ( IsSectorHighlight(searchsector) ) { for (short i = 0; i < highlightsectorcnt; i++) FloorFunc(highlightsector[i]); } else FloorFunc(searchsector); } /******************************************************************************* FUNCTION: ProcessKeys3D() DESCRIPTION: PARAMETERS: NOTES: *******************************************************************************/ void ProcessKeys3D( void ) { int startwall, endwall; short nSector; long i, j, doubvel, changedir; long goalz, xvect, yvect, hiz, loz; short hitsect, hitwall, hitsprite; long hitx, hity, hitz, hihit, lohit; long x, y; int nXSector, nXWall, nXSprite; BYTE key; static short tabang; // tawhat? static short temptype; int zTop, zBot; // these were globals not referenced elsewhere static long hvel = 0; BYTE shift, ctrl, alt, pad5; shift = keystatus[KEY_LSHIFT] | keystatus[KEY_RSHIFT]; ctrl = keystatus[KEY_LCTRL] | keystatus[KEY_RCTRL]; alt = keystatus[KEY_LALT] | keystatus[KEY_RALT]; pad5 = keystatus[KEY_PAD5]; if (ctrl && pad5) horiz = 100; if ( angvel ) { doubvel = gFrameTicks; if ( shift ) doubvel += gFrameTicks / 2; ang = (short)((ang + (angvel * doubvel >> 4)) & kAngleMask); } if ( vel | svel ) { doubvel = gFrameTicks; if ( shift ) doubvel += gFrameTicks; xvect = 0, yvect = 0; if ( vel ) { xvect += mulscale30(vel * doubvel >> 2, Cos(ang)); yvect += mulscale30(vel * doubvel >> 2, Sin(ang)); } if (svel != 0) { xvect += mulscale30(svel * doubvel >> 2, Sin(ang)); yvect -= mulscale30(svel * doubvel >> 2, Cos(ang)); } ClipMove(&posx, &posy, &posz, &cursectnum, xvect, yvect, kPlayerRadius, 4<<8, 4<<8, 0); } getzrange(posx, posy, posz, cursectnum, &hiz, &hihit, &loz, &lohit, kPlayerRadius, 0); // ceiling hit? if ( (hihit & kHitTypeMask) == kHitSector && (hihit & kHitIndexMask) == cursectnum) { if (sector[cursectnum].ceilingstat & kSectorParallax) hiz = 0x80000000; // largest negative number } if ( keystatus[KEY_H] ) { dprintf("kensplayerheight = %i, current height = %i\n", kensplayerheight, loz - posz); keystatus[KEY_H] = 0; } if (zmode == 0) { goalz = loz - kensplayerheight; // playerheight pixels above floor if (goalz < hiz + (16<<8)) //ceiling&floor too close goalz = (loz + hiz) / 2; if ( keystatus[KEY_A] ) { if ( ctrl ) { if (horiz > 0) horiz -= 4; } else { goalz -= (16<<8); if ( shift ) goalz -= (24<<8); } } if (keystatus[KEY_Z]) //Z (stand low) { if ( ctrl ) { if (horiz < 200) horiz += 4; } else { goalz += (12<<8); if ( shift ) goalz += 12 << 8; } } if (goalz != posz) { if (posz < goalz) hvel += 32; if (posz > goalz) hvel = ((goalz-posz)>>3); posz += hvel; if (posz > loz-(4<<8)) posz = loz-(4<<8), hvel = 0; if (posz < hiz+(4<<8)) posz = hiz+(4<<8), hvel = 0; } } else { goalz = posz; if (keystatus[KEY_A]) { if ( keystatus[KEY_LCTRL] ) { if (horiz > 0) horiz -= 4; } else { if (zmode != 1) goalz -= (8<<8); else { zlock += (4<<8); keystatus[KEY_A] = 0; } } } if (keystatus[KEY_Z]) //Z (stand low) { if ( keystatus[KEY_LCTRL] ) { if (horiz < 200) horiz += 4; } else { if (zmode != 1) goalz += (8<<8); else if (zlock > 0) { zlock -= (4<<8); keystatus[KEY_Z] = 0; } } } if (goalz < hiz+(4<<8)) goalz = hiz+(4<<8); if (goalz > loz-(4<<8)) goalz = loz-(4<<8); if (zmode == 1) goalz = loz-zlock; if (goalz < hiz+(4<<8)) //ceiling&floor too close goalz = (loz + hiz) / 2; if (zmode == 1) posz = goalz; if (goalz != posz) { if (posz < goalz) hvel += 32; if (posz > goalz) hvel -= 32; posz += hvel; if (posz > loz-(4<<8)) posz = loz-(4<<8), hvel = 0; if (posz < hiz+(4<<8)) posz = hiz+(4<<8), hvel = 0; } else hvel = 0; } Mouse::Read(gFrameTicks); searchx = ClipRange(Mouse::X, 1, xdim - 2); searchy = ClipRange(Mouse::Y, 1, ydim - 2); searchit = 2; // keep search vars if left mouse button pressed if (searchstat >= 0 && (Mouse::buttons & 1) ) searchit = 0; // draw mouse Video.SetColor(gStdColor[23 + mulscale30(8, Sin(gFrameClock * kAngle360 / kTimerRate))]); gfxHLine(searchy, searchx - 6, searchx - 2); gfxHLine(searchy, searchx + 2, searchx + 6); gfxVLine(searchx, searchy - 5, searchy - 2); gfxVLine(searchx, searchy + 2, searchy + 5); if (searchstat < 0) return; key = keyGet(); switch ( key ) { case KEY_PAGEUP: gStep = 0x400; if ( shift ) gStep = 0x100; if (( searchstat == SS_WALL ) && (wall[searchwall].nextsector != -1) ) TranslateWallToSector(); if ( searchstat == SS_WALL ) searchstat = SS_CEILING; // adjust ceilings when pointing at white walls if (ctrl) { switch (searchstat) { case SS_CEILING: { int nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].ceilingz, -1, -1); // if (nNeighbor == -1) // nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].ceilingz, -1, 1); if (nNeighbor != -1) SetCeilingZ( searchsector, sector[nNeighbor].ceilingz ); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); } break; case SS_FLOOR: { int nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].floorz, 1, -1); // if (nNeighbor == -1) // nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].floorz, 1, 1); if (nNeighbor != -1) SetFloorZ( searchsector, sector[nNeighbor].floorz ); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); } break; case SS_SPRITE: ProcessHighlightSprites(PutSpriteOnCeiling); sprintf(buffer, "sprite[%i].z: %i", searchwall, sprite[searchwall].z); scrSetMessage(buffer); break; } } else if (alt) { switch (searchstat) { case SS_CEILING: { int dzInPixels = (sector[searchsector].floorz - sector[searchsector].ceilingz) / 256; gStep = GetNumberBox("height off floor", dzInPixels, dzInPixels) * 256; SetCeilingZ(searchsector, sector[searchsector].floorz - gStep); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); break; } case SS_FLOOR: { int dzInPixels = (sector[searchsector].floorz - sector[searchsector].ceilingz) / 256; gStep = GetNumberBox("height off ceiling", dzInPixels, dzInPixels) * 256; SetFloorZ(searchsector, sector[searchsector].ceilingz + gStep); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); break; } } } else if (shift) { switch (searchstat) { case SS_CEILING: gStep = GetNumberBox("shift ceiling up", 128, 128) * 256; SetCeilingZ(searchsector, sector[searchsector].ceilingz - gStep); break; case SS_FLOOR: gStep = GetNumberBox("shift floor up", 128, 128) * 256; SetFloorZ(searchsector, sector[searchsector].floorz - gStep); break; } } else { switch (searchstat) { case SS_CEILING: ProcessHighlightSectors(RaiseCeiling); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); break; case SS_FLOOR: ProcessHighlightSectors(RaiseFloor); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); break; case SS_SPRITE: ProcessHighlightSprites(RaiseSprite); sprintf(buffer, "sprite[%i].z: %i", searchwall, sprite[searchwall].z); scrSetMessage(buffer); break; } } BeepOkay(); break; case KEY_PAGEDN: gStep = 0x400; if ( shift ) gStep = 0x100; if (( searchstat == SS_WALL ) && (wall[searchwall].nextsector != -1) ) TranslateWallToSector(); if ( searchstat == SS_WALL ) searchstat = SS_CEILING; // adjust ceilings when pointing at white walls if (ctrl) { switch (searchstat) { case SS_CEILING: { int nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].ceilingz, -1, 1); // if (nNeighbor == -1) // nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].ceilingz, -1, -1); if (nNeighbor != -1) SetCeilingZ( searchsector, sector[nNeighbor].ceilingz ); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); } break; case SS_FLOOR: { int nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].floorz, 1, 1); // if (nNeighbor == -1) // nNeighbor = nextsectorneighborz( searchsector, sector[searchsector].floorz, 1, -1); if (nNeighbor != -1) SetFloorZ( searchsector, sector[nNeighbor].floorz ); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); } break; case SS_SPRITE: ProcessHighlightSprites(PutSpriteOnFloor); sprintf(buffer, "sprite[%i].z: %i", searchwall, sprite[searchwall].z); scrSetMessage(buffer); break; } } else if (alt) { switch (searchstat) { case SS_CEILING: { int dzInPixels = (sector[searchsector].floorz - sector[searchsector].ceilingz) / 256; gStep = GetNumberBox("height off floor", dzInPixels, dzInPixels) * 256; SetCeilingZ(searchsector, sector[searchsector].floorz - gStep); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); break; } case SS_FLOOR: { int dzInPixels = (sector[searchsector].floorz - sector[searchsector].ceilingz) / 256; gStep = GetNumberBox("height off ceiling", dzInPixels, dzInPixels) * 256; SetFloorZ(searchsector, sector[searchsector].ceilingz + gStep); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); break; } } } else if (shift) { switch (searchstat) { case SS_CEILING: gStep = GetNumberBox("shift ceiling down", 128, 128) * 256; SetCeilingZ(searchsector, sector[searchsector].ceilingz + gStep); break; case SS_FLOOR: gStep = GetNumberBox("shift floor down", 128, 128) * 256; SetFloorZ(searchsector, sector[searchsector].floorz + gStep); break; } } else { switch (searchstat) { case SS_CEILING: ProcessHighlightSectors(LowerCeiling); sprintf(buffer, "sector[%i].ceilingz: %i", searchsector, sector[searchsector].ceilingz); scrSetMessage(buffer); break; case SS_FLOOR: ProcessHighlightSectors(LowerFloor); sprintf(buffer, "sector[%i].floorz: %i", searchsector, sector[searchsector].floorz); scrSetMessage(buffer); break; case SS_SPRITE: ProcessHighlightSprites(LowerSprite); sprintf(buffer, "sprite[%i].z: %i", searchwall, sprite[searchwall].z); scrSetMessage(buffer); break; } } BeepOkay(); break; case KEY_DELETE: if (searchstat == 3) { deletesprite(searchwall); updatenumsprites(); BeepOkay(); } else BeepError(); break; case KEY_TAB: switch (searchstat) { case SS_WALL: dprintf("Wall %i copied\n", searchwall); temppicnum = wall[searchwall].picnum; tempshade = wall[searchwall].shade; temppal = wall[searchwall].pal; tempxrepeat = wall[searchwall].xrepeat; tempyrepeat = wall[searchwall].yrepeat; tempcstat = wall[searchwall].cstat; tempextra = wall[searchwall].extra; temptype = wall[searchwall].type; break; case SS_CEILING: dprintf("Ceiling %i copied\n", searchsector); temppicnum = sector[searchsector].ceilingpicnum; tempshade = sector[searchsector].ceilingshade; temppal = sector[searchsector].ceilingpal; tempxrepeat = sector[searchsector].ceilingxpanning; tempyrepeat = sector[searchsector].ceilingypanning; tempcstat = sector[searchsector].ceilingstat; tempextra = sector[searchsector].extra; tempvisibility = sector[searchsector].visibility; temptype = sector[searchsector].type; break; case SS_FLOOR: dprintf("Floor %i copied\n", searchsector); temppicnum = sector[searchsector].floorpicnum; tempshade = sector[searchsector].floorshade; temppal = sector[searchsector].floorpal; tempxrepeat = sector[searchsector].floorxpanning; tempyrepeat = sector[searchsector].floorypanning; tempcstat = sector[searchsector].floorstat; tempextra = sector[searchsector].extra; tempvisibility = sector[searchsector].visibility; temptype = sector[searchsector].type; break; case SS_SPRITE: dprintf("Sprite %i copied\n", searchwall); temppicnum = sprite[searchwall].picnum; tempshade = sprite[searchwall].shade; temppal = sprite[searchwall].pal; tempxrepeat = sprite[searchwall].xrepeat; tempyrepeat = sprite[searchwall].yrepeat; tempcstat = sprite[searchwall].cstat; tempextra = sprite[searchwall].extra; tabang = sprite[searchwall].ang; temptype = sprite[searchwall].type; break; case SS_MASKED: dprintf("Masked wall %i copied\n", searchwall); temppicnum = wall[searchwall].overpicnum; tempshade = wall[searchwall].shade; temppal = wall[searchwall].pal; tempxrepeat = wall[searchwall].xrepeat; tempyrepeat = wall[searchwall].yrepeat; tempcstat = wall[searchwall].cstat; tempextra = wall[searchwall].extra; temptype = wall[searchwall].type; break; } somethingintab = (char)searchstat; break; case KEY_CAPSLOCK: zmode = IncRotate(zmode, 3); if (zmode == 1) zlock = (loz-posz) & ~0x3FF; sprintf(buffer, "ZMode = %s", gZModeMsg[zmode]); scrSetMessage(buffer); break; case KEY_ENTER: if (somethingintab == 255) // must have something to paste { BeepError(); break; } if ( ctrl ) { switch (searchstat) { // Paste to every wall in loop case SS_WALL: case SS_MASKED: i = searchwall; do { if ( shift ) { wall[i].shade = tempshade; wall[i].pal = temppal; } else { wall[i].picnum = temppicnum; if (somethingintab == SS_WALL || somethingintab == SS_MASKED) { wall[i].xrepeat = tempxrepeat; wall[i].yrepeat = tempyrepeat; wall[i].cstat = tempcstat; } fixrepeats((short)i); } i = wall[i].point2; } while (i != searchwall); BeepOkay(); break; // paste to all parallax ceilings case SS_CEILING: for (i = 0; i < numsectors; i++) { if ( sector[i].ceilingstat & kSectorParallax ) { sector[i].ceilingpicnum = temppicnum; sector[i].ceilingshade = tempshade; sector[i].ceilingpal = temppal; if (somethingintab == SS_CEILING || somethingintab == SS_FLOOR) { sector[i].ceilingxpanning = tempxrepeat; sector[i].ceilingypanning = tempyrepeat; sector[i].ceilingstat = (uchar)(tempcstat | kSectorParallax); } } } BeepOkay(); break; // paste to all parallax floors case SS_FLOOR: for (i = 0; i < numsectors; i++) { if ( sector[i].floorstat & kSectorParallax ) { sector[i].floorpicnum = temppicnum; sector[i].floorshade = tempshade; sector[i].floorpal = temppal; if (somethingintab == SS_CEILING || somethingintab == SS_FLOOR) { sector[i].floorxpanning = tempxrepeat; sector[i].floorypanning = tempyrepeat; sector[i].floorstat = (uchar)(tempcstat | kSectorParallax); } } } BeepOkay(); break; default: BeepError(); break; } break; } if ( shift ) // paste shade and palette { switch (searchstat) { case SS_WALL: wall[searchwall].shade = tempshade; wall[searchwall].pal = temppal; break; case SS_CEILING: if ( IsSectorHighlight(searchsector) ) { for(i = 0; i < highlightsectorcnt; i++) { sector[i].ceilingshade = tempshade; sector[i].ceilingpal = temppal; sector[i].visibility = tempvisibility; } } else { sector[searchsector].ceilingshade = tempshade; sector[searchsector].ceilingpal = temppal; sector[searchsector].visibility = tempvisibility; // if this sector is a parallaxed sky... if ( sector[searchsector].ceilingstat & kSectorParallax ) { // propagate shade data on all parallaxed sky sectors for (i = 0; i < numsectors; i++) if (sector[i].ceilingstat & kSectorParallax) { sector[i].ceilingshade = tempshade; sector[i].ceilingpal = temppal; sector[i].visibility = tempvisibility; } } } break; case SS_FLOOR: if ( IsSectorHighlight(searchsector) ) { for(i = 0; i < highlightsectorcnt; i++) { sector[i].floorshade = tempshade; sector[i].floorpal = temppal; sector[i].visibility = tempvisibility; } } else { sector[searchsector].floorshade = tempshade; sector[searchsector].floorpal = temppal; sector[searchsector].visibility = tempvisibility; } break; case SS_SPRITE: sprite[searchwall].shade = tempshade; sprite[searchwall].pal = temppal; break; case SS_MASKED: wall[searchwall].shade = tempshade; wall[searchwall].pal = temppal; break; } BeepOkay(); break; } if ( alt ) // copy extra structures { switch (searchstat) { case SS_WALL: case SS_MASKED: if (somethingintab == SS_WALL || somethingintab == SS_MASKED) { wall[searchwall].extra = tempextra; wall[searchwall].type = temptype; CleanUp(); BeepOkay(); } else BeepError(); break; case SS_CEILING: case SS_FLOOR: if (somethingintab == SS_CEILING || somethingintab == SS_FLOOR) { if ( IsSectorHighlight(searchsector) ) { for(i = 0; i < highlightsectorcnt; i++) { sector[i].extra = tempextra; sector[i].type = temptype; } } else { sector[searchsector].extra = tempextra; sector[searchsector].type = temptype; } CleanUp(); BeepOkay(); } else BeepError(); break; case SS_SPRITE: if (somethingintab == SS_SPRITE) { sprite[searchwall].type = temptype; sprite[searchwall].extra = tempextra; CleanUp(); BeepOkay(); } else BeepError(); break; } break; } switch (searchstat) { case SS_WALL: wall[searchwall].picnum = temppicnum; wall[searchwall].shade = tempshade; wall[searchwall].pal = temppal; if (somethingintab == SS_WALL) { wall[searchwall].xrepeat = tempxrepeat; wall[searchwall].yrepeat = tempyrepeat; wall[searchwall].cstat = tempcstat; } fixrepeats(searchwall); break; case SS_CEILING: sector[searchsector].ceilingpicnum = temppicnum; sector[searchsector].ceilingshade = tempshade; sector[searchsector].ceilingpal = temppal; if (somethingintab == SS_CEILING || somethingintab == SS_FLOOR) { sector[searchsector].ceilingxpanning = tempxrepeat; sector[searchsector].ceilingypanning = tempyrepeat; sector[searchsector].ceilingstat = (uchar)tempcstat; sector[searchsector].visibility = tempvisibility; } break; case SS_FLOOR: sector[searchsector].floorpicnum = temppicnum; sector[searchsector].floorshade = tempshade; sector[searchsector].floorpal = temppal; if (somethingintab == SS_CEILING || somethingintab == SS_FLOOR) { sector[searchsector].floorxpanning= tempxrepeat; sector[searchsector].floorypanning= tempyrepeat; sector[searchsector].floorstat = (uchar)tempcstat; sector[searchsector].visibility = tempvisibility; } break; case SS_SPRITE: sprite[searchwall].picnum = temppicnum; sprite[searchwall].shade = tempshade; sprite[searchwall].pal = temppal; if (somethingintab == SS_SPRITE) { sprite[searchwall].xrepeat = tempxrepeat; sprite[searchwall].yrepeat = tempyrepeat; if (sprite[searchwall].xrepeat < 1) sprite[searchwall].xrepeat = 1; if (sprite[searchwall].yrepeat < 1) sprite[searchwall].yrepeat = 1; sprite[searchwall].cstat = tempcstat; } GetSpriteExtents(&sprite[searchwall], &zTop, &zBot); if ( !(sector[sprite[searchwall].sectnum].ceilingstat & kSectorParallax) ) sprite[searchwall].z += ClipLow(sector[sprite[searchwall].sectnum].ceilingz - zTop, 0); if ( !(sector[sprite[searchwall].sectnum].floorstat & kSectorParallax) ) sprite[searchwall].z += ClipHigh(sector[sprite[searchwall].sectnum].floorz - zBot, 0); break; case SS_MASKED: wall[searchwall].overpicnum = temppicnum; if (wall[searchwall].nextwall >= 0) wall[wall[searchwall].nextwall].overpicnum = temppicnum; wall[searchwall].shade = tempshade; wall[searchwall].pal = temppal; if (somethingintab == SS_MASKED) { wall[searchwall].xrepeat = tempxrepeat; wall[searchwall].yrepeat = tempyrepeat; wall[searchwall].cstat = tempcstat; } fixrepeats(searchwall); break; } BeepOkay(); break; case KEY_LBRACE: gStep = 256; if ( shift ) gStep = 32; switch (searchstat) { case SS_CEILING: if ( IsSectorHighlight(searchsector) ) { for (i = 0; i < highlightsectorcnt; i++) SetCeilingSlope(highlightsector[i], (short)DecNext(sector[highlightsector[i]].ceilingslope, gStep)); sprintf(buffer, "adjusted %i ceilings by %i", highlightsectorcnt, gStep); scrSetMessage(buffer); } else { SetCeilingSlope(searchsector, (short)DecNext(sector[searchsector].ceilingslope, gStep)); sprintf(buffer, "sector[%i].ceilingslope: %i", searchsector, sector[searchsector].ceilingslope); scrSetMessage(buffer); } break; case SS_FLOOR: if ( IsSectorHighlight(searchsector) ) { for(i = 0; i < highlightsectorcnt; i++) SetFloorSlope(highlightsector[i], (short)DecNext(sector[highlightsector[i]].floorslope, gStep)); sprintf(buffer, "adjusted %i floors by %i", highlightsectorcnt, gStep); scrSetMessage(buffer); } else { SetFloorSlope(searchsector, (short)DecNext(sector[searchsector].floorslope, gStep)); sprintf(buffer, "sector[%i].floorslope: %i", searchsector, sector[searchsector].floorslope); scrSetMessage(buffer); } break; } BeepOkay(); break; case KEY_RBRACE: gStep = 256; if ( shift ) gStep = 32; switch (searchstat) { case SS_CEILING: if ( IsSectorHighlight(searchsector) ) { for (i = 0; i < highlightsectorcnt; i++) SetCeilingSlope(highlightsector[i], (short)IncNext(sector[highlightsector[i]].ceilingslope, gStep)); sprintf(buffer, "adjusted %i ceilings by %i", highlightsectorcnt, gStep); scrSetMessage(buffer); } else { SetCeilingSlope(searchsector, (short)IncNext(sector[searchsector].ceilingslope, gStep)); sprintf(buffer, "sector[%i].ceilingslope: %i", searchsector, sector[searchsector].ceilingslope); scrSetMessage(buffer); } break; case SS_FLOOR: if ( IsSectorHighlight(searchsector) ) { for(i = 0; i < highlightsectorcnt; i++) SetFloorSlope(highlightsector[i], (short)IncNext(sector[highlightsector[i]].floorslope, gStep)); sprintf(buffer, "adjusted %i floors by %i", highlightsectorcnt, gStep); scrSetMessage(buffer); } else { SetFloorSlope(searchsector, (short)IncNext(sector[searchsector].floorslope, gStep)); sprintf(buffer, "sector[%i].floorslope: %i", searchsector, sector[searchsector].floorslope); scrSetMessage(buffer); } break; } BeepOkay(); break; case KEY_COMMA: switch (searchstat) { // rotate sprite ccw case SS_SPRITE: gStep = shift ? 16 : 256; i = searchwall; sprite[i].ang = (short)(IncNext(sprite[i].ang, gStep) & kAngleMask); sprintf(buffer, "sprite[%i].ang: %i", searchwall, sprite[searchwall].ang); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_PERIOD: switch (searchstat) { // rotate sprite cw case SS_SPRITE: gStep = shift ? 16 : 256; i = searchwall; sprite[i].ang = (short)(DecNext(sprite[i].ang, gStep) & kAngleMask); sprintf(buffer, "sprite[%i].ang: %i", searchwall, sprite[searchwall].ang); scrSetMessage(buffer); BeepOkay(); break; // search and fix panning to the right case SS_WALL: case SS_MASKED: AutoAlignWalls(searchwall); BeepOkay(); break; default: BeepError(); } break; case KEY_BACKSLASH: // Reset slope to 0 switch (searchstat) { case SS_CEILING: sector[searchsector].ceilingslope = 0; sector[searchsector].ceilingstat &= ~kSectorSloped; sprintf(buffer, "sector[%i] ceiling slope reset", searchsector); scrSetMessage(buffer); break; case SS_FLOOR: sector[searchsector].floorslope = 0; sector[searchsector].floorstat &= ~kSectorSloped; sprintf(buffer, "sector[%i] floor slope reset", searchsector); scrSetMessage(buffer); break; } BeepOkay(); break; case KEY_SLASH: // Reset panning, repeat, and flip switch (searchstat) { case SS_WALL: case SS_MASKED: if ( shift ) wall[searchwall].xrepeat = wall[searchwall].yrepeat; else { wall[searchwall].xpanning = 0; wall[searchwall].ypanning = 0; wall[searchwall].xrepeat = 8; wall[searchwall].yrepeat = 8; wall[searchwall].cstat = 0; } fixrepeats((short)searchwall); sprintf(buffer, "wall[%i] pan/repeat reset", searchwall); scrSetMessage(buffer); break; case SS_CEILING: sector[searchsector].ceilingxpanning = 0; sector[searchsector].ceilingypanning = 0; sector[searchsector].ceilingstat &= ~kSectorFlipMask; sprintf(buffer, "sector[%i] ceiling pan reset", searchsector); scrSetMessage(buffer); break; case SS_FLOOR: sector[searchsector].floorxpanning = 0; sector[searchsector].floorypanning = 0; sector[searchsector].floorstat &= ~kSectorFlipMask; sprintf(buffer, "sector[%i] floor pan reset", searchsector); scrSetMessage(buffer); break; case SS_SPRITE: if ( shift ) sprite[searchwall].xrepeat = sprite[searchwall].yrepeat; else sprite[searchwall].xrepeat = sprite[searchwall].yrepeat = 64; sprintf(buffer, "sprite[%i].xrepeat: %i yrepeat: %i", searchwall, sprite[searchwall].xrepeat, sprite[searchwall].yrepeat ); scrSetMessage(buffer); break; } BeepOkay(); break; case KEY_MINUS: // decrease sector visibility if (ctrl && alt) { if ( IsSectorHighlight(searchsector) ) { for (i = 0; i < highlightsectorcnt; i++) { // higher numbers are less visibility sector[highlightsector[i]].visibility++; } scrSetMessage("highlighted sectors less visible"); } else { // higher numbers are less visibility sector[searchsector].visibility++; sprintf(buffer, "Visibility: %i", sector[searchsector].visibility); scrSetMessage(buffer); } BeepOkay(); break; } // decrease lighting effect amplitude if ( ctrl ) { nXSector = GetXSector(searchsector); xsector[nXSector].amplitude++; sprintf(buffer, "Amplitude: %i", xsector[nXSector].amplitude); scrSetMessage(buffer); BeepOkay(); break; } // decrease lighting effect phase if ( shift ) { nXSector = GetXSector(searchsector); xsector[nXSector].phase -= 6; sprintf(buffer, "Phase: %i", xsector[nXSector].phase); scrSetMessage(buffer); BeepOkay(); break; } // adjust gamma level if ( keystatus[KEY_G] ) { gGamma = ClipLow(gGamma - 1, 0); BloodINI.PutKeyInt("View", "Gamma", gGamma); BloodINI.Save(); sprintf(buffer, "Gamma correction level %i", gGamma); scrSetMessage(buffer); scrSetGamma(gGamma); scrSetDac(0); break; } if ( keystatus[KEY_D] ) { visibility = ClipHigh(visibility + 16, 4096); sprintf(buffer, "Depth cueing level %i", visibility); scrSetMessage(buffer); BeepOkay(); break; } break; case KEY_PLUS: // increase sector visibility if ( ctrl && alt ) { if ( IsSectorHighlight(searchsector) ) { for (i = 0; i < highlightsectorcnt; i++) { sector[highlightsector[i]].visibility--; } scrSetMessage("highlighted sectors more visible"); } else { // lower numbers are more visibility sector[searchsector].visibility--; sprintf(buffer, "Visibility: %i", sector[searchsector].visibility); scrSetMessage(buffer); } BeepOkay(); break; } // increase lighting effect amplitude if ( ctrl ) { nXSector = GetXSector(searchsector); xsector[nXSector].amplitude--; sprintf(buffer, "Amplitude: %i", xsector[nXSector].amplitude); scrSetMessage(buffer); BeepOkay(); break; } // increae lighting effect phase if ( shift ) { nXSector = GetXSector(searchsector); xsector[nXSector].phase += 6; sprintf(buffer, "Phase: %i", xsector[nXSector].phase); scrSetMessage(buffer); BeepOkay(); break; } // adjust gamma level if ( keystatus[KEY_G] ) { gGamma = ClipHigh(gGamma + 1, gGammaLevels - 1); BloodINI.PutKeyInt("View", "Gamma", gGamma); BloodINI.Save(); sprintf(buffer, "Gamma correction level %i", gGamma); scrSetMessage(buffer); scrSetGamma(gGamma); scrSetDac(0); break; } if ( keystatus[KEY_D] ) { visibility = ClipLow(visibility - 16, 128); sprintf(buffer, "Depth cueing level %i", visibility); scrSetMessage(buffer); BeepOkay(); break; } break; case KEY_B: // B (clip Blocking xor) (3D) switch (searchstat) { case SS_WALL: case SS_MASKED: if ( TestBitString(show2dwall, searchwall) ) // highlighted? { for (int i = 0; i < highlightcnt; i++) if ( (highlight[i] & 0xC000) == 0 ) { int nWall = highlight[ i ]; wall[ nWall ].cstat ^= kWallBlocking; } } else { wall[searchwall].cstat ^= kWallBlocking; if (wall[searchwall].nextwall >= 0) { wall[wall[searchwall].nextwall].cstat &= ~kWallBlocking; wall[wall[searchwall].nextwall].cstat |= (short)(wall[searchwall].cstat & kWallBlocking); } } sprintf(buffer, "Wall %i blocking flag is %s", searchwall, gBoolNames[ wall[searchwall].cstat & kWallBlocking ? 1 : 0 ]); scrSetMessage(buffer); BeepOkay(); break; case SS_SPRITE: sprite[searchwall].cstat ^= kSpriteBlocking; sprintf(buffer, "sprite[%i] %s blocking", searchwall, (sprite[searchwall].cstat & kSpriteBlocking) ? "is" : "not" ); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_C: if (alt) // change all tiles matching target to tab picnum { if (somethingintab != searchstat) { BeepError(); break; } switch (searchstat) { case SS_WALL: j = wall[searchwall].picnum; if ( TestBitString(show2dwall, searchwall) ) // highlighted? { for (int i = 0; i < highlightcnt; i++) if ( (highlight[i] & 0xC000) == 0 ) { int nWall = highlight[ i ]; if (wall[nWall].picnum == j) { if (wall[nWall].picnum != temppicnum) wall[nWall].picnum = temppicnum; else if (wall[nWall].pal != temppal) wall[nWall].pal = temppal; } } } else { for ( i = 0; i < numwalls; i++) if (wall[i].picnum == j) { if (wall[i].picnum != temppicnum) wall[i].picnum = temppicnum; else if (wall[i].pal != temppal) wall[i].pal = temppal; } } break; case SS_CEILING: j = sector[searchsector].ceilingpicnum; for (i = 0; i < numsectors; i++) if (sector[i].ceilingpicnum == j) { if (sector[i].ceilingpicnum != temppicnum) sector[i].ceilingpicnum = temppicnum; else if (sector[i].ceilingpal != temppal) sector[i].ceilingpal = temppal; } break; case SS_FLOOR: j = sector[searchsector].floorpicnum; for (i = 0; i < numsectors; i++) if (sector[i].floorpicnum == j) { if (sector[i].floorpicnum != temppicnum) sector[i].floorpicnum = temppicnum; else if (sector[i].floorpal != temppal) sector[i].floorpal = temppal; } break; case SS_SPRITE: j = sprite[searchwall].picnum; for (i = 0; i < kMaxSprites; i++) if (sprite[i].statnum < kMaxStatus && sprite[i].picnum == j) { sprite[i].picnum = temppicnum; sprite[i].type = temptype; } break; case SS_MASKED: j = wall[searchwall].overpicnum; for (i = 0; i < numwalls; i++) if (wall[i].overpicnum == j) { wall[i].overpicnum = temppicnum; } break; } BeepOkay(); break; } /* switch (searchstat) { case SS_SPRITE: sprite[searchwall].cstat ^= kSpriteOriginAlign; BeepOkay(); break; } */ break; case KEY_E: // E (expand) switch (searchstat) { case SS_CEILING: sector[searchsector].ceilingstat ^= kSectorExpand; sprintf(buffer, "ceiling texture %s expanded", (sector[searchsector].ceilingstat & kSectorExpand) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; case SS_FLOOR: sector[searchsector].floorstat ^= kSectorExpand; sprintf(buffer, "floor texture %s expanded", (sector[searchsector].floorstat & kSectorExpand) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_F: if ( alt ) { switch ( searchstat ) { case SS_WALL: case SS_MASKED: nSector = sectorofwall(searchwall); sector[searchsector].ceilingstat &= ~kSectorFlipMask; sector[searchsector].ceilingstat |= kSectorRelAlign; SetFirstWall(nSector, searchwall); BeepOkay(); break; case SS_CEILING: sector[searchsector].ceilingstat &= ~kSectorFlipMask; sector[searchsector].ceilingstat |= kSectorRelAlign; SetFirstWall(searchsector, sector[searchsector].wallptr + 1); BeepOkay(); break; case SS_FLOOR: sector[searchsector].floorstat &= ~kSectorFlipMask; sector[searchsector].floorstat |= kSectorRelAlign; SetFirstWall(searchsector, sector[searchsector].wallptr + 1); BeepOkay(); break; default: BeepError(); } break; } switch (searchstat) { case SS_WALL: case SS_MASKED: i = wall[searchwall].cstat & kWallFlipMask; switch(i) { case 0: i = kWallFlipX; break; case kWallFlipX: i = kWallFlipX | kWallFlipY; break; case kWallFlipX | kWallFlipY: i = kWallFlipY; break; case kWallFlipY: i = 0; break; } wall[searchwall].cstat &= ~kWallFlipMask; wall[searchwall].cstat |= (short)i; sprintf(buffer, "wall[%i]",searchwall); if (wall[searchwall].cstat & kWallFlipX) strcat(buffer," x-flipped"); if (wall[searchwall].cstat & kWallFlipY) { if (wall[searchwall].cstat & kWallFlipX) strcat(buffer," and"); strcat(buffer," y-flipped"); } scrSetMessage(buffer); BeepOkay(); break; // 8-way ceiling flipping (bits 2,4,5) case SS_CEILING: i = sector[searchsector].ceilingstat & kSectorFlipMask; switch (i) { case 0x00: i = 0x10; break; case 0x10: i = 0x30; break; case 0x30: i = 0x20; break; case 0x20: i = 0x04; break; case 0x04: i = 0x14; break; case 0x14: i = 0x34; break; case 0x34: i = 0x24; break; case 0x24: i = 0x00; break; } sector[searchsector].ceilingstat &= ~kSectorFlipMask; sector[searchsector].ceilingstat |= (uchar)i; break; // 8-way floor flipping (bits 2,4,5) case SS_FLOOR: i = sector[searchsector].floorstat & kSectorFlipMask; switch (i) { case 0x00: i = 0x10; break; case 0x10: i = 0x30; break; case 0x30: i = 0x20; break; case 0x20: i = 0x04; break; case 0x04: i = 0x14; break; case 0x14: i = 0x34; break; case 0x34: i = 0x24; break; case 0x24: i = 0x00; break; } sector[searchsector].floorstat &= ~kSectorFlipMask; sector[searchsector].floorstat |= (uchar)i; break; case SS_SPRITE: i = sprite[searchwall].cstat; // two-sided floor sprite? if ( (i & kSpriteRMask) == kSpriteFloor && !(i & kSpriteOneSided) ) { // what the hell is this supposed to be doing? sprite[searchwall].cstat &= ~kSpriteFlipY; sprite[searchwall].cstat ^= kSpriteFlipX; } else { i = i & 0xC; switch(i) { case 0x0: i = 0x4; break; case 0x4: i = 0xC; break; case 0xC: i = 0x8; break; case 0x8: i = 0x0; break; } sprite[searchwall].cstat &= ~0xC; sprite[searchwall].cstat |= (short)i; } break; } BeepOkay(); break; case KEY_G: if ( alt ) { // change global palette gPalette = IncRotate(gPalette, kMaxPalettes); scrSetPalette(gPalette); scrSetDac(0); // force update of DAC registers switch (gPalette) { case kPalNormal: scrSetMessage("Normal palette"); break; case kPalWater: scrSetMessage("Water palette"); break; case kPalBeast: scrSetMessage("Beast palette"); break; } } break; case KEY_H: // H (hitscan sensitivity) switch (searchstat) { case SS_WALL: case SS_MASKED: wall[searchwall].cstat ^= kWallHitscan; if (wall[searchwall].nextwall >= 0) { wall[wall[searchwall].nextwall].cstat &= ~kWallHitscan; wall[wall[searchwall].nextwall].cstat |= (short)(wall[searchwall].cstat & kWallHitscan); } sprintf(buffer, "wall[%i] %s hitscan sensitive", searchwall, (wall[searchwall].cstat & kWallHitscan) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; case SS_SPRITE: sprite[searchwall].cstat ^= kSpriteHitscan; sprintf(buffer, "sprite[%i] %s hitscan sensitive", searchwall, (sprite[searchwall].cstat & kSpriteHitscan) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_L: switch (searchstat) { case SS_SPRITE: { GetSpriteExtents(&sprite[searchwall], &zTop, &zBot); ushort oldcstat = sprite[searchwall].cstat; sprite[searchwall].cstat &= ~kSpriteHitscan; SetupLightBomb(); LightBomb(sprite[searchwall].x, sprite[searchwall].y, zTop, sprite[searchwall].sectnum); sprite[searchwall].cstat = oldcstat; BeepOkay(); break; } case SS_FLOOR: if (sector[searchsector].ceilingstat & kSectorParallax) { sector[searchsector].floorstat ^= kSectorFloorShade; sprintf(buffer, "Forced floor shading is %s", gBoolNames[ sector[searchsector].floorstat & kSectorFloorShade ? 1 : 0 ]); scrSetMessage(buffer); } else scrSetMessage("Sky must be parallaxed to force floorshade"); break; default: BeepError(); } break; case KEY_M: // M (masking walls) switch (searchstat) { case SS_WALL: case SS_MASKED: case SS_CEILING: case SS_FLOOR: i = wall[searchwall].nextwall; if (i < 0) { BeepError(); break; } wall[searchwall].cstat ^= kWallMasked; if (wall[searchwall].cstat & kWallMasked) { wall[searchwall].cstat &= ~kWallFlipX; if ( !shift ) { wall[i].cstat |= kWallFlipX; //auto other-side flip wall[i].cstat |= kWallMasked; if (wall[searchwall].overpicnum < 0) wall[searchwall].overpicnum = 0; wall[i].overpicnum = wall[searchwall].overpicnum; } } else { wall[searchwall].cstat &= ~kWallFlipX; if ( !shift ) { wall[i].cstat &= ~kWallFlipX; //auto other-side unflip wall[i].cstat &= ~kWallMasked; } } wall[searchwall].cstat &= ~kWallOneWay; if ( !shift ) wall[i].cstat &= ~kWallOneWay; sprintf(buffer, "wall[%i] %s masked", searchwall, (wall[searchwall].cstat & kWallMasked) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_O: if (alt) { asksave = OptionsMenu(); } else { switch (searchstat) { // O (top/bottom orientation - for doors) case SS_WALL: case SS_MASKED: wall[searchwall].cstat ^= kWallBottomOrg; if ( wall[searchwall].nextwall == -1 ) sprintf(buffer, "Texture pegged at %s", (wall[searchwall].cstat & kWallBottomOrg) ? "bottom" : "top"); else sprintf(buffer, "Texture pegged at %s", (wall[searchwall].cstat & kWallOutsideOrg) ? "outside" : "inside"); scrSetMessage(buffer); BeepOkay(); break; // O (ornament onto wall) case SS_SPRITE: { if (sprite[searchwall].z > sector[sprite[searchwall].sectnum].floorz) { scrSetMessage("Sprite z is below floor"); BeepError(); break; } if (sprite[searchwall].z < sector[sprite[searchwall].sectnum].ceilingz) { scrSetMessage("Sprite z is above ceiling"); BeepError(); break; } HITINFO hitInfo; int hitType = HitScan(&sprite[searchwall], sprite[searchwall].z, Cos(sprite[searchwall].ang + kAngle180) >> 16, Sin(sprite[searchwall].ang + kAngle180) >> 16, 0, &hitInfo); if ( hitType == SS_WALL || hitType == SS_MASKED ) { int nx, ny; GetWallNormal(hitInfo.hitwall, &nx, &ny); sprite[searchwall].x = hitInfo.hitx + (nx >> 14); sprite[searchwall].y = hitInfo.hity + (ny >> 14); sprite[searchwall].z = hitInfo.hitz; sprite[searchwall].cstat &= ~kSpriteBlocking; sprite[searchwall].cstat |= kSpriteOneSided; changespritesect(searchwall, hitInfo.hitsect); sprite[searchwall].ang = (short)((GetWallAngle(hitInfo.hitwall) + kAngle90) & kAngleMask); dprintf("Sprite ornamented onto wall %d\n", hitInfo.hitwall); BeepOkay(); } else BeepError(); break; } default: BeepError(); } } break; case KEY_P: if ( ctrl ) { parallaxtype = (char)IncRotate(parallaxtype, 3); sprintf(buffer, "Parallax type: %i", parallaxtype); scrSetMessage(buffer); BeepOkay(); break; } if ( alt ) { switch (searchstat) { case SS_WALL: case SS_MASKED: wall[searchwall].pal = (char)GetNumberBox("Wall palookup", wall[searchwall].pal, wall[searchwall].pal); break; case SS_CEILING: sector[searchsector].ceilingpal = (char)GetNumberBox("Ceiling palookup", sector[searchsector].ceilingpal, sector[searchsector].ceilingpal); break; case SS_FLOOR: sector[searchsector].floorpal = (char)GetNumberBox("Floor palookup", sector[searchsector].floorpal, sector[searchsector].floorpal); break; case SS_SPRITE: sprite[searchwall].pal = (char)GetNumberBox("Sprite palookup", sprite[searchwall].pal, sprite[searchwall].pal); break; } BeepOkay(); break; } switch (searchstat) { case SS_CEILING: sector[searchsector].ceilingstat ^= kSectorParallax; sprintf(buffer, "sector[%i] ceiling %s parallaxed", searchsector, (sector[searchsector].ceilingstat & kSectorParallax) ? "is" : "not"); scrSetMessage(buffer); if ( sector[searchsector].ceilingstat & kSectorParallax ) SetupSky(sector[searchsector].ceilingpicnum); else sector[searchsector].floorstat &= ~kSectorFloorShade; // clear forced floor shading bit BeepOkay(); break; case SS_FLOOR: sector[searchsector].floorstat ^= kSectorParallax; sprintf(buffer, "sector[%i] floor %s parallaxed", searchsector, (sector[searchsector].floorstat & kSectorParallax) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_R: // R (relative alignment, rotation) switch (searchstat) { case SS_CEILING: sector[searchsector].ceilingstat ^= kSectorRelAlign; sprintf(buffer, "sector[%i] ceiling %s relative", searchsector, (sector[searchsector].ceilingstat & kSectorRelAlign) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; case SS_FLOOR: sector[searchsector].floorstat ^= kSectorRelAlign; sprintf(buffer, "sector[%i] floor %s relative", searchsector, (sector[searchsector].floorstat & kSectorRelAlign) ? "is" : "not"); scrSetMessage(buffer); BeepOkay(); break; case SS_SPRITE: i = sprite[searchwall].cstat & kSpriteRMask; switch (i) { case 0x00: i = 0x10; sprintf( buffer, "sprite[%i] is wall sprite", searchwall ); break; case 0x10: i = 0x20; sprintf( buffer, "sprite[%i] is floor sprite", searchwall ); break; case 0x20: i = 0x00; sprintf( buffer, "sprite[%i] is face sprite", searchwall ); break; default: i = 0x00; sprintf( buffer, "sprite[%i] is face sprite", searchwall ); break; } scrSetMessage(buffer); sprite[searchwall].cstat &= ~kSpriteRMask; sprite[searchwall].cstat |= (ushort)i; BeepOkay(); break; default: BeepError(); } break; case KEY_S: if ( alt && (searchstat == SS_CEILING || searchstat == SS_FLOOR) ) { x = 1 << 14; y = divscale(searchx - xdim / 2, xdim / 2, 14); RotateVector(&x, &y, ang); hitsect = hitwall = hitsprite = -1; hitscan(posx, posy, posz, cursectnum, //Start position x, y, (searchy-horiz) * 2000, //vector of 3D ang &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz); if (hitsect >= 0) { x = hitx; y = hity; if ((gridlock > 0) && (grid > 0)) { x = (x+(1024 >> grid)) & (0xffffffff<<(11-grid)); y = (y+(1024 >> grid)) & (0xffffffff<<(11-grid)); } asksave = InsertGameObject( searchstat, hitsect, x, y, hitz, ang ); } else BeepError(); break; } switch (searchstat) { case SS_WALL: case SS_MASKED: { x = 16384; y = divscale(searchx - xdim / 2, xdim / 2, 14); RotateVector(&x, &y, ang); hitsect = hitwall = hitsprite = -1; hitscan(posx, posy, posz, cursectnum, //Start position x, y, (searchy - horiz) * 2000, //vector of 3D ang &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz); // NOTE: Unfortunately this case short-circuits masked walls that are not hitscan sensitive. if (hitwall != searchwall) break; int nx, ny; short picnum; if (somethingintab != SS_SPRITE) { picnum = tilePick(-1, -1, SS_FLATSPRITE); if (picnum == -1) break; } i = insertsprite(searchsector, kStatDefault); sprite[i].ang = (short)((GetWallAngle( hitwall ) + kAngle90) & kAngleMask); GetWallNormal( hitwall, &nx, &ny ); sprite[i].x = hitx + (nx >> 14); sprite[i].y = hity + (ny >> 14); sprite[i].z = hitz; if (somethingintab == SS_SPRITE) { sprite[i].picnum = temppicnum; sprite[i].shade = tempshade; sprite[i].pal = temppal; sprite[i].xrepeat = tempxrepeat; sprite[i].yrepeat = tempyrepeat; if (sprite[i].xrepeat < 1) sprite[i].xrepeat = 1; if (sprite[i].yrepeat < 1) sprite[i].yrepeat = 1; sprite[i].cstat = (short)(tempcstat | kSpriteWall | kSpriteOneSided); } else { sprite[i].cstat |= kSpriteWall | kSpriteOneSided; sprite[i].shade = -8; sprite[i].pal = 0; sprite[i].picnum = picnum; if (tilesizy[sprite[i].picnum] >= 32) sprite[i].cstat |= kSpriteBlocking; } updatenumsprites(); BeepOkay(); break; } case SS_CEILING: case SS_FLOOR: x = 1 << 14; y = divscale(searchx - xdim / 2, xdim / 2, 14); RotateVector(&x, &y, ang); hitsect = hitwall = hitsprite = -1; hitscan(posx, posy, posz, cursectnum, //Start position x, y, (searchy-horiz) * 2000, //vector of 3D ang &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz); if (hitsect >= 0) { x = hitx; y = hity; if ((gridlock > 0) && (grid > 0)) { x = (x+(1024 >> grid)) & (0xffffffff<<(11-grid)); y = (y+(1024 >> grid)) & (0xffffffff<<(11-grid)); } short picnum; if (somethingintab != SS_SPRITE) { picnum = tilePick(-1, -1, SS_SPRITE); if (picnum == -1) break; } i = insertsprite(hitsect, kStatDefault); sprite[i].x = x, sprite[i].y = y; if (somethingintab == SS_SPRITE) { sprite[i].picnum = temppicnum; sprite[i].shade = tempshade; sprite[i].pal = temppal; sprite[i].xrepeat = tempxrepeat; sprite[i].yrepeat = tempyrepeat; sprite[i].ang = tabang; if (sprite[i].xrepeat < 1) sprite[i].xrepeat = 1; if (sprite[i].yrepeat < 1) sprite[i].yrepeat = 1; sprite[i].cstat = tempcstat; } else { sprite[i].shade = -8; sprite[i].pal = 0; sprite[i].picnum = picnum; if (tilesizy[sprite[i].picnum] >= 32) sprite[i].cstat |= kSpriteBlocking; } GetSpriteExtents(&sprite[i], &zTop, &zBot); if ( searchstat == SS_FLOOR) sprite[i].z += getflorzofslope(hitsect, x, y) - zBot; else sprite[i].z += getceilzofslope(hitsect, x, y) - zTop; // fix sprite type attributes AutoAdjustSprites(); updatenumsprites(); BeepOkay(); } else BeepError(); break; default: BeepError(); } break; case KEY_T: switch (searchstat) { case SS_MASKED: { int level = 0; if ( wall[searchwall].cstat & kWallTranslucent ) { level = 1; if ( wall[searchwall].cstat & kWallTranslucentR ) level = 2; } level = IncRotate(level, 3); switch ( level ) { case 0: wall[searchwall].cstat &= ~kWallTranslucent & ~kWallTranslucentR; break; case 1: wall[searchwall].cstat &= ~kWallTranslucentR; wall[searchwall].cstat |= kWallTranslucent; break; case 2: wall[searchwall].cstat |= kWallTranslucent | kWallTranslucentR; break; } if (wall[searchwall].nextwall >= 0) { wall[wall[searchwall].nextwall].cstat &= ~kWallTranslucent & kWallTranslucentR; wall[wall[searchwall].nextwall].cstat |= (short)(wall[searchwall].cstat & (kWallTranslucent | kWallTranslucentR)); } sprintf(buffer, "wall[%i] translucent type %d", searchsector, level); scrSetMessage(buffer); BeepOkay(); break; } case SS_SPRITE: { int level = 0; if ( sprite[searchwall].cstat & kSpriteTranslucent ) { level = 1; if ( sprite[searchwall].cstat & kSpriteTranslucentR ) level = 2; } level = IncRotate(level, 3); switch ( level ) { case 0: sprite[searchwall].cstat &= ~kSpriteTranslucent & ~kSpriteTranslucentR; break; case 1: sprite[searchwall].cstat &= ~kSpriteTranslucentR; sprite[searchwall].cstat |= kSpriteTranslucent; break; case 2: sprite[searchwall].cstat |= kSpriteTranslucent | kSpriteTranslucentR; break; } sprintf(buffer, "sprite[%i] translucent type %d", searchsector, level); scrSetMessage(buffer); BeepOkay(); break; } default: BeepError(); } break; case KEY_U: for (i = 0; i < numsectors; i++) { sector[i].visibility = 0; } scrSetMessage("All sector visibility values set to 0"); break; case KEY_V: switch (searchstat) { case SS_WALL: { short oldPicnum = wall[searchwall].picnum; wall[searchwall].picnum = tilePick(wall[searchwall].picnum, wall[searchwall].picnum, SS_WALL); vel = svel = angvel = 0; if ( wall[searchwall].picnum != oldPicnum ) BeepOkay(); break; } case SS_CEILING: { short oldPicnum = sector[searchsector].ceilingpicnum; sector[searchsector].ceilingpicnum = tilePick(sector[searchsector].ceilingpicnum, sector[searchsector].ceilingpicnum, SS_CEILING); vel = svel = angvel = 0; if (sector[searchsector].ceilingstat & kSectorParallax) SetupSky( sector[searchsector].ceilingpicnum ); else sector[searchsector].floorstat &= ~kSectorFloorShade; // clear forced floor shading bit if ( sector[searchsector].ceilingpicnum != oldPicnum ) BeepOkay(); break; } case SS_FLOOR: { short oldPicnum = sector[searchsector].floorpicnum; sector[searchsector].floorpicnum = tilePick(sector[searchsector].floorpicnum, sector[searchsector].floorpicnum, SS_FLOOR); vel = svel = angvel = 0; if ( sector[searchsector].floorpicnum != oldPicnum ) BeepOkay(); break; } case SS_SPRITE: { if ( (sprite[searchwall].cstat & kSpriteRMask) != kSpriteFace ) searchstat = SS_FLATSPRITE; int oldPicnum = sprite[searchwall].picnum; sprite[searchwall].picnum = tilePick(sprite[searchwall].picnum, sprite[searchwall].picnum, searchstat); GetSpriteExtents(&sprite[searchwall], &zTop, &zBot); if ( !(sector[sprite[searchwall].sectnum].ceilingstat & kSectorParallax) ) sprite[searchwall].z += ClipLow(sector[sprite[searchwall].sectnum].ceilingz - zTop, 0); if ( !(sector[sprite[searchwall].sectnum].floorstat & kSectorParallax) ) sprite[searchwall].z += ClipHigh(sector[sprite[searchwall].sectnum].floorz - zBot, 0); vel = svel = angvel = 0; if ( sprite[searchwall].picnum != oldPicnum ) BeepOkay(); break; } case SS_MASKED: { dprintf("wall[%d].overpicnum = %d\n",searchwall,wall[searchwall].overpicnum); short oldPicnum = wall[searchwall].overpicnum; wall[searchwall].overpicnum = tilePick(wall[searchwall].overpicnum, wall[searchwall].overpicnum, SS_MASKED); vel = svel = angvel = 0; if ( wall[searchwall].nextwall >= 0 ) wall[wall[searchwall].nextwall].overpicnum = wall[searchwall].overpicnum; if ( wall[searchwall].overpicnum != oldPicnum ) BeepOkay(); break; } } break; case KEY_W: // change lighting waveform nXSector = GetXSector(searchsector); do { xsector[nXSector].wave = IncRotate(xsector[nXSector].wave, 15); } while ( gWaveNames[xsector[nXSector].wave] == NULL ); scrSetMessage(gWaveNames[xsector[nXSector].wave]); break; case KEY_PADMINUS: gStep = ctrl ? 256 : 1; if ( IsSectorHighlight(searchsector) ) { for (i = 0; i < highlightsectorcnt; i++) { nSector = highlightsector[i]; sector[nSector].ceilingshade = (schar)ClipHigh(sector[nSector].ceilingshade + gStep, kPalLookups - 1); sector[nSector].floorshade = (schar)ClipHigh(sector[nSector].floorshade + gStep, kPalLookups - 1); startwall = sector[nSector].wallptr; endwall = startwall + sector[nSector].wallnum - 1; for (j = startwall; j <= endwall; j++) wall[j].shade = (schar)ClipHigh(wall[j].shade + gStep, kPalLookups - 1); } } else { signed char newshade; switch (searchstat) { case SS_WALL: newshade = wall[searchwall].shade = (schar)ClipHigh(wall[searchwall].shade + gStep, kPalLookups - 1); break; case SS_CEILING: newshade = sector[searchsector].ceilingshade = (schar)ClipHigh(sector[searchsector].ceilingshade + gStep, kPalLookups - 1); break; case SS_FLOOR: newshade = sector[searchsector].floorshade = (schar)ClipHigh(sector[searchsector].floorshade + gStep, kPalLookups - 1); break; case SS_SPRITE: newshade = sprite[searchwall].shade = (schar)ClipHigh(sprite[searchwall].shade + gStep, kPalLookups - 1); break; case SS_MASKED: newshade = wall[searchwall].shade = (schar)ClipHigh(wall[searchwall].shade + gStep, kPalLookups - 1); break; } sprintf(buffer, "shade: %i", newshade); scrSetMessage(buffer); } BeepOkay(); break; case KEY_PADPLUS: gStep = ctrl ? 256 : 1; if ( IsSectorHighlight(searchsector) ) { for( i = 0; i < highlightsectorcnt; i++) { nSector = highlightsector[i]; sector[nSector].ceilingshade = (schar)ClipLow(sector[nSector].ceilingshade - gStep, -128); sector[nSector].floorshade = (schar)ClipLow(sector[nSector].floorshade - gStep, -128); startwall = sector[nSector].wallptr; endwall = startwall + sector[nSector].wallnum - 1; for(j=startwall;j<=endwall;j++) wall[j].shade = (schar)ClipLow(wall[j].shade - 1, -128); } } else { signed char newshade; switch (searchstat) { case SS_WALL: newshade = wall[searchwall].shade = (schar)ClipLow(wall[searchwall].shade - gStep, -128); break; case SS_CEILING: newshade = sector[searchsector].ceilingshade = (schar)ClipLow(sector[searchsector].ceilingshade - gStep, -128); break; case SS_FLOOR: newshade = sector[searchsector].floorshade = (schar)ClipLow(sector[searchsector].floorshade - gStep, -128); break; case SS_SPRITE: newshade = sprite[searchwall].shade = (schar)ClipLow(sprite[searchwall].shade - gStep, -128); break; case SS_MASKED: newshade = wall[searchwall].shade = (schar)ClipLow(wall[searchwall].shade - gStep, -128); break; } sprintf(buffer, "shade: %i", newshade); scrSetMessage(buffer); } BeepOkay(); break; case KEY_PAD0: // set the shade of something to 0 brightness if ( IsSectorHighlight(searchsector) ) { for( i = 0; i < highlightsectorcnt; i++) { nSector = highlightsector[i]; sector[nSector].ceilingshade = 0; sector[nSector].floorshade = 0; startwall = sector[nSector].wallptr; //wall shade endwall = startwall + sector[nSector].wallnum - 1; for(j=startwall;j<=endwall;j++) wall[j].shade = 0; } } else { switch (searchstat) { case SS_WALL: wall[searchwall].shade = 0; break; case SS_CEILING: sector[searchsector].ceilingshade = 0; break; case SS_FLOOR: sector[searchsector].floorshade = 0; break; case SS_SPRITE: sprite[searchwall].shade = 0; break; case SS_MASKED: wall[searchwall].shade = 0; break; } } BeepOkay(); break; case KEY_PADLEFT: case KEY_PADRIGHT: { BOOL bCoarse = gOldKeyMapping ? pad5 : (BOOL)!shift; BOOL bPan = gOldKeyMapping ? shift : ctrl; changedir = (key == KEY_PADLEFT) ? 1 : -1; switch (searchstat) { case SS_WALL: case SS_MASKED: if ( bPan ) { wall[searchwall].xpanning = changechar(wall[searchwall].xpanning, changedir, bCoarse, 0); sprintf(buffer, "wall %i xpanning: %i ypanning: %i", searchwall, wall[searchwall].xpanning, wall[searchwall].ypanning ); } else { wall[searchwall].xrepeat = changechar(wall[searchwall].xrepeat, changedir, bCoarse, 1); sprintf(buffer, "wall %i xrepeat: %i yrepeat: %i", searchwall, wall[searchwall].xrepeat, wall[searchwall].yrepeat ); } scrSetMessage(buffer); break; case SS_CEILING: sector[searchsector].ceilingxpanning = changechar(sector[searchsector].ceilingxpanning, changedir, bCoarse, 0); break; case SS_FLOOR: sector[searchsector].floorxpanning = changechar(sector[searchsector].floorxpanning, changedir, bCoarse, 0); break; case SS_SPRITE: if ( bPan ) { sprite[searchwall].xoffset = changechar(sprite[searchwall].xoffset, changedir, bCoarse, 0); sprintf(buffer, "sprite %i xoffset: %i yoffset: %i", searchwall, sprite[searchwall].xoffset, sprite[searchwall].yoffset); } else { sprite[searchwall].xrepeat = (uchar)ClipLow(changechar(sprite[searchwall].xrepeat, -changedir, bCoarse, 1), 4); sprintf(buffer, "sprite %i xrepeat: %i yrepeat: %i", searchwall, sprite[searchwall].xrepeat, sprite[searchwall].yrepeat); } scrSetMessage(buffer); break; } BeepOkay(); break; } case KEY_PADUP: case KEY_PADDOWN: { BOOL bCoarse = gOldKeyMapping ? pad5 : (BOOL)!shift; BOOL bPan = gOldKeyMapping ? shift : ctrl; changedir = (key == KEY_PADUP) ? -1 : 1; switch (searchstat) { case SS_WALL: case SS_MASKED: if ( bPan ) { wall[searchwall].ypanning = changechar(wall[searchwall].ypanning, changedir, bCoarse, 0); sprintf(buffer, "wall %i xpanning: %i ypanning: %i", searchwall, wall[searchwall].xpanning, wall[searchwall].ypanning ); } else { wall[searchwall].yrepeat = changechar(wall[searchwall].yrepeat, changedir, bCoarse, 1); sprintf(buffer, "wall %i xrepeat: %i yrepeat: %i", searchwall, wall[searchwall].xrepeat, wall[searchwall].yrepeat ); } scrSetMessage(buffer); break; case SS_CEILING: sector[searchsector].ceilingypanning = changechar(sector[searchsector].ceilingypanning, changedir, bCoarse, 0); break; case SS_FLOOR: sector[searchsector].floorypanning = changechar(sector[searchsector].floorypanning, changedir, bCoarse, 0); break; case SS_SPRITE: if ( bPan ) { sprite[searchwall].yoffset = changechar(sprite[searchwall].yoffset, changedir, bCoarse, 0); sprintf(buffer, "sprite %i xoffset: %i yoffset: %i", searchwall, sprite[searchwall].xoffset, sprite[searchwall].yoffset); } else { sprite[searchwall].yrepeat = (uchar)ClipLow(changechar(sprite[searchwall].yrepeat, -changedir, bCoarse, 1), 4); sprintf(buffer, "sprite %i xrepeat: %i yrepeat: %i", searchwall, sprite[searchwall].xrepeat, sprite[searchwall].yrepeat); } scrSetMessage(buffer); break; } BeepOkay(); break; } case KEY_PADENTER: overheadeditor(); clearview(0); scrNextPage(); keyFlushStream(); CleanUp(); break; case KEY_F2: // toggle xstructure state switch (searchstat) { case SS_WALL: case SS_MASKED: nXWall = wall[searchwall].extra; if (nXWall > 0) { xwall[nXWall].state ^= 1; xwall[nXWall].busy = xwall[nXWall].state << 16; scrSetMessage(gBoolNames[xwall[nXWall].state]); BeepOkay(); } else BeepError(); break; case SS_CEILING: case SS_FLOOR: nXSector = sector[searchsector].extra; if (nXSector > 0) { xsector[nXSector].state ^= 1; xsector[nXSector].busy = xsector[nXSector].state << 16; scrSetMessage(gBoolNames[xsector[nXSector].state]); BeepOkay(); } else BeepError(); break; case SS_SPRITE: nXSprite = sprite[searchwall].extra; if (nXSprite > 0) { xsprite[nXSprite].state ^= 1; xsprite[nXSprite].busy = xsprite[nXSprite].state << 16; scrSetMessage(gBoolNames[xsprite[nXSprite].state]); BeepOkay(); } else BeepError(); break; } break; case KEY_F3: nSector = searchsector; if ( alt ) { // capture off positions for sector ceiling and floor height switch (searchstat) { case SS_WALL: if (wall[searchwall].nextsector != -1) nSector = wall[searchwall].nextsector; // fall through to nXSector processing case SS_CEILING: case SS_FLOOR: nXSector = sector[nSector].extra; if ( nXSector > 0 ) { xsector[nXSector].offFloorZ = sector[nSector].floorz; xsector[nXSector].offCeilZ = sector[nSector].ceilingz; sprintf( buffer, "SET offFloorZ= %i offCeilZ= %i", xsector[nXSector].offFloorZ, xsector[nXSector].offCeilZ ); scrSetMessage(buffer); BeepOkay(); asksave = 1; } else { scrSetMessage("Sector type must first be set in 2D mode."); BeepError(); } break; } break; } // set sector height to captured off positions switch (searchstat) { case SS_WALL: if (wall[searchwall].nextsector != -1) nSector = wall[searchwall].nextsector; // fall through to nXSector processing case SS_CEILING: case SS_FLOOR: nXSector = sector[nSector].extra; if ( nXSector > 0 ) { switch( sector[nSector].type ) { case kSectorZMotion: case kSectorZCrusher: sector[nSector].floorz = xsector[nXSector].offFloorZ; sector[nSector].ceilingz = xsector[nXSector].offCeilZ; sprintf( buffer, "SET floorz= %i ceilingz= %i", sector[nSector].floorz, sector[nSector].ceilingz ); scrSetMessage(buffer); BeepOkay(); asksave = 1; break; } } else { scrSetMessage("Sector type must first be set in 2D mode."); BeepError(); } break; } break; case KEY_F4: nSector = searchsector; if ( alt ) { // capture on positions for sector ceiling and floor height switch (searchstat) { case SS_WALL: if (wall[searchwall].nextsector != -1) nSector = wall[searchwall].nextsector; // fall through to nXSector processing case SS_CEILING: case SS_FLOOR: nXSector = sector[nSector].extra; if ( nXSector > 0 ) { xsector[nXSector].onFloorZ = sector[nSector].floorz; xsector[nXSector].onCeilZ = sector[nSector].ceilingz; sprintf( buffer, "SET onFloorZ= %i onCeilZ= %i", xsector[nXSector].onFloorZ, xsector[nXSector].onCeilZ ); scrSetMessage(buffer); BeepOkay(); asksave = 1; } else { scrSetMessage("Sector type must first be set in 2D mode."); BeepError(); } break; } break; } // set sector height to captured on positions switch (searchstat) { case SS_WALL: if (wall[searchwall].nextsector != -1) nSector = wall[searchwall].nextsector; // fall through to nXSector processing case SS_CEILING: case SS_FLOOR: nXSector = sector[nSector].extra; if ( nXSector > 0 ) { switch( sector[nSector].type ) { case kSectorZMotion: case kSectorZCrusher: sector[nSector].floorz = xsector[nXSector].onFloorZ; sector[nSector].ceilingz = xsector[nXSector].onCeilZ; sprintf( buffer, "SET floorz= %i ceilingz= %i", sector[nSector].floorz, sector[nSector].ceilingz ); scrSetMessage(buffer); BeepOkay(); asksave = 1; break; } } else { scrSetMessage("Sector type must first be set in 2D mode."); BeepError(); } break; } break; case KEY_F9: // create a set of stairs switch (searchstat) { case SS_FLOOR: // clear visited bits memset(visited, FALSE, sizeof(visited)); sector[searchsector].floorstat &= ~0x80; gStairHeight = GetNumberBox("Step height", 8, 8); BuildStairsF(searchsector); break; case SS_CEILING: // clear visited bits memset(visited, FALSE, sizeof(visited)); sector[searchsector].ceilingstat &= ~0x80; gStairHeight = GetNumberBox("Step height", 8, 8); BuildStairsC(searchsector); break; } break; case KEY_F11: gPanning = !gPanning; sprintf(buffer, "Global panning is %s", gBoolNames[gPanning]); scrSetMessage(buffer); BeepOkay(); break; case KEY_F12: gBeep = !gBeep; sprintf(buffer, "Beeps are %s", gBoolNames[gBeep]); scrSetMessage(buffer); BeepOkay(); break; case KEY_1: switch (searchstat) { case SS_WALL: case SS_MASKED: wall[searchwall].cstat ^= kWallOneWay; sprintf(buffer, "Wall %i one-way flag is %s", searchwall, gBoolNames[wall[searchwall].cstat & kWallOneWay ? 1 : 0 ]); scrSetMessage(buffer); BeepOkay(); break; case SS_SPRITE: sprite[searchwall].cstat ^= kSpriteOneSided; i = sprite[searchwall].cstat; if ((i & kSpriteRMask) == kSpriteFloor) // floor sprite { // use y-flip bit to indicate whether its visible from above or below sprite[searchwall].cstat &= ~kSpriteFlipY; if (i & kSpriteOneSided) if (posz > sprite[searchwall].z) sprite[searchwall].cstat |= kSpriteFlipY; } sprintf(buffer, "Sprite %i one-sided flag is %s", searchwall, gBoolNames[sprite[searchwall].cstat & kSpriteOneSided ? 1 : 0 ]); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_2: // 2 (bottom wall swapping) switch (searchstat) { case SS_WALL: case SS_MASKED: wall[searchwall].cstat ^= kWallBottomSwap; sprintf(buffer, "Wall %i bottom swap flag is %s", searchwall, gBoolNames[wall[searchwall].cstat & kWallBottomSwap ? 1 : 0 ]); scrSetMessage(buffer); BeepOkay(); break; default: BeepError(); } break; case KEY_PRINTSCREEN: screencapture("captxxxx.pcx"); BeepOkay(); break; } // let's see if this fixes the input buffering problem if (key != 0) keyFlushStream(); }