578 lines
9.1 KiB
C
578 lines
9.1 KiB
C
/******************/
|
||
/* SNAKE ROUTINES */
|
||
/******************/
|
||
|
||
#define _WINDOWS
|
||
#include <windows.h>
|
||
#include <port1632.h>
|
||
|
||
#include "snake.h"
|
||
#include "res.h"
|
||
#include "rtns.h"
|
||
#include "grafix.h"
|
||
#include "blk.h"
|
||
#include "pref.h"
|
||
#include "sound.h"
|
||
#include "util.h"
|
||
|
||
|
||
/*** Global/Local Variables ***/
|
||
|
||
|
||
BLK mpposblk[posMax]; /* The playing grid */
|
||
|
||
|
||
POS posHead; /* current position of head */
|
||
POS posTail; /* current position of tail */
|
||
|
||
BOOL fHead; /* True if head is visible */
|
||
BOOL fTail;
|
||
|
||
BOOL fExit = fFalse;
|
||
|
||
BOOL fPlum;
|
||
POS posPlum;
|
||
INT offPlumNS;
|
||
INT offPlumEW;
|
||
|
||
INT cDelayHead; /* Delay before head shows up */
|
||
INT cDelayTail;
|
||
|
||
INT cfoodRemain; /* Amount of food left to eat */
|
||
INT cfoodUsed; /* Amount of food eaten */
|
||
INT cLevel = 0; /* current level */
|
||
|
||
INT cLives = cLivesStart; /* Count of Lives */
|
||
|
||
#define idirMax 10
|
||
DIR rgdir[idirMax];
|
||
|
||
INT idirCurr;
|
||
INT idirLast;
|
||
|
||
INT ctickMove;
|
||
INT ctickMoveMac = 4;
|
||
|
||
INT ctickTime;
|
||
INT ctickTimeMac = 1;
|
||
|
||
#define cTimeLeftMax dxpTime
|
||
INT cTimeLeft = cTimeLeftMax;
|
||
|
||
#define lenStart 7
|
||
|
||
extern INT score;
|
||
extern BOOL fWipe;
|
||
|
||
INT rgtickSkill[3] = {5, 3, 1};
|
||
|
||
BLK mpdirdirblk[4][4] =
|
||
{
|
||
{blkBodyNS, blkBodySE, blkNull, blkBodySW}, /* N */
|
||
{blkBodyNW, blkBodyEW, blkBodySW, blkNull }, /* E */
|
||
{blkNull, blkBodyNE, blkBodyNS, blkBodyNW}, /* S */
|
||
{blkBodyNE, blkNull, blkBodySE, blkBodyEW} /* W */
|
||
};
|
||
|
||
|
||
#define IncIDir(idir) (idir = (idir+1) % idirMax)
|
||
|
||
#define BlkTailFromDir(dir) (blkTailN + (dir))
|
||
#define BlkHeadFromDir(dir) (blkHeadN + (dir))
|
||
#define DirFromBlk(blk) ((DIR) ((blk) & 0x0003))
|
||
|
||
|
||
/*** Global/External Variables ***/
|
||
|
||
extern STATUS status;
|
||
extern PREF Preferences;
|
||
extern BOOL fUpdateIni;
|
||
extern INT cMoveScore;
|
||
|
||
|
||
|
||
|
||
/****** C H A N G E P O S B L K ******/
|
||
|
||
VOID ChangePosBlk(POS pos, BLK blk)
|
||
{
|
||
mpposblk[pos] = blk;
|
||
DisplayPos(pos);
|
||
}
|
||
|
||
|
||
/****** D R O P F O O D ******/
|
||
|
||
POS DropFood(VOID)
|
||
{
|
||
POS pos;
|
||
|
||
while (mpposblk[pos = Rnd(posMax)] != blkNull)
|
||
;
|
||
mpposblk[pos] = blkFood;
|
||
|
||
return pos;
|
||
}
|
||
|
||
|
||
/****** P O S F R O M P O S D I R ******/
|
||
|
||
POS PosFromPosDir(POS pos, DIR dir)
|
||
{
|
||
switch (dir)
|
||
{
|
||
case dirN:
|
||
return (pos - xMax);
|
||
|
||
case dirE:
|
||
return (pos + 1);
|
||
|
||
case dirS:
|
||
return (pos + xMax);
|
||
|
||
case dirW:
|
||
return (pos - 1);
|
||
|
||
#ifdef DEBUG
|
||
default:
|
||
Oops("PosFromPosDir: Invalid Direction");
|
||
return pos;
|
||
#endif
|
||
}
|
||
|
||
// program should not reach this section of code!
|
||
return pos;
|
||
}
|
||
|
||
/****** D I R F R O M P O S P O S ******/
|
||
|
||
DIR DirFromPosPos(POS posSrc, POS posDest)
|
||
{
|
||
INT dpos = posDest-posSrc;
|
||
|
||
if (dpos == xMax)
|
||
return dirS;
|
||
|
||
else if (dpos == 1)
|
||
return dirE;
|
||
|
||
else if (dpos == -xMax)
|
||
return dirN;
|
||
|
||
#ifndef DEBUG
|
||
else
|
||
return dirW;
|
||
#else
|
||
else if (dpos == -1)
|
||
return dirW;
|
||
|
||
else
|
||
Oops("DirFromPosPos: Invalid Direction");
|
||
return dirN;
|
||
#endif
|
||
}
|
||
|
||
|
||
/****** B L K T A I L F R O M B L K B L K ******/
|
||
|
||
BLK BlkTailFromBlkBlk(BLK blkSrc, BLK blkDest)
|
||
{
|
||
if (blkDest == blkBodyNS || blkDest == blkBodyEW)
|
||
return blkSrc;
|
||
|
||
if (blkDest == blkBodyNE)
|
||
if (blkSrc == blkTailW)
|
||
return blkTailN;
|
||
else
|
||
return blkTailE;
|
||
|
||
else if (blkDest == blkBodySE)
|
||
if (blkSrc == blkTailW)
|
||
return blkTailS;
|
||
else
|
||
return blkTailE;
|
||
|
||
else if (blkDest == blkBodySW)
|
||
if (blkSrc == blkTailE)
|
||
return blkTailS;
|
||
else
|
||
return blkTailW;
|
||
|
||
else /* if (blkDest == blkBodyNW) */
|
||
if (blkSrc == blkTailE)
|
||
return blkTailN;
|
||
else
|
||
return blkTailW;
|
||
}
|
||
|
||
|
||
|
||
|
||
/****** D O C H A N G E D I R ******/
|
||
|
||
VOID DoChangeDir(DIR dir)
|
||
{
|
||
if (!fHead)
|
||
return;
|
||
|
||
if (((dir+2) % 4) == rgdir[idirLast]) /* Don't allow about faces */
|
||
return;
|
||
|
||
if (IncIDir(idirLast) == idirCurr) /* Watch for overflow */
|
||
{
|
||
if (--idirLast < 0)
|
||
idirLast = idirMax-1;
|
||
return;
|
||
}
|
||
|
||
rgdir[idirLast] = dir;
|
||
}
|
||
|
||
|
||
/****** D O C H A N G E R E L D I R ******/
|
||
|
||
VOID DoChangeRelDir(DIR dirRel)
|
||
{
|
||
if ( (dirRel = rgdir[idirLast] + dirRel) < 0)
|
||
dirRel = dirW;
|
||
else if (dirRel > dirW)
|
||
dirRel = dirN;
|
||
|
||
DoChangeDir(dirRel);
|
||
}
|
||
|
||
|
||
/****** K I L L S N A K E ******/
|
||
|
||
VOID KillSnake(VOID)
|
||
{
|
||
fHead = fTail = fFalse;
|
||
fPlum = fFalse;
|
||
|
||
if (cLives--)
|
||
{
|
||
PlayTune(TUNE_HITHEAD);
|
||
StartLevel();
|
||
}
|
||
else
|
||
{
|
||
PlayTune(TUNE_LOSEGAME);
|
||
cLives = 0;
|
||
ClrStatusPlay();
|
||
if (score > Preferences.HiScore)
|
||
{
|
||
/* Congrats, etc. */
|
||
Preferences.HiScore = score;
|
||
Preferences.HiLevel = cLevel;
|
||
fUpdateIni = fTrue;
|
||
}
|
||
DisplayGameOver();
|
||
}
|
||
}
|
||
|
||
|
||
/****** D E C T I M E ******/
|
||
|
||
/* Decrement time left */
|
||
|
||
VOID DecTime(VOID)
|
||
{
|
||
if (--cTimeLeft < 0)
|
||
{
|
||
INT i;
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
DisplayPos(DropFood());
|
||
cfoodRemain++;
|
||
}
|
||
cTimeLeft = cTimeLeftMax;
|
||
DisplayTime();
|
||
}
|
||
else
|
||
UpdateTime();
|
||
}
|
||
|
||
|
||
|
||
/****** D O M O V E ******/
|
||
|
||
VOID DoMove(VOID)
|
||
{
|
||
POS posPrev;
|
||
BLK blkPrev;
|
||
DIR dir;
|
||
|
||
if (fHead)
|
||
{
|
||
if (posHead == posLeave && fExit)
|
||
{
|
||
ChangePosBlk(posHead, blkBodyNS);
|
||
fHead = fFalse;
|
||
}
|
||
else
|
||
{
|
||
/* Check heading/direction */
|
||
|
||
if ((idirCurr != idirLast) && (posHead != posEnter))
|
||
IncIDir(idirCurr);
|
||
|
||
/* Change head into body */
|
||
|
||
blkPrev = mpposblk[posPrev = posHead];
|
||
posHead = PosFromPosDir(posPrev, dir = rgdir[idirCurr]);
|
||
|
||
if (mpposblk[posHead] != blkNull)
|
||
{
|
||
if (mpposblk[posHead] < blkHeadN) /*** EAT FOOD ***/
|
||
{
|
||
AddScore(1);
|
||
cTimeLeft = cTimeLeftMax;
|
||
DisplayTime();
|
||
|
||
if (--cfoodRemain == 0)
|
||
{
|
||
fExit = fTrue;
|
||
ctickTime = tickNil;
|
||
cTimeLeft = cTimeLeftMax;
|
||
DisplayTime();
|
||
ChangePosBlk(posLeave, blkNull);
|
||
}
|
||
fTail = fFalse;
|
||
cDelayTail += 4;
|
||
}
|
||
else
|
||
{
|
||
KillSnake();
|
||
return;
|
||
}
|
||
}
|
||
|
||
ChangePosBlk(posPrev, mpdirdirblk[DirFromBlk(blkPrev)][dir]);
|
||
ChangePosBlk(posHead, BlkHeadFromDir(dir));
|
||
}
|
||
|
||
}
|
||
else if (cDelayHead)
|
||
{
|
||
if (--cDelayHead == 0)
|
||
{
|
||
ChangePosBlk(posHead, blkHeadN);
|
||
fHead = fTrue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ctickMove = 1; /* Must be leaving (fExit == fTrue) */
|
||
}
|
||
|
||
if (fTail)
|
||
{
|
||
if (posTail == posLeave)
|
||
{
|
||
ChangePosBlk(posTail, blkNull);
|
||
fTail = fFalse;
|
||
|
||
if ((++cLevel % lvlMax) == 0)
|
||
{
|
||
if (ctickMoveMac > 1)
|
||
ctickMoveMac--;
|
||
PlayTune(TUNE_WINGAME);
|
||
}
|
||
else
|
||
PlayTune(TUNE_WINLEVEL);
|
||
|
||
StartLevel();
|
||
AddScore(10);
|
||
}
|
||
|
||
else
|
||
{
|
||
/* Remove old tail */
|
||
|
||
blkPrev = mpposblk[posPrev = posTail];
|
||
if (posTail == posEnter)
|
||
ChangePosBlk(posPrev, blkWallEW); /* Close entrance */
|
||
else
|
||
ChangePosBlk(posPrev, blkNull);
|
||
|
||
/* Display new tail */
|
||
|
||
posTail = PosFromPosDir(posPrev, DirFromBlk(blkPrev));
|
||
|
||
ChangePosBlk(posTail, BlkTailFromBlkBlk(blkPrev, mpposblk[posTail]));
|
||
}
|
||
}
|
||
else if (cDelayTail)
|
||
{
|
||
if (--cDelayTail == 0)
|
||
{
|
||
if (posTail == posEnter)
|
||
{
|
||
ChangePosBlk(posTail, blkTailN);
|
||
}
|
||
fTail = fTrue;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
/****** F K I L L B O U N C E ******/
|
||
|
||
BOOL FKillBounce(POS pos)
|
||
{
|
||
if (mpposblk[pos] != blkNull)
|
||
{
|
||
if (pos == posHead)
|
||
KillSnake();
|
||
return fTrue;
|
||
}
|
||
else
|
||
if (pos == posLeave) /* Don't go into exit */
|
||
return fTrue;
|
||
else
|
||
return fFalse;
|
||
}
|
||
|
||
|
||
/****** D O T I M E R ******/
|
||
|
||
VOID DoTimer(VOID)
|
||
{
|
||
BOOL fBounce = 0; /* Variable - there must be a better way */
|
||
|
||
if (cMoveScore != 0)
|
||
MoveScore();
|
||
|
||
if (!FPlay())
|
||
return;
|
||
|
||
if (--ctickMove == 0)
|
||
{
|
||
ctickMove = ctickMoveMac;
|
||
DoMove();
|
||
|
||
if (fPlum)
|
||
{
|
||
if (FKillBounce(posPlum + offPlumNS))
|
||
{
|
||
offPlumNS = -offPlumNS;
|
||
fBounce = 1;
|
||
}
|
||
if (FKillBounce(posPlum + offPlumEW))
|
||
{
|
||
offPlumEW = -offPlumEW;
|
||
fBounce = 2;
|
||
}
|
||
|
||
if (!fBounce)
|
||
{
|
||
if (FKillBounce(posPlum + offPlumNS + offPlumEW))
|
||
{
|
||
offPlumNS = -offPlumNS;
|
||
offPlumEW = -offPlumEW;
|
||
}
|
||
}
|
||
else if (FKillBounce(posPlum+offPlumNS + offPlumEW))
|
||
{
|
||
if (fBounce == 1)
|
||
offPlumEW = -offPlumEW;
|
||
else
|
||
offPlumNS = -offPlumNS;
|
||
}
|
||
|
||
if (fPlum && (mpposblk[posPlum + offPlumNS + offPlumEW] == blkNull))
|
||
{
|
||
ChangePosBlk(posPlum, blkNull);
|
||
posPlum += offPlumNS + offPlumEW;
|
||
ChangePosBlk(posPlum, blkPlum);
|
||
}
|
||
}
|
||
|
||
}
|
||
else if (ctickMove < -50 && (cMoveScore==0)) /* This happens at the start of a game */
|
||
{
|
||
fPlum = (cLevel >= lvlMax);
|
||
ctickMove = ctickMoveMac;
|
||
ctickTime = ctickTimeMac;
|
||
cTimeLeft = cTimeLeftMax;
|
||
fWipe = fFalse;
|
||
SetLevelColor();
|
||
DisplayScreen();
|
||
}
|
||
|
||
if (--ctickTime == 0)
|
||
{
|
||
ctickTime = ctickTimeMac;
|
||
DecTime();
|
||
}
|
||
}
|
||
|
||
|
||
/****** S T A R T L E V E L *******/
|
||
|
||
VOID StartLevel(VOID)
|
||
{
|
||
fPlum = fFalse;
|
||
|
||
cTimeLeft = cTimeLeftMax;
|
||
|
||
DisplayLevelNum();
|
||
|
||
SetupLevelData();
|
||
|
||
if (cLevel >= lvlMax)
|
||
{
|
||
offPlumNS = xMax;
|
||
offPlumEW = 1;
|
||
posPlum = 4*xMax + 3;
|
||
while (mpposblk[posPlum] != blkNull)
|
||
posPlum--;
|
||
mpposblk[posPlum] = blkPlum;
|
||
}
|
||
|
||
/* Distribute Food */
|
||
|
||
cfoodUsed = cfoodRemain = 10;
|
||
while (cfoodUsed--)
|
||
{
|
||
DropFood();
|
||
}
|
||
|
||
|
||
/* Setup player position */
|
||
|
||
fTail = fHead = fFalse;
|
||
|
||
cDelayHead = 3;
|
||
cDelayTail = lenStart;
|
||
|
||
posTail = posHead = posEnter;
|
||
|
||
ctickMove = -1; /* Indicate Starting Level */
|
||
ctickTime = -1;
|
||
|
||
rgdir[idirCurr=idirLast=0] = dirN;
|
||
|
||
ClrStatusIcon();
|
||
SetStatusPlay();
|
||
}
|
||
|
||
|
||
/****** S T A R T G A M E *******/
|
||
|
||
VOID StartGame(INT lvl)
|
||
{
|
||
cLevel = lvl;
|
||
cLives = cLivesStart;
|
||
|
||
ctickMoveMac = max(0,rgtickSkill[Preferences.skill] - cLevel/lvlMax);
|
||
ctickTimeMac = Preferences.skill ? 1 : 2;
|
||
|
||
ResetScore();
|
||
DisplayScore();
|
||
StartLevel();
|
||
}
|
||
|