{ 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.