mirror of
https://github.com/Blzut3/Wolf3D-Mac.git
synced 2024-11-28 15:50:19 +00:00
1 line
13 KiB
C
1 line
13 KiB
C
#include "wolfdef.h"
|
|
#include <string.h>
|
|
|
|
/**********************************
|
|
|
|
Return the absolute value
|
|
|
|
**********************************/
|
|
|
|
Word w_abs(int val)
|
|
{
|
|
return val>=0 ? val : -val;
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Return a seeded random number
|
|
|
|
**********************************/
|
|
|
|
Byte rndtable[256] = {
|
|
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66,
|
|
74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36,
|
|
95,110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188,
|
|
52,140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224,
|
|
149,104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242,
|
|
145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0,
|
|
175,143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235,
|
|
25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113,
|
|
94,161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75,
|
|
136,156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196,
|
|
135,106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113,
|
|
80,250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241,
|
|
24,223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224,
|
|
145,224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95,
|
|
28,139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226,
|
|
71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36,
|
|
17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106,
|
|
197,242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136,
|
|
120,163, 236, 249
|
|
};
|
|
|
|
Word rndindex = 0;
|
|
|
|
Word w_rnd(void)
|
|
{
|
|
rndindex = (rndindex+1)&0xff; /* Next index */
|
|
return rndtable[rndindex]; /* Return the number */
|
|
}
|
|
|
|
|
|
/**********************************
|
|
|
|
Return an angle value based on a slope.
|
|
Assume that x is >= to y.
|
|
|
|
**********************************/
|
|
|
|
Word AngleFromSlope2(Word y,Word x)
|
|
{
|
|
return tantoangle[((LongWord)y<<SLOPEBITS)/x];
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Convert an arbitrary point from the viewxy into an angle.
|
|
|
|
To get a global angle from cartesian coordinates, the coordinates are flipped until
|
|
they are in the first octant of the coordinate system, then the y (<=x) is scaled and
|
|
divided by x to get a tangent (slope) value which is looked up in the tantoangle[] table.
|
|
The +1 size is to handle the case when x==y without additional checking.
|
|
|
|
**********************************/
|
|
|
|
#define ANG90 0x4000
|
|
#define ANG180 0x8000
|
|
#define ANG270 0xc000
|
|
angle_t PointToAngle(fixed_t x, fixed_t y)
|
|
{
|
|
x -= viewx; /* Adjust the x and y based on the camera */
|
|
y = viewy - y;
|
|
|
|
if (x>=0) { /* x is positive? */
|
|
if (y>=0) { /* y is positive? */
|
|
if (x>y) {
|
|
return AngleFromSlope2(y,x); /* octant 0*/
|
|
} else {
|
|
return ANG90-1-AngleFromSlope2(x,y); /* octant 1 */
|
|
}
|
|
} else { /* y<0 */
|
|
y = -y; /* Negate y (Make positive) */
|
|
if (x>y) {
|
|
return -AngleFromSlope2(y,x); /* octant 8 */
|
|
} else {
|
|
return ANG270+AngleFromSlope2 (x,y); /* octant 7 */
|
|
}
|
|
}
|
|
} else { /* x<0*/
|
|
x = -x; /* Force x positive */
|
|
if (y>= 0) { /* Is y positive? */
|
|
if (x>y) {
|
|
return ANG180-1-AngleFromSlope2(y,x); /* octant 3 */
|
|
} else {
|
|
return ANG90+AngleFromSlope2(x,y); /* octant 2 */
|
|
}
|
|
} else { /* y<0*/
|
|
y = -y; /* Force y positive also */
|
|
if (x>y) {
|
|
return ANG180+AngleFromSlope2 (y,x); /* octant 4 */
|
|
}
|
|
}
|
|
}
|
|
return ANG270-1-AngleFromSlope2(x,y); /* octant 5 */
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Died() has already spun the view toward the killer
|
|
GameOver() scales the "game over" sprite in and pulses it until an event
|
|
|
|
**********************************/
|
|
|
|
void GameOver(void)
|
|
{
|
|
topspritenum = S_GAMEOVER; /* Game over words */
|
|
topspritescale = 8; /* Start the scale factor */
|
|
do {
|
|
RenderView(); /* Draw the 3-d view */
|
|
topspritescale+=8;
|
|
} while (topspritescale<120);
|
|
|
|
for (;;) { /* Stay here forever */
|
|
do {
|
|
if (WaitTicksEvent(1)) { /* Canceled? */
|
|
return;
|
|
}
|
|
RenderView(); /* Show the 3-D view */
|
|
topspritescale-=2; /* Make smaller */
|
|
} while(topspritescale>100); /* Not too small... */
|
|
do {
|
|
if (WaitTicksEvent(1)) { /* Canceled? */
|
|
return;
|
|
}
|
|
RenderView(); /* Show the 3-D view */
|
|
topspritescale+=2; /* Make bigger */
|
|
} while (topspritescale<120); /* Not too big... */
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Show the word "Victory" scaling in...
|
|
|
|
**********************************/
|
|
|
|
void VictoryScale(void)
|
|
{
|
|
topspritenum = S_VICTORY;
|
|
topspritescale = 16;
|
|
|
|
do {
|
|
RenderView();
|
|
topspritescale += 16;
|
|
} while (topspritescale<240);
|
|
|
|
for (;;) {
|
|
do {
|
|
if (WaitTicksEvent(1)) {
|
|
return;
|
|
}
|
|
RenderView();
|
|
topspritescale-=4;
|
|
} while (topspritescale>200);
|
|
do {
|
|
if (WaitTicksEvent(1)) {
|
|
return;
|
|
}
|
|
RenderView();
|
|
topspritescale+=4;
|
|
} while (topspritescale<240);
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
|
|
You died...
|
|
|
|
**********************************/
|
|
|
|
void Died (void)
|
|
{
|
|
Word Timer; /* Time mark */
|
|
Word Adds; /* Number of tics elapsed */
|
|
Word Total;
|
|
angle_t SrcAngle; /* Angle of view */
|
|
angle_t DestAngle; /* Angle of death */
|
|
fixed_t Motion; /* Motion constant */
|
|
|
|
gamestate.attackframe = 0; /* Remove the gun shape */
|
|
NoWeaponDraw = TRUE; /* The weapon is not drawn */
|
|
PlaySound(SND_PDIE); /* ARRRGGGHHHH!! */
|
|
IO_DrawFace(9); /* Show the dead face */
|
|
|
|
/* find angle to face attacker */
|
|
|
|
SrcAngle = gamestate.viewangle<<SHORTTOANGLESHIFT; /* Get the fine current angle */
|
|
DestAngle = PointToAngle(killx,killy)&(-1<<SHORTTOANGLESHIFT); /* What's the direction of the kill angle */
|
|
|
|
/* rotate to attacker */
|
|
|
|
if (SrcAngle!=DestAngle) { /* Do I need to rotate? */
|
|
Motion = (fixed_t)(DestAngle-SrcAngle)/ (fixed_t)30; /* Differance in fine angles */
|
|
LastTicCount = ReadTick();
|
|
Total = 30; /* Number of tics to elapse */
|
|
for (;;) { /* There yet? */
|
|
Timer = ReadTick(); /* How much time has elapsed */
|
|
Adds = Timer-LastTicCount; /* Save the time */
|
|
LastTicCount = Timer; /* Mark the new time */
|
|
if (Adds>=Total) { /* Too much time? */
|
|
break; /* Get out now! */
|
|
}
|
|
Total-=Adds; /* Remove elapsed time */
|
|
SrcAngle += Motion*Adds; /* Spin */
|
|
gamestate.viewangle = SrcAngle>>SHORTTOANGLESHIFT; /* Set the new view angle */
|
|
RenderView(); /* Show the view */
|
|
}
|
|
gamestate.viewangle = DestAngle>>SHORTTOANGLESHIFT; /* Finish the motion */
|
|
RenderView(); /* Draw the screen */
|
|
}
|
|
/* done */
|
|
|
|
if (!gamestate.lives) {
|
|
GameOver(); /* Show Game over... */
|
|
} else {
|
|
WaitEvent(); /* Wait for an event */
|
|
gamestate.health = 100; /* Restore health */
|
|
if (difficulty) { /* Remove weapons if difficult */
|
|
gamestate.weapon = gamestate.pendingweapon = WP_PISTOL;
|
|
gamestate.machinegun = FALSE;
|
|
gamestate.chaingun = FALSE;
|
|
gamestate.missile = FALSE;
|
|
gamestate.flamethrower = FALSE;
|
|
gamestate.ammo = STARTAMMO;
|
|
gamestate.missiles = 0;
|
|
gamestate.gas = 0;
|
|
gamestate.maxammo = 99;
|
|
gamestate.keys = 0;
|
|
} else {
|
|
if (gamestate.ammo < STARTAMMO*2) { /* If easy, then reload... */
|
|
gamestate.ammo = STARTAMMO*2;
|
|
}
|
|
}
|
|
gamestate.attackframe = 0; /* Reset the attack frame */
|
|
gamestate.attackcount = 0;
|
|
}
|
|
FadeToBlack(); /* Fade the screen to black */
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Calls draw face if time to change
|
|
|
|
**********************************/
|
|
|
|
void UpdateFace(void)
|
|
{
|
|
Word Base;
|
|
|
|
if (facecount>TicCount) { /* Time to change the face? */
|
|
facecount-=TicCount; /* Wait a bit */
|
|
} else {
|
|
Base = (gamestate.health <= 25) ? 5 : 0;
|
|
if (faceframe==Base) { /* Normal frame? */
|
|
++Base; /* Use alternate */
|
|
}
|
|
faceframe=Base; /* Set the new face */
|
|
facecount = (w_rnd ()&31)+4; /* Random time */
|
|
IO_DrawFace(faceframe); /* Draw the face */
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Prepare for a game loop
|
|
|
|
**********************************/
|
|
|
|
void PrepPlayLoop (void)
|
|
{
|
|
StartSong(SongListPtr[gamestate.mapon+2]); /* start music */
|
|
if (!SetupGameLevel()) { /* Load the game map */
|
|
ReleaseMap(); /* Release map memory */
|
|
ReleaseScalers(); /* Release the compiled scalers */
|
|
PlaySong(0);
|
|
while (!SetupGameLevel()) { /* Try loading it again */
|
|
Again:
|
|
ReleaseMap(); /* Oh oh... */
|
|
if (!GameViewSize) { /* Smallest screen? */
|
|
BailOut(); /* Leave... */
|
|
}
|
|
--GameViewSize; /* Smaller screen size */
|
|
GameViewSize = NewGameWindow(GameViewSize);
|
|
}
|
|
}
|
|
if (!StartupRendering(GameViewSize)) {
|
|
ReleaseScalers();
|
|
goto Again;
|
|
}
|
|
topspritescale = 0; /* No overlay sprite */
|
|
faceframe = 1; /* First face */
|
|
facecount = 1; /* change face next tic */
|
|
firstframe = 1; /* Force a fade in */
|
|
NoWeaponDraw = 0; /* Allow drawing of weapon */
|
|
memset(buttonstate,0,sizeof(buttonstate)); /* Kill the mouse system */
|
|
if (playstate!=EX_LOADGAME) {
|
|
PushWallRec.pwallcount = 0; /* No pushwalls */
|
|
gamestate.playtime = 0; /* Game has started */
|
|
} else {
|
|
FinishLoadGame(); /* Finish the load game code */
|
|
}
|
|
RedrawStatusBar(); /* Redraw the main bar */
|
|
mousey = 0; /* Reset the mouse y */
|
|
playstate = EX_STILLPLAYING; /* Game is in progress */
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Play the game
|
|
|
|
**********************************/
|
|
|
|
void PlayLoop(void)
|
|
{
|
|
LongWord Timer;
|
|
LastTicCount = ReadTick();
|
|
do {
|
|
Timer = ReadTick(); /* How much time has elapsed */
|
|
TicCount = (Timer-LastTicCount);
|
|
gamestate.playtime += TicCount; /* Add the physical time to the elapsed time */
|
|
|
|
LastTicCount=Timer;
|
|
if (!SlowDown) {
|
|
TicCount = 4; /* Adjust from 4 */
|
|
}
|
|
if (TicCount>=5) {
|
|
TicCount = 4;
|
|
}
|
|
IO_CheckInput(); /* Read the controls from the system */
|
|
madenoise = FALSE; /* No noise made (Yet) */
|
|
MoveDoors(); /* Open and close all doors */
|
|
MovePWalls(); /* Move all push walls */
|
|
MovePlayer(); /* Move the player */
|
|
MoveActors(); /* Move all the bad guys */
|
|
MoveMissiles(); /* Move all projectiles */
|
|
UpdateFace(); /* Draw BJ's face and animate it */
|
|
viewx = actors[0].x; /* Where is the camera? */
|
|
viewy = actors[0].y;
|
|
RenderView(); /* Draw the 3D view */
|
|
} while (playstate==EX_STILLPLAYING);
|
|
StopSong();
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Set up new game to start from the beginning
|
|
|
|
**********************************/
|
|
|
|
void NewGame(void)
|
|
{
|
|
nextmap = 0; /* No next map inited */
|
|
memset(&gamestate,0,sizeof(gamestate)); /* Zap the game variables */
|
|
gamestate.weapon = gamestate.pendingweapon = WP_PISTOL; /* Set the pistol */
|
|
gamestate.health = 100; /* Reset the health */
|
|
gamestate.ammo = STARTAMMO; /* Reset the ammo */
|
|
if (!difficulty) {
|
|
gamestate.ammo += STARTAMMO; /* Double the ammo if easy */
|
|
}
|
|
gamestate.maxammo = 99; /* Refill the ammo */
|
|
gamestate.lives = 3; /* 3 lives */
|
|
gamestate.nextextra = EXTRAPOINTS; /* Next free life score needed */
|
|
gamestate.godmode = 0; /* Force god mode off */
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Redraw the main status bar at the bottom
|
|
|
|
**********************************/
|
|
|
|
void RedrawStatusBar(void)
|
|
{
|
|
IO_DrawStatusBar(); /* Draw the status bar */
|
|
IO_DrawFloor(gamestate.mapon); /* Draw the current floor # */
|
|
IO_DrawScore(gamestate.score); /* Draw the current score */
|
|
IO_DrawTreasure(gamestate.treasure); /* Draw the treasure score */
|
|
IO_DrawLives(gamestate.lives); /* Draw the life count */
|
|
IO_DrawHealth(gamestate.health); /* Draw the health */
|
|
switch (gamestate.weapon) { /* Draw the proper ammo */
|
|
case WP_FLAMETHROWER:
|
|
IO_DrawAmmo(gamestate.gas);
|
|
break;
|
|
case WP_MISSILE:
|
|
IO_DrawAmmo(gamestate.missiles);
|
|
break;
|
|
default:
|
|
IO_DrawAmmo(gamestate.ammo);
|
|
break;
|
|
}
|
|
IO_DrawKeys(gamestate.keys); /* Draw the keys held */
|
|
IO_DrawFace(faceframe); /* Draw BJ's face */
|
|
}
|
|
|
|
/**********************************
|
|
|
|
Play the game!
|
|
|
|
**********************************/
|
|
|
|
void GameLoop (void)
|
|
{
|
|
for(;;) {
|
|
skipbrief:
|
|
ShowGetPsyched();
|
|
PrepPlayLoop(); /* Init internal variables */
|
|
EndGetPsyched();
|
|
PlayLoop(); /* Play the game */
|
|
if (playstate == EX_DIED) { /* Did you die? */
|
|
--gamestate.lives; /* Remove a life */
|
|
Died(); /* Run the death code */
|
|
if (!gamestate.lives) { /* No more lives? */
|
|
return; /* Exit then */
|
|
}
|
|
goto skipbrief; /* Try again */
|
|
}
|
|
|
|
if (playstate == EX_SECRET) { /* Going to the secret level? */
|
|
nextmap = MapListPtr->InfoArray[gamestate.mapon].SecretLevel;
|
|
} else if (playstate != EX_WARPED) {
|
|
nextmap = MapListPtr->InfoArray[gamestate.mapon].NextLevel; /* Normal warp? */
|
|
} /* If warped, then nextmap is preset */
|
|
if (nextmap == 0xffff) { /* Last level? */
|
|
VictoryScale(); /* You win!! */
|
|
ReleaseMap(); /* Unload the map */
|
|
Intermission(); /* Display the wrapup... */
|
|
ShareWareEnd(); /* End the game for the shareware version */
|
|
/* VictoryIntermission(); /* Wrapup for victory! */
|
|
return;
|
|
}
|
|
ReleaseMap(); /* Unload the game map */
|
|
gamestate.keys = 0; /* Zap the keys */
|
|
WaitTicks(1); /* Flush the ticker */
|
|
WaitTicks(30); /* Wait for the sound to finish */
|
|
if (playstate != EX_WARPED) { /* No bonus for warping! */
|
|
Intermission(); /* Show the wrap up... */
|
|
}
|
|
gamestate.mapon = nextmap; /* Next level */
|
|
}
|
|
}
|