MacLO/src/PlayScene.c
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

245 lines
8.8 KiB
C

// 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
/** 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;
const Rect *pContentRect = &(pGameWindow->Window->portRect);
// Setup Playfield
pGameWindow->PlayScene.PlayfieldRect.top = PlayfieldMargin;
pGameWindow->PlayScene.PlayfieldRect.bottom = pContentRect->bottom - PlayfieldMargin;
pGameWindow->PlayScene.PlayfieldRect.left = pGameWindow->PlayScene.PlayfieldRect.top;
pGameWindow->PlayScene.PlayfieldRect.right = pGameWindow->PlayScene.PlayfieldRect.bottom;
// Setup HUD
pGameWindow->PlayScene.HUDRect.top = HUDMargin;
pGameWindow->PlayScene.HUDRect.bottom = pContentRect->bottom - HUDMargin;
pGameWindow->PlayScene.HUDRect.left = pGameWindow->PlayScene.PlayfieldRect.right + HUDMargin;
pGameWindow->PlayScene.HUDRect.right = pContentRect->right - HUDMargin;
// Setup level
GetScaledPicFrame(pGameWindow->Engine.SetB ? pGameWindow->Bitmaps.BCharPict : pGameWindow->Bitmaps.ACharPict, LevelTextScale, &(pGameWindow->PlayScene.LevelRect));
Bitmaps_GetNumberRect(&(pGameWindow->Bitmaps), 1 + pGameWindow->Engine.Level, LevelTextScale, &r);
ConcatenateRect(&(pGameWindow->PlayScene.LevelRect), &r, &(pGameWindow->PlayScene.LevelRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), Top, &r);
CenterRect(&r, &(pGameWindow->PlayScene.LevelRect));
// Setup half-stars
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), 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);
ConcatenateRect(&(pGameWindow->PlayScene.ScoreRect), &r, &(pGameWindow->PlayScene.ScoreRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), Bottom, &r);
CenterRect(&r, &(pGameWindow->PlayScene.ScoreRect));
// Setup retry button
GetPictureRect(pGameWindow->Bitmaps.RetryButtonPict, &(pGameWindow->PlayScene.RetryButtonRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), BottomLeft, &r);
CenterRect(&r, &(pGameWindow->PlayScene.RetryButtonRect));
// Setup sound button
Bitmaps_GetSoundRect(&(pGameWindow->Bitmaps), 1, &(pGameWindow->PlayScene.SoundButtonRect));
GetBoxRect(&(pGameWindow->PlayScene.HUDRect), BottomRight, &r);
CenterRect(&r, &(pGameWindow->PlayScene.SoundButtonRect));
}
void PlayScene_SetLightRect(const GameWindow *pGameWindow, Rect *pRect, const int8_t c, const int8_t r)
{
pRect->top = pGameWindow->PlayScene.PlayfieldRect.top + PlayfieldPadding + LightMargin + (r * (LightMargin + LightSize));
pRect->bottom = pRect->top + LightSize;
pRect->left = pGameWindow->PlayScene.PlayfieldRect.left + PlayfieldPadding + LightMargin + (c * (LightMargin + LightSize));
pRect->right = pRect->left + LightSize;
}
void PlayScene_Draw(const GameWindow *pGameWindow, bool fullRefresh)
{
int8_t r, c;
Rect lightRect;
if (fullRefresh)
{
// Fill backgrounds
ForeColor(whiteColor);
FrameRoundRect(&(pGameWindow->PlayScene.PlayfieldRect), PlayfieldCornerSize, PlayfieldCornerSize);
ForeColor(blackColor);
}
// Draw Playfield
// Draw lights
for (r = 0; r < PuzzleSize; r++)
{
for (c = 0; c < PuzzleSize; c++)
{
if (fullRefresh || GameEngine_LightChanged(&(pGameWindow->Engine), c, r))
{
PlayScene_SetLightRect(pGameWindow, &lightRect, c, r);
if (GameEngine_GetLight(&(pGameWindow->Engine), c, r))
{
// Draw ON light
DrawPicture(pGameWindow->Bitmaps.LightOnPict, &lightRect);
}
else
{
// Draw OFF light
DrawPicture(pGameWindow->Bitmaps.LightOffPict, &lightRect);
}
}
}
}
// Draw HUD
// Draw level
if (fullRefresh)
{
MoveTo(pGameWindow->PlayScene.LevelRect.left, pGameWindow->PlayScene.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);
}
// Draw half-stars
if (fullRefresh || GameEngine_HalfStarsChanged(&(pGameWindow->Engine)))
{
MoveTo(pGameWindow->PlayScene.HalfStarsRect.left, pGameWindow->PlayScene.HalfStarsRect.top);
Bitmaps_DrawHalfStars(&(pGameWindow->Bitmaps), GameEngine_GetHalfStars(&(pGameWindow->Engine)), MaxStars, HalfStarScale);
}
// Draw score
if (fullRefresh)
{
MoveTo(pGameWindow->PlayScene.ScoreRect.left, pGameWindow->PlayScene.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
if (fullRefresh)
{
DrawPicture(pGameWindow->Bitmaps.RetryButtonPict, &(pGameWindow->PlayScene.RetryButtonRect));
}
// Draw sound button
MoveTo(pGameWindow->PlayScene.SoundButtonRect.left, pGameWindow->PlayScene.SoundButtonRect.top);
Bitmaps_DrawSound(&(pGameWindow->Bitmaps), pGameWindow->Sounds.Enabled, 1);
}
void PlayScene_Click(GameWindow *pGameWindow, const Point *pPosition)
{
int8_t r, c;
Rect lightRect;
if (PtInRect(*pPosition, &(pGameWindow->PlayScene.PlayfieldRect)))
{
// Click within Playfield
for (r = 0; r < PuzzleSize; r++)
{
for (c = 0; c < PuzzleSize; c++)
{
PlayScene_SetLightRect(pGameWindow, &lightRect, c, r);
if (PtInRect(*pPosition, &lightRect))
{
// Toggle clicked-on light
GameEngine_ToggleLights(&(pGameWindow->Engine), c, r);
GameWindow_Draw(pGameWindow, false);
Sounds_PlayClickSnd(&(pGameWindow->Sounds));
break;
}
}
}
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));
}
}
else if (PtInRect(*pPosition, &(pGameWindow->PlayScene.HUDRect)))
{
// Click within HUD
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)))
{
// Click on sound button
GameWindow_ToggleSound(pGameWindow);
}
}
}