Compare commits

...

7 Commits
v1.0 ... main

Author SHA1 Message Date
Jon Thysell 3780806d69 Version 1.1 release 2021-12-17 13:13:19 -08:00
Jon Thysell e4cb9a7af8 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
2021-12-16 12:57:00 -08:00
Jon Thysell cdf67289a9 Added saving of progress to the application's 'SAVE' resource 2021-12-12 15:45:28 -08:00
Jon Thysell 5a4928ba39 Doxygen comments part 2 - c files 2021-12-08 14:35:06 -08:00
Jon Thysell e35349b418 Doxygen comments part 1 - headers 2021-12-07 11:07:28 -08:00
Jon Thysell 50a753b35e Minor cleanup of resources
Made sure all resources are no longer marked as purgeable so they stay
in memory (not that we were running out), also cleaned up some other
code issues.
2021-12-01 10:08:07 -08:00
Jon Thysell 3542861603 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.
2021-11-30 18:38:36 -08:00
38 changed files with 1694 additions and 154 deletions

12
CHANGELOG.md Normal file
View File

@ -0,0 +1,12 @@
# MacLO ChangeLog #
## v1.1 ##
* Added progress saving and level select
* Added Sound toggle to menu
* Reduced memory usage
* Optimized drawing functions
## v1.0 ##
* First release of MacLO!

View File

@ -4,7 +4,7 @@
MacLO is a clone of the puzzle game [Lights Out](https://en.wikipedia.org/wiki/Lights_Out_(game)) for the 68k Macintosh. It is a port of [ArduLO](https://github.com/jonthysell/ArduLO) for the Arduboy.
MacLO is B&W and has been tested on System 6.0.8 and System 7.5.5.
MacLO is B&W and 32-bit clean. It requires at least a Mac Plus running System 6.0.8, but will also work on newer machines up to Mac OS 9.2.2.
## Build ##
@ -22,7 +22,7 @@ The objective is to turn off all of the lights in as few moves as possible. Clic
![MacLO Level Done Screenshot](./.github/screenshots/leveldone.gif)
After finshing a puzzle, you'll be given a score in one to three stars. Here you can either move on to the next puzzle or retry for a better score. Try to get three stars on all 50 levels in the set!
After finshing a puzzle, you'll be given a score in one to three stars. Here you can either move on to the next puzzle or retry for a better score. Try to get three stars on all 50 levels in each set!
## Errata ##

BIN
assets/prev.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

View File

@ -1,32 +1,93 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Bitmaps.c
*
* This file provides implementations for Bitmaps.h.
*/
#include <OSUtils.h>
#include "Bitmaps.h"
/** The first PICT resource ID. */
#define BasePictResID BaseResID
/** Index of the title PICT resource ID. */
#define TitlePictResID 0
/** Starting index of the number character PICT resource IDs. */
#define NumCharPictBaseResID (TitlePictResID + 1)
/** Index of the A character PICT resource ID. */
#define ACharPictResID (NumCharPictBaseResID + NumCharPictCount)
/** Index of the B character PICT resource ID. */
#define BCharPictResID (ACharPictResID + 1)
/** Index of the / character PICT resource ID. */
#define SlashCharPictResID (BCharPictResID + 1)
/** Starting index of the star PICT resource IDs. */
#define StarPictBaseResID (SlashCharPictResID + 1)
#define NextButtonPictResID (StarPictBaseResID + StarPictCount)
/** Index of the previous button PICT resource ID. */
#define PrevButtonPictResID (StarPictBaseResID + StarPictCount)
/** Index of the next button PICT resource ID. */
#define NextButtonPictResID (PrevButtonPictResID + 1)
/** Index of the retry button PICT resource ID. */
#define RetryButtonPictResID (NextButtonPictResID + 1)
/** Index of the sound off PICT resource ID. */
#define SoundOffPictResID (RetryButtonPictResID + 1)
/** Index of the sound on PICT resource ID. */
#define SoundOnPictResID (SoundOffPictResID + 1)
/** Index of the light off PICT resource ID. */
#define LightOffPictResID (SoundOnPictResID + 1)
/** Index of the light on PICT resource ID. */
#define LightOnPictResID (LightOffPictResID + 1)
/** The total number of PICT resources. */
#define TotalPictCount (LightOnPictResID + 1)
/** Amount of padding to place between star PICTs. */
#define StarRectPadding 2
/** Mapping of months to the day of the year of their first day. */
const int16_t MonthOffset[] = {
0, // Jan
31, // Feb
60, // Mar
91, // Apr
121, // May
152, // Jun
182, // Jul
213, // Aug
244, // Sep
274, // Oct
305, // Nov
335, // Dec
};
/**
* Gets the override base resource ID.
* @return The base resource ID.
*/
int16_t Bitmaps_GetOverrideBaseResID();
PicHandle Bitmaps_GetPict(const int16_t holidayResID, const int16_t offset);
/**
* Gets the PICT specified by the base resource ID and given offset.
* @param baseResID The base resource ID.
* @param offset The offset.
* @return The PICT resource.
*/
PicHandle Bitmaps_GetPict(const int16_t baseResID, const int16_t offset);
void Bitmaps_Init(Bitmaps *pBitmaps)
{
@ -89,6 +150,13 @@ void Bitmaps_Init(Bitmaps *pBitmaps)
ShowError("\pSlash char PICT resource missing!", true);
}
// Load prev button
pBitmaps->PrevButtonPict = 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)
@ -151,12 +219,12 @@ int16_t Bitmaps_GetOverrideBaseResID()
PicHandle Bitmaps_GetPict(const int16_t baseResID, const int16_t offset)
{
PicHandle pic;
pic = GetPicture(baseResID + offset);
pic = (PicHandle)Get1Resource('PICT', baseResID + offset);
if (pic == nil)
{
// No override pic, get default
pic = GetPicture(BasePictResID + offset);
pic = (PicHandle)Get1Resource('PICT', BasePictResID + offset);
}
return pic;
@ -239,7 +307,7 @@ void Bitmaps_DrawSlashChar(const Bitmaps *pBitmaps, const uint8_t scale)
DrawScaledPic(pBitmaps->SlashCharPict, scale);
}
void Bitmaps_GetHalfStarsRect(const Bitmaps *pBitmaps, const uint8_t halfStars, const uint8_t maxStars, const uint8_t scale, Rect *pDestRect)
void Bitmaps_GetHalfStarsRect(const Bitmaps *pBitmaps, const uint8_t maxStars, const uint8_t scale, Rect *pDestRect)
{
uint8_t stars;
Rect starRect, paddingRect;
@ -295,7 +363,7 @@ void Bitmaps_DrawHalfStars(const Bitmaps *pBitmaps, const uint8_t halfStars, con
}
}
void Bitmaps_GetSoundRect(const Bitmaps *pBitmaps, const bool enabled, const uint8_t scale, Rect *pDestRect)
void Bitmaps_GetSoundRect(const Bitmaps *pBitmaps, const uint8_t scale, Rect *pDestRect)
{
Rect r;

View File

@ -1,14 +1,26 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Bitmaps.h
*
* This file provides a Bitmaps type which contains handles to every
* picture resource used by the game, as well as functions for loading
* and drawing those pictures.
*/
#ifndef BITMAPS_H
#define BITMAPS_H
#include "MacCommon.h"
/** The number of numeric digit pictures. */
#define NumCharPictCount 10
/** The number of star pictures. */
#define StarPictCount 3
/** Struct containing handles to every picture resource. */
typedef struct sBitmaps
{
PicHandle TitlePict;
@ -17,6 +29,7 @@ typedef struct sBitmaps
PicHandle BCharPict;
PicHandle SlashCharPict;
PicHandle StarPicts[StarPictCount];
PicHandle PrevButtonPict;
PicHandle NextButtonPict;
PicHandle RetryButtonPict;
PicHandle SoundOffPict;
@ -25,19 +38,82 @@ typedef struct sBitmaps
PicHandle LightOnPict;
} Bitmaps;
/**
* Initializes the Bitmaps by loading each picture resource.
* @param pBitmaps The Bitmaps.
*/
void Bitmaps_Init(Bitmaps *pBitmaps);
/**
* Gets the rect bounding a number drawn using the (scaled) pictures.
* @param pBitmaps The Bitmaps.
* @param number The target number.
* @param scale The scale factor.
* @param pDestRect The Rect bounding the scaled picture.
*/
void Bitmaps_GetNumberRect(const Bitmaps *pBitmaps, const uint32_t number, const uint8_t scale, Rect *pDestRect);
/**
* Draws a number using the (scaled) pictures at the pen's location.
* @param pBitmaps The Bitmaps.
* @param number The target number.
* @param scale The scale factor.
*/
void Bitmaps_DrawNumber(const Bitmaps *pBitmaps, const uint32_t number, const uint8_t scale);
/**
* Draws an "A" using the (scaled) picture at the pen's location.
* @param pBitmaps The Bitmaps.
* @param scale The scale factor.
*/
void Bitmaps_DrawAChar(const Bitmaps *pBitmaps, const uint8_t scale);
/**
* Draws a "B" using the (scaled) picture at the pen's location.
* @param pBitmaps The Bitmaps.
* @param scale The scale factor.
*/
void Bitmaps_DrawBChar(const Bitmaps *pBitmaps, const uint8_t scale);
/**
* Draws an "/" using the (scaled) picture at the pen's location.
* @param pBitmaps The Bitmaps.
* @param scale The scale factor.
*/
void Bitmaps_DrawSlashChar(const Bitmaps *pBitmaps, const uint8_t scale);
void Bitmaps_GetHalfStarsRect(const Bitmaps *pBitmaps, const uint8_t halfStars, const uint8_t maxStars, const uint8_t scale, Rect *pDestRect);
/**
* Gets the rect bounding stars drawn using the (scaled) pictures.
* @param pBitmaps The Bitmaps.
* @param maxStars The number of stars.
* @param scale The scale factor.
* @param pDestRect The Rect bounding the scaled picture.
*/
void Bitmaps_GetHalfStarsRect(const Bitmaps *pBitmaps, const uint8_t maxStars, const uint8_t scale, Rect *pDestRect);
/**
* Draws (partially) filled stars using the (scaled) pictures.
* @param pBitmaps The Bitmaps.
* @param halfStars The number of half-stars to fill.
* @param maxStars The total number of stars.
* @param scale The scale factor.
*/
void Bitmaps_DrawHalfStars(const Bitmaps *pBitmaps, const uint8_t halfStars, const uint8_t maxStars, const uint8_t scale);
void Bitmaps_GetSoundRect(const Bitmaps *pBitmaps, const bool enabled, const uint8_t scale, Rect *pDestRect);
/**
* Gets the rect bounding the sound icon using the (scaled) pictures.
* @param pBitmaps The Bitmaps.
* @param scale The scale factor.
* @param pDestRect The Rect bounding the scaled picture.
*/
void Bitmaps_GetSoundRect(const Bitmaps *pBitmaps, const uint8_t scale, Rect *pDestRect);
/**
* Draws the sound icon using the (scaled) pictures.
* @param pBitmaps The Bitmaps.
* @param enabled Whether sound is enabled or not.
* @param scale The scale factor.
*/
void Bitmaps_DrawSound(const Bitmaps *pBitmaps, const bool enabled, const uint8_t scale);
#endif

View File

@ -1,19 +1,37 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Common.h
*
* This file includes some common utility functions and macros.
*/
#ifndef COMMON_H
#define COMMON_H
#include "stdbool.h"
#include "stdint.h"
/** Get the minimum of a and b. */
#define min(a,b) ((a)<(b)?(a):(b))
/** Get the maximum of a and b. */
#define max(a,b) ((a)>(b)?(a):(b))
/** Gets the bit within value. */
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
/** Sets the bit within value. */
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
/** Clears the bit within value. */
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
/** Toggles the bit within value. */
#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))
#define bitWrite(value, bit, bitValue) ((bitBalue) ? bitSet(value, bit) : bitClear(value, bit))
/** Sets the bit within value to bitValue. */
#define bitWrite(value, bit, bitValue) ((bitValue) ? bitSet(value, bit) : bitClear(value, bit))
#endif

View File

@ -1,9 +1,18 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameEndScene.c
*
* This file provides implementations for GameEndScene.h.
*/
#include "GameEndScene.h"
/** Scale factor for the A/B set text. */
#define SetTextScale 6
/** Scale factor for the score text. */
#define ScoreTextScale 3
void GameEndScene_Init(GameWindow *pGameWindow)
@ -23,7 +32,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);
@ -37,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), pGameWindow->Engine.Score, 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

@ -1,13 +1,36 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameEndScene.h
*
* This file provides the functions to initialize, draw, and respond to
* clicks when the GameWindow is set to the LevelSelect scene.
*/
#ifndef GAMEENDSCENE_H
#define GAMEENDSCENE_H
#include "GameWindow.h"
/**
* Initializes the GameEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
*/
void GameEndScene_Init(GameWindow *pGameWindow);
/**
* Draws the GameEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole scene needs to be redrawn.
*/
void GameEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the GameEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void GameEndScene_Click(GameWindow *pGameWindow, const Point *pPosition);
#endif

View File

@ -1,8 +1,14 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
#include "Common.h"
/**
* @file GameEngine.c
*
* This file provides implementations for GameEngine.h.
*/
#include "GameEngine.h"
#include "Common.h"
const int8_t PuzzleSize = 5;
@ -12,28 +18,96 @@ const uint8_t MinHalfStars = 1;
const uint16_t PerfectScore = 300; // LevelCount * MaxHalfStars
/**
* Loads the given level as the current level.
* @param level The level.
* @param setB The set.
*/
void GameEngine_LoadLevel(GameEngine *pGameEngine, const int8_t level, const bool setB);
/**
* Gets the number of half-stars to award given the par for the level, and the
* number of moves already taken.
* @param par The par for the level.
* @param moves The number of moves.
* @return The number of half-stars.
*/
uint8_t GameEngine_CalculateHalfStars(const uint16_t par, const uint16_t moves);
/**
* Toggles the single light at the given coordinates.
* @param pGameEngine The GameEngine.
* @param x The x coordinate of the light.
* @param y The y coordinate of the light.
*/
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++)
{
pGameEngine->ScoresA[level] = 0;
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 int8_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) % LevelCount);
}
}
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,14 +130,24 @@ 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_HasPlayedLevel(pGameEngine, level - 1));
}
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)
@ -71,6 +155,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);

View File

@ -1,22 +1,38 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameEngine.h
*
* This file provides the GameEngine type, which manages the the game of
* Light Out, including current level information and the user's progress.
*/
#ifndef GAMEENGINE_H
#define GAMEENGINE_H
#include "Levels.h"
/** The size (width and height) of a puzzle. */
extern const int8_t PuzzleSize;
/** The max number of full stars for completing a puzzle. */
extern const uint8_t MaxStars;
/** The max number of half-stars for completing a puzzle. */
extern const uint8_t MaxHalfStars;
/** The min number of half-stars for completing a puzzle. */
extern const uint8_t MinHalfStars;
/** The max number of half-stars for completing every puzzle in a set. */
extern const uint16_t PerfectScore;
/** Struct containing all the data needed for a game. */
typedef struct GameEngine
{
uint16_t Score;
uint8_t ScoresA[LevelCount];
uint8_t ScoresB[LevelCount];
int8_t Level;
bool SetB;
uint32_t Lights;
@ -25,24 +41,134 @@ typedef struct GameEngine
uint16_t Moves;
} GameEngine;
/**
* Initializes the GameEngine.
* @param pGameEngine The GameEngine.
*/
void GameEngine_Init(GameEngine *pGameEngine);
/**
* Starts a new game, selecting the first unfinshed level in the set.
* @param pGameEngine The GameEngine.
* @param setB The set.
*/
void GameEngine_NewGame(GameEngine *pGameEngine, const bool setB);
/**
* Resets the scores for all levels to zero.
* @param pGameEngine The GameEngine.
*/
void GameEngine_ResetGame(GameEngine *pGameEngine);
/**
* Starts the given level.
* @param pGameEngine The GameEngine.
* @param level The level to start.
*/
void GameEngine_StartLevel(GameEngine *pGameEngine, const int8_t level);
/**
* Marks the current level as complete, saving the score.
* @param pGameEngine The GameEngine.
*/
void GameEngine_CompleteLevel(GameEngine *pGameEngine);
/**
* Advances the game to the next level in the set.
* @param pGameEngine The GameEngine.
*/
void GameEngine_NextLevel(GameEngine *pGameEngine);
/**
* Resets the current level to its initial state.
* @param pGameEngine The GameEngine.
*/
void GameEngine_ResetLevel(GameEngine *pGameEngine);
/**
* Gets the state of the light at the given coordinates.
* @param pGameEngine The GameEngine.
* @param x The x coordinate of the light.
* @param y The y coordinate of the light.
* @return Whether the light is on or not.
*/
bool GameEngine_GetLight(const GameEngine *pGameEngine, const int8_t x, const int8_t y);
/**
* Gets whether the current level has been completed.
* @param pGameEngine The GameEngine.
* @return Whether all of the lights are off.
*/
bool GameEngine_IsCompleted(const GameEngine *pGameEngine);
bool GameEngine_IsGameOver(const GameEngine *pGameEngine);
/**
* Gets whether or not the given level can be played.
* @param pGameEngine The GameEngine.
* @param level The level.
* @return Whether the level can be played.
*/
bool GameEngine_IsEnabled(const GameEngine *pGameEngine, const int8_t level);
/**
* Gets whether the current level is the last of a set.
* @param pGameEngine The GameEngine.
* @return Whether the current level is the last of a set.
*/
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
* state of the level they're trying to solve.
* @param pGameEngine The GameEngine.
* @return The number of half-stars.
*/
uint8_t GameEngine_GetHalfStars(const GameEngine *pGameEngine);
/**
* Gets the number of half-stars the user won for a given level.
* @param pGameEngine The GameEngine.
* @param level The level.
* @return The number of half-stars won.
*/
uint8_t GameEngine_GetScore(const GameEngine *pGameEngine, const int8_t level);
/**
* Gets the total number of half-stars the user has won across the set.
* @param pGameEngine The GameEngine.
* @return The total number of half-stars won.
*/
uint16_t GameEngine_GetTotalScore(const GameEngine *pGameEngine);
/**
* Toggles the lights centered at the given coordinates.
* @param pGameEngine The GameEngine.
* @param x The x coordinate of the center light.
* @param y The y coordinate of the center light.
*/
void GameEngine_ToggleLights(GameEngine *pGameEngine, const int8_t x, const int8_t y);
/**
* Gets whether the given light changed after the last toggle.
* @param pGameEngine The GameEngine.
* @param x The x coordinate of the light.
* @param y The y coordinate of the light.
* @return Whether the light changed.
*/
bool GameEngine_LightChanged(const GameEngine *pGameEngine, const int8_t x, const int8_t y);
/**
* Gets whether the number of half-stars changed after the last toggle.
* @param pGameEngine The GameEngine.
* @return Whether the number of half-stars changed.
*/
bool GameEngine_HalfStarsChanged(const GameEngine *pGameEngine);
#endif

80
src/GameSave.c Normal file
View File

@ -0,0 +1,80 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameSave.c
*
* This file provides implementations for GameSave.h.
*/
#include "GameSave.h"
#include "MacCommon.h"
/** The save resource type. */
#define SaveResType 'SAVE'
/** The save resource ID. */
#define SaveResID 128
/** The save resource ID. */
#define SaveResSize (sizeof(uint8_t) * SetCount * LevelCount)
void GameSave_Init(GameSave *pGameSave)
{
pGameSave->Save = GetOrAddResource(SaveResType, SaveResID, SaveResSize, EmptyString);
if (pGameSave->Save == nil)
{
ShowError("\pGame's SAVE resource couldn't be created!", true);
}
}
void GameSave_LoadData(const GameSave *pSaveGame, GameEngine *pGameEngine)
{
int8_t level, scoreA, scoreB;
bool resetA, resetB;
HLock(pSaveGame->Save);
resetA = false;
resetB = false;
for (level = 0; level < LevelCount; level++)
{
scoreA = min(MaxHalfStars, (*pSaveGame->Save)[level]);
resetA = resetA || (scoreA < MinHalfStars);
pGameEngine->ScoresA[level] = resetA ? 0 : scoreA;
scoreB = min(MaxHalfStars, (*pSaveGame->Save)[LevelCount + level]);
resetB = resetB || (scoreB < MinHalfStars);
pGameEngine->ScoresB[level] = resetB ? 0 : scoreB;
}
HUnlock(pSaveGame->Save);
}
void GameSave_SaveData(GameSave *pSaveGame, const GameEngine *pGameEngine)
{
int8_t level;
bool dataChanged;
HLock(pSaveGame->Save);
dataChanged = false;
for (level = 0; level < LevelCount; level++)
{
dataChanged = dataChanged
|| ((*pSaveGame->Save)[level] != pGameEngine->ScoresA[level])
|| ((*pSaveGame->Save)[LevelCount + level] != pGameEngine->ScoresB[level]);
(*pSaveGame->Save)[level] = pGameEngine->ScoresA[level];
(*pSaveGame->Save)[LevelCount + level] = pGameEngine->ScoresB[level];
}
HUnlock(pSaveGame->Save);
if (dataChanged)
{
ChangedResource(pSaveGame->Save);
WriteResource(pSaveGame->Save);
}
}

41
src/GameSave.h Normal file
View File

@ -0,0 +1,41 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameSave.h
*
* This file provides a GameSave type which manages saving scores to disk.
*/
#ifndef GAMESAVE_H
#define GAMESAVE_H
#include "GameEngine.h"
/** Struct containing handle to the save resource. */
typedef struct sGameSave
{
Handle Save;
} GameSave;
/**
* Initializes the GameSave.
* @param pGameSave The GameSave.
*/
void GameSave_Init(GameSave *pSaveGame);
/**
* Loads data from the GameSave into the GameEngine.
* @param pGameSave The GameSave.
* @param pGameEngine The GameEngine.
*/
void GameSave_LoadData(const GameSave *pSaveGame, GameEngine *pGameEngine);
/**
* Saves data from the GameEngine into the GameSave.
* @param pGameSave The GameSave.
* @param pGameEngine The GameEngine.
*/
void GameSave_SaveData(GameSave *pSaveGame, const GameEngine *pGameEngine);
#endif

View File

@ -1,8 +1,16 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameWindow.c
*
* This file provides implementations for GameWindow.h.
*/
#include "GameWindow.h"
#include "MacLO.h"
#include "TitleScene.h"
#include "LevelSelectScene.h"
#include "PlayScene.h"
#include "LevelEndScene.h"
#include "GameEndScene.h"
@ -21,19 +29,28 @@ 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));
// Initialize game save
GameSave_Init(&(pGameWindow->GameSave));
// 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);
// Load data from saved game
GameSave_LoadData(&(pGameWindow->GameSave), &(pGameWindow->Engine));
GameWindow_SetScene(pGameWindow, Title);
}
@ -58,6 +75,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 +101,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 +123,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 +145,20 @@ 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?"))
{
GameEngine_ResetGame(&(pGameWindow->Engine));
GameSave_SaveData(&(pGameWindow->GameSave), &(pGameWindow->Engine));
GameWindow_SetScene(pGameWindow, Title);
}
}

View File

@ -1,37 +1,90 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameWindow.h
*
* This file provides the GameWindow type, which, as MacLO is essentially
* played entirely within one Window, essentially manages the entire game.
* GameWindow keeps track of the core GameEngine as well as the current scene,
* which it calls to handle rendering and to handle user interaction based
* on the state of both.
*/
#ifndef GAMEWINDOW_H
#define GAMEWINDOW_H
#include "MacCommon.h"
#include "WindowBuffer.h"
#include "GameEngine.h"
#include "GameSave.h"
#include "Bitmaps.h"
#include "Sounds.h"
#include "Scenes.h"
/** The background pattern of the window. */
#define WindowPattern black
/** Struct containing everything the game needs. */
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;
GameSave GameSave;
Bitmaps Bitmaps;
Sounds Sounds;
SceneId CurrentSceneId;
TitleScene TitleScene;
LevelSelectScene LevelSelectScene;
PlayScene PlayScene;
LevelEndScene LevelEndScene;
GameEndScene GameEndScene;
} GameWindow;
/**
* Initializes the GameWindow.
* @param pGameWindow The GameWindow.
*/
void GameWindow_Init(GameWindow *pGameWindow);
/**
* Draws the GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole screen needs to be redrawn.
*/
void GameWindow_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void GameWindow_Click(GameWindow *pGameWindow, const Point *pPosition);
/**
* Changes the GameWindow to the given scene.
* @param pGameWindow The GameWindow.
* @param sceneId The new scene.
*/
void GameWindow_SetScene(GameWindow *pGameWindow, const SceneId sceneId);
/**
* Activates the GameWindow.
* @param pGameWindow The GameWindow.
*/
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.
*/
void GameWindow_ClearScores(GameWindow *pGameWindow);
#endif

View File

@ -1,10 +1,21 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file LevelEndScene.c
*
* This file provides implementations for LevelEndScene.h.
*/
#include "LevelEndScene.h"
/** Scale factor for the level text. */
#define LevelTextScale 4
/** Scale factor for the stars. */
#define HalfStarScale 3
/** Scale factor for score text. */
#define ScoreTextScale 1
void LevelEndScene_Init(GameWindow *pGameWindow)
@ -22,13 +33,13 @@ void LevelEndScene_Init(GameWindow *pGameWindow)
CenterRect(&r, &(pGameWindow->LevelEndScene.LevelRect));
// Setup half-stars
Bitmaps_GetHalfStarsRect(&(pGameWindow->Bitmaps), GameEngine_GetHalfStars(&(pGameWindow->Engine)), MaxStars, HalfStarScale, &(pGameWindow->LevelEndScene.HalfStarsRect));
Bitmaps_GetHalfStarsRect(&(pGameWindow->Bitmaps), MaxStars, HalfStarScale, &(pGameWindow->LevelEndScene.HalfStarsRect));
GetBoxRect(pContentRect, Center, &r);
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);
@ -53,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), pGameWindow->Engine.Score, 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

@ -1,13 +1,36 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file LevelEndScene.h
*
* This file provides the functions to initialize, draw, and respond to
* clicks when the GameWindow is set to the LevelEnd scene.
*/
#ifndef LEVELENDSCENE_H
#define LEVELENDSCENE_H
#include "GameWindow.h"
/**
* Initializes the LevelEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
*/
void LevelEndScene_Init(GameWindow *pGameWindow);
/**
* Draws the LevelEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole scene needs to be redrawn.
*/
void LevelEndScene_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the LevelEnd scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void LevelEndScene_Click(GameWindow *pGameWindow, const Point *pPosition);
#endif

236
src/LevelSelectScene.c Normal file
View File

@ -0,0 +1,236 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file LevelSelectScene.c
*
* This file provides implementations for LevelSelectScene.h.
*/
#include "LevelSelectScene.h"
/** Scale factor for the A/B set text. */
#define SetTextScale 3
/** Scale factor for the score text. */
#define ScoreTextScale 1
/** Margin for each level selection. */
#define LevelMargin 10
/** Width for each level selection. */
#define LevelWidth 80
/** Height for each level selection. */
#define LevelHeight 60
/** The number of rows of levels to show. */
#define LevelRowCount 2
/** The number of columns of levels to show. */
#define LevelColumnCount 5
/** The total number of levels to show per page. */
#define LevelsPerPage (LevelRowCount * LevelColumnCount)
/** Scale factor for the level text. */
#define LevelTextScale 1
/**
* Gets the Rect of the level selection at the given index.
* @param pGameWindow The GameWindow.
* @param pRect. The Rect of the level selection.
* @param i The index of the level onscreen.
*/
void LevelSelectScene_SetLevelRect(const GameWindow *pGameWindow, Rect *pRect, const int8_t i);
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);
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);
CenterRect(&r, &(pGameWindow->LevelSelectScene.ScoreRect));
// Setup previous button
GetPictureRect(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelSelectScene.PrevButtonRect));
GetBoxRect(pContentRect, BottomLeft, &r);
CenterRect(&r, &(pGameWindow->LevelSelectScene.PrevButtonRect));
// Setup next button
GetPictureRect(pGameWindow->Bitmaps.NextButtonPict, &(pGameWindow->LevelSelectScene.NextButtonRect));
GetBoxRect(pContentRect, BottomRight, &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), 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))
{
// Clicked on level button
GameEngine_StartLevel(&(pGameWindow->Engine), level);
GameWindow_SetScene(pGameWindow, Play);
Sounds_PlayRetrySnd(&(pGameWindow->Sounds));
break;
}
}
}
}
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--;
GameWindow_Draw(pGameWindow, false);
}
else
{
GameWindow_SetScene(pGameWindow, Title);
}
}
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++;
GameWindow_Draw(pGameWindow, false);
}
}
}

36
src/LevelSelectScene.h Normal file
View File

@ -0,0 +1,36 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file LevelSelectScene.h
*
* This file provides the functions to initialize, draw, and respond to
* clicks when the GameWindow is set to the LevelSelect scene.
*/
#ifndef LEVELSELECTSCENE_H
#define LEVELSELECTSCENE_H
#include "GameWindow.h"
/**
* Initializes the LevelSelect scene for the given GameWindow.
* @param pGameWindow The GameWindow.
*/
void LevelSelectScene_Init(GameWindow *pGameWindow);
/**
* Draws the LevelSelect scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole scene needs to be redrawn.
*/
void LevelSelectScene_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the LevelSelect scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void LevelSelectScene_Click(GameWindow *pGameWindow, const Point *pPosition);
#endif

View File

@ -1,11 +1,15 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
#include "Common.h"
/**
* @file Levels.c
*
* This file provides implementations for Levels.h.
*/
#include "Levels.h"
const int8_t LevelCount = 50;
/** The levels from Set A, stored in 32-bit integers. */
const uint32_t Levels_LightsA[] = {
0x0005400UL, 0x15A82B5UL, 0x0ADEF6AUL, 0x1B88360UL, 0x1BC5EEFUL, 0x0EAD400UL, 0x0F8C62FUL, 0x0AAA880UL, 0x07D3BEAUL,
0x00039CEUL, 0x0EAD6B5UL, 0x0A76D5FUL, 0x022AA88UL, 0x0210800UL, 0x0010040UL, 0x1F08421UL, 0x1F71000UL, 0x0455544UL,
@ -15,6 +19,7 @@ const uint32_t Levels_LightsA[] = {
0x0477DC4UL, 0x10917E4UL, 0x0089220UL, 0x1151151UL, 0x1FFFFFFUL,
};
/** The levels from Set B, stored in 32-bit integers. */
const uint32_t Levels_LightsB[] = {
0x1B06C1BUL, 0x1F2009FUL, 0x1F5115FUL, 0x11D822AUL, 0x0466CC4UL, 0x0AFD7EAUL, 0x158EE35UL, 0x0013800UL, 0x0531110UL,
0x11AC6A0UL, 0x1F739DFUL, 0x1150151UL, 0x0E4394EUL, 0x093BD2FUL, 0x0EAD6B5UL, 0x0E4384EUL, 0x1F8D63FUL, 0x1505415UL,

View File

@ -1,16 +1,39 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Levels.h
*
* This file provides functions for loading the light puzzles, or levels,
* as well as the "par" for each level. Each light puzzle is stored as 25
* bits within a 32-bit integer.
*/
#ifndef LEVELS_H
#define LEVELS_H
#include "stdint.h"
#include "stdbool.h"
extern const int8_t LevelCount;
/** The number of levels in each set. */
#define LevelCount 50
/** The number of sets. */
#define SetCount 2
/**
* Gets the lights for a given set and level number.
* @param level The level number.
* @param setB Whether or not to load from Set B.
* @return The lights.
*/
uint32_t Levels_GetLightsForLevel(const int8_t level, const bool setB);
/**
* Gets the minimum number of toggles to solve a given level number.
* @param level The level number.
* @return The minimum number of toggles to complete the level.
*/
uint16_t Levels_GetParForLevel(const int8_t level);
#endif

View File

@ -1,22 +1,22 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file MacCommon.c
*
* This file provides implementations for MacCommon.h.
*/
#include "MacCommon.h"
const int16_t MonthOffset[] = {
0, // Jan
31, // Feb
60, // Mar
91, // Apr
121, // May
152, // Jun
182, // Jul
213, // Aug
244, // Sep
274, // Oct
305, // Nov
335, // Dec
};
/** Resource ID for the error alert. */
#define ErrorAlertResID BaseResID
/** Resource ID for the confirm alert. */
#define ConfirmAlertResID (ErrorAlertResID + 1)
/** Item ID for a Yes result from the confirm alert. */
#define ConfirmYesResult 2
void ShowError(Str255 message, bool isFatal)
{
@ -29,6 +29,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 == ConfirmYesResult;
}
void CenterRect(const Rect *pOuterRect, Rect *pInnerRect)
{
CenterRectH(pOuterRect, pInnerRect);
@ -154,3 +164,28 @@ void DrawScaledPic(const PicHandle pic, const uint8_t scale)
DrawPicture(pic, &destRect);
MoveTo(destRect.right, destRect.top);
}
Handle GetOrAddResource(ResType resType, uint16_t resID, Size byteCount, Str255 resName)
{
Handle result;
result = GetResource(resType, resID);
if (result != nil && GetHandleSize(result) != byteCount)
{
// Resource was the wrong size, delete it
RmveResource(result);
ReleaseResource(result);
result = nil;
}
if (result == nil)
{
// Resource didn't exist, create it
result = NewHandleClear(byteCount);
HNoPurge(result);
AddResource(result, resType, resID, resName);
}
return result;
}

View File

@ -1,21 +1,31 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file MacCommon.h
*
* This file includes some common utility functions for use by Macintosh
* applications and the Macintosh Toolbox.
*/
#ifndef MACCOMMON_H
#define MACCOMMON_H
#include "Common.h"
/** The first non-reserved resource ID. */
#define BaseResID 128
/** Constant to put a Window to the top of the stack. */
#define MoveToFront (WindowPtr)-1L
/** An empty Pascal string. */
#define EmptyString "\p"
/** A nil filter. */
#define NilFilterProc nil
#define ErrorAlertResID BaseResID
extern const int16_t MonthOffset[];
/** Enum defining nine equal boxes within a rectangle. */
typedef enum eBoxAlignment
{
Top,
@ -29,21 +39,97 @@ typedef enum eBoxAlignment
Center
} BoxAlignment;
pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, int16_t newItem) = { 0x303C, 0x0304, 0xAA68 };
/**
* Toolbox function to set the default Button in a Dialog.
* @param theDialog The target Dialog.
* @param itemID The ID of the Button to set as the default.
* @return An error code.
*/
pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, int16_t itemID) = { 0x303C, 0x0304, 0xAA68 };
/**
* Displays an error message and optionally terminates the application.
* @param message The message to display.
* @param isFatal Whether or not the application should terminate.
*/
void ShowError(Str255 message, bool isFatal);
/**
* Prompts the user to confirm continuing with a (destructive) action.
* @param message The message to display.
* @return The user's choice whether or not to continue.
*/
bool ShowConfirm(Str255 message);
/**
* Centers an inner Rect within a static outer Rect.
* @param pOuterRect The static outer Rect.
* @param pInnerRect The inner Rect to move.
*/
void CenterRect(const Rect *pOuterRect, Rect *pInnerRect);
/**
* Centers an inner Rect horizontally within a static outer Rect.
* @param pOuterRect The static outer Rect.
* @param pInnerRect The inner Rect to move.
*/
void CenterRectH(const Rect *pOuterRect, Rect *pInnerRect);
/**
* Centers an inner Rect vertically within a static outer Rect.
* @param pOuterRect The static outer Rect.
* @param pInnerRect The inner Rect to move.
*/
void CenterRectV(const Rect *pOuterRect, Rect *pInnerRect);
/**
* Combines two Rects into a new Rect, by moving the right Rect directly
* to the right of the left Rect (tops aligned), and finding the smallest
* Rect which contains them both.
* @param pLeftRect The Rect on the left.
* @param pRightRect The Rect on the right.
* @param pDestRect The concatenated Rect.
*/
void ConcatenateRect(const Rect *pLeftRect, const Rect *pRightRect, Rect *pDestRect);
/**
* Gets the Rect at the given BoxAlignment within the static outer Rect.
* @param pOuterRect The static outer Rect.
* @param boxAlignment The target BoxAlignment.
* @param pBoxRect The Rect within the outer rect at the BoxAlignment.
*/
void GetBoxRect(const Rect *pOuterRect, const BoxAlignment boxAlignment, Rect *pBoxRect);
/**
* Gets the Rect which bounds the given picture.
* @param picHandle The target picture.
* @param pDestRect The Rect bounding the picture.
*/
void GetPictureRect(const PicHandle picHandle, Rect *pDestRect);
/**
* Gets the Rect which bounds the given picture if it had been scaled.
* @param picHandle The target picture.
* @param scale The scale factor.
* @param pDestRect The Rect bounding the scaled picture.
*/
void GetScaledPicFrame(const PicHandle picHandle, const uint8_t scale, Rect *pDestRect);
/**
* Draws the given picture scaled at the pen's location.
* @param picHandle The target picture.
* @param scale The scale factor.
*/
void DrawScaledPic(const PicHandle pic, const uint8_t scale);
/**
* Gets the specified resource or creates it if it doesn't exist.
* @param resType The resource type.
* @param resID The resource ID.
* @param byteCount The size of the resource if it needs to be created.
* @param resName The resource name if it needs to be created.
* @return Handle to the resource.
*/
Handle GetOrAddResource(ResType resType, uint16_t resID, Size byteCount, Str255 resName);
#endif

View File

@ -1,34 +1,100 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
#include "GameWindow.h"
/**
* @file MacLO.c
*
* This file provides implementations for MacLO.h.
*/
#include "MacLO.h"
#include "GameWindow.h"
#define AppleMenuResID BaseResID
#define AboutMenuItemID 1
/** Resource ID for the apple menu. */
#define AppleMenuResID BaseResID
#define GameMenuResID BaseResID+1
#define NewMenuItemID 1
#define QuitMenuItemID 3
/** ID for the about menu item. */
#define AboutMenuItemID 1
#define AboutDialogResID BaseResID
#define AboutDialogOKID 1
/** Resource ID for the game menu. */
#define GameMenuResID BaseResID+1
/** ID for the title menu item. */
#define GoToTitleMenuItemID 1
/** ID for the sound item. */
#define SoundMenuItemID 2
/** 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
/** Resource ID for the about dialog's ok button. */
#define AboutDialogOKID 1
/** GameWindow global instance. */
GameWindow gGameWindow;
Boolean gExitApp;
/** Bool to signal that the app should exit. */
bool gExitApp;
/**
* Processes the update event with the given EventRecord.
* @param pEvent The EventRecord.
*/
void MacLO_HandleUpdate(const EventRecord *pEvent);
/**
* Processes the mouse down event with the given EventRecord.
* @param pEvent The EventRecord.
*/
void MacLO_HandleMouseDown(const EventRecord *pEvent);
/**
* Processes the mouse up event with the given EventRecord.
* @param pEvent The EventRecord.
*/
void MacLO_HandleMouseUp(const EventRecord *pEvent);
/**
* Processes when a menu has been selected.
* @param menuChoice The menu selected.
*/
void MacLO_HandleMenuChoice(const int32_t menuChoice);
/**
* Processes when an item was selected in the apple menu.
* @param item The item in the menu selected.
*/
void MacLO_HandleAppleMenuChoice(const int16_t item);
/**
* Shows the about dialog.
*/
void MacLO_ShowAboutDialog();
/**
* Launches the item selected in the apple menu.
* @param item The item in the menu selected.
*/
void MacLO_LaunchAppleMenuItem(const int16_t item);
/**
* Processes when an item was selected in the game menu.
* @param item The item in the menu selected.
*/
void MacLO_HandleGameMenuChoice(const int16_t item);
/**
* Quits the application.
*/
void MacLO_Quit();
void MacLO_ToolBoxInit()
{
MaxApplZone();
@ -46,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()
@ -89,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;
}
@ -97,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;
@ -181,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:
@ -233,11 +332,26 @@ void MacLO_HandleGameMenuChoice(const int16_t item)
{
switch (item)
{
case NewMenuItemID:
case GoToTitleMenuItemID:
GameWindow_SetScene(&gGameWindow, Title);
break;
case SoundMenuItemID:
GameWindow_ToggleSound(&gGameWindow);
break;
case ClearMenuItemID:
GameWindow_ClearScores(&gGameWindow);
break;
case QuitMenuItemID:
gExitApp = true;
MacLO_Quit();
break;
}
}
void MacLO_Quit()
{
if (ShowConfirm("\pAre you sure you want to quit MacLO?"))
{
gExitApp = true;
GameSave_SaveData(&(gGameWindow.GameSave), &(gGameWindow.Engine));
}
}

View File

@ -1,11 +1,33 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file MacLO.h
*
* This file provides the functions necessary to setup the application.
*/
#ifndef MACLO_H
#define MACLO_H
/**
* Initialize the Macintosh Toolbox managers.
*/
void MacLO_ToolBoxInit();
/**
* Initialize the application.
*/
void MacLO_AppInit();
/**
* Run the main loop of the application.
*/
void MacLO_MainLoop();
/**
* Update menus of the application.
*/
void MacLO_UpdateMenus();
#endif

Binary file not shown.

Binary file not shown.

View File

@ -1,21 +1,50 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file PlayScene.c
*
* This file provides implementations for PlayScene.h.
*/
#include "PlayScene.h"
/** Margin around the Playfield area. */
#define PlayfieldMargin 4
#define PlayfieldPadding 2
#define PlayfieldCornerSize 12
#define LightMargin 6
#define LightSize 50
#define LightCornerSize 12
/** Padding within the Playfield area. */
#define PlayfieldPadding 2
/** Corner size of the Playfield area. */
#define PlayfieldCornerSize 12
/** Margin around each light. */
#define LightMargin 6
/** Size (width and height) of each light. */
#define LightSize 50
/** Margin around the HUD area. */
#define HUDMargin PlayfieldMargin
/** Scale factor for the level text. */
#define LevelTextScale 3
/** Scale factor for the stars. */
#define HalfStarScale 2
/** Scale factor for the score text. */
#define ScoreTextScale 1
/**
* Gets the Rect of the light at the given coordinates.
* @param pGameWindow The GameWindow.
* @param pRect. The Rect of the light.
* @param c The column.
* @param r The row.
*/
void PlayScene_SetLightRect(const GameWindow *pGameWindow, Rect *pRect, const int8_t c, const int8_t r);
void PlayScene_Init(GameWindow *pGameWindow)
{
Rect r;
@ -43,13 +72,13 @@ void PlayScene_Init(GameWindow *pGameWindow)
CenterRect(&r, &(pGameWindow->PlayScene.LevelRect));
// Setup half-stars
Bitmaps_GetHalfStarsRect(&(pGameWindow->Bitmaps), GameEngine_GetHalfStars(&(pGameWindow->Engine)), MaxStars, HalfStarScale, &(pGameWindow->PlayScene.HalfStarsRect));
Bitmaps_GetHalfStarsRect(&(pGameWindow->Bitmaps), MaxStars, HalfStarScale, &(pGameWindow->PlayScene.HalfStarsRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), Center, &r);
CenterRect(&r, &(pGameWindow->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);
@ -65,7 +94,7 @@ void PlayScene_Init(GameWindow *pGameWindow)
CenterRect(&r, &(pGameWindow->PlayScene.RetryButtonRect));
// Setup sound button
Bitmaps_GetSoundRect(&(pGameWindow->Bitmaps), pGameWindow->Sounds.Enabled, 1, &(pGameWindow->PlayScene.SoundButtonRect));
Bitmaps_GetSoundRect(&(pGameWindow->Bitmaps), 1, &(pGameWindow->PlayScene.SoundButtonRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), BottomRight, &r);
CenterRect(&r, &(pGameWindow->PlayScene.SoundButtonRect));
}
@ -144,7 +173,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);
}
@ -176,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));
@ -187,7 +217,9 @@ 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);
GameSave_SaveData(&(pGameWindow->GameSave), &(pGameWindow->Engine));
GameWindow_SetScene(pGameWindow, LevelEnd);
Sounds_PlayDoneSnd(&(pGameWindow->Sounds));
}
@ -198,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

@ -1,13 +1,36 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file PlayScene.h
*
* This file provides the functions to initialize, draw, and respond to
* clicks when the GameWindow is set to the Play scene.
*/
#ifndef PLAYSCENE_H
#define PLAYSCENE_H
#include "GameWindow.h"
/**
* Initializes the Play scene for the given GameWindow.
* @param pGameWindow The GameWindow.
*/
void PlayScene_Init(GameWindow *pGameWindow);
/**
* Draws the Play scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole scene needs to be redrawn.
*/
void PlayScene_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the Play scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition);
#endif

View File

@ -1,19 +1,30 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Scenes.h
*
* This file provides a variety of scene-specific types which contain the
* data that those scenes require to render properly.
*/
#ifndef SCENES_H
#define SCENES_H
#define NumScenes 4
/** The total number of scenes. */
#define NumScenes 5
/** Enum defining unique IDs for each scene. */
typedef enum eSceneId
{
Title,
LevelSelect,
Play,
LevelEnd,
GameEnd
} SceneId;
/** Struct containing data needed for the Title scene. */
typedef struct sTitleScene
{
Rect TitleRect;
@ -22,6 +33,18 @@ typedef struct sTitleScene
Rect SoundButtonRect;
} TitleScene;
/** Struct containing data needed for the LevelSelect scene. */
typedef struct sLevelSelectScene
{
int8_t PageNumber;
Rect SetRect;
Rect ScoreRect;
Rect PrevButtonRect;
Rect NextButtonRect;
Rect LevelGridRect;
} LevelSelectScene;
/** Struct containing data needed for the Play scene. */
typedef struct sPlayScene
{
Rect PlayfieldRect;
@ -33,6 +56,7 @@ typedef struct sPlayScene
Rect SoundButtonRect;
} PlayScene;
/** Struct containing data needed for the LevelEnd scene. */
typedef struct sLevelEndScene
{
Rect LevelRect;
@ -42,6 +66,7 @@ typedef struct sLevelEndScene
Rect NextButtonRect;
} LevelEndScene;
/** Struct containing data needed for the GameEnd scene. */
typedef struct sGameEndScene
{
Rect SetRect;

View File

@ -1,34 +1,49 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Sounds.c
*
* This file provides implementations for Sounds.h.
*/
#include "Sounds.h"
#define SndBaseResId 8192
/** The first snd resource ID. */
#define SndBaseResID 8192
#define ClickSndResID SndBaseResId
#define RetrySndResID (ClickSndResID + 1)
#define DoneSndResID (RetrySndResID + 1)
/** The click snd resource ID. */
#define ClickSndResID SndBaseResID
/** The retry snd resource ID. */
#define RetrySndResID (ClickSndResID + 1)
/** The done snd resource ID. */
#define DoneSndResID (RetrySndResID + 1)
/** Whether or not sound is enabled by default. */
#define DefaultEnabled true
/** Whether or not to play sound async. */
#define PlaySoundsAsync false
void Sounds_Init(Sounds *pSounds)
{
pSounds->Enabled = DefaultEnabled;
pSounds->ClickSnd = GetResource('snd ', ClickSndResID);
pSounds->ClickSnd = Get1Resource('snd ', ClickSndResID);
if (pSounds->ClickSnd == nil)
{
ShowError("\pClick snd resource missing!", true);
}
pSounds->RetrySnd = GetResource('snd ', RetrySndResID);
pSounds->RetrySnd = Get1Resource('snd ', RetrySndResID);
if (pSounds->RetrySnd == nil)
{
ShowError("\pRetry snd resource missing!", true);
}
pSounds->DoneSnd = GetResource('snd ', DoneSndResID);
pSounds->DoneSnd = Get1Resource('snd ', DoneSndResID);
if (pSounds->DoneSnd == nil)
{
ShowError("\pDone snd resource missing!", true);

View File

@ -1,6 +1,13 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file Sounds.h
*
* This file provides a Sounds type which contains handles to every
* sound resource used by the game, as well as functions for playing them.
*/
#ifndef SOUNDS_H
#define SOUNDS_H
@ -8,6 +15,7 @@
#include "MacCommon.h"
/** Struct containing handles to every sound resource. */
typedef struct sSounds
{
bool Enabled;
@ -16,10 +24,28 @@ typedef struct sSounds
Handle DoneSnd;
} Sounds;
/**
* Initializes the Sounds by loading each sound resource.
* @param pSounds The Sounds.
*/
void Sounds_Init(Sounds *pSounds);
/**
* Plays the "click" sound.
* @param pSounds The Sounds.
*/
void Sounds_PlayClickSnd(const Sounds *pSounds);
/**
* Plays the "retry" sound.
* @param pSounds The Sounds.
*/
void Sounds_PlayRetrySnd(const Sounds *pSounds);
/**
* Plays the "done" sound.
* @param pSounds The Sounds.
*/
void Sounds_PlayDoneSnd(const Sounds *pSounds);
#endif

View File

@ -1,9 +1,16 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file TitleScene.c
*
* This file provides implementations for TitleScene.h.
*/
#include "TitleScene.h"
#include "Bitmaps.h"
/** Scale factor for the text. */
#define TitleTextScale 2
void TitleScene_Init(GameWindow *pGameWindow)
@ -14,7 +21,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));
@ -27,7 +36,7 @@ void TitleScene_Init(GameWindow *pGameWindow)
CenterRect(&r, &(pGameWindow->TitleScene.SetBRect));
// Setup sound button
Bitmaps_GetSoundRect(&(pGameWindow->Bitmaps), pGameWindow->Sounds.Enabled, TitleTextScale, &(pGameWindow->TitleScene.SoundButtonRect));
Bitmaps_GetSoundRect(&(pGameWindow->Bitmaps), TitleTextScale, &(pGameWindow->TitleScene.SoundButtonRect));
GetBoxRect(pContentRect, Bottom, &r);
CenterRect(&r, &(pGameWindow->TitleScene.SoundButtonRect));
}
@ -35,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);
@ -54,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, Play);
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, Play);
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);
}
}

View File

@ -1,13 +1,36 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file TitleScene.h
*
* This file provides the functions to initialize, draw, and respond to
* clicks when the GameWindow is set to the Title scene.
*/
#ifndef TITLESCENE_H
#define TITLESCENE_H
#include "GameWindow.h"
/**
* Initializes the Title scene for the given GameWindow.
* @param pGameWindow The GameWindow.
*/
void TitleScene_Init(GameWindow *pGameWindow);
/**
* Draws the Title scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param fullRefresh Whether or not the whole scene needs to be redrawn.
*/
void TitleScene_Draw(const GameWindow *pGameWindow, bool fullRefresh);
/**
* Handles clicks on the Title scene for the given GameWindow.
* @param pGameWindow The GameWindow.
* @param pPosition The local position where the click occured.
*/
void TitleScene_Click(GameWindow *pGameWindow, const Point *pPosition);
#endif

View File

@ -1,6 +1,12 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file WindowBuffer.c
*
* This file provides implementations for WindowBuffer.h.
*/
#include "WindowBuffer.h"
#include "MacCommon.h"

View File

@ -1,17 +1,41 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file WindowBuffer.h
*
* This file provides a WindowBuffer type which manages offscreen buffering
* of Toolbox drawing commands on behalf of a Window.
*/
#ifndef WINDOWBUFFER_H
#define WINDOWBUFFER_H
/** Struct containing pointers to the Window and its offscreen buffer. */
typedef struct sWindowBuffer
{
WindowPtr Window;
GrafPtr Buffer;
} WindowBuffer;
/**
* Initializes the WindowBuffer for the given Window.
* @param pWindowBuffer The WindowBuffer.
* @param window The Window to buffer.
*/
void WindowBuffer_Init(WindowBuffer *pWindowBuffer, const WindowPtr window);
/**
* Start redirecting drawing to the offscreen buffer.
* @param pWindowBuffer The WindowBuffer.
*/
void WindowBuffer_StartDraw(const WindowBuffer *pWindowBuffer);
/**
* Stop redirecting drawing to the offscreen buffer, and immediately copy
* everything from the buffer to the Window.
* @param pWindowBuffer The WindowBuffer.
*/
void WindowBuffer_EndDraw(const WindowBuffer *pWindowBuffer);
#endif
#endif

View File

@ -1,8 +1,17 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file main.c
*
* This file provides the main() entry point.
*/
#include "MacLO.h"
/**
* Entry point for the application.
*/
void main(void)
{
MacLO_ToolBoxInit();

View File

@ -1,9 +1,16 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file stdbool.h
*
* This file backports the 'bool' boolean type from C99 to C89.
*/
#ifndef STDBOOL_H
#define STDBOOL_H
/** Boolean type. */
typedef Boolean bool;
#endif

View File

@ -1,41 +1,72 @@
// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file stdint.h
*
* This file backports the fixed-width integer types from C99 to C89.
*/
#ifndef STDINT_H
#define STDINT_H
#include <limits.h>
#if !defined(UINT8_MAX) && defined(UCHAR_MAX) && (UCHAR_MAX) == 0xFFU
/** Unsigned integer type with width of exactly 8 bits. */
typedef unsigned char uint8_t;
/** Signed integer type with width of exactly 8 bits. */
typedef signed char int8_t;
/** The max value of an uint8_t. */
#define UINT8_MAX UCHAR_MAX
/** The max value of an int8_t. */
#define INT8_MAX CHAR_MAX
/** The min value of an int8_t. */
#define INT8_MIN CHAR_MIN
#endif
#if !defined(UINT16_MAX) && defined(USHRT_MAX) && (USHRT_MAX) == 0xFFFFU
typedef unsigned short uint16_t;
typedef signed short int16_t;
#define UINT16_MAX USHRT_MAX
#define INT16_MAX SHRT_MAX
#define INT16_MIN SHRT_MIN
#endif
#if !defined(UINT32_MAX) && defined(UINT_MAX) && (UINT_MAX) == 0xFFFFFFFFUL
typedef unsigned int uint32_t;
typedef signed int int32_t;
#define UINT32_MAX UINT_MAX
#define INT32_MAX INT_MAX
#define INT32_MIN INT_MIN
/** Unsigned integer type with width of exactly 16 bits. */
typedef unsigned short uint16_t;
/** Signed integer type with width of exactly 16 bits. */
typedef signed short int16_t;
/** The max value of an uint16_t. */
#define UINT16_MAX USHRT_MAX
/** The max value of an int16_t. */
#define INT16_MAX SHRT_MAX
/** The min value of an int16_t. */
#define INT16_MIN SHRT_MIN
#endif
#if !defined(UINT32_MAX) && defined(ULONG_MAX) && (ULONG_MAX) == 0xFFFFFFFFUL
/** Unsigned integer type with width of exactly 32 bits. */
typedef unsigned long uint32_t;
/** Signed integer type with width of exactly 32 bits. */
typedef signed long int32_t;
/** The max value of an uint32_t. */
#define UINT32_MAX ULONG_MAX
/** The max value of an int32_t. */
#define INT32_MAX LONG_MAX
/** The min value of an int32_t. */
#define INT32_MIN LONG_MIN
#endif
#endif