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();
|
|||
|
}
|
|||
|
|