Added sound menu item and other optimizations

* Added sound menu which keeps in sync with pressing sound button
* Changed navigation behavior between scenes to go straight to next
  level if it hasn't been beaten, otherwise return to level select
  so the player can view their progress in the set
* More drawing optimizations
This commit is contained in:
Jon Thysell 2021-12-16 12:57:00 -08:00
parent cdf67289a9
commit e4cb9a7af8
15 changed files with 220 additions and 92 deletions

View File

@ -46,23 +46,29 @@ void GameEndScene_Init(GameWindow *pGameWindow)
void GameEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh)
{
// Draw set
MoveTo(pGameWindow->GameEndScene.SetRect.left, pGameWindow->GameEndScene.SetRect.top);
DrawScaledPic(pGameWindow->Bitmaps.StarPicts[StarPictCount - 1], SetTextScale);
if (pGameWindow->Engine.SetB)
if (fullRefresh)
{
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), SetTextScale);
MoveTo(pGameWindow->GameEndScene.SetRect.left, pGameWindow->GameEndScene.SetRect.top);
DrawScaledPic(pGameWindow->Bitmaps.StarPicts[StarPictCount - 1], SetTextScale);
if (pGameWindow->Engine.SetB)
{
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), SetTextScale);
}
else
{
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), SetTextScale);
}
DrawScaledPic(pGameWindow->Bitmaps.StarPicts[StarPictCount - 1], SetTextScale);
}
else
{
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), SetTextScale);
}
DrawScaledPic(pGameWindow->Bitmaps.StarPicts[StarPictCount - 1], SetTextScale);
// Draw score
MoveTo(pGameWindow->GameEndScene.ScoreRect.left, pGameWindow->GameEndScene.ScoreRect.top);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale);
Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale);
if (fullRefresh)
{
MoveTo(pGameWindow->GameEndScene.ScoreRect.left, pGameWindow->GameEndScene.ScoreRect.top);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale);
Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale);
}
}
void GameEndScene_Click(GameWindow *pGameWindow, const Point *pPosition)

View File

@ -7,8 +7,8 @@
* This file provides implementations for GameEngine.h.
*/
#include "Common.h"
#include "GameEngine.h"
#include "Common.h"
const int8_t PuzzleSize = 5;
@ -62,22 +62,8 @@ void GameEngine_Init(GameEngine *pGameEngine)
void GameEngine_NewGame(GameEngine *pGameEngine, const bool setB)
{
int8_t level, startLevel;
pGameEngine->SetB = setB;
startLevel = 0;
for (level = 0; level < LevelCount; level++)
{
// Find the first uncompleted level
if (GameEngine_GetScore(pGameEngine, level) == 0)
{
startLevel = level;
break;
}
}
GameEngine_StartLevel(pGameEngine, startLevel);
GameEngine_StartLevel(pGameEngine, 0);
}
void GameEngine_ResetGame(GameEngine *pGameEngine)
@ -115,7 +101,7 @@ void GameEngine_NextLevel(GameEngine *pGameEngine)
{
if (GameEngine_IsCompleted(pGameEngine))
{
GameEngine_StartLevel(pGameEngine, pGameEngine->Level + 1);
GameEngine_StartLevel(pGameEngine, (pGameEngine->Level + 1) % LevelCount);
}
}
@ -146,7 +132,7 @@ bool GameEngine_GetLight(const GameEngine *pGameEngine, const int8_t x, const in
bool GameEngine_IsEnabled(const GameEngine *pGameEngine, const int8_t level)
{
return level == 0 || (level < LevelCount && GameEngine_GetScore(pGameEngine, level - 1) > 0);
return level == 0 || (level < LevelCount && GameEngine_HasPlayedLevel(pGameEngine, level - 1));
}
bool GameEngine_IsCompleted(const GameEngine *pGameEngine)
@ -154,9 +140,14 @@ bool GameEngine_IsCompleted(const GameEngine *pGameEngine)
return pGameEngine->Lights == 0;
}
bool GameEngine_IsGameOver(const GameEngine *pGameEngine)
bool GameEngine_IsLastLevel(const GameEngine *pGameEngine)
{
return pGameEngine->Level >= LevelCount;
return pGameEngine->Level == LevelCount - 1;
}
bool GameEngine_HasPlayedLevel(const GameEngine *pGameEngine, const int8_t level)
{
return GameEngine_GetScore(pGameEngine, level) > 0;
}
uint8_t GameEngine_GetHalfStars(const GameEngine *pGameEngine)

View File

@ -110,11 +110,19 @@ bool GameEngine_IsCompleted(const GameEngine *pGameEngine);
bool GameEngine_IsEnabled(const GameEngine *pGameEngine, const int8_t level);
/**
* Gets whether or not the last level has just been completed.
* Gets whether the current level is the last of a set.
* @param pGameEngine The GameEngine.
* @return Whether or not the game is over.
* @return Whether the current level is the last of a set.
*/
bool GameEngine_IsGameOver(const GameEngine *pGameEngine);
bool GameEngine_IsLastLevel(const GameEngine *pGameEngine);
/**
* Gets whether the given level has been completed before.
* @param pGameEngine The GameEngine.
* @param level The level.
* @return Whether the given level has been completed before.
*/
bool GameEngine_HasPlayedLevel(const GameEngine *pGameEngine, const int8_t level);
/**
* Gets the number of half-stars the user stands to earn given the current

View File

@ -8,6 +8,7 @@
*/
#include "GameWindow.h"
#include "MacLO.h"
#include "TitleScene.h"
#include "LevelSelectScene.h"
#include "PlayScene.h"
@ -145,6 +146,13 @@ void GameWindow_Show(const GameWindow *pGameWindow)
ShowWindow(pGameWindow->Window);
}
void GameWindow_ToggleSound(GameWindow *pGameWindow)
{
pGameWindow->Sounds.Enabled = !pGameWindow->Sounds.Enabled;
MacLO_UpdateMenus();
GameWindow_Draw(pGameWindow, false);
}
void GameWindow_ClearScores(GameWindow *pGameWindow)
{
if (ShowConfirm("\pAre you sure you want to clear all scores?"))

View File

@ -75,6 +75,12 @@ void GameWindow_SetScene(GameWindow *pGameWindow, const SceneId sceneId);
*/
void GameWindow_Show(const GameWindow *pGameWindow);
/**
* Toggles whether sound is enabled/disabled for the GameWindow.
* @param pGameWindow The GameWindow.
*/
void GameWindow_ToggleSound(GameWindow *pGameWindow);
/**
* Resets the level scores of the GameWindow.
* @param pGameWindow The GameWindow.

View File

@ -64,54 +64,83 @@ void LevelEndScene_Init(GameWindow *pGameWindow)
void LevelEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh)
{
// Draw level
MoveTo(pGameWindow->LevelEndScene.LevelRect.left, pGameWindow->LevelEndScene.LevelRect.top);
if (pGameWindow->Engine.SetB)
if (fullRefresh)
{
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), LevelTextScale);
MoveTo(pGameWindow->LevelEndScene.LevelRect.left, pGameWindow->LevelEndScene.LevelRect.top);
if (pGameWindow->Engine.SetB)
{
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), LevelTextScale);
}
else
{
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), LevelTextScale);
}
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), 1 + pGameWindow->Engine.Level, LevelTextScale);
}
else
{
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), LevelTextScale);
}
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), 1 + pGameWindow->Engine.Level, LevelTextScale);
// Draw half-stars
MoveTo(pGameWindow->LevelEndScene.HalfStarsRect.left, pGameWindow->LevelEndScene.HalfStarsRect.top);
Bitmaps_DrawHalfStars(&(pGameWindow->Bitmaps), GameEngine_GetHalfStars(&(pGameWindow->Engine)), MaxStars, HalfStarScale);
if (fullRefresh)
{
MoveTo(pGameWindow->LevelEndScene.HalfStarsRect.left, pGameWindow->LevelEndScene.HalfStarsRect.top);
Bitmaps_DrawHalfStars(&(pGameWindow->Bitmaps), GameEngine_GetHalfStars(&(pGameWindow->Engine)), MaxStars, HalfStarScale);
}
// Draw score
MoveTo(pGameWindow->LevelEndScene.ScoreRect.left, pGameWindow->LevelEndScene.ScoreRect.top);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale);
Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale);
if (fullRefresh)
{
MoveTo(pGameWindow->LevelEndScene.ScoreRect.left, pGameWindow->LevelEndScene.ScoreRect.top);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), GameEngine_GetTotalScore(&(pGameWindow->Engine)), ScoreTextScale);
Bitmaps_DrawSlashChar(&(pGameWindow->Bitmaps), ScoreTextScale);
Bitmaps_DrawNumber(&(pGameWindow->Bitmaps), PerfectScore, ScoreTextScale);
}
// Draw retry button
DrawPicture(pGameWindow->Bitmaps.RetryButtonPict, &(pGameWindow->LevelEndScene.RetryButtonRect));
if (fullRefresh)
{
DrawPicture(pGameWindow->Bitmaps.RetryButtonPict, &(pGameWindow->LevelEndScene.RetryButtonRect));
}
// Draw next button
DrawPicture(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelEndScene.NextButtonRect));
if (fullRefresh)
{
DrawPicture(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelEndScene.NextButtonRect));
}
}
void LevelEndScene_Click(GameWindow *pGameWindow, const Point *pPosition)
{
if (PtInRect(*pPosition, &(pGameWindow->LevelEndScene.RetryButtonRect)))
{
// Clicked on retry button
GameEngine_ResetLevel(&(pGameWindow->Engine));
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
}
else if (PtInRect(*pPosition, &(pGameWindow->LevelEndScene.NextButtonRect)))
{
GameEngine_NextLevel(&(pGameWindow->Engine));
if (GameEngine_IsGameOver(&(pGameWindow->Engine)))
// Clicked on next button
if (GameEngine_IsLastLevel(&(pGameWindow->Engine)))
{
// Finished the last level, go to game end scene
GameWindow_SetScene(pGameWindow, GameEnd);
Sounds_PlayDoneSnd(&(pGameWindow->Sounds));
}
else
{
GameWindow_SetScene(pGameWindow, Play);
if (GameEngine_HasPlayedLevel(&(pGameWindow->Engine), pGameWindow->Engine.Level + 1))
{
// Next level has been played before, assume they're
// replaying old levels and return to level select scene
GameWindow_SetScene(pGameWindow, LevelSelect);
}
else
{
// First time for next level, load and go to play scene
GameEngine_NextLevel(&(pGameWindow->Engine));
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
}
}
}
}

View File

@ -202,8 +202,10 @@ void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition)
if (PtInRect(*pPosition, &levelRect))
{
// Clicked on level button
GameEngine_StartLevel(&(pGameWindow->Engine), level);
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
break;
}
}
@ -211,6 +213,7 @@ void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition)
}
else if (PtInRect(*pPosition, &(pGameWindow->LevelSelectScene.PrevButtonRect)))
{
// Clicked on previous button, go to previous page or title scene
if (pGameWindow->LevelSelectScene.PageNumber > 0)
{
pGameWindow->LevelSelectScene.PageNumber--;
@ -223,6 +226,7 @@ void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition)
}
else if (PtInRect(*pPosition, &(pGameWindow->LevelSelectScene.NextButtonRect)))
{
// Clicked on next button, go to next page if possible
if ((pGameWindow->LevelSelectScene.PageNumber + 1) * LevelsPerPage < LevelCount)
{
pGameWindow->LevelSelectScene.PageNumber++;

View File

@ -7,7 +7,6 @@
* This file provides implementations for Levels.h.
*/
#include "Common.h"
#include "Levels.h"
/** The levels from Set A, stored in 32-bit integers. */

View File

@ -7,32 +7,35 @@
* This file provides implementations for MacLO.h.
*/
#include "GameWindow.h"
#include "MacLO.h"
#include "GameWindow.h"
/** Resource ID for the apple menu. */
#define AppleMenuResID BaseResID
#define AppleMenuResID BaseResID
/** Resource ID for the about menu item. */
#define AboutMenuItemID 1
/** ID for the about menu item. */
#define AboutMenuItemID 1
/** Resource ID for the game menu. */
#define GameMenuResID BaseResID+1
#define GameMenuResID BaseResID+1
/** Resource ID for the title menu id. */
#define TitleMenuItemID 1
/** ID for the title menu item. */
#define GoToTitleMenuItemID 1
/** Resource ID for the clear menu id. */
#define ClearMenuItemID 2
/** ID for the sound item. */
#define SoundMenuItemID 2
/** Resource ID for the quit menu id. */
#define QuitMenuItemID 4
/** ID for the clear menu item. */
#define ClearMenuItemID 4
/** ID for the quit menu item. */
#define QuitMenuItemID 6
/** Resource ID for the about dialog. */
#define AboutDialogResID BaseResID
#define AboutDialogResID BaseResID
/** Resource ID for the about dialog's ok button. */
#define AboutDialogOKID 1
#define AboutDialogOKID 1
/** GameWindow global instance. */
GameWindow gGameWindow;
@ -109,21 +112,39 @@ void MacLO_ToolBoxInit()
void MacLO_AppInit()
{
Handle menuBar;
MenuHandle appleMenu;
MenuHandle appleMenu, gameMenu;
// Add the menu bar
menuBar = GetNewMBar(BaseResID);
if (menuBar == nil)
{
ShowError("\pMacLO MBAR resource missing!", true);
}
SetMenuBar(menuBar);
// Populate the apple menu
appleMenu = GetMHandle(AppleMenuResID);
if (appleMenu == nil)
{
ShowError("\pApple MENU resource missing!", true);
}
AddResMenu(appleMenu, 'DRVR');
DrawMenuBar();
gameMenu = GetMHandle(GameMenuResID);
if (gameMenu == nil)
{
ShowError("\pGame MENU resource missing!", true);
}
// Setup the game window
GameWindow_Init(&gGameWindow);
MacLO_UpdateMenus();
GameWindow_Show(&gGameWindow);
// Update menus now that the game window is ready
MacLO_UpdateMenus();
}
void MacLO_MainLoop()
@ -152,7 +173,17 @@ void MacLO_MainLoop()
cmdChar = event.message & charCodeMask;
if ((event.modifiers & cmdKey) != 0)
{
MacLO_HandleMenuChoice(MenuKey(cmdChar));
if (cmdChar == 'Q' || cmdChar == 'q')
{
// Always handle quit properly, don't rely
// on the quit menu existing or working properly
MacLO_Quit();
}
else
{
// Try to invoke menu
MacLO_HandleMenuChoice(MenuKey(cmdChar));
}
}
break;
}
@ -160,6 +191,15 @@ void MacLO_MainLoop()
}
}
void MacLO_UpdateMenus()
{
MenuHandle gameMenu = GetMHandle(GameMenuResID);
CheckItem(gameMenu, SoundMenuItemID, gGameWindow.Sounds.Enabled);
DrawMenuBar();
}
void MacLO_HandleUpdate(const EventRecord *pEvent)
{
WindowPtr window;
@ -244,10 +284,6 @@ void MacLO_HandleMenuChoice(const int32_t menuChoice)
void MacLO_HandleAppleMenuChoice(const int16_t item)
{
MenuHandle appleMenu;
Str255 accName;
int16_t accNumber;
switch (item)
{
case AboutMenuItemID:
@ -296,9 +332,12 @@ void MacLO_HandleGameMenuChoice(const int16_t item)
{
switch (item)
{
case TitleMenuItemID:
case GoToTitleMenuItemID:
GameWindow_SetScene(&gGameWindow, Title);
break;
case SoundMenuItemID:
GameWindow_ToggleSound(&gGameWindow);
break;
case ClearMenuItemID:
GameWindow_ClearScores(&gGameWindow);
break;

View File

@ -25,4 +25,9 @@ void MacLO_AppInit();
*/
void MacLO_MainLoop();
/**
* Update menus of the application.
*/
void MacLO_UpdateMenus();
#endif

Binary file not shown.

Binary file not shown.

View File

@ -205,6 +205,7 @@ void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition)
if (PtInRect(*pPosition, &lightRect))
{
// Toggle clicked-on light
GameEngine_ToggleLights(&(pGameWindow->Engine), c, r);
GameWindow_Draw(pGameWindow, false);
Sounds_PlayClickSnd(&(pGameWindow->Sounds));
@ -229,14 +230,15 @@ void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition)
if (PtInRect(*pPosition, &(pGameWindow->PlayScene.RetryButtonRect)))
{
// Click on retry button, reset level
GameEngine_ResetLevel(&(pGameWindow->Engine));
GameWindow_Draw(pGameWindow, false);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
}
else if (PtInRect(*pPosition, &(pGameWindow->PlayScene.SoundButtonRect)))
{
pGameWindow->Sounds.Enabled = !pGameWindow->Sounds.Enabled;
GameWindow_Draw(pGameWindow, false);
// Click on sound button
GameWindow_ToggleSound(pGameWindow);
}
}
}

View File

@ -13,13 +13,13 @@
#define SndBaseResID 8192
/** The click snd resource ID. */
#define ClickSndResID SndBaseResID
#define ClickSndResID SndBaseResID
/** The retry snd resource ID. */
#define RetrySndResID (ClickSndResID + 1)
#define RetrySndResID (ClickSndResID + 1)
/** The done snd resource ID. */
#define DoneSndResID (RetrySndResID + 1)
#define DoneSndResID (RetrySndResID + 1)
/** Whether or not sound is enabled by default. */
#define DefaultEnabled true

View File

@ -44,15 +44,24 @@ void TitleScene_Init(GameWindow *pGameWindow)
void TitleScene_Draw(const GameWindow *pGameWindow, bool fullRefresh)
{
// Draw Title
DrawPicture(pGameWindow->Bitmaps.TitlePict, &(pGameWindow->TitleScene.TitleRect));
if (fullRefresh)
{
DrawPicture(pGameWindow->Bitmaps.TitlePict, &(pGameWindow->TitleScene.TitleRect));
}
// Draw Set A
MoveTo(pGameWindow->TitleScene.SetARect.left, pGameWindow->TitleScene.SetARect.top);
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), TitleTextScale);
if (fullRefresh)
{
MoveTo(pGameWindow->TitleScene.SetARect.left, pGameWindow->TitleScene.SetARect.top);
Bitmaps_DrawAChar(&(pGameWindow->Bitmaps), TitleTextScale);
}
// Draw Set B
MoveTo(pGameWindow->TitleScene.SetBRect.left, pGameWindow->TitleScene.SetBRect.top);
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), TitleTextScale);
if (fullRefresh)
{
MoveTo(pGameWindow->TitleScene.SetBRect.left, pGameWindow->TitleScene.SetBRect.top);
Bitmaps_DrawBChar(&(pGameWindow->Bitmaps), TitleTextScale);
}
// Draw sound button
MoveTo(pGameWindow->TitleScene.SoundButtonRect.left, pGameWindow->TitleScene.SoundButtonRect.top);
@ -63,17 +72,39 @@ void TitleScene_Click(GameWindow *pGameWindow, const Point *pPosition)
{
if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetARect)))
{
// Click on Set A button
GameEngine_NewGame(&(pGameWindow->Engine), false);
GameWindow_SetScene(pGameWindow, LevelSelect);
if (GameEngine_HasPlayedLevel(&(pGameWindow->Engine), 0))
{
// Not first time, go to level select
GameWindow_SetScene(pGameWindow, LevelSelect);
}
else
{
// First time, go straight to play scene
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
}
}
else if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SetBRect)))
{
// Click on Set B button
GameEngine_NewGame(&(pGameWindow->Engine), true);
GameWindow_SetScene(pGameWindow, LevelSelect);
if (GameEngine_HasPlayedLevel(&(pGameWindow->Engine), 0))
{
// Not first time, go to level select
GameWindow_SetScene(pGameWindow, LevelSelect);
}
else
{
// First time, go straight to play scene
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
}
}
else if (PtInRect(*pPosition, &(pGameWindow->TitleScene.SoundButtonRect)))
{
pGameWindow->Sounds.Enabled = !pGameWindow->Sounds.Enabled;
GameWindow_Draw(pGameWindow, false);
// Click on sound button
GameWindow_ToggleSound(pGameWindow);
}
}