raze/source/games/sw/src/jsector.cpp
2021-12-30 09:57:34 +01:00

968 lines
29 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
// JSECTOR.C
// This is all Jim's programming having to do with sectors.
#include "ns.h"
#include "build.h"
#include "names.h"
#include "names2.h"
#include "panel.h"
#include "game.h"
#include "tags.h"
#include "sector.h"
#include "player.h"
#include "sprite.h"
#include "jsector.h"
#include "jtags.h"
#include "lists.h"
#include "pal.h"
#include "parent.h"
#include "v_video.h"
#include "render.h"
EXTERN_CVAR(Bool, testnewrenderer)
BEGIN_SW_NS
// V A R I A B L E D E C L A R A T I O N S //////////////////////////////////////////////////////
MIRRORTYPE mirror[MAXMIRRORS];
short mirrorcnt; //, floormirrorcnt;
//short floormirrorsector[MAXMIRRORS];
bool mirrorinview;
uint32_t oscilationclock;
// Voxel stuff
//bool bVoxelsOn = true; // Turn voxels on by default
bool bSpinBobVoxels = false; // Do twizzly stuff to voxels, but
// not by default
bool bAutoSize = true; // Autosizing on/off
//extern int chainnumpages;
extern AMB_INFO ambarray[];
extern short NormalVisibility;
extern ParentalStruct aVoxelArray[MAXTILES];
// F U N C T I O N S //////////////////////////////////////////////////////////////////////////////
short CheckTileSound(short picnum)
{
short sndnum = -1;
switch (picnum)
{
case 163: // Sizzly Lava
case 167:
sndnum = DIGI_VOLCANOSTEAM1;
break;
case 175: // Flowing Lava
sndnum = DIGI_ERUPTION;
break;
case 179: // Bubbly lava
sndnum = DIGI_LAVAFLOW1;
break;
case 300: // Water fall tile
sndnum = DIGI_WATERFALL1;
break;
case 334: // Teleporter Pad
sndnum = DIGI_ENGROOM1;
break;
case 2690: // Jet engine fan
sndnum = DIGI_JET;
break;
case 2672: // X-Ray Machine engine
sndnum = DIGI_ENGROOM5;
break;
case 768: // Electricity
// sndnum = DIGI_;
break;
case 2714: // Pachinko Machine
// sndnum = DIGI_;
break;
case 2782: // Telepad
sndnum = DIGI_ENGROOM4;
break;
case 3382: // Gears
sndnum = DIGI_ENGROOM5;
break;
case 2801: // Computers
case 2804:
case 2807:
case 3352:
case 3385:
case 3389:
case 3393:
case 3397:
case 3401:
case 3405:
// sndnum = DIGI_;
break;
case 3478: // Radar screen
// sndnum = DIGI_;
break;
default:
sndnum = -1;
break;
}
return sndnum;
}
ANIMATOR GenerateDrips;
/////////////////////////////////////////////////////
// Initialize any of my special use sprites
/////////////////////////////////////////////////////
void JS_SpriteSetup(void)
{
SPRITEp sp;
USERp u;
SWStatIterator it(STAT_DEFAULT);
while (auto actor = it.Next())
{
short tag;
sp = &actor->s();
tag = sp->hitag;
// Non static camera. Camera sprite will be drawn!
if (tag == MIRROR_CAM && sp->picnum != ST1)
{
// Just change it to static, sprite has all the info I need
change_actor_stat(actor, STAT_SPAWN_SPOT);
}
switch (sp->picnum)
{
case ST1:
if (tag == MIRROR_CAM)
{
// Just change it to static, sprite has all the info I need
// ST1 cameras won't move with SOBJ's!
change_actor_stat(actor, STAT_ST1);
}
else if (tag == MIRROR_SPAWNSPOT)
{
// Just change it to static, sprite has all the info I need
change_actor_stat(actor, STAT_ST1);
}
else if (tag == AMBIENT_SOUND)
{
change_actor_stat(actor, STAT_AMBIENT);
}
else if (tag == TAG_ECHO_SOUND)
{
change_actor_stat(actor, STAT_ECHO);
}
else if (tag == TAG_DRIPGEN)
{
u = SpawnUser(actor, 0, nullptr);
ASSERT(u != nullptr);
u->RotNum = 0;
u->WaitTics = sp->lotag * 120;
u->ActorActionFunc = GenerateDrips;
change_actor_stat(actor, STAT_NO_STATE);
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
}
break;
// Sprites in editart that should play ambient sounds
// automatically
case 380:
case 396:
case 430:
case 443:
case 512:
case 521:
case 541:
case 2720:
case 3143:
case 3157:
PlaySound(DIGI_FIRE1, actor, v3df_follow|v3df_dontpan|v3df_doppler);
break;
case 795:
case 880:
PlaySound(DIGI_WATERFLOW1, actor, v3df_follow|v3df_dontpan|v3df_doppler);
break;
case 460: // Wind Chimes
InitAmbient(79, actor);
break;
}
}
// Check for certain walls to make sounds
for(auto& wal : wall)
{
int picnum = wal.picnum;
// Set the don't stick bit for liquid tiles
switch (picnum)
{
case 175:
case 179:
case 300:
case 320:
case 330:
case 352:
case 780:
case 890:
case 2608:
case 2616:
//case 3834:
SET(wal.extra, WALLFX_DONT_STICK);
break;
}
}
}
/////////////////////////////////////////////////////
// Initialize the mirrors
/////////////////////////////////////////////////////
void JS_InitMirrors(void)
{
short startwall, endwall;
int i, j, s;
bool Found_Cam = false;
// Set all the mirror struct values to -1
memset(mirror, 0xFF, sizeof(mirror));
mirrorinview = false; // Initially set global mirror flag
// to no mirrors seen
// Scan wall tags for mirrors
mirrorcnt = 0;
tileDelete(MIRROR);
oscilationclock = I_GetBuildTime();
for (i = 0; i < MAXMIRRORS; i++)
{
tileDelete(i + MIRRORLABEL);
mirror[i].campic = -1;
mirror[i].camspriteActor = nullptr;
mirror[i].cameraActor = nullptr;
mirror[i].ismagic = false;
}
for(auto& wal : wall)
{
if (wal.twoSided() && (wal.overpicnum == MIRROR) && (wal.cstat & CSTAT_WALL_1WAY))
{
auto sec = wal.nextSector();
if ((sec->floorstat & CSTAT_SECTOR_SKY) == 0)
{
if (mirrorcnt >= MAXMIRRORS)
{
Printf("MAXMIRRORS reached! Skipping mirror wall\n");
wal.overpicnum = sec->ceilingpicnum;
continue;
}
wal.overpicnum = MIRRORLABEL + mirrorcnt;
wal.picnum = MIRRORLABEL + mirrorcnt;
sec->ceilingpicnum = MIRRORLABEL + mirrorcnt;
sec->floorpicnum = MIRRORLABEL + mirrorcnt;
sec->floorstat |= CSTAT_SECTOR_SKY;
mirror[mirrorcnt].mirrorWall = &wal;
mirror[mirrorcnt].mirrorSector = sec;
mirror[mirrorcnt].numspawnspots = 0;
mirror[mirrorcnt].ismagic = false;
do if (wal.lotag == TAG_WALL_MAGIC_MIRROR)
{
int ii;
SPRITEp sp;
Found_Cam = false;
SWStatIterator it(STAT_ST1);
while (auto itActor = it.Next())
{
sp = &itActor->s();
// if correct type and matches
if (sp->hitag == MIRROR_CAM && sp->lotag == wal.hitag)
{
mirror[mirrorcnt].cameraActor = itActor;
// Set up camera variables
SP_TAG5(sp) = sp->ang; // Set current angle to
// sprite angle
Found_Cam = true;
}
}
it.Reset(STAT_SPAWN_SPOT);
while (auto itActor = it.Next())
{
sp = &itActor->s();
// if correct type and matches
if (sp->hitag == MIRROR_CAM && sp->lotag == wal.hitag)
{
mirror[mirrorcnt].cameraActor = itActor;
// Set up camera variables
SP_TAG5(sp) = sp->ang; // Set current angle to
// sprite angle
Found_Cam = true;
}
}
if (!Found_Cam)
{
Printf("Cound not find the camera view sprite for match %d\n", wal.hitag);
Printf("Map Coordinates: x = %d, y = %d\n", wal.pos.X, wal.pos.Y);
break;
}
mirror[mirrorcnt].ismagic = true;
Found_Cam = false;
if (TEST_BOOL1(&mirror[mirrorcnt].cameraActor->s()))
{
it.Reset(STAT_DEFAULT);
while (auto itActor = it.Next())
{
sp = &itActor->s();
if (sp->picnum >= CAMSPRITE && sp->picnum < CAMSPRITE + 8 &&
sp->hitag == wal.hitag)
{
mirror[mirrorcnt].campic = sp->picnum;
mirror[mirrorcnt].camspriteActor = itActor;
// JBF: commenting out this line results in the screen in $BULLET being visible
tileDelete(mirror[mirrorcnt].campic);
Found_Cam = true;
}
}
if (!Found_Cam)
{
Printf("Did not find drawtotile for camera number %d\n", mirrorcnt);
Printf("wall(%d).hitag == %d\n", wallnum(&wal), wal.hitag);
Printf("Map Coordinates: x = %d, y = %d\n", wal.pos.X, wal.pos.Y);
RESET_BOOL1(&mirror[mirrorcnt].cameraActor->s());
}
}
// For magic mirrors, set allowable viewing time to 30
// secs
// Base rate is supposed to be 120, but time is double
// what I expect
mirror[mirrorcnt].maxtics = 60 * 30;
}
while (0);
mirror[mirrorcnt].mstate = m_normal;
// Set tics used to none
mirror[mirrorcnt].tics = 0;
mirrorcnt++;
}
else
wal.overpicnum = sec->ceilingpicnum;
}
}
// Invalidate textures in sector behind mirror
for (i = 0; i < mirrorcnt; i++)
{
for (auto& wal : wallsofsector(mirror[i].mirrorSector))
{
wal.picnum = MIRROR;
wal.overpicnum = MIRROR;
}
}
} // InitMirrors
/////////////////////////////////////////////////////
// Draw a 3d screen to a specific tile
/////////////////////////////////////////////////////
void drawroomstotile(int daposx, int daposy, int daposz,
binangle ang, fixedhoriz horiz, sectortype* dacursect, short tilenume, double smoothratio)
{
auto canvas = renderSetTarget(tilenume);
if (!canvas) return;
screen->RenderTextureView(canvas, [=](IntRect& rect)
{
if (!testnewrenderer)
{
renderDrawRoomsQ16(daposx, daposy, daposz, ang.asq16(), horiz.asq16(), dacursect, false);
analyzesprites(pm_tsprite, pm_spritesortcnt, daposx, daposy, daposz, ang.asbuild());
renderDrawMasks();
}
else
{
render_camtex(nullptr, { daposx, daposy, daposz }, dacursect, ang, horiz, buildang(0), tileGetTexture(tilenume), rect, smoothratio);
}
});
renderRestoreTarget();
}
void
JS_ProcessEchoSpot()
{
SPRITEp tp;
int j,dist;
PLAYERp pp = Player+screenpeek;
int16_t reverb;
bool reverb_set = false;
// Process echo sprites
SWStatIterator it(STAT_ECHO);
while (auto actor = it.Next())
{
dist = 0x7fffffff;
tp = &actor->s();
j = abs(tp->pos.X - pp->pos.X);
j += abs(tp->pos.Y - pp->pos.Y);
if (j < dist)
dist = j;
if (dist <= SP_TAG4(tp)) // tag4 = ang
{
reverb = SP_TAG2(actor);
if (reverb > 200) reverb = 200;
if (reverb < 100) reverb = 100;
COVER_SetReverb(reverb);
reverb_set = true;
}
}
if (!TEST(pp->Flags, PF_DIVING) && !reverb_set && pp->Reverb <= 0)
COVER_SetReverb(0);
}
/////////////////////////////////////////////////////
// Draw one mirror, the one closest to player
// Cams and see to teleporters do NOT support room above room!
/////////////////////////////////////////////////////
#define MAXCAMDIST 8000
int camloopcnt = 0; // Timer to cycle through player
int lastcamclock;
// views
short camplayerview = 1; // Don't show yourself!
// Hack job alert!
// Mirrors and cameras are maintained in the same data structure, but for hardware rendering they cannot be interleaved.
// So this function replicates JS_DrawMirrors to only process the camera textures but not change any global state.
void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz, double smoothratio)
{
int j, cnt;
int dist;
int tposx, tposy; // Camera
int* longptr;
bool bIsWallMirror = false;
int camclock = I_GetBuildTime();
camloopcnt += camclock - lastcamclock;
if (camloopcnt > (60 * 5)) // 5 seconds per player view
{
camloopcnt = 0;
camplayerview++;
if (camplayerview >= numplayers)
camplayerview = 1;
}
lastcamclock = camclock;
{
uint32_t oscilation_delta = camclock - oscilationclock;
oscilation_delta -= oscilation_delta % 4;
oscilationclock += oscilation_delta;
oscilation_delta *= 2;
for (cnt = MAXMIRRORS - 1; cnt >= 0; cnt--)
{
if (!mirror[cnt].ismagic) continue; // these are definitely not camera textures.
if (testgotpic(cnt + MIRRORLABEL) || ((unsigned)mirror[cnt].campic < MAXTILES && testgotpic(mirror[cnt].campic)))
{
// Do not change any global state here!
bIsWallMirror = testgotpic(cnt + MIRRORLABEL);
dist = 0x7fffffff;
if (bIsWallMirror)
{
j = abs(mirror[cnt].mirrorWall->pos.X - tx);
j += abs(mirror[cnt].mirrorWall->pos.Y - ty);
if (j < dist)
dist = j;
}
else
{
SPRITEp tp;
tp = &mirror[cnt].camspriteActor->s();
j = abs(tp->pos.X - tx);
j += abs(tp->pos.Y - ty);
if (j < dist)
dist = j;
}
SPRITEp sp = nullptr;
short w;
int dx, dy, dz, tdx, tdy, tdz, midx, midy;
ASSERT(mirror[cnt].cameraActor != nullptr);
sp = &mirror[cnt].cameraActor->s();
// Calculate the angle of the mirror wall
auto wal = mirror[cnt].mirrorWall;
// Get wall midpoint for offset in mirror view
midx = (wal->pos.X + wal->point2Wall()->pos.X) / 2;
midy = (wal->pos.Y + wal->point2Wall()->pos.Y) / 2;
// Finish finding offsets
tdx = abs(midx - tx);
tdy = abs(midy - ty);
if (midx >= tx)
dx = sp->pos.X - tdx;
else
dx = sp->pos.X + tdx;
if (midy >= ty)
dy = sp->pos.Y - tdy;
else
dy = sp->pos.Y + tdy;
tdz = abs(tz - sp->pos.Z);
if (tz >= sp->pos.Z)
dz = sp->pos.Z + tdz;
else
dz = sp->pos.Z - tdz;
// Is it a TV cam or a teleporter that shows destination?
// true = It's a TV cam
mirror[cnt].mstate = m_normal;
if (TEST_BOOL1(sp))
mirror[cnt].mstate = m_viewon;
// Show teleport destination
// NOTE: Adding true lets you draw a room, even if
// you are outside of it!
if (mirror[cnt].mstate == m_viewon)
{
bool DoCam = false;
if (mirror[cnt].campic == -1)
{
Printf("Missing campic for mirror %d\n",cnt);
Printf("Map Coordinates: x = %d, y = %d\n",midx,midy);
return;
}
// BOOL2 = Oscilate camera
if (TEST_BOOL2(sp) && MoveSkip2 == 0)
{
if (TEST_BOOL3(sp)) // If true add increment to
// angle else subtract
{
// Store current angle in TAG5
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
// TAG6 = Turn radius
if (abs(getincangle(sp->ang, SP_TAG5(sp))) >= SP_TAG6(sp))
{
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
RESET_BOOL3(sp); // Reverse turn
// direction.
}
}
else
{
// Store current angle in TAG5
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
// TAG6 = Turn radius
if (abs(getincangle(sp->ang, SP_TAG5(sp))) >= SP_TAG6(sp))
{
SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
SET_BOOL3(sp); // Reverse turn
// direction.
}
}
}
else if (!TEST_BOOL2(sp))
{
SP_TAG5(sp) = sp->ang; // Copy sprite angle to
// tag5
}
// Set the horizon value.
auto camhoriz = q16horiz(clamp(IntToFixed(SP_TAG7(sp) - 100), gi->playerHorizMin(), gi->playerHorizMax()));
// If player is dead still then update at MoveSkip4
// rate.
if (pp->pos.X == pp->opos.X && pp->pos.Y == pp->opos.Y && pp->pos.Z == pp->opos.Z)
DoCam = true;
// Set up the tile for drawing
TileFiles.MakeCanvas(mirror[cnt].campic, 128, 128);
{
if (dist < MAXCAMDIST)
{
PLAYERp cp = Player + camplayerview;
if (TEST_BOOL11(sp) && numplayers > 1)
{
drawroomstotile(cp->pos.X, cp->pos.Y, cp->pos.Z, cp->angle.ang, cp->horizon.horiz, cp->cursector, mirror[cnt].campic, smoothratio);
}
else
{
drawroomstotile(sp->pos.X, sp->pos.Y, sp->pos.Z, buildang(SP_TAG5(sp)), camhoriz, sp->sector(), mirror[cnt].campic, smoothratio);
}
}
}
}
}
}
}
}
// Workaround until the camera code can be refactored to process all camera textures that were visible last frame.
// Need to stash the parameters for later use. This is only used to find the nearest camera.
static PLAYERp cam_pp;
static int cam_tx, cam_ty, cam_tz;
static int oldstat;
void JS_CameraParms(PLAYERp pp, int tx, int ty, int tz)
{
cam_pp = pp;
cam_tx = tx;
cam_ty = ty;
cam_tz = tz;
}
void GameInterface::UpdateCameras(double smoothratio)
{
JS_DrawCameras(cam_pp, cam_tx, cam_ty, cam_tz, smoothratio);
}
void GameInterface::EnterPortal(DCoreActor* viewer, int type)
{
if (type == PORTAL_WALL_MIRROR) display_mirror++;
}
void GameInterface::LeavePortal(DCoreActor* viewer, int type)
{
if (type == PORTAL_WALL_MIRROR) display_mirror--;
}
void DoAutoSize(tspriteptr_t tspr)
{
if (!bAutoSize)
return;
switch (tspr->picnum)
{
case ICON_STAR: // 1793
break;
case ICON_UZI: // 1797
tspr->xrepeat = 43;
tspr->yrepeat = 40;
break;
case ICON_UZIFLOOR: // 1807
tspr->xrepeat = 43;
tspr->yrepeat = 40;
break;
case ICON_LG_UZI_AMMO: // 1799
break;
case ICON_HEART: // 1824
break;
case ICON_HEART_LG_AMMO: // 1820
break;
case ICON_GUARD_HEAD: // 1814
break;
case ICON_FIREBALL_LG_AMMO: // 3035
break;
case ICON_ROCKET: // 1843
break;
case ICON_SHOTGUN: // 1794
tspr->xrepeat = 57;
tspr->yrepeat = 58;
break;
case ICON_LG_ROCKET: // 1796
break;
case ICON_LG_SHOTSHELL: // 1823
break;
case ICON_MICRO_GUN: // 1818
break;
case ICON_MICRO_BATTERY: // 1800
break;
case ICON_GRENADE_LAUNCHER: // 1817
tspr->xrepeat = 54;
tspr->yrepeat = 52;
break;
case ICON_LG_GRENADE: // 1831
break;
case ICON_LG_MINE: // 1842
break;
case ICON_RAIL_GUN: // 1811
tspr->xrepeat = 50;
tspr->yrepeat = 54;
break;
case ICON_RAIL_AMMO: // 1812
break;
case ICON_SM_MEDKIT: // 1802
break;
case ICON_MEDKIT: // 1803
break;
case ICON_CHEMBOMB:
tspr->xrepeat = 64;
tspr->yrepeat = 47;
break;
case ICON_FLASHBOMB:
tspr->xrepeat = 32;
tspr->yrepeat = 34;
break;
case ICON_NUKE:
break;
case ICON_CALTROPS:
tspr->xrepeat = 37;
tspr->yrepeat = 30;
break;
case ICON_BOOSTER: // 1810
tspr->xrepeat = 30;
tspr->yrepeat = 38;
break;
case ICON_HEAT_CARD: // 1819
tspr->xrepeat = 46;
tspr->yrepeat = 47;
break;
case ICON_REPAIR_KIT: // 1813
break;
case ICON_EXPLOSIVE_BOX: // 1801
break;
case ICON_ENVIRON_SUIT: // 1837
break;
case ICON_FLY: // 1782
break;
case ICON_CLOAK: // 1826
break;
case ICON_NIGHT_VISION: // 3031
tspr->xrepeat = 59;
tspr->yrepeat = 71;
break;
case ICON_NAPALM: // 3046
break;
case ICON_RING: // 3050
break;
case ICON_RINGAMMO: // 3054
break;
case ICON_NAPALMAMMO: // 3058
break;
case ICON_GRENADE: // 3059
break;
case ICON_ARMOR: // 3030
tspr->xrepeat = 82;
tspr->yrepeat = 84;
break;
case BLUE_KEY: // 1766
break;
case RED_KEY: // 1770
break;
case GREEN_KEY: // 1774
break;
case YELLOW_KEY: // 1778
break;
case BLUE_CARD:
case RED_CARD:
case GREEN_CARD:
case YELLOW_CARD:
tspr->xrepeat = 36;
tspr->yrepeat = 33;
break;
case GOLD_SKELKEY:
case SILVER_SKELKEY:
case BRONZE_SKELKEY:
case RED_SKELKEY:
tspr->xrepeat = 39;
tspr->yrepeat = 45;
break;
case SKEL_LOCKED:
case SKEL_UNLOCKED:
tspr->xrepeat = 47;
tspr->yrepeat = 40;
break;
case RAMCARD_LOCKED:
case RAMCARD_UNLOCKED:
case CARD_LOCKED:
case CARD_UNLOCKED:
break;
default:
break;
}
}
// Rotation angles for sprites
short rotang = 0;
void JAnalyzeSprites(tspriteptr_t tspr)
{
rotang += 4;
if (rotang > 2047)
rotang = 0;
// Take care of autosizing
DoAutoSize(tspr);
if (md_tilehasmodel(tspr->picnum, 0) >= 0 && hw_models) return;
// Check for voxels
//if (bVoxelsOn)
if (r_voxels)
{
if (aVoxelArray[tspr->picnum].Voxel >= 0 && !(tspr->ownerActor->sprext.flags & SPREXT_NOTMD))
{
// Turn on voxels
tspr->picnum = aVoxelArray[tspr->picnum].Voxel; // Get the voxel number
tspr->cstat |= CSTAT_SPRITE_ALIGNMENT_SLAB; // Set stat to voxelize sprite
}
}
else
{
switch (tspr->picnum)
{
case 764: // Gun barrel
if (!r_voxels || (tspr->ownerActor->sprext.flags & SPREXT_NOTMD))
{
tspr->cstat |= CSTAT_SPRITE_ALIGNMENT_WALL;
break;
}
if (aVoxelArray[tspr->picnum].Voxel >= 0)
{
// Turn on voxels
tspr->picnum = aVoxelArray[tspr->picnum].Voxel; // Get the voxel number
tspr->cstat |= CSTAT_SPRITE_ALIGNMENT_SLAB; // Set stat to voxelize sprite
}
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void UnlockKeyLock(short key_num, DSWActor* hitActor)
{
int color = 0;
// Get palette by looking at key number
switch (key_num - 1)
{
case 0: // RED_KEY
color = PALETTE_PLAYER9;
break;
case 1: // BLUE_KEY
color = PALETTE_PLAYER7;
break;
case 2: // GREEN_KEY
color = PALETTE_PLAYER6;
break;
case 3: // YELLOW_KEY
color = PALETTE_PLAYER4;
break;
case 4: // SILVER_SKELKEY
color = PALETTE_PLAYER4;
break;
case 5: // GOLD_SKELKEY
color = PALETTE_PLAYER1;
break;
case 6: // BRONZE_SKELKEY
color = PALETTE_PLAYER8;
break;
case 7: // RED_SKELKEY
color = PALETTE_PLAYER9;
break;
}
SWStatIterator it(STAT_DEFAULT);
while (auto itActor = it.Next())
{
auto sp = &itActor->s();
switch (sp->picnum)
{
case SKEL_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_UNLOCK, itActor, v3df_doppler | v3df_dontpan);
if (itActor == hitActor)
sp->picnum = SKEL_UNLOCKED;
}
break;
case RAMCARD_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_CARDUNLOCK, itActor, v3df_doppler | v3df_dontpan);
sp->picnum = RAMCARD_UNLOCKED;
}
break;
case CARD_LOCKED:
if (sp->pal == color)
{
PlaySound(DIGI_RAMUNLOCK, itActor, v3df_doppler | v3df_dontpan);
if (itActor == hitActor)
sp->picnum = CARD_UNLOCKED;
else
sp->picnum = CARD_UNLOCKED+1;
}
break;
}
}
}
END_SW_NS