mirror of https://github.com/jonthysell/MacLO.git
222 lines
6.2 KiB
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);
|
|
}
|