MacLO/src/GameEngine.c

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);
}