394 lines
9.5 KiB
ObjectPascal
394 lines
9.5 KiB
ObjectPascal
{ MainLoop -- main loop }
|
|
unit game_m;
|
|
|
|
interface
|
|
|
|
uses level_m, enemy_packs_m;
|
|
|
|
type
|
|
state = (
|
|
gameLevelAnnounce, gameExit, gameMenu, gameStartLevel, gameKeyInfo,
|
|
gamePause, gameUnpauseLevel, gameOver, gameComplete,
|
|
gameLevelComplete, gameLevelLoop, gameContinueLevel
|
|
);
|
|
menuState = (menuNewGame, menuKeyInfo, menuContinue);
|
|
gameState = record
|
|
curExit: boolean;
|
|
curMenu: menuState;
|
|
curState: state;
|
|
level, score, life: integer;
|
|
enemyPack: enemyPackType;
|
|
shutdown, newGame, unpause, levelInited, skipScene: boolean;
|
|
end;
|
|
|
|
procedure InitGame(var g: gameState);
|
|
procedure MainLoop(var g: gameState);
|
|
procedure NextExitState(var g: gameState);
|
|
procedure PreviousExitState(var g: gameState);
|
|
|
|
implementation
|
|
|
|
uses arena_m, arena_graphics_m, crt, creature_m, graphics_m, hamster_m,
|
|
keys_m, debug_m;
|
|
|
|
const
|
|
KeyDelayMs = 25;
|
|
MoveDelayMs = 120;
|
|
EraseLifeThreshold = 10;
|
|
AnnounceDelayMs = 1500;
|
|
LevelCompleteDelayMs = 1500;
|
|
LevelCount = 2;
|
|
StartLifeN = 3;
|
|
|
|
procedure DecreaseLife(var life: integer);
|
|
begin
|
|
if life = EraseLifeThreshold then
|
|
EraseLifesNumber(life);
|
|
life := life - 1;
|
|
DrawLifesNumber(life)
|
|
end;
|
|
|
|
procedure InitGame(var g: gameState);
|
|
begin
|
|
g.curMenu := menuNewGame;
|
|
g.curState := gameMenu;
|
|
g.enemyPack := enemyPack1;
|
|
g.score := 0;
|
|
g.shutdown := false;
|
|
g.newGame := false;
|
|
g.skipScene := false;
|
|
g.life := StartLifeN
|
|
end;
|
|
|
|
procedure RunExitState(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawExit(g);
|
|
while (g.curState = gameExit) and not g.shutdown do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level)
|
|
end;
|
|
EraseExit
|
|
end;
|
|
|
|
procedure RunInfoState(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawKeyInfo;
|
|
while (g.curState = gameKeyInfo) and not g.shutdown do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level)
|
|
end;
|
|
EraseKeyInfo
|
|
end;
|
|
|
|
procedure RunPauseState(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawPause;
|
|
while (g.curState = gamePause) and not g.shutdown do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level)
|
|
end;
|
|
if g.curState = gameMenu then
|
|
EraseLevel;
|
|
if g.curState = gameUnpauseLevel then
|
|
begin
|
|
DrawLevelUnpause(level);
|
|
level.unpause := true
|
|
end
|
|
end;
|
|
|
|
procedure RunGameOverState(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawGameOver;
|
|
DisposeCreatureList(level.enemyList);
|
|
g.score := 0;
|
|
g.life := StartLifeN;
|
|
while (g.curState = gameOver) and not g.shutdown do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level)
|
|
end;
|
|
EraseGameOver;
|
|
if g.curState = gameLevelAnnounce then
|
|
begin
|
|
InitLevel(level, enemyPack1)
|
|
end
|
|
else
|
|
begin
|
|
g.levelInited := false;
|
|
DisposeCreatureList(level.enemyList)
|
|
end;
|
|
end;
|
|
|
|
procedure GameCutPart(var g: gameState; var level: levelState);
|
|
var
|
|
beforeCut: integer;
|
|
begin
|
|
beforeCut := level.cut;
|
|
SetArenaBorder(level.t, level.a);
|
|
ArenaCutPart(level.h, level.t, level.cut, level.a);
|
|
FillCompleteBar(level.cut);
|
|
g.score := g.score + (level.cut - beforeCut);
|
|
DrawScore(g.score)
|
|
end;
|
|
|
|
procedure GameNextLevel(var g: gameState; var level: levelState);
|
|
begin
|
|
g.level := g.level + 1;
|
|
DisposeCreatureList(level.enemyList);
|
|
if g.level > LevelCount then
|
|
begin
|
|
g.levelInited := false;
|
|
g.curState := gameComplete
|
|
end
|
|
else
|
|
begin
|
|
g.curState := gameLevelComplete
|
|
end
|
|
end;
|
|
|
|
procedure GameKillHamster(var g: gameState; var level: levelState);
|
|
begin
|
|
if g.life <= 0 then
|
|
begin
|
|
g.curState := gameOver;
|
|
Exit
|
|
end;
|
|
DecreaseLife(g.life);
|
|
KillHamster(level.h, level.t, level.a);
|
|
DrawAliveEnemies(level.enemyList);
|
|
level.h.alive := true
|
|
end;
|
|
|
|
procedure PollGameKeys(var g: gameState; var level: levelState);
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := 1 to (MoveDelayMs div KeyDelayMs) do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level);
|
|
if g.curState = gamePause then
|
|
break
|
|
end
|
|
end;
|
|
|
|
procedure MakeEnemyTurnStages(var level: levelState);
|
|
begin
|
|
KillCapturedEnemies(level.a, level.enemyList);
|
|
TurnStubbornEnemies(level.a, level.enemyList);
|
|
EraseEnemies(level.a, level.enemyList);
|
|
MakeEnemySteps(level.a, level.h, level.t, level.enemyList);
|
|
|
|
UpdateEnemyStates(level.enemyList);
|
|
|
|
DrawAliveEnemies(level.enemyList)
|
|
end;
|
|
|
|
procedure MakeHamsterTurnStages(var g: gameState; var level: levelState);
|
|
begin
|
|
if not level.h.alive then
|
|
GameKillHamster(g, level);
|
|
if g.curState = gameOver then
|
|
exit;
|
|
if not HamsterStepPossible(level.h, level.t, level.a) then
|
|
StopCreature(level.h);
|
|
if not ((level.h.dX = 0) and (level.h.dY = 0)) then
|
|
MakeHamsterStep(level.h, level.t, level.a);
|
|
DrawCreature(level.h)
|
|
end;
|
|
|
|
procedure LevelLoop(var g: gameState; var level: levelState);
|
|
begin
|
|
while (g.curState = gameLevelLoop) and not g.shutdown do
|
|
begin
|
|
PollGameKeys(g, level);
|
|
if g.curState = gamePause then
|
|
break;
|
|
if ArenaSplited(level.h, level.t, level.a) then
|
|
GameCutPart(g, level);
|
|
if IsLevelComplete(level) then
|
|
begin
|
|
GameNextLevel(g, level);
|
|
if g.curState = gameComplete then
|
|
EraseLevel;
|
|
break
|
|
end;
|
|
MakeEnemyTurnStages(level);
|
|
MakeHamsterTurnStages(g, level);
|
|
if g.curState = gameOver then
|
|
begin
|
|
EraseLevel;
|
|
break
|
|
end
|
|
end
|
|
end;
|
|
|
|
procedure RunLevelState(var g: gameState; var level: levelState);
|
|
begin
|
|
if g.newGame then
|
|
begin
|
|
g.levelInited := true;
|
|
g.level := 1;
|
|
g.life := StartLifeN;
|
|
g.newGame := false;
|
|
g.score := 0
|
|
end;
|
|
g.curState := gameLevelLoop;
|
|
InitLevel(level, enemyPack1);
|
|
DrawLevel(level, g.life, g.score);
|
|
LevelLoop(g, level)
|
|
end;
|
|
|
|
procedure UnpauseLevel(var g: gameState; var level: levelState);
|
|
begin
|
|
if level.unpause then
|
|
level.unpause := false
|
|
else
|
|
DrawLevel(level, g.life, g.score);
|
|
g.curState := gameLevelLoop;
|
|
LevelLoop(g, level)
|
|
end;
|
|
|
|
procedure ContinueLevel(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawLevel(level, g.life, g.score);
|
|
g.curState := gamePause
|
|
end;
|
|
|
|
procedure RunMenuState(var g: gameState; var level: levelState);
|
|
var
|
|
prevMenu: boolean = false;
|
|
begin
|
|
g.curState := gameMenu;
|
|
while (g.curState = gameMenu) and not g.shutdown do
|
|
begin
|
|
if (g.curState = gameMenu) and not prevMenu then
|
|
begin
|
|
DrawMenu(g);
|
|
prevMenu := true
|
|
end;
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level);
|
|
if (g.curState <> gameMenu) and prevMenu then
|
|
begin
|
|
EraseMenu;
|
|
prevMenu := false
|
|
end;
|
|
if (g.curState <> gameMenu) then
|
|
if g.shutdown then
|
|
break
|
|
end
|
|
end;
|
|
|
|
procedure RunAnnounceState(var g: gameState; var level: levelState);
|
|
var
|
|
i: integer;
|
|
begin
|
|
DrawAnnounce(g.level);
|
|
for i := 1 to AnnounceDelayMs div KeyDelayMs do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level);
|
|
if g.shutdown then
|
|
exit;
|
|
if g.skipScene then
|
|
break
|
|
end;
|
|
g.skipScene := false;
|
|
g.curState := gameStartLevel;
|
|
EraseAnnounce(g.level)
|
|
end;
|
|
|
|
procedure RunLevelCompleteState(var g: gameState; var level: levelState);
|
|
var
|
|
i: integer;
|
|
begin
|
|
FillCellsCapture(level.a);
|
|
DrawCreature(level.h);
|
|
for i := 1 to LevelCompleteDelayMs div KeyDelayMs do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level);
|
|
if g.shutdown then
|
|
exit;
|
|
if g.skipScene then
|
|
break
|
|
end;
|
|
g.skipScene := false;
|
|
g.curState := gameLevelAnnounce;
|
|
EraseLevel
|
|
end;
|
|
|
|
procedure RunGameCompleteState(var g: gameState; var level: levelState);
|
|
begin
|
|
DrawGameComplete(g.score);
|
|
while (g.curState = gameComplete) and not g.shutdown do
|
|
begin
|
|
delay(KeyDelayMs);
|
|
if keypressed then
|
|
HandleKey(g, level)
|
|
end;
|
|
EraseLevel
|
|
end;
|
|
|
|
procedure MainLoop(var g: gameState);
|
|
var
|
|
level: levelState;
|
|
begin
|
|
while not g.shutdown do
|
|
case g.curState of
|
|
gameLevelAnnounce:
|
|
RunAnnounceState(g, level);
|
|
gameExit:
|
|
RunExitState(g, level);
|
|
gameKeyInfo:
|
|
RunInfoState(g, level);
|
|
gamePause:
|
|
RunPauseState(g, level);
|
|
gameStartLevel:
|
|
RunLevelState(g, level);
|
|
gameUnpauseLevel:
|
|
UnpauseLevel(g, level);
|
|
gameContinueLevel:
|
|
ContinueLevel(g, level);
|
|
gameOver:
|
|
RunGameOverState(g, level);
|
|
gameMenu:
|
|
RunMenuState(g, level);
|
|
gameLevelComplete:
|
|
RunLevelCompleteState(g, level);
|
|
gameComplete:
|
|
RunGameCompleteState(g, level)
|
|
end;
|
|
EraseAll
|
|
end;
|
|
|
|
procedure NextExitState(var g: gameState);
|
|
begin
|
|
if not g.curExit then
|
|
g.curExit := true
|
|
else
|
|
g.curExit := false
|
|
end;
|
|
|
|
procedure PreviousExitState(var g: gameState);
|
|
begin
|
|
if g.curExit then
|
|
g.curExit := false
|
|
else
|
|
g.curExit := true
|
|
end;
|
|
|
|
end.
|