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

222 lines
6.2 KiB
C

// Copyright (c) Jon Thysell <http://jonthysell.com>
// Licensed under the MIT License.
/**
* @file GameEngine.c
*
* This file provides implementations for GameEngine.h.
*/
#include "GameEngine.h"
#include "Common.h"
const int8_t PuzzleSize = 5;
const uint8_t MaxStars = 3;
const uint8_t MaxHalfStars = 6;
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->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))
{
GameEngine_StartLevel(pGameEngine, (pGameEngine->Level + 1) % LevelCount);
}
}
void GameEngine_ResetLevel(GameEngine *pGameEngine)
{
GameEngine_StartLevel(pGameEngine, pGameEngine->Level);
}
void GameEngine_LoadLevel(GameEngine *pGameEngine, const int8_t level, const bool setB)
{
pGameEngine->Level = level;
pGameEngine->SetB = setB;
pGameEngine->PreviousLights = pGameEngine->Lights;
pGameEngine->Lights = Levels_GetLightsForLevel(pGameEngine->Level, setB);
pGameEngine->Par = Levels_GetParForLevel(pGameEngine->Level);
pGameEngine->Moves = 0;
}
bool GameEngine_GetLight(const GameEngine *pGameEngine, const int8_t x, const int8_t y)
{
if (x >= 0 && x < PuzzleSize && y >= 0 && y < PuzzleSize)
{
return bitRead(pGameEngine->Lights, y * PuzzleSize + x);
}
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_IsLastLevel(const GameEngine *pGameEngine)
{
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)
{
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);
return max(MinHalfStars, MaxHalfStars - halfStarsLost);
}
void GameEngine_ToggleLights(GameEngine *pGameEngine, const int8_t x, const int8_t y)
{
int8_t targetX = max(0, min(x, PuzzleSize - 1));
int8_t targetY = max(0, min(y, PuzzleSize - 1));
pGameEngine->PreviousLights = pGameEngine->Lights;
GameEngine_ToggleSingleLight(pGameEngine, targetX, targetY);
GameEngine_ToggleSingleLight(pGameEngine, targetX + 1, targetY);
GameEngine_ToggleSingleLight(pGameEngine, targetX, targetY + 1);
GameEngine_ToggleSingleLight(pGameEngine, targetX - 1, targetY);
GameEngine_ToggleSingleLight(pGameEngine, targetX, targetY - 1);
pGameEngine->Moves++;
}
void GameEngine_ToggleSingleLight(GameEngine *pGameEngine, const int8_t x, const int8_t y)
{
if (x >= 0 && x < PuzzleSize && y >= 0 && y < PuzzleSize)
{
bitToggle(pGameEngine->Lights, y * PuzzleSize + x);
}
}
bool GameEngine_LightChanged(const GameEngine *pGameEngine, const int8_t x, const int8_t y)
{
if (x >= 0 && x < PuzzleSize && y >= 0 && y < PuzzleSize)
{
return bitRead(pGameEngine->Lights, y * PuzzleSize + x) != bitRead(pGameEngine->PreviousLights, y * PuzzleSize + x);
}
return false;
}
bool GameEngine_HalfStarsChanged(const GameEngine *pGameEngine)
{
return pGameEngine->Moves == 0 ||
GameEngine_CalculateHalfStars(pGameEngine->Par, pGameEngine->Moves - 1) != GameEngine_GetHalfStars(pGameEngine);
}