windows-nt/Source/XPSP1/NT/shell/osshell/ep/snake/rtns.c
2020-09-26 16:20:57 +08:00

578 lines
9.1 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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