From 3542861603e1890ab0765deb37bf997a434f9c26 Mon Sep 17 00:00:00 2001 From: Jon Thysell Date: Tue, 30 Nov 2021 18:38:36 -0800 Subject: [PATCH] Added level select scene and more I've added a new level select scene after picking your set, and I've updated the engine to store the result of each puzzle separately. The level select is paged (can't fit all 50 on one screen), so needed to add a new "previous button" graphic. Added confirmation prompts, so I can prompt before quitting, and for a new menu option to clear your scores if you want to reset your progress. Progress is still not persisted when you exit, as I haven't figured out how to do that yet. I've also rearranged the title screen a bit. --- assets/prev.gif | Bin 0 -> 85 bytes src/Bitmaps.c | 10 +- src/Bitmaps.h | 1 + src/GameEndScene.c | 4 +- src/GameEngine.c | 83 ++++++++++++++++- src/GameEngine.h | 17 +++- src/GameWindow.c | 28 +++++- src/GameWindow.h | 23 +++-- src/LevelEndScene.c | 4 +- src/LevelSelectScene.c | 206 +++++++++++++++++++++++++++++++++++++++++ src/LevelSelectScene.h | 13 +++ src/Levels.c | 2 - src/Levels.h | 2 +- src/MacCommon.c | 15 +++ src/MacCommon.h | 4 +- src/MacLO.c | 22 ++++- src/MacLO.pi.bin | Bin 16000 -> 16128 bytes src/MacLO.pi.rsrc.bin | Bin 35712 -> 35968 bytes src/PlayScene.c | 7 +- src/Scenes.h | 11 +++ src/TitleScene.c | 8 +- 21 files changed, 422 insertions(+), 38 deletions(-) create mode 100644 assets/prev.gif create mode 100644 src/LevelSelectScene.c create mode 100644 src/LevelSelectScene.h diff --git a/assets/prev.gif b/assets/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..42cb4ed8c43111c76b9bdc85bc236abc8c63d61a GIT binary patch literal 85 zcmZ?wbhEHb6kyPrevButtonPict = Bitmaps_GetPict(baseResID, PrevButtonPictResID); + if (pBitmaps->PrevButtonPict == nil) + { + ShowError("\pPrev button PICT resource missing!", true); + } + // Load next button pBitmaps->NextButtonPict = Bitmaps_GetPict(baseResID, NextButtonPictResID); if (pBitmaps->NextButtonPict == nil) diff --git a/src/Bitmaps.h b/src/Bitmaps.h index 8eb89ae..15dcdbd 100644 --- a/src/Bitmaps.h +++ b/src/Bitmaps.h @@ -17,6 +17,7 @@ typedef struct sBitmaps PicHandle BCharPict; PicHandle SlashCharPict; PicHandle StarPicts[StarPictCount]; + PicHandle PrevButtonPict; PicHandle NextButtonPict; PicHandle RetryButtonPict; PicHandle SoundOffPict; diff --git a/src/GameEndScene.c b/src/GameEndScene.c index 02ca7d6..a9758e7 100644 --- a/src/GameEndScene.c +++ b/src/GameEndScene.c @@ -23,7 +23,7 @@ void GameEndScene_Init(GameWindow *pGameWindow) CenterRect(&r, &(pGameWindow->GameEndScene.SetRect)); // Setup score - Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale, &(pGameWindow->GameEndScene.ScoreRect)); + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale, &(pGameWindow->GameEndScene.ScoreRect)); GetScaledPicFrame(pGameWindow->Bitmaps.SlashCharPict, ScoreTextScale, &r); ConcatenateRect(&(pGameWindow->GameEndScene.ScoreRect), &r, &(pGameWindow->GameEndScene.ScoreRect)); Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale, &r); @@ -51,7 +51,7 @@ void GameEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh) // Draw score MoveTo(pGameWindow->GameEndScene.ScoreRect.left, pGameWindow->GameEndScene.ScoreRect.top); - Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale); Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale); Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale); } diff --git a/src/GameEngine.c b/src/GameEngine.c index 7357377..100540d 100644 --- a/src/GameEngine.c +++ b/src/GameEngine.c @@ -16,24 +16,73 @@ void GameEngine_LoadLevel(GameEngine *pGameEngine, const int8_t level, const boo uint8_t GameEngine_CalculateHalfStars(const uint16_t par, const uint16_t moves); void GameEngine_ToggleSingleLight(GameEngine *pGameEngine, const int8_t x, const int8_t y); +void GameEngine_Init(GameEngine *pGameEngine) +{ + int8_t level; + + for (level = 0; level < LevelCount; level++) + { + // TODO: Load actual scores + pGameEngine->ScoresA[level] = 1; + pGameEngine->ScoresB[level] = 0; + } + + pGameEngine->Level = -1; + pGameEngine->SetB = false; + pGameEngine->PreviousLights = 0; + pGameEngine->Lights = 0; + pGameEngine->Par = 0; + pGameEngine->Moves = 0; +} + void GameEngine_NewGame(GameEngine *pGameEngine, const bool setB) { - pGameEngine->Score = 0; - GameEngine_LoadLevel(pGameEngine, 0, setB); + pGameEngine->SetB = setB; + GameEngine_StartLevel(pGameEngine, 0); +} + +void GameEngine_ResetGame(GameEngine *pGameEngine) +{ + int8_t level; + + for (level = 0; level < LevelCount; level++) + { + pGameEngine->ScoresA[level] = 0; + pGameEngine->ScoresB[level] = 0; + } +} + +void GameEngine_StartLevel(GameEngine *pGameEngine, const uint8_t level) +{ + GameEngine_LoadLevel(pGameEngine, level, pGameEngine->SetB); +} + +void GameEngine_CompleteLevel(GameEngine *pGameEngine) +{ + if (GameEngine_IsCompleted(pGameEngine)) + { + if (pGameEngine->SetB) + { + pGameEngine->ScoresB[pGameEngine->Level] = max(GameEngine_GetHalfStars(pGameEngine), pGameEngine->ScoresB[pGameEngine->Level]); + } + else + { + pGameEngine->ScoresA[pGameEngine->Level] = max(GameEngine_GetHalfStars(pGameEngine), pGameEngine->ScoresA[pGameEngine->Level]); + } + } } void GameEngine_NextLevel(GameEngine *pGameEngine) { if (GameEngine_IsCompleted(pGameEngine)) { - pGameEngine->Score += GameEngine_GetHalfStars(pGameEngine); - GameEngine_LoadLevel(pGameEngine, pGameEngine->Level + 1, pGameEngine->SetB); + GameEngine_StartLevel(pGameEngine, pGameEngine->Level + 1); } } void GameEngine_ResetLevel(GameEngine *pGameEngine) { - GameEngine_LoadLevel(pGameEngine, pGameEngine->Level, pGameEngine->SetB); + GameEngine_StartLevel(pGameEngine, pGameEngine->Level); } void GameEngine_LoadLevel(GameEngine *pGameEngine, const int8_t level, const bool setB) @@ -56,6 +105,11 @@ bool GameEngine_GetLight(const GameEngine *pGameEngine, const int8_t x, const in return false; } +bool GameEngine_IsEnabled(const GameEngine *pGameEngine, const int8_t level) +{ + return level == 0 || (level < LevelCount && GameEngine_GetScore(pGameEngine, level - 1) > 0); +} + bool GameEngine_IsCompleted(const GameEngine *pGameEngine) { return pGameEngine->Lights == 0; @@ -71,6 +125,25 @@ uint8_t GameEngine_GetHalfStars(const GameEngine *pGameEngine) return GameEngine_CalculateHalfStars(pGameEngine->Par, pGameEngine->Moves); } +uint8_t GameEngine_GetScore(const GameEngine *pGameEngine, const int8_t level) +{ + return pGameEngine->SetB ? pGameEngine->ScoresB[level] : pGameEngine->ScoresA[level]; +} + +uint16_t GameEngine_GetTotalScore(const GameEngine *pGameEngine) +{ + uint8_t level; + uint16_t score; + + score = 0; + for (level = 0; level < LevelCount; level++) + { + score += GameEngine_GetScore(pGameEngine, level); + } + + return score; +} + uint8_t GameEngine_CalculateHalfStars(const uint16_t par, const uint16_t moves) { uint8_t halfStarsLost = moves <= par ? 0 : max(0, (1 + moves - par) / 2); diff --git a/src/GameEngine.h b/src/GameEngine.h index 2a2e3d4..de76126 100644 --- a/src/GameEngine.h +++ b/src/GameEngine.h @@ -16,7 +16,8 @@ extern const uint16_t PerfectScore; typedef struct GameEngine { - uint16_t Score; + uint8_t ScoresA[LevelCount]; + uint8_t ScoresB[LevelCount]; int8_t Level; bool SetB; uint32_t Lights; @@ -25,8 +26,16 @@ typedef struct GameEngine uint16_t Moves; } GameEngine; +void GameEngine_Init(GameEngine *pGameEngine); + void GameEngine_NewGame(GameEngine *pGameEngine, const bool setB); +void GameEngine_ResetGame(GameEngine *pGameEngine); + +void GameEngine_StartLevel(GameEngine *pGameEngine, const uint8_t level); + +void GameEngine_CompleteLevel(GameEngine *pGameEngine); + void GameEngine_NextLevel(GameEngine *pGameEngine); void GameEngine_ResetLevel(GameEngine *pGameEngine); @@ -35,10 +44,16 @@ bool GameEngine_GetLight(const GameEngine *pGameEngine, const int8_t x, const in bool GameEngine_IsCompleted(const GameEngine *pGameEngine); +bool GameEngine_IsEnabled(const GameEngine *pGameEngine, const int8_t level); + bool GameEngine_IsGameOver(const GameEngine *pGameEngine); uint8_t GameEngine_GetHalfStars(const GameEngine *pGameEngine); +uint8_t GameEngine_GetScore(const GameEngine *pGameEngine, const int8_t level); + +uint16_t GameEngine_GetTotalScore(const GameEngine *pGameEngine); + void GameEngine_ToggleLights(GameEngine *pGameEngine, const int8_t x, const int8_t y); bool GameEngine_LightChanged(const GameEngine *pGameEngine, const int8_t x, const int8_t y); diff --git a/src/GameWindow.c b/src/GameWindow.c index e891366..ff56839 100644 --- a/src/GameWindow.c +++ b/src/GameWindow.c @@ -3,6 +3,7 @@ #include "GameWindow.h" #include "TitleScene.h" +#include "LevelSelectScene.h" #include "PlayScene.h" #include "LevelEndScene.h" #include "GameEndScene.h" @@ -21,15 +22,18 @@ void GameWindow_Init(GameWindow *pGameWindow) ShowError("\pGameWindow WIND resource missing!", true); } + // Initialize window buffer + WindowBuffer_Init(&(pGameWindow->WindowBuffer), pGameWindow->Window); + + // Initialize game engine + GameEngine_Init(&(pGameWindow->Engine)); + // Load PICT resources Bitmaps_Init(&(pGameWindow->Bitmaps)); // Load snd resources Sounds_Init(&(pGameWindow->Sounds)); - // Initialize window buffer - WindowBuffer_Init(&(pGameWindow->WindowBuffer), pGameWindow->Window); - // Setup graphics before first draw SetPort(pGameWindow->Window); FillRect(&(pGameWindow->Window->portRect), WindowPattern); @@ -58,6 +62,9 @@ void GameWindow_Draw(const GameWindow *pGameWindow, bool fullRefresh) case Title: TitleScene_Draw(pGameWindow, fullRefresh); break; + case LevelSelect: + LevelSelectScene_Draw(pGameWindow, fullRefresh); + break; case Play: PlayScene_Draw(pGameWindow, fullRefresh); break; @@ -81,6 +88,9 @@ void GameWindow_Click(GameWindow *pGameWindow, const Point *pPosition) case Title: TitleScene_Click(pGameWindow, pPosition); break; + case LevelSelect: + LevelSelectScene_Click(pGameWindow, pPosition); + break; case Play: PlayScene_Click(pGameWindow, pPosition); break; @@ -100,6 +110,9 @@ void GameWindow_SetScene(GameWindow *pGameWindow, const SceneId sceneId) case Title: TitleScene_Init(pGameWindow); break; + case LevelSelect: + LevelSelectScene_Init(pGameWindow); + break; case Play: PlayScene_Init(pGameWindow); break; @@ -119,3 +132,12 @@ void GameWindow_Show(const GameWindow *pGameWindow) { ShowWindow(pGameWindow->Window); } + +void GameWindow_ClearScores(GameWindow *pGameWindow) +{ + if (ShowConfirm("\pClear all scores?")) + { + GameEngine_ResetGame(&(pGameWindow->Engine)); + GameWindow_SetScene(pGameWindow, Title); + } +} diff --git a/src/GameWindow.h b/src/GameWindow.h index 8248fcd..8ef5ce8 100644 --- a/src/GameWindow.h +++ b/src/GameWindow.h @@ -15,16 +15,17 @@ typedef struct sGameWindow { - WindowPtr Window; - WindowBuffer WindowBuffer; - GameEngine Engine; - Bitmaps Bitmaps; - Sounds Sounds; - SceneId CurrentSceneId; - TitleScene TitleScene; - PlayScene PlayScene; - LevelEndScene LevelEndScene; - GameEndScene GameEndScene; + WindowPtr Window; + WindowBuffer WindowBuffer; + GameEngine Engine; + Bitmaps Bitmaps; + Sounds Sounds; + SceneId CurrentSceneId; + TitleScene TitleScene; + LevelSelectScene LevelSelectScene; + PlayScene PlayScene; + LevelEndScene LevelEndScene; + GameEndScene GameEndScene; } GameWindow; void GameWindow_Init(GameWindow *pGameWindow); @@ -34,4 +35,6 @@ void GameWindow_Click(GameWindow *pGameWindow, const Point *pPosition); void GameWindow_SetScene(GameWindow *pGameWindow, const SceneId sceneId); void GameWindow_Show(const GameWindow *pGameWindow); +void GameWindow_ClearScores(GameWindow *pGameWindow); + #endif diff --git a/src/LevelEndScene.c b/src/LevelEndScene.c index 48b40f2..4a2f364 100644 --- a/src/LevelEndScene.c +++ b/src/LevelEndScene.c @@ -28,7 +28,7 @@ void LevelEndScene_Init(GameWindow *pGameWindow) CenterRect(&r, &(pGameWindow->LevelEndScene.HalfStarsRect)); // Setup score - Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale, &(pGameWindow->LevelEndScene.ScoreRect)); + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale, &(pGameWindow->LevelEndScene.ScoreRect)); GetScaledPicFrame(pGameWindow->Bitmaps.SlashCharPict, ScoreTextScale, &r); ConcatenateRect(&(pGameWindow->LevelEndScene.ScoreRect), &r, &(pGameWindow->LevelEndScene.ScoreRect)); Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale, &r); @@ -70,7 +70,7 @@ void LevelEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh) // Draw score MoveTo(pGameWindow->LevelEndScene.ScoreRect.left, pGameWindow->LevelEndScene.ScoreRect.top); - Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale); Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale); Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale); diff --git a/src/LevelSelectScene.c b/src/LevelSelectScene.c new file mode 100644 index 0000000..aae1e9c --- /dev/null +++ b/src/LevelSelectScene.c @@ -0,0 +1,206 @@ +// Copyright (c) Jon Thysell +// Licensed under the MIT License. + +#include "LevelSelectScene.h" + +#define SetTextScale 3 +#define ScoreTextScale 1 + +#define LevelMargin 10 +#define LevelWidth 80 +#define LevelHeight 60 +#define LevelRowCount 2 +#define LevelColumnCount 5 +#define LevelsPerPage (LevelRowCount * LevelColumnCount) +#define LevelTextScale 1 + +void LevelSelectScene_Init(GameWindow *pGameWindow) +{ + Rect r; + + const Rect *pContentRect = &(pGameWindow->Window->portRect); + + pGameWindow->LevelSelectScene.PageNumber = pGameWindow->Engine.Level / LevelsPerPage; + + // Setup set + GetScaledPicFrame(pGameWindow->Engine.SetB ? pGameWindow->Bitmaps.BCharPict : pGameWindow->Bitmaps.ACharPict, SetTextScale, &(pGameWindow->LevelSelectScene.SetRect)); + + GetBoxRect(pContentRect, Top, &r); + //GetBoxRect(&r, Top, &r); + CenterRect(&r, &(pGameWindow->LevelSelectScene.SetRect)); + + // Setup score + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale, &(pGameWindow->LevelSelectScene.ScoreRect)); + GetScaledPicFrame(pGameWindow->Bitmaps.SlashCharPict, ScoreTextScale, &r); + ConcatenateRect(&(pGameWindow->LevelSelectScene.ScoreRect), &r, &(pGameWindow->LevelSelectScene.ScoreRect)); + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale, &r); + ConcatenateRect(&(pGameWindow->LevelSelectScene.ScoreRect), &r, &(pGameWindow->LevelSelectScene.ScoreRect)); + + GetBoxRect(pContentRect, Bottom, &r); + //GetBoxRect(&r, Bottom, &r); + CenterRect(&r, &(pGameWindow->LevelSelectScene.ScoreRect)); + + // Setup previous button + GetPictureRect(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelSelectScene.PrevButtonRect)); + + GetBoxRect(pContentRect, BottomLeft, &r); + //GetBoxRect(&r, Bottom, &r); + CenterRect(&r, &(pGameWindow->LevelSelectScene.PrevButtonRect)); + + // Setup next button + GetPictureRect(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelSelectScene.NextButtonRect)); + + GetBoxRect(pContentRect, BottomRight, &r); + //GetBoxRect(&r, Bottom, &r); + CenterRect(&r, &(pGameWindow->LevelSelectScene.NextButtonRect)); + + // Setup LevelGrid + pGameWindow->LevelSelectScene.LevelGridRect.top = 0; + pGameWindow->LevelSelectScene.LevelGridRect.bottom = (LevelRowCount * (LevelHeight + LevelMargin)) - LevelMargin; + pGameWindow->LevelSelectScene.LevelGridRect.left = 0; + pGameWindow->LevelSelectScene.LevelGridRect.right = (LevelColumnCount * (LevelWidth + LevelMargin)) - LevelMargin; + CenterRect(pContentRect, &(pGameWindow->LevelSelectScene.LevelGridRect)); +} + +void LevelSelectScene_SetLevelRect(const GameWindow *pGameWindow, Rect *pRect, const int8_t i) +{ + int8_t c, r; + c = i % LevelColumnCount; + r = i / LevelColumnCount; + + pRect->top = pGameWindow->LevelSelectScene.LevelGridRect.top + (r * (LevelHeight + LevelMargin)); + pRect->bottom = pRect->top + LevelHeight; + pRect->left = pGameWindow->LevelSelectScene.LevelGridRect.left + (c * (LevelWidth + LevelMargin)); + pRect->right = pRect->left + LevelWidth; +} + +void LevelSelectScene_Draw(const GameWindow *pGameWindow, bool fullRefresh) +{ + int8_t i, level; + Rect levelRect, levelTextRect, levelHalfStarsRect, r; + + // Draw set + if (fullRefresh) + { + MoveTo(pGameWindow->LevelSelectScene.SetRect.left, pGameWindow->LevelSelectScene.SetRect.top); + if (pGameWindow->Engine.SetB) + { + Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), SetTextScale); + } + else + { + Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), SetTextScale); + } + } + + // Draw score + if (fullRefresh) + { + MoveTo(pGameWindow->LevelSelectScene.ScoreRect.left, pGameWindow->LevelSelectScene.ScoreRect.top); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale); + Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale); + } + + // Draw prev button + if (fullRefresh) + { + DrawPicture(pGameWindow->Bitmaps.PrevButtonPict, &(pGameWindow->LevelSelectScene.PrevButtonRect)); + } + + // Draw next button + if ((pGameWindow->LevelSelectScene.PageNumber + 1) * LevelsPerPage < LevelCount) + { + DrawPicture(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelSelectScene.NextButtonRect)); + } + else + { + FillRect(&(pGameWindow->LevelSelectScene.NextButtonRect), WindowPattern); + } + + // Draw levels + for (i = 0; i < LevelsPerPage; i++) + { + level = i + (LevelsPerPage * pGameWindow->LevelSelectScene.PageNumber); + + LevelSelectScene_SetLevelRect(pGameWindow, &levelRect, i); + + if (!fullRefresh) + { + FillRect(&levelRect, WindowPattern); + } + + if (level < LevelCount) + { + // Draw level number + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), 1 + level, LevelTextScale, &levelTextRect); + + GetBoxRect(&levelRect, Top, &r); + GetBoxRect(&r, Bottom, &r); + CenterRect(&r, &levelTextRect); + + MoveTo(levelTextRect.left, levelTextRect.top); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), 1 + level, LevelTextScale); + + if (GameEngine_IsEnabled(&(pGameWindow->Engine), level)) + { + // Draw half-stars + Bitmaps_GetHalfStarsRect(&(pGameWindow->Bitmaps), GameEngine_GetScore(&(pGameWindow->Engine), level), MaxStars, LevelTextScale, &levelHalfStarsRect); + + GetBoxRect(&levelRect, Bottom, &r); + GetBoxRect(&r, Top, &r); + CenterRect(&r, &levelHalfStarsRect); + + MoveTo(levelHalfStarsRect.left, levelHalfStarsRect.top); + Bitmaps_DrawHalfStars(&(pGameWindow->Bitmaps), GameEngine_GetScore(&(pGameWindow->Engine), level), MaxStars, LevelTextScale); + } + } + } +} + +void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition) +{ + int8_t i, level; + Rect levelRect; + + if (PtInRect(*pPosition, &(pGameWindow->LevelSelectScene.LevelGridRect))) + { + // Click within LevelGrid + for (i = 0; i < LevelsPerPage; i++) + { + level = i + (LevelsPerPage * pGameWindow->LevelSelectScene.PageNumber); + + if (GameEngine_IsEnabled(&(pGameWindow->Engine), level)) + { + LevelSelectScene_SetLevelRect(pGameWindow, &levelRect, i); + + if (PtInRect(*pPosition, &levelRect)) + { + GameEngine_StartLevel(&(pGameWindow->Engine), level); + GameWindow_SetScene(pGameWindow, Play); + break; + } + } + } + } + else if (PtInRect(*pPosition, &(pGameWindow->LevelSelectScene.PrevButtonRect))) + { + if (pGameWindow->LevelSelectScene.PageNumber > 0) + { + pGameWindow->LevelSelectScene.PageNumber--; + GameWindow_Draw(pGameWindow, false); + } + else + { + GameWindow_SetScene(pGameWindow, Title); + } + } + else if (PtInRect(*pPosition, &(pGameWindow->LevelSelectScene.NextButtonRect))) + { + if ((pGameWindow->LevelSelectScene.PageNumber + 1) * LevelsPerPage < LevelCount) + { + pGameWindow->LevelSelectScene.PageNumber++; + GameWindow_Draw(pGameWindow, false); + } + } +} diff --git a/src/LevelSelectScene.h b/src/LevelSelectScene.h new file mode 100644 index 0000000..b552297 --- /dev/null +++ b/src/LevelSelectScene.h @@ -0,0 +1,13 @@ +// Copyright (c) Jon Thysell +// Licensed under the MIT License. + +#ifndef LEVELSELECTSCENE_H +#define LEVELSELECTSCENE_H + +#include "GameWindow.h" + +void LevelSelectScene_Init(GameWindow *pGameWindow); +void LevelSelectScene_Draw(const GameWindow *pGameWindow, bool fullRefresh); +void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition); + +#endif diff --git a/src/Levels.c b/src/Levels.c index bbe790b..182cbce 100644 --- a/src/Levels.c +++ b/src/Levels.c @@ -4,8 +4,6 @@ #include "Common.h" #include "Levels.h" -const int8_t LevelCount = 50; - const uint32_t Levels_LightsA[] = { 0x0005400UL, 0x15A82B5UL, 0x0ADEF6AUL, 0x1B88360UL, 0x1BC5EEFUL, 0x0EAD400UL, 0x0F8C62FUL, 0x0AAA880UL, 0x07D3BEAUL, 0x00039CEUL, 0x0EAD6B5UL, 0x0A76D5FUL, 0x022AA88UL, 0x0210800UL, 0x0010040UL, 0x1F08421UL, 0x1F71000UL, 0x0455544UL, diff --git a/src/Levels.h b/src/Levels.h index cd8a216..0e42a3c 100644 --- a/src/Levels.h +++ b/src/Levels.h @@ -7,7 +7,7 @@ #include "stdint.h" #include "stdbool.h" -extern const int8_t LevelCount; +#define LevelCount 50 uint32_t Levels_GetLightsForLevel(const int8_t level, const bool setB); diff --git a/src/MacCommon.c b/src/MacCommon.c index c24bc29..ce23085 100644 --- a/src/MacCommon.c +++ b/src/MacCommon.c @@ -3,6 +3,11 @@ #include "MacCommon.h" +#define ErrorAlertResID BaseResID +#define ConfirmAlertResID (ErrorAlertResID + 1) + +#define YesResult 2 + const int16_t MonthOffset[] = { 0, // Jan 31, // Feb @@ -29,6 +34,16 @@ void ShowError(Str255 message, bool isFatal) } } +bool ShowConfirm(Str255 message) +{ + uint16_t result; + + ParamText(message, EmptyString, EmptyString, EmptyString); + result = CautionAlert(ConfirmAlertResID, NilFilterProc); + + return result == YesResult; +} + void CenterRect(const Rect *pOuterRect, Rect *pInnerRect) { CenterRectH(pOuterRect, pInnerRect); diff --git a/src/MacCommon.h b/src/MacCommon.h index da8cb26..c344fd8 100644 --- a/src/MacCommon.h +++ b/src/MacCommon.h @@ -12,8 +12,6 @@ #define EmptyString "\p" #define NilFilterProc nil -#define ErrorAlertResID BaseResID - extern const int16_t MonthOffset[]; typedef enum eBoxAlignment @@ -33,6 +31,8 @@ pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, int16_t newItem) = { 0x30 void ShowError(Str255 message, bool isFatal); +bool ShowConfirm(Str255 message); + void CenterRect(const Rect *pOuterRect, Rect *pInnerRect); void CenterRectH(const Rect *pOuterRect, Rect *pInnerRect); void CenterRectV(const Rect *pOuterRect, Rect *pInnerRect); diff --git a/src/MacLO.c b/src/MacLO.c index 1efcdb9..2a09934 100644 --- a/src/MacLO.c +++ b/src/MacLO.c @@ -8,8 +8,9 @@ #define AboutMenuItemID 1 #define GameMenuResID BaseResID+1 -#define NewMenuItemID 1 -#define QuitMenuItemID 3 +#define TitleMenuItemID 1 +#define ClearMenuItemID 2 +#define QuitMenuItemID 4 #define AboutDialogResID BaseResID #define AboutDialogOKID 1 @@ -29,6 +30,8 @@ void MacLO_LaunchAppleMenuItem(const int16_t item); void MacLO_HandleGameMenuChoice(const int16_t item); +void MacLO_Quit(); + void MacLO_ToolBoxInit() { MaxApplZone(); @@ -233,11 +236,22 @@ void MacLO_HandleGameMenuChoice(const int16_t item) { switch (item) { - case NewMenuItemID: + case TitleMenuItemID: GameWindow_SetScene(&gGameWindow, Title); break; + case ClearMenuItemID: + GameWindow_ClearScores(&gGameWindow); + break; case QuitMenuItemID: - gExitApp = true; + MacLO_Quit(); break; } } + +void MacLO_Quit() +{ + if (ShowConfirm("\pQuit MacLO?")) + { + gExitApp = true; + } +} diff --git a/src/MacLO.pi.bin b/src/MacLO.pi.bin index c2bc12a87fb9d65029240dfda7258e3f4ad47c94..cde80a918c78d152e94db4527d7b5dcffe467449 100644 GIT binary patch delta 3067 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 mryLf3Moprp3h-c-UrEiqTb||3z)z|R7asw7 zZVj2$V*XWD%MBeDczqX}uqw*)Lf>v&j{Y$5#y}JNs-|mY%_0n|AiH-fy$nVF7Y#du^Tl zR2==b0i)3<-NionG_+pr8APi@ARE^Zz_M_`*VZ;C+dRMy(eZjqBNTC{=~&Gf2DCTT zncE;l_wmN&lRSIsYn#p7M_cM^E!Zrk2JBrL6y*cF$kC9oqDJjm$;#rH8tt2bH!kM^ z>X8&{YSi&N#e2vLNfs444|5PQKnyV8Mar;1{YjG7y(0&bync~BNOIZ|^)xo2v5Ajr z)EVLjIHF-hkh`dF(U5LoLkk!}6MfE;tLRflpM0{Fd2|*|zs{ttWYTm$4KLu64Gq6! zA{Ij#6FsN?BjxjnQ_M|rrh$4a>0gk+hC&#Uu?6x4KJmvO7kbEV;Fnxf#afHFmEd1$ zRJYG!SRRUFGi%5nkwT+nA1CK{iV4ZX@^iQ~V6TSV9n`;U(ggNv us60%=D@{R_22(lN75+K8HJleL*|5IWVejtg?_1l~)m(C19SnCgTK)oVR!byHSyIy@cJ%-BEE|!@gP>gLk=Fi)PraRuZkBf&zX6-xUlfe%+Bnx>LW#cpj1ya zm(7*A)zOvZwf5TPZCf?XI10D~?)efx3Ae*vtNKN6?NzUP+1f%?fU>CzQ7~2U-X%11 z)EyE*2!Li60W8b<;~Y6Vom>}7&ISSjh~Y>-Rh{Fx3o|f|GK(z}S3Un<<{R5Y!;D*l?{;c!T*K z+fDD#eYV$sKo8j7P)FlX=)y;Q#NI`;l;BD@jLRYXDi7u-F)!`&t}FDYhKp>?%Hzdu zr(J+ z@)#~q(2ie^>n3@33%ksI%dU_+RQ4jd^9cJE`xpB*dCxQU9dg$cyGq{cVb|Do_FZ%y z_7ynLsj$C?y~O^)zDEw(L+@Lbg#Oo*-?2t6hS`6~+E=m!I3&SWWeez&5WK`zpkKnk iY4XPSY$}PlayScene.HalfStarsRect)); // Setup score - Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale, &(pGameWindow->PlayScene.ScoreRect)); + Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale, &(pGameWindow->PlayScene.ScoreRect)); GetScaledPicFrame(pGameWindow->Bitmaps.SlashCharPict, ScoreTextScale, &r); ConcatenateRect(&(pGameWindow->PlayScene.ScoreRect), &r, &(pGameWindow->PlayScene.ScoreRect)); Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale, &r); @@ -144,7 +146,7 @@ void PlayScene_Draw(const GameWindow *pGameWindow, bool fullRefresh) if (fullRefresh) { MoveTo(pGameWindow->PlayScene.ScoreRect.left, pGameWindow->PlayScene.ScoreRect.top); - Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), pGameWindow->Engine.Score, ScoreTextScale); + Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale); Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale); Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale); } @@ -187,6 +189,7 @@ void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition) if (GameEngine_IsCompleted(&(pGameWindow->Engine))) { // Level was completed in the last click + GameEngine_CompleteLevel(&(pGameWindow->Engine)); GameWindow_Draw(pGameWindow, false); GameWindow_SetScene(pGameWindow, LevelEnd); Sounds_PlayDoneSnd(&(pGameWindow->Sounds)); diff --git a/src/Scenes.h b/src/Scenes.h index 597de22..be21742 100644 --- a/src/Scenes.h +++ b/src/Scenes.h @@ -9,6 +9,7 @@ typedef enum eSceneId { Title, + LevelSelect, Play, LevelEnd, GameEnd @@ -22,6 +23,16 @@ typedef struct sTitleScene Rect SoundButtonRect; } TitleScene; +typedef struct sLevelSelectScene +{ + int8_t PageNumber; + Rect SetRect; + Rect ScoreRect; + Rect PrevButtonRect; + Rect NextButtonRect; + Rect LevelGridRect; +} LevelSelectScene; + typedef struct sPlayScene { Rect PlayfieldRect; diff --git a/src/TitleScene.c b/src/TitleScene.c index ecc2e22..92e9995 100644 --- a/src/TitleScene.c +++ b/src/TitleScene.c @@ -14,7 +14,9 @@ void TitleScene_Init(GameWindow *pGameWindow) // Setup Title GetPictureRect(pGameWindow->Bitmaps.TitlePict, &(pGameWindow->TitleScene.TitleRect)); - CenterRect(pContentRect, &(pGameWindow->TitleScene.TitleRect)); + GetBoxRect(pContentRect, Center, &r); + GetBoxRect(&r, Top, &r); + CenterRect(&r, &(pGameWindow->TitleScene.TitleRect)); // Setup Set A GetScaledPicFrame(pGameWindow->Bitmaps.ACharPict, TitleTextScale, &(pGameWindow->TitleScene.SetARect)); @@ -55,12 +57,12 @@ void TitleScene_Click(GameWindow *pGameWindow, const Point *pPosition) if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetARect))) { GameEngine_NewGame(&(pGameWindow->Engine), false); - GameWindow_SetScene(pGameWindow, Play); + GameWindow_SetScene(pGameWindow, LevelSelect); } else if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetBRect))) { GameEngine_NewGame(&(pGameWindow->Engine), true); - GameWindow_SetScene(pGameWindow, Play); + GameWindow_SetScene(pGameWindow, LevelSelect); } else if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SoundButtonRect))) {