summaryrefslogtreecommitdiff
path: root/source/logic
diff options
context:
space:
mode:
Diffstat (limited to 'source/logic')
-rw-r--r--source/logic/AllegroWrappers.cpp116
-rw-r--r--source/logic/AllegroWrappers.h112
-rw-r--r--source/logic/Car.cpp75
-rw-r--r--source/logic/Car.h57
-rw-r--r--source/logic/Checkpoint.cpp30
-rw-r--r--source/logic/Checkpoint.h53
-rw-r--r--source/logic/CollisionDetector.cpp64
-rw-r--r--source/logic/CollisionDetector.h75
-rw-r--r--source/logic/DestroyedObjectPopup.cpp6
-rw-r--r--source/logic/DestroyedObjectPopup.h33
-rw-r--r--source/logic/EnemyCar.cpp104
-rw-r--r--source/logic/EnemyCar.h97
-rw-r--r--source/logic/Game.cpp185
-rw-r--r--source/logic/Game.h135
-rw-r--r--source/logic/GameObject.cpp27
-rw-r--r--source/logic/GameObject.h82
-rw-r--r--source/logic/LimitedTimeObject.cpp16
-rw-r--r--source/logic/LimitedTimeObject.h45
-rw-r--r--source/logic/Maze.cpp77
-rw-r--r--source/logic/Maze.h100
-rw-r--r--source/logic/MazeMath.cpp18
-rw-r--r--source/logic/MazeMath.h59
-rw-r--r--source/logic/PlayerCar.cpp87
-rw-r--r--source/logic/PlayerCar.h99
-rw-r--r--source/logic/Rock.cpp6
-rw-r--r--source/logic/Rock.h29
-rw-r--r--source/logic/Smokescreen.cpp6
-rw-r--r--source/logic/Smokescreen.h31
28 files changed, 1824 insertions, 0 deletions
diff --git a/source/logic/AllegroWrappers.cpp b/source/logic/AllegroWrappers.cpp
new file mode 100644
index 0000000..4094cfe
--- /dev/null
+++ b/source/logic/AllegroWrappers.cpp
@@ -0,0 +1,116 @@
+#include "AllegroWrappers.h"
+
+int AllegroInit::_initCount = 0;
+
+AllegroInit::AllegroInit()
+{
+ if (_initCount==0)
+ {
+ if (!al_init())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroInit::AllegroInit(const AllegroInit& ref)
+{
+ if (_initCount==0)
+ {
+ if (!al_init())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroInit::~AllegroInit()
+{
+ --_initCount;
+ if (_initCount==0)
+ {
+ al_uninstall_system();
+ }
+}
+
+
+int AllegroKeyboardInit::_initCount = 0;
+
+AllegroKeyboardInit::AllegroKeyboardInit()
+{
+ if (_initCount==0)
+ {
+ if (!al_install_keyboard())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroKeyboardInit::AllegroKeyboardInit(const AllegroKeyboardInit& ref)
+{
+ if (_initCount==0)
+ {
+ if (!al_install_keyboard())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroKeyboardInit::~AllegroKeyboardInit()
+{
+ --_initCount;
+ if (_initCount==0) al_uninstall_keyboard();
+}
+
+int AllegroDrawingInit::_initCount = 0;
+
+AllegroDrawingInit::AllegroDrawingInit()
+{
+ if (_initCount==0)
+ {
+ if (!al_init_primitives_addon())
+ {
+ throw InstallFailure();
+ }
+ al_init_font_addon();
+ if (!al_init_ttf_addon())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroDrawingInit::AllegroDrawingInit(const AllegroDrawingInit& ref)
+{
+ if (_initCount==0)
+ {
+ if (!al_init_primitives_addon())
+ {
+ throw InstallFailure();
+ }
+ al_init_font_addon();
+ if (!al_init_ttf_addon())
+ {
+ throw InstallFailure();
+ }
+ }
+ ++_initCount;
+}
+
+AllegroDrawingInit::~AllegroDrawingInit()
+{
+ --_initCount;
+ if (_initCount==0)
+ {
+ al_shutdown_ttf_addon();
+ al_shutdown_font_addon();
+ al_shutdown_primitives_addon();
+ }
+}
diff --git a/source/logic/AllegroWrappers.h b/source/logic/AllegroWrappers.h
new file mode 100644
index 0000000..851f219
--- /dev/null
+++ b/source/logic/AllegroWrappers.h
@@ -0,0 +1,112 @@
+#ifndef ALLEGRO_WRAPPERS_H
+#define ALLEGRO_WRAPPERS_H
+
+#include <allegro5/allegro.h>
+#include <allegro5/allegro_primitives.h>
+#include <allegro5/allegro_font.h>
+#include <allegro5/allegro_ttf.h>
+
+/**
+* @brief Exception to be thrown if any component of Allegro fails to install at runtime.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class InstallFailure {};
+
+/**
+* @brief Class ensures that Allegro is initialized and uninstalled when appropriate.
+*
+* Any classes that use Allegro should include this class as a data member.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class AllegroInit
+{
+ public:
+ /**
+ * @brief Constructor calls al_init() if it is the first instance.
+ */
+ AllegroInit();
+ /**
+ * @brief Copy constructor, implemented to be included in instance count.
+ */
+ AllegroInit(const AllegroInit& ref);
+ /**
+ * @brief Destructor calls al_uninstall_system() if it is the last instant.
+ */
+
+ //assignment operator provided by compiler. _initCount does not need incrementing on assignment,
+ //because assignment does not make a new instance, just changes one.
+
+ ~AllegroInit();
+ private:
+ static int _initCount; ///< Count of the current number of initialised AllegroInit objects.
+};
+
+/**
+* @brief Class ensures that Allegro's keyboard is installed and uninstalled when appropriate.
+*
+* Any classes that use the keyboard for input should include this class as a data member.
+* This class includes AllegroInit, so both of them do not need to be included.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class AllegroKeyboardInit
+{
+ public:
+ /**
+ * @brief Constructor calls al_install_keyboard() if it is the first instance.
+ */
+ AllegroKeyboardInit();
+ /**
+ * @brief Copy constructor, implemented to be included in instance count.
+ */
+ AllegroKeyboardInit(const AllegroKeyboardInit& ref);
+ /**
+ * @brief Destructor calls al_uninstall_keyboard() if it is the last instant.
+ */
+ ~AllegroKeyboardInit();
+ private:
+ static int _initCount; ///< Count of the current number of initialised AllegroKeyboardInit objects.
+ AllegroInit _allegro; ///< Depends on Allegro being initialised.
+
+ //assignment operator provided by compiler. _initCount does not need incrementing on assignment,
+ //because assignment does not make a new instance, just changes one.
+};
+
+/**
+* @brief Class ensures that Allegro's primitive and text drawing is installed and uninstalled when appropriate.
+*
+* Any classes that draw primitives should include this class as a data member.
+* This class includes AllegroInit, so both of them do not need to be included.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class AllegroDrawingInit
+{
+ public:
+ /**
+ * @brief Constructor calls al_init_primitives_addon(), al_init_font_addon(), and al_init_ttf_addon() if it is the first instance.
+ */
+ AllegroDrawingInit();
+ /**
+ * @brief Copy constructor, implemented to be included in instance count.
+ */
+ AllegroDrawingInit(const AllegroDrawingInit& ref);
+ /**
+ * @brief Destructor calls al_shutdown_primitives_addon(), al_shutdown_font_addon(), and al_shutdown_ttf_addon() if it is the last instant.
+ */
+ ~AllegroDrawingInit();
+ private:
+ static int _initCount;
+ AllegroInit _allegro;
+
+ //assignment operator provided by compiler. _initCount does not need incrementing on assignment,
+ //because assignment does not make a new instance, just changes one.
+};
+
+#endif
diff --git a/source/logic/Car.cpp b/source/logic/Car.cpp
new file mode 100644
index 0000000..3e531a3
--- /dev/null
+++ b/source/logic/Car.cpp
@@ -0,0 +1,75 @@
+#include "Car.h"
+
+Car::Car(double x, double y, BitmapStore::Image image, Maze::Direction facing)
+ :GameObject(x,y,image,facing),
+ _speed(_baseSpeed)
+{
+}
+
+double Car::speed() const
+{
+ return _speed;
+}
+
+void Car::move(const Maze& maze)
+{
+ double targetX = 0;
+ double targetY = 0;
+
+ int checkX = 0;
+ int checkY = 0;
+
+ switch(_facing)
+ {
+ case Maze::UP:
+ targetX = MazeMath::round(_x);
+ targetY = _y - _speed;
+ checkX = floor(targetX);
+ checkY = floor(targetY);
+ break;
+ case Maze::DOWN:
+ targetX = MazeMath::round(_x);
+ targetY = _y + _speed;
+ checkX = floor(targetX);
+ checkY = ceil(targetY);
+ break;
+ case Maze::LEFT:
+ targetX = _x - _speed;
+ targetY = MazeMath::round(_y);
+ checkX = floor(targetX);
+ checkY = floor(targetY);
+ break;
+ case Maze::RIGHT:
+ targetX = _x + _speed;
+ targetY = MazeMath::round(_y);
+ checkX = ceil(targetX);
+ checkY = floor(targetY);
+ break;
+ }
+
+ if (!maze.getSolid(checkX, checkY))
+ {
+ //can move that way
+ _x = targetX;
+ _y = targetY;
+ }
+ else
+ {
+ //can not move to targetX and targetY, but move to the edge of current block
+ switch(_facing)
+ {
+ case Maze::UP:
+ _y = floor(_y);
+ break;
+ case Maze::DOWN:
+ _y = ceil(_y);
+ break;
+ case Maze::LEFT:
+ _x = floor(_x);
+ break;
+ case Maze::RIGHT:
+ _x = ceil(_x);
+ break;
+ }
+ }
+}
diff --git a/source/logic/Car.h b/source/logic/Car.h
new file mode 100644
index 0000000..4b8d16c
--- /dev/null
+++ b/source/logic/Car.h
@@ -0,0 +1,57 @@
+#ifndef CAR_H
+#define CAR_H
+
+#include <cmath>
+using namespace std;
+
+#include "../presentation/BitmapStore.h"
+#include "../logic/GameObject.h"
+#include "../logic/MazeMath.h"
+
+/**
+* @brief GameObject that moves through the maze and changes direction.
+*
+* Should not be instantiated directly, but rather instantiated through one of the subclasses,
+* PlayerCar or EnemyCar.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Car : public GameObject
+{
+ public:
+ /**
+ * @brief Creates a Car at the given position, with the given image, facing in the given direction.
+ *
+ * @param [in] x x coordinate of initial position.
+ * @param [in] y y coordinate of initial position.
+ * @param [in] image Bitmap to be drawn on the screen to represent the car.
+ * @param [in] facing Direction in which the Car is initially facing.
+ */
+ Car(double x, double y, BitmapStore::Image image, Maze::Direction facing);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Function to access the current speed of the car.
+ *
+ * @return The current speed of the car, in pixels per update.
+ */
+ double speed() const;
+
+ protected:
+ /**
+ * @brief Moves the car by its current speed in the direction of its facing.
+ *
+ * Only moves along the x or y axis, and snaps to the grid in the other direction.
+ * Does not allow movement through solid parts of the maze.
+ *
+ * @param [in] maze The maze in which the Car is moving, confining its movements.
+ */
+ void move(const Maze& maze);
+
+ double _speed; ///< The current speed that the Car is moving at.
+ static const double _baseSpeed = 0.1; ///< The speed that a Car moves at in normal conditions.
+};
+
+#endif // CAR_H
diff --git a/source/logic/Checkpoint.cpp b/source/logic/Checkpoint.cpp
new file mode 100644
index 0000000..8a6406c
--- /dev/null
+++ b/source/logic/Checkpoint.cpp
@@ -0,0 +1,30 @@
+#include "Checkpoint.h"
+
+int Checkpoint::_checkpointCount = 0;
+
+Checkpoint::Checkpoint(double x, double y)
+ :GameObject(x,y,BitmapStore::CHECKPOINT)
+{
+ ++_checkpointCount;
+}
+
+Checkpoint::Checkpoint(const Checkpoint& ref)
+ :GameObject(ref._x,ref._y,ref._image)
+{
+ ++_checkpointCount;
+}
+
+Checkpoint::~Checkpoint()
+{
+ --_checkpointCount;
+}
+
+int Checkpoint::checkpointCount()
+{
+ return _checkpointCount;
+}
+
+void Checkpoint::collect()
+{
+ _destroyed = true;
+}
diff --git a/source/logic/Checkpoint.h b/source/logic/Checkpoint.h
new file mode 100644
index 0000000..f6dfeb3
--- /dev/null
+++ b/source/logic/Checkpoint.h
@@ -0,0 +1,53 @@
+#ifndef CHECKPOINT_H
+#define CHECKPOINT_H
+
+#include "../logic/GameObject.h"
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief GameObject that the player needs to pick up by driving over.
+*
+* The level is complete when all checkpoints have been collected.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Checkpoint: public GameObject
+{
+ public:
+ /**
+ * @brief Function for accessing the number of checkpoints that currently exist.
+ *
+ * @return The number of checkpoints that currently exist.
+ */
+ static int checkpointCount();
+
+ /**
+ * @brief Creates a checkpoint at the given coordinates.
+ *
+ * @param [in] x x coordinate of Checkpoint's position.
+ * @param [in] y y coordinate of Checkpoint's position.
+ */
+ Checkpoint(double x, double y);
+ /**
+ * @brief Copy constuctor, overwritten to include in the counting of Checkpoints.
+ */
+ Checkpoint(const Checkpoint& ref);
+
+ //assignment operator has been left with the compiler generated version.
+
+ /**
+ * @brief Destructor, decreases the number of Checkpoints in existence.
+ */
+ ~Checkpoint();
+
+ /**
+ * @brief Function to be called when a PlayerCar collects the Checkpoint.
+ */
+ void collect();
+
+ private:
+ static int _checkpointCount; ///< Count of the number of Checkpoints currently in existence.
+};
+
+#endif // CHECKPOINT_H
diff --git a/source/logic/CollisionDetector.cpp b/source/logic/CollisionDetector.cpp
new file mode 100644
index 0000000..85ebaa8
--- /dev/null
+++ b/source/logic/CollisionDetector.cpp
@@ -0,0 +1,64 @@
+#include "CollisionDetector.h"
+
+void CollisionDetector::checkCollisions(list<PlayerCar>& players, list<EnemyCar>& enemies, list<Checkpoint>& checkpoints, list<Rock>& rocks, list<Smokescreen>& smokescreens)
+{
+ for (list<PlayerCar>::iterator playIter = players.begin(); playIter!=players.end(); ++playIter)
+ {
+ for (list<EnemyCar>::iterator enemyIter = enemies.begin(); enemyIter!=enemies.end(); ++enemyIter)
+ {
+ if ((abs(playIter->x() - enemyIter->x())<1)&&(abs(playIter->y() - enemyIter->y())<1))
+ {
+ collision(*playIter, *enemyIter);
+ }
+ }
+
+ for (list<Checkpoint>::iterator checkIter = checkpoints.begin(); checkIter!=checkpoints.end(); ++checkIter)
+ {
+ if ((abs(playIter->x() - checkIter->x())<1)&&(abs(playIter->y() - checkIter->y())<1))
+ {
+ collision(*playIter, *checkIter);
+ }
+ }
+
+ for (list<Rock>::iterator rockIter = rocks.begin(); rockIter!=rocks.end(); ++rockIter)
+ {
+ if ((abs(playIter->x() - rockIter->x())<1)&&(abs(playIter->y() - rockIter->y())<1))
+ {
+ collision(*playIter, *rockIter);
+ }
+ }
+ }
+
+ for (list<EnemyCar>::iterator enemyIter = enemies.begin(); enemyIter!=enemies.end(); ++enemyIter)
+ {
+ for (list<Smokescreen>::iterator smokeIter = smokescreens.begin(); smokeIter!=smokescreens.end(); ++smokeIter)
+ {
+ if ((abs(enemyIter->x() - smokeIter->x())<1)&&(abs(enemyIter->y() - smokeIter->y())<1))
+ {
+ collision(*enemyIter, *smokeIter);
+ }
+ }
+ }
+}
+
+void CollisionDetector::collision(PlayerCar& player, Checkpoint& checkpoint)
+{
+ player.gotCheckpoint();
+ checkpoint.collect();
+}
+
+void CollisionDetector::collision(PlayerCar& player, Rock& rock)
+{
+ player.crash();
+}
+
+void CollisionDetector::collision(PlayerCar& player, EnemyCar& enemy)
+{
+ player.crash();
+ enemy.crash();
+}
+
+void CollisionDetector::collision(EnemyCar& enemy, Smokescreen& smokescreen)
+{
+ enemy.blind();
+}
diff --git a/source/logic/CollisionDetector.h b/source/logic/CollisionDetector.h
new file mode 100644
index 0000000..294852d
--- /dev/null
+++ b/source/logic/CollisionDetector.h
@@ -0,0 +1,75 @@
+#ifndef COLLISIONDETECTOR_H
+#define COLLISIONDETECTOR_H
+
+#include <list>
+using namespace std;
+
+#include "../logic/PlayerCar.h"
+#include "../logic/EnemyCar.h"
+#include "../logic/Checkpoint.h"
+#include "../logic/Rock.h"
+#include "../logic/Smokescreen.h"
+
+/**
+* @brief Object for handling collisions between GameObjects.
+*
+* Collisions between all relevant objects are checked and the appropriate methods on the GameObjects
+* are called when a collision occurs.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class CollisionDetector
+{
+ public:
+ /**
+ * @brief Checks for collisions between all relevant pairs of objects, and calls the relevant collision function if one is found.
+ *
+ * A collision occurs if the distance between two object's x values is less than 1,
+ * and the distance between their y values is also less than 1.
+ *
+ * @param [in,out] players List of PlayerCars, that can collide with EnemieCars, Checkpoints, or Rocks.
+ * @param [in,out] enemies List of EnemyCars, that can collide with PlayerCars, or Smokescreens.
+ * @param [in,out] checkpoints List of Checkpoints, that can collide with PlayerCars.
+ * @param [in,out] rocks List of Rocks, that can collide with PlayerCars.
+ * @param [in,out] smokescreens List of Smokescreens, that can collide with EnemyCars.
+ */
+ void checkCollisions(list<PlayerCar>& players, list<EnemyCar>& enemies, list<Checkpoint>& checkpoints, list<Rock>& rocks, list<Smokescreen>& smokescreens);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ private:
+ /**
+ * @brief Collision between a PlayerCar and a Checkpoint.
+ *
+ * @param [in,out] player PlayerCar involved in the collision.
+ * @param [in,out] checkpoint Checkpoint involved in the collision.
+ */
+ void collision(PlayerCar& player, Checkpoint& checkpoint);
+
+ /**
+ * @brief Collision between a PlayerCar and an EnemyCar.
+ *
+ * @param [in,out] player PlayerCar involved in the collision.
+ * @param [in,out] enemy EnemyCar involved in the collision.
+ */
+ void collision(PlayerCar& player, EnemyCar& enemy);
+
+ /**
+ * @brief Collision between a PlayerCar and a Rock.
+ *
+ * @param [in,out] player PlayerCar involved in the collision.
+ * @param [in,out] rock Rock involved in the collision.
+ */
+ void collision(PlayerCar& player, Rock& rock);
+
+ /**
+ * @brief Collision between an EnemyCar and a Smokescreen.
+ *
+ * @param [in,out] enemy EnemyCar involved in the collision.
+ * @param [in,out] smokescreen Smokescreen involved in the collision.
+ */
+ void collision(EnemyCar& enemy, Smokescreen& smokescreen);
+};
+
+#endif // COLLISIONDETECTOR_H
diff --git a/source/logic/DestroyedObjectPopup.cpp b/source/logic/DestroyedObjectPopup.cpp
new file mode 100644
index 0000000..42ce08d
--- /dev/null
+++ b/source/logic/DestroyedObjectPopup.cpp
@@ -0,0 +1,6 @@
+#include "DestroyedObjectPopup.h"
+
+DestroyedObjectPopup::DestroyedObjectPopup(double x, double y, BitmapStore::Image image)
+ :LimitedTimeObject(x, y, image, POPUP_TIME)
+{
+}
diff --git a/source/logic/DestroyedObjectPopup.h b/source/logic/DestroyedObjectPopup.h
new file mode 100644
index 0000000..3e694f5
--- /dev/null
+++ b/source/logic/DestroyedObjectPopup.h
@@ -0,0 +1,33 @@
+#ifndef DESTROYEDOBJECTPOPUP_H
+#define DESTROYEDOBJECTPOPUP_H
+
+#include "../logic/LimitedTimeObject.h"
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief Object that appears on the screen for a short time when another object has been destroyed.
+*
+* Used to give extra visual feedback when a checkpoint has been collected or a Car crashes.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class DestroyedObjectPopup : public LimitedTimeObject
+{
+ public:
+ /**
+ * @brief Creates the popup at the given location, with the given image.
+ *
+ * @param [in] x The x coordinate of the object's position.
+ * @param [in] y The y coordinate of the object's position.
+ * @param [in] image The bitmap to be shown until the popup disappears.
+ */
+ DestroyedObjectPopup(double x, double y, BitmapStore::Image image);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ private:
+ static const int POPUP_TIME = 30; ///< The number of frames that the DestroyedObjectPopup exists before it is destroyed. 1 second at FPS=30.
+};
+
+#endif // DESTROYEDOBJECTPOPUP_H
diff --git a/source/logic/EnemyCar.cpp b/source/logic/EnemyCar.cpp
new file mode 100644
index 0000000..ce455a6
--- /dev/null
+++ b/source/logic/EnemyCar.cpp
@@ -0,0 +1,104 @@
+#include "EnemyCar.h"
+
+EnemyCar::EnemyCar(double x, double y)
+ :Car(x,y,BitmapStore::ENEMY,Maze::UP),
+ _state(CHASING),
+ _targetX(x),
+ _targetY(y)
+{
+}
+
+void EnemyCar::update(const Maze& maze, const list<PlayerCar>& players, const list<Rock>& rocks)
+{
+ if (!players.empty()) checkFacing(maze, players.front().x(), players.front().y(), rocks);
+
+ if (_state!=BLINDED)
+ {
+ move(maze);
+ }
+ else
+ {
+ _state = CHASING;
+ _speed = _baseSpeed;
+ }
+}
+
+void EnemyCar::checkFacing(const Maze& maze, double chasingX, double chasingY, const list<Rock>& rocks)
+{
+ if (abs(_x - _targetX)>_speed || abs(_y - _targetY)>_speed) return;
+
+ map<Maze::Direction, pair<double, double> > adjacentBlocks;
+ pair<double, double> evaluatingTarget;
+
+ adjacentBlocks[Maze::LEFT] = make_pair(MazeMath::round(_x-1), MazeMath::round(_y));
+ adjacentBlocks[Maze::RIGHT] = make_pair(MazeMath::round(_x+1), MazeMath::round(_y));
+ adjacentBlocks[Maze::UP] = make_pair(MazeMath::round(_x), MazeMath::round(_y-1));
+ adjacentBlocks[Maze::DOWN] = make_pair(MazeMath::round(_x), MazeMath::round(_y+1));
+
+ //remove adjacent blocks that would result in crashing into a rock or a wall
+ for (map<Maze::Direction, pair<double, double> >::iterator iter=adjacentBlocks.begin(); iter!=adjacentBlocks.end(); )
+ {
+ if (rockAtLocation(iter->second.first, iter->second.second, rocks) || maze.getSolid(static_cast<int>(iter->second.first),static_cast<int>(iter->second.second)))
+ {
+ adjacentBlocks.erase(iter);
+ iter = adjacentBlocks.begin();
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ if (adjacentBlocks.empty())
+ {
+ _speed = 0;
+ return;
+ }
+ else
+ {
+ _speed = _baseSpeed;
+ }
+
+ map<Maze::Direction, pair<double, double> >::iterator reverseFacing = adjacentBlocks.find(Maze::backwards(_facing));
+ if ((reverseFacing != adjacentBlocks.end()) && (adjacentBlocks.size()>1))
+ {
+ adjacentBlocks.erase(reverseFacing);
+ }
+
+ map<Maze::Direction, pair<double, double> >::const_iterator closestAdjacent = adjacentBlocks.begin();
+ double closestDistance = MazeMath::distance(closestAdjacent->second.first, closestAdjacent->second.second, chasingX, chasingY);
+
+ for (map<Maze::Direction, pair<double, double> >::const_iterator iter = ++adjacentBlocks.begin(); iter!=adjacentBlocks.end(); ++iter)
+ {
+ double newDistance = MazeMath::distance(iter->second.first, iter->second.second, chasingX, chasingY);
+ if (newDistance < closestDistance)
+ {
+ closestDistance = newDistance;
+ closestAdjacent = iter;
+ }
+ }
+
+ _targetX = closestAdjacent->second.first;
+ _targetY = closestAdjacent->second.second;
+ _facing = closestAdjacent->first;
+}
+
+bool EnemyCar::rockAtLocation(double x, double y, const list<Rock>& rocks)
+{
+ for (list<Rock>::const_iterator iter = rocks.begin(); iter!=rocks.end(); ++iter)
+ {
+ if (abs(x - iter->x())<1 && abs(y - iter->y())<1) return true;
+ }
+ return false;
+}
+
+void EnemyCar::crash()
+{
+ _destroyed = true;
+}
+
+void EnemyCar::blind()
+{
+ _state = BLINDED;
+ _speed = 0;
+}
diff --git a/source/logic/EnemyCar.h b/source/logic/EnemyCar.h
new file mode 100644
index 0000000..b56c9d3
--- /dev/null
+++ b/source/logic/EnemyCar.h
@@ -0,0 +1,97 @@
+#ifndef ENEMYCAR_H
+#define ENEMYCAR_H
+
+#include <cmath>
+
+#include "../presentation/BitmapStore.h"
+
+#include "../logic/Car.h"
+#include "../logic/PlayerCar.h"
+#include "../logic/Rock.h"
+#include "../logic/MazeMath.h"
+
+/**
+* @brief GameObject that chases the player around the maze.
+*
+* Attempts to collide with the player, causing the player to lose.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class EnemyCar: public Car
+{
+ public:
+ /**
+ * @brief Creates an EnemyCar at the given coordinates.
+ *
+ * @param [in] x The x coordinate of the EnemyCar's initial position.
+ * @param [in] y The y coordinate of the EnemyCar's initial position.
+ */
+ EnemyCar(double x, double y);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Processes one frame's worth of activity for the object, called every frame.
+ *
+ * Primarily adjusts the facing if neccesary and then moves using the inhereted move function.
+ *
+ * @param maze The maze that confines the EnemyCar's movements.
+ * @param players The list of PlayerCars that the EnemyCar can chase.
+ * @param rocks The list of Rocks that need to be avoided.
+ */
+ void update(const Maze& maze, const list<PlayerCar>& players, const list<Rock>& rocks);
+
+ /**
+ * @brief Function that is called when an EnemyCar crashes into a PlayerCar.
+ */
+ void crash();
+
+ /**
+ * @brief Function that is called when an EnemyCar drives into a Smokescreen.
+ */
+ void blind();
+
+ private:
+ /**
+ * @brief States that define how the EnemyCar's AI should behave.
+ *
+ * This would need to be expanded to include more states in order to make the enemies appear smarter.
+ */
+ enum States {
+ BLINDED, ///< The EnemyCar can not see, and so does not move.
+ CHASING ///< The EnemyCar tries to drive to the block that the player is currently on.
+ };
+
+ States _state; ///< The state that the object is currently in.
+ double _targetX; ///< The x coordinate that the EnemyCar is driving towards.
+ double _targetY; ///< The y coordinate that the EnemyCar is driving towards.
+
+ /**
+ * @brief Updates the direction that the EnemyCar is facing, if neccesary.
+ *
+ * The facing is only changed once the current _targetX and _targetY are reached. After that, a facing is
+ * chosen that points into an empty block (no maze walls or rocks) that is closest to the chasing x and y
+ * using a straight line. This results in the enemy not always taking the shortest route, but it makes it
+ * possible to escape enemies. _targetX and _targetY are updated to one block in the new facing direction.
+ * The enemy may only turn around and head backwards if there is no other options, so once the enemy starts
+ * driving down narrow a path it will continue to the end of the path.
+ *
+ * @param [in] maze The maze that confines the EnemyCar's movements.
+ * @param [in] chasingX The x coordinate that the EnemyCar is ultimately trying to reach.
+ * @param [in] chasingY The y coordinate that the EnemyCar is ultimately trying to reach.
+ * @param [in] rocks The Rocks that the EnemyCar needs to avoid.
+ */
+ void checkFacing(const Maze& maze, double chasingX, double chasingY, const list<Rock>& rocks);
+
+ /**
+ * @brief Iterates through a list of Rocks and determines if moving to a given position would result in a collision.
+ *
+ * @param [in] x The potential new x coordinate.
+ * @param [in] y The potential new y coordinate.
+ * @param [in] rocks The Rocks that are checked for a collision at x and y.
+ */
+ bool rockAtLocation(double x, double y, const list<Rock>& rocks);
+};
+
+#endif // ENEMYCAR_H
diff --git a/source/logic/Game.cpp b/source/logic/Game.cpp
new file mode 100644
index 0000000..57abd79
--- /dev/null
+++ b/source/logic/Game.cpp
@@ -0,0 +1,185 @@
+#include "Game.h"
+
+Game::Game()
+ :_config("config.txt"),
+ _screen(_config.screenWidth(), _config.screenHeight(), _config.fullscreen())
+{
+ _timer = al_create_timer(1.0/FPS);
+ _timerEvents = al_create_event_queue();
+ al_register_event_source(_timerEvents, al_get_timer_event_source(_timer));
+}
+
+Game::~Game()
+{
+ al_destroy_event_queue(_timerEvents);
+ al_destroy_timer(_timer);
+
+}
+
+void Game::start()
+{
+ while (!_screen.exitClicked())
+ {
+ string filename = _screen.getLevel();
+ if (!filename.empty())
+ {
+ initLevel(filename);
+ runloop();
+ }
+ }
+}
+
+void Game::initLevel(const string& levelFile)
+{
+ clearLists();
+ LevelReader reader(levelFile);
+ reader.readLevel(_maze, _players, _enemies, _checkpoints, _rocks);
+}
+
+void Game::runloop()
+{
+ bool gameWon = false;
+ bool gameLost = false;
+ al_start_timer(_timer);
+
+ while (!_screen.exitClicked())
+ {
+ al_wait_for_event(_timerEvents, NULL);
+ al_flush_event_queue(_timerEvents);
+
+ update();
+ _collisionDetector.checkCollisions(_players, _enemies, _checkpoints, _rocks, _smokescreens);
+ cleanup();
+ _screen.draw(_maze, _players, _enemies, _checkpoints, _rocks, _smokescreens, _popups);
+
+ gameLost = _players.empty();
+ gameWon = Checkpoint::checkpointCount()==0;
+
+ if (gameLost)
+ {
+ _screen.drawLoss();
+ for (int i=0; i<90; i++)
+ {
+ al_wait_for_event(_timerEvents, NULL);
+ al_drop_next_event(_timerEvents);
+ }
+ break;
+ }
+ else if (gameWon)
+ {
+ _screen.drawWin();
+ for (int i=0; i<90; i++)
+ {
+ al_wait_for_event(_timerEvents, NULL);
+ al_drop_next_event(_timerEvents);
+ }
+ break;
+ }
+ }
+ al_stop_timer(_timer);
+}
+
+void Game::update()
+{
+ for (list<PlayerCar>::iterator iter = _players.begin(); iter!=_players.end(); ++iter)
+ {
+ iter->update(_maze, _smokescreens);
+ }
+
+ for (list<EnemyCar>::iterator iter = _enemies.begin(); iter!=_enemies.end(); ++iter)
+ {
+ iter->update(_maze, _players, _rocks);
+ }
+
+ for (list<Smokescreen>::iterator iter = _smokescreens.begin(); iter!=_smokescreens.end(); ++iter)
+ {
+ iter->update();
+ }
+ for (list<DestroyedObjectPopup>::iterator iter = _popups.begin(); iter!=_popups.end(); ++iter)
+ {
+ iter->update();
+ }
+}
+
+void Game::cleanup()
+{
+ for (list<PlayerCar>::iterator iter = _players.begin(); iter!=_players.end();)
+ {
+ if (iter->destroyed())
+ {
+ _popups.push_back(DestroyedObjectPopup(iter->x(), iter->y(), BitmapStore::CRASHED_CAR));
+ iter = _players.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ for (list<EnemyCar>::iterator iter = _enemies.begin(); iter!=_enemies.end();)
+ {
+ if (iter->destroyed())
+ {
+ _popups.push_back(DestroyedObjectPopup(iter->x(), iter->y(), BitmapStore::CRASHED_CAR));
+ iter = _enemies.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ for (list<Checkpoint>::iterator iter = _checkpoints.begin(); iter!=_checkpoints.end();)
+ {
+ if (iter->destroyed())
+ {
+ _popups.push_back(DestroyedObjectPopup(iter->x(), iter->y(), BitmapStore::CLAIMED_CHECKPOINT));
+ iter = _checkpoints.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ for (list<Rock>::iterator iter = _rocks.begin(); iter!=_rocks.end();)
+ {
+ if (iter->destroyed())
+ {
+ iter = _rocks.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ for (list<Smokescreen>::iterator iter = _smokescreens.begin(); iter!=_smokescreens.end();)
+ {
+ if (iter->destroyed())
+ {
+ iter = _smokescreens.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ for (list<DestroyedObjectPopup>::iterator iter = _popups.begin(); iter!=_popups.end();)
+ {
+ if (iter->destroyed())
+ {
+ iter = _popups.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+}
+
+void Game::clearLists()
+{
+ _players.clear();
+ _enemies.clear();
+ _checkpoints.clear();
+ _rocks.clear();
+ _smokescreens.clear();
+ _popups.clear();
+}
diff --git a/source/logic/Game.h b/source/logic/Game.h
new file mode 100644
index 0000000..9adff76
--- /dev/null
+++ b/source/logic/Game.h
@@ -0,0 +1,135 @@
+#ifndef GAME_H
+#define GAME_H
+
+#include <list>
+#include <algorithm>
+using namespace std;
+
+#include <allegro5/allegro.h>
+
+#include "../presentation/Screen.h"
+#include "../presentation/BitmapStore.h"
+
+#include "../logic/Maze.h"
+#include "../logic/PlayerCar.h"
+#include "../logic/EnemyCar.h"
+#include "../logic/Checkpoint.h"
+#include "../logic/Rock.h"
+#include "../logic/Smokescreen.h"
+#include "../logic/DestroyedObjectPopup.h"
+#include "../logic/AllegroWrappers.h"
+#include "../logic/CollisionDetector.h"
+
+#include "../data/LevelReader.h"
+#include "../data/Config.h"
+
+/**
+* @brief The object that controls the flow of the game, and the launch point of the game.
+*
+* Game contains the various components, including the screen, the maze, and all of the
+* objects in the maze. The timing of the gameloop also falls under Game's control.
+* Essencially, Game is the central point that everything connects to.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Game
+{
+ public:
+ static const unsigned int FPS = 30; ///< Frames per second, the number of times the gameloop is run every second.
+
+ /**
+ * @brief Constructor, that creates the relevant Allegro entities.
+ */
+ Game();
+
+ /**
+ * @brief Constructor, that destroys the relevant Allegro entities.
+ */
+ ~Game();
+
+ /**
+ * @brief Entry point for the program. This should be called from main.
+ */
+ void start();
+
+ private:
+ /**
+ * @brief Unimplemented copy constructor, prevents copying of Game objects.
+ *
+ * Copying a Game is unneccesary as there should only be a single Game object.
+ */
+ Game(const Game& ref);
+ /**
+ * @brief Unimplemented assignment operator.
+ *
+ * @see Game::Game(const Game& ref)
+ */
+ Game& operator=(const Game& rhs);
+
+ /**
+ * @brief Initialises all of the GameObject lists using a file.
+ *
+ * @param [in] levelFile The path of the file that contains the level layout.
+ */
+ void initLevel(const string& levelFile);
+
+ /**
+ * @brief Main part of the game, performs the actions in each frame FPS times per second until the game is over.
+ *
+ * Each frame runs the update methods of each of the GameObjects in the lists. The CollisionDetector
+ * then checks for collisions between objects. Any GameObjects that have been destroyed are then removed
+ * from their lists. Finally, the Screen is called to draw all of the GameObjects that still exist in
+ * their new positions.
+ *
+ * Before the next iteration begins, a check is done for the victory and loss conditions. The loop is
+ * ended if either of these are met, or if the player has quit the game.
+ */
+ void runloop();
+
+ /**
+ * @brief Calls the update method on each of the GameObjects in the game.
+ */
+ void update();
+ /**
+ * @brief Removes any GameObjects that have been destroyed from their lists.
+ */
+ void cleanup();
+
+ /**
+ * @brief Destroys all GameObjects in the game, resetting the lists for a new level to be loaded.
+ *
+ * This should always be called before a new level is loaded.
+ */
+ void clearLists();
+
+ AllegroInit _allegro; ///< Handles dependencies on Allegro being installed.
+
+ Config _config; ///< Loads configuration from file on construction, used to set resolution of screen.
+ Screen _screen; ///< Handles all drawing operations.
+ ALLEGRO_TIMER* _timer; ///< Creates FPS events per second, that are put into _timerEvents.
+ ALLEGRO_EVENT_QUEUE* _timerEvents; ///< Catches events from _timer, used to regulate speed of runloop.
+
+ Maze _maze; ///< The environment that confines the movements of GameObjects, specifically Cars.
+ /**
+ * @brief Typically a single PlayerCar, controlled by the person playing the game.
+ *
+ * A list was used for _players to allow the Game object to be constructed without needing to initialise
+ * a meaningless PlayerCar object. This also allows the PlayerCar to be destroyed by Rocks or EnemyCars.
+ * An added benefit is that it adds the ease of extending the game to allow multiple players. To add
+ * multiplayer, the KeyboardHandler would need to be modified to allow different sets of input keys,
+ * and the Screen would need to be modified to keep all players visible, but the Game class would be
+ * able to remain largely unchanged.
+ */
+ list<PlayerCar> _players;
+
+ list<EnemyCar> _enemies; ///< List of all EnemyCars chasing the player.
+ list<Checkpoint> _checkpoints; ///< List of checkpoints that the player needs to collect.
+ list<Rock> _rocks; ///< List of rocks that the player and EnemyCars need to avoid.
+ list<Smokescreen> _smokescreens; ///< List of Smokescreen objects that are currently able to delay EnemyCars.
+ list<DestroyedObjectPopup> _popups; ///< List of purely visual DestroyedObjectPopups that need to be drawn.
+
+ CollisionDetector _collisionDetector; ///< Object that checks for collisions each frame.
+};
+
+#endif // GAME_H
diff --git a/source/logic/GameObject.cpp b/source/logic/GameObject.cpp
new file mode 100644
index 0000000..07957e5
--- /dev/null
+++ b/source/logic/GameObject.cpp
@@ -0,0 +1,27 @@
+#include "GameObject.h"
+
+GameObject::GameObject(double x, double y, BitmapStore::Image image, Maze::Direction facing)
+ :_x(x), _y(y), _destroyed(false), _image(image), _facing(facing)
+{
+}
+
+double GameObject::x() const
+{
+ return _x;
+}
+double GameObject::y() const
+{
+ return _y;
+}
+bool GameObject::destroyed() const
+{
+ return _destroyed;
+}
+BitmapStore::Image GameObject::image() const
+{
+ return _image;
+}
+Maze::Direction GameObject::facing() const
+{
+ return _facing;
+}
diff --git a/source/logic/GameObject.h b/source/logic/GameObject.h
new file mode 100644
index 0000000..bd7fd0b
--- /dev/null
+++ b/source/logic/GameObject.h
@@ -0,0 +1,82 @@
+#ifndef GAMEOBJECT_H
+#define GAMEOBJECT_H
+
+#include "../presentation/BitmapStore.h"
+#include "../logic/Maze.h"
+
+/**
+* @brief Parent class for objects that are placed in the maze.
+*
+* These objects are one maze block big. The image in the bitmap store will be drawn
+* on the screen every frame at the Screen class's discression, being rotated to face
+* in the 'facing' direction. Coordinates are given in terms of the Maze class's coordinate
+* system. For example, increasing the x coordinate of an object by 1 will move it one maze
+* block to the right. The number of pixels that this corresponds to is handled by the
+* Screen class.
+*
+* When an object is in a situation that it should be destroyed, it is marked for destruction.
+* It is then the responsibility of the Game class to actually destroy it.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class GameObject
+{
+ public:
+ /**
+ * @brief Creates a GameObject with the given parameters.
+ *
+ * @param [in] x The x coordinate of the new object.
+ * @param [in] y The y coordinate of the new object.
+ * @param [in] image The image that is drawn to represent the object.
+ * @param [in] facing The direction that the object is facing. If the object has no direction,
+ * such as with Checkpoint or Rock, the default value of Maze::UP should be used.
+ */
+ GameObject(double x, double y, BitmapStore::Image image, Maze::Direction facing=Maze::UP);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Provides access to the x coordinate of the object.
+ *
+ * @return The x coordinate of the object, in maze blocks, where 0 is the far left column of the maze.
+ */
+ double x() const;
+
+ /**
+ * @brief Provides access to the y coordinate of the object.
+ *
+ * @return The y coordinate of the object, in maze blocks, where 0 is the top row of the maze.
+ */
+ double y() const;
+
+ /**
+ * @brief Checks if an object has been marked for destruction, for example through a collision.
+ *
+ * @return True is the object has been marked for destruction, false otherwise.
+ */
+ bool destroyed() const;
+
+ /**
+ * @brief Provides access to the image that should be drawn to represent the object.
+ *
+ * @return An image, corresponding to an enumerated type that can be converted into a bitmap by the BitmapStore class.
+ */
+ BitmapStore::Image image() const;
+
+ /**
+ * @brief Provides access to the direction that the object is facing.
+ *
+ * @return A direction, corresponding to the rotation that should be done to the drawn image and, in the case of Cars, the direction that they move forward.
+ */
+ Maze::Direction facing() const;
+
+ protected:
+ double _x; ///< The x coordinate of the object's position.
+ double _y; ///< The y coordinate of the object's position.
+ bool _destroyed; ///< True if the object has been marked for destruction.
+ BitmapStore::Image _image; ///< The bitmap that should be drawn on the screen to represent the object.
+ Maze::Direction _facing; ///< The direction in which the object is facing, up, down, left, or right.
+};
+
+#endif // GAMEOBJECT_H
diff --git a/source/logic/LimitedTimeObject.cpp b/source/logic/LimitedTimeObject.cpp
new file mode 100644
index 0000000..55ff31b
--- /dev/null
+++ b/source/logic/LimitedTimeObject.cpp
@@ -0,0 +1,16 @@
+#include "LimitedTimeObject.h"
+
+LimitedTimeObject::LimitedTimeObject(double x, double y, BitmapStore::Image image, int time)
+ :GameObject(x,y,image),
+ _remainingTime(time)
+{
+}
+
+void LimitedTimeObject::update()
+{
+ --_remainingTime;
+ if (_remainingTime<=0)
+ {
+ _destroyed = true;
+ }
+}
diff --git a/source/logic/LimitedTimeObject.h b/source/logic/LimitedTimeObject.h
new file mode 100644
index 0000000..242965b
--- /dev/null
+++ b/source/logic/LimitedTimeObject.h
@@ -0,0 +1,45 @@
+#ifndef LIMITEDTIMEOBJECT_H
+#define LIMITEDTIMEOBJECT_H
+
+#include "../logic/GameObject.h"
+#include "../logic/Maze.h"
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief Parent class for GameObjects that are created, exist for a given time, and are then destroyed.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class LimitedTimeObject: public GameObject
+{
+ public:
+ /**
+ * @brief Creates a LimitedTimeObject with the given parameters.
+ *
+ * @param [in] x The x coordinate of the new object.
+ * @param [in] y The y coordinate of the new object.
+ * @param [in] image The image that is drawn to represent the object.
+ * @param [in] time The number of times that the update function is run before the object is destroyed.
+ */
+ LimitedTimeObject(double x, double y, BitmapStore::Image image, int time);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Function that should be run on every iteration of the gameloop.
+ *
+ * The time remaining is decremented, and the object is marked for destruction when it reaches zero.
+ */
+ void update();
+
+ private:
+ /**
+ * @brief The number of times that update still needs to be run before the object is marked for destruction.
+ *
+ * For example, if the remaining time is 1, then the object is marked on the next update.
+ */
+ int _remainingTime;
+};
+
+#endif // LIMITEDTIMEOBJECT_H
diff --git a/source/logic/Maze.cpp b/source/logic/Maze.cpp
new file mode 100644
index 0000000..ab24035
--- /dev/null
+++ b/source/logic/Maze.cpp
@@ -0,0 +1,77 @@
+#include "Maze.h"
+
+Maze::Maze()
+ :_width(0),
+ _height(0)
+{
+}
+
+void Maze::generateMaze(const vector<pair<int,int> >& walls, int maxObjectX, int maxObjectY)
+{
+ //find bounds so that rectangular vector can be generated
+ int maxX = maxObjectX;
+ int maxY = maxObjectY;
+ for (vector<pair<int,int> >::const_iterator iter = walls.begin(); iter!=walls.end(); ++iter)
+ {
+ if (iter->first > maxX) maxX = iter->first;
+ if (iter->second > maxY) maxY = iter->second;
+ }
+ _width = maxX+1; //need to convert from highest index to required size
+ _height = maxY+1;
+
+
+ _wallLocations.clear();
+
+ for (int x=0; x<_width; ++x)
+ {
+ _wallLocations.push_back(vector<bool>());
+ for (int y=0; y<_height; ++y)
+ {
+ _wallLocations.back().push_back(false);
+ }
+ }
+
+ for (vector<pair<int,int> >::const_iterator iter = walls.begin(); iter!=walls.end(); ++iter)
+ {
+ _wallLocations.at(iter->first).at(iter->second) = true;
+ }
+}
+
+bool Maze::getSolid(const int& x, const int& y) const
+{
+ if (x<0 || y<0) return true;
+ if (x>=width() || y>=height()) return true;
+ //bounds have already been checked, can use more efficient, less safe, indexing
+ return _wallLocations[x][y];
+}
+
+int Maze::width() const
+{
+ return _width;
+}
+int Maze::height() const
+{
+ return _height;
+}
+
+Maze::Direction Maze::backwards(Direction forwards)
+{
+ Direction backwards;
+ switch (forwards)
+ {
+ case LEFT:
+ backwards = RIGHT;
+ break;
+ case RIGHT:
+ backwards = LEFT;
+ break;
+ case UP:
+ backwards = DOWN;
+ break;
+ case DOWN:
+ backwards = UP;
+ break;
+ }
+
+ return backwards;
+}
diff --git a/source/logic/Maze.h b/source/logic/Maze.h
new file mode 100644
index 0000000..245528d
--- /dev/null
+++ b/source/logic/Maze.h
@@ -0,0 +1,100 @@
+#ifndef MAZE_H
+#define MAZE_H
+
+#include <vector>
+#include <utility>
+using namespace std;
+
+/**
+* @brief A rectangular 2D boolean array, representing where cars can drive and where they cannot.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Maze
+{
+ public:
+ /**
+ * @brief Defines the directions in which movement can happen in the maze.
+ */
+ enum Direction {UP, DOWN, LEFT, RIGHT};
+
+ /**
+ * @brief Creates an empty Maze with width and height of zero.
+ */
+ Maze();
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Generates a new Maze from the vector of wall coordinates.
+ *
+ * The size of the Maze is chosen to just fit all of the walls. If objects exist outside
+ * of the walls, the x of the rightmost object and the y of the bottommost object can be
+ * passed in to make the Maze at least reach those coordinates.
+ *
+ * @param [in] walls A vector of x,y coordinate pairs representing the locations of each wall block.
+ * @param [in] maxObjectX The minimum x value that the Maze must be able to index.
+ * @param [in] maxObjectY The minimum y value that the Maze must be able to index.
+ */
+ void generateMaze(const vector<pair<int,int> >& walls, int maxObjectX=0, int maxObjectY=0);
+
+ /**
+ * @brief Checks if a given position contains a wall or not.
+ *
+ * This function is one of the most called as it is called for each block drawing the
+ * Maze on the Screen, by any Car checking if it can move, and by the EnemyCar to choose a
+ * viable direction to face. As such, it has been optimised by passing the parameters by
+ * constant reference, even though they are primitives. Further, the vector class's bounds
+ * checking is bypassed, with bounds checking performed manually with the assumption that the
+ * 2D vector is rectangular, to increase performance. Neither of these changes impare readability.
+ *
+ * @param [in] x The x coordinate being queried.
+ * @param [in] y The y coordinate being queried.
+ *
+ * @return True if the location contains a wall. Also returns true if the coordinate is outside of the Maze.
+ */
+ bool getSolid(const int& x, const int& y) const;
+
+ /**
+ * @brief Provides access to the width of the Maze object.
+ *
+ * @return The amount of blocks in each row of the maze.
+ */
+ int width() const;
+ /**
+ * @brief Provides access to the height of the Maze object.
+ *
+ * @return The amount of blocks in each column of the maze.
+ */
+ int height() const;
+
+ /**
+ * @brief Inverts a given direction, to give the value to face in the opposite direction.
+ *
+ * @param [in] forwards The direction to be inverted.
+ *
+ * @return The inverse of the given direction.
+ */
+ static Direction backwards(Direction forwards);
+
+ private:
+ /**
+ * @brief Provides an easier to read pseudonym for a 2 dimensional boolean vector.
+ */
+ typedef vector<vector<bool> > BoolGrid;
+
+ /**
+ * @brief The 2 dimensional vector that stores the locations of walls.
+ *
+ * The outer vector is columns, indexed with the x coordinate, and the inner vectors
+ * are the vertical positions in the column, indexed with the y coordinate.
+ * This results in a vector that is acced with _wallLocations.at(x).at(y).
+ */
+ BoolGrid _wallLocations;
+
+ int _width; ///< The number of blocks in each row.
+ int _height; ///< The number of blocks in each column.
+};
+
+#endif // MAZE_H
diff --git a/source/logic/MazeMath.cpp b/source/logic/MazeMath.cpp
new file mode 100644
index 0000000..84b27dd
--- /dev/null
+++ b/source/logic/MazeMath.cpp
@@ -0,0 +1,18 @@
+#include "MazeMath.h"
+
+double MazeMath::round(double value)
+{
+ if (static_cast<int>(value*10)%10 < 5)
+ {
+ return floor(value);
+ }
+ else
+ {
+ return ceil(value);
+ }
+}
+
+double MazeMath::distance(double x1, double y1, double x2, double y2)
+{
+ return sqrt(pow(x1-x2, 2) + pow(y1-y2, 2));
+}
diff --git a/source/logic/MazeMath.h b/source/logic/MazeMath.h
new file mode 100644
index 0000000..d5a2aa9
--- /dev/null
+++ b/source/logic/MazeMath.h
@@ -0,0 +1,59 @@
+#ifndef MAZEMATH_H
+#define MAZEMATH_H
+
+#include <cmath>
+
+/**
+* @brief Class of static methods for common math functions that occur in the 2D maze setting.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class MazeMath
+{
+ public:
+ /**
+ * @brief Rounds a value to the nearest integer.
+ *
+ * Values with a decimal fraction less than 0.5 are floored, while values with
+ * a decimal fraction greater than or eqaul to 0.5 are ceiled.
+ *
+ * @param [in] value The number to be rounded off.
+ *
+ * @return The rounded off version of the given value.
+ */
+ static double round(double value);
+
+ /**
+ * @brief Finds the straight line distance between two points on a 2D plane.
+ *
+ * Implemented using Pythagoras' Theorem.
+ *
+ * @param [in] x1 The x coordinate of the first point.
+ * @param [in] y1 The y coordinate of the first point.
+ * @param [in] x2 The x coordinate of the second point.
+ * @param [in] y2 The y coordinate of the second point.
+ *
+ * @return The distance between the two given points.
+ */
+ static double distance(double x1, double y1, double x2, double y2);
+
+ private:
+ /**
+ * @brief Unimplemented constructor.
+ *
+ * being a grouping of static functions, construction and destruction of MazeMath
+ * objects is unneccesary.
+ */
+ MazeMath();
+ /**
+ * @brief Unimplemented copy constructor.
+ */
+ MazeMath(const MazeMath& ref);
+ /**
+ * @brief Unimplemented assignment operator.
+ */
+ MazeMath& operator=(const MazeMath& rhs);
+};
+
+#endif // MAZEMATH_H
diff --git a/source/logic/PlayerCar.cpp b/source/logic/PlayerCar.cpp
new file mode 100644
index 0000000..5da4ba0
--- /dev/null
+++ b/source/logic/PlayerCar.cpp
@@ -0,0 +1,87 @@
+#include "PlayerCar.h"
+
+PlayerCar::PlayerCar(double x, double y, Maze::Direction facing)
+ :Car(x,y,BitmapStore::PLAYER, facing),
+ _input(_facing),
+ _petrol(1)
+{
+}
+
+void PlayerCar::update(const Maze& maze, list<Smokescreen>& currentSmoke)
+{
+ _petrol -= PETROL_USE_RATE;
+ if (_petrol<0)
+ {
+ _speed = _baseSpeed/2;
+ _petrol = 0;
+ }
+
+ _facing = _input.getFacing();
+ move(maze);
+
+ if (_input.getSmokescreen())
+ {
+ makeSmoke(currentSmoke);
+ }
+}
+
+void PlayerCar::makeSmoke(list<Smokescreen>& currentSmoke)
+{
+ if (_petrol < PETROL_USE_SMOKESCREEN) return;
+
+ double targetX = 0;
+ double targetY = 0;
+
+ switch(_facing)
+ {
+ case Maze::UP:
+ targetX = round(_x);
+ targetY = round(_y+1);
+ break;
+ case Maze::DOWN:
+ targetX = round(_x);
+ targetY = round(_y-1);
+ break;
+ case Maze::LEFT:
+ targetX = round(_x+1);
+ targetY = round(_y);
+ break;
+ case Maze::RIGHT:
+ targetX = round(_x-1);
+ targetY = round(_y);
+ break;
+ }
+
+ bool overlap = false;
+
+ for (list<Smokescreen>::const_iterator iter = currentSmoke.begin(); iter!=currentSmoke.end(); ++iter)
+ {
+ if ((abs(iter->x() - targetX)<1)&&(abs(iter->y() - targetY)<1))
+ {
+ overlap = true;
+ break;
+ }
+ }
+
+ if (!overlap)
+ {
+ currentSmoke.push_back(Smokescreen(targetX, targetY));
+ _petrol -= PETROL_USE_SMOKESCREEN;
+ }
+}
+
+void PlayerCar::crash()
+{
+ _destroyed = true;
+}
+
+void PlayerCar::gotCheckpoint()
+{
+ _petrol+=PETROL_FROM_CHECKPOINT;
+ _speed = _baseSpeed;
+}
+
+double PlayerCar::petrol() const
+{
+ return _petrol;
+}
diff --git a/source/logic/PlayerCar.h b/source/logic/PlayerCar.h
new file mode 100644
index 0000000..8e9338d
--- /dev/null
+++ b/source/logic/PlayerCar.h
@@ -0,0 +1,99 @@
+#ifndef PLAYERCAR_H
+#define PLAYERCAR_H
+
+#include <cmath>
+#include <list>
+using namespace std;
+
+#include "../presentation/KeyboardHandler.h"
+#include "../presentation/BitmapStore.h"
+
+#include "../logic/Car.h"
+#include "../logic/Maze.h"
+#include "../logic/Smokescreen.h"
+
+/**
+* @brief A GameObject that is controlled by the player.
+*
+* Contains a KeyboardHandler to accept user input.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class PlayerCar: public Car
+{
+ public:
+ /**
+ * @brief Creates a PlayerCar at the given location facing in the given direction.
+ *
+ * In the current form of the level files, there is no way to distinguish the direction
+ * that the player is facing, so the default of UP is used. However, the ability to
+ * pass in a facing is useful in the unit tests.
+ *
+ * @param [in] x The x coordinate of the object's initial position.
+ * @param [in] y The y coordinate of the object's initial position.
+ * @param [in] facing The direction that the object is initially facing.
+ */
+ PlayerCar(double x, double y, Maze::Direction facing=Maze::UP);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+
+ /**
+ * @brief Processes one frame's worth of activity for the object, called every frame.
+ *
+ * The Keyboard handler is called for the user's input. Based on this, the direction
+ * can be changed, and a Smokescreen can be created and added to the list of already
+ * existing Smokescreens. The PlayerCar is then moved.
+ * Petrol is decreased by PETROL_USE_RATE on every update.
+ *
+ * @param [in] maze The Maze that confines the PlayerCar's movement.
+ * @param [in,out] currentSmoke The list of Smokescreens being drawn, that the new Smokescreens will be added to the back of.
+ */
+ void update(const Maze& maze, list<Smokescreen>& currentSmoke);
+
+ /**
+ * @brief Creates a Smokescreen one block behind the player if the action is viable.
+ *
+ * The action is viable if the object has more than PETROL_USE_SMOKESCREEN petrol. Further,
+ * the position must not overlap with existing Smokescreens. This allows the player to hold down
+ * the Smokescreen button without creating a new Smokescreen every frame.
+ * Creating a Smokescreen decreases the petrol by PETROL_USE_SMOKESCREEN.
+ *
+ * @param [in,out] currentSmoke The list of Smokescreens being drawn, that the new Smokescreens will be added to the back of.
+ */
+ void makeSmoke(list<Smokescreen>& currentSmoke);
+
+ /**
+ * @brief Function that is called when the PlayerCar collides with a Checkpoint.
+ *
+ * Increases the amount of petrol by PETROL_FROM_CHECKPOINT.
+ */
+ void gotCheckpoint();
+
+ /**
+ * @brief Function that is called when the PlayerCar collides with an EnemyCar.
+ */
+ void crash();
+
+ /**
+ * @brief Function to allow access to the amount of petrol that the PlayerCar still has.
+ */
+ double petrol() const;
+
+ private:
+ KeyboardHandler _input; ///< Object that handles all interaction with the player.
+
+ /**
+ * @brief The amount of petrol that the PlayerCar still has.
+ *
+ * Measured as a fraction, with 1 being a full tank and 0 being empty. When the petrol reaches
+ * 0, it is kept at zero and the PlayerCar's speed is halved.
+ */
+ double _petrol;
+
+ static const double PETROL_USE_RATE = 0.0007; ///< The amount of petrol used every frame.
+ static const double PETROL_USE_SMOKESCREEN = 0.05; ///< The amount of petrol used to create a Smokescreen.
+ static const double PETROL_FROM_CHECKPOINT = 0.2; ///< The amount of petrol gained from collecting a Checkpoint.
+};
+
+#endif // PLAYERCAR_H
diff --git a/source/logic/Rock.cpp b/source/logic/Rock.cpp
new file mode 100644
index 0000000..bb1053a
--- /dev/null
+++ b/source/logic/Rock.cpp
@@ -0,0 +1,6 @@
+#include "Rock.h"
+
+Rock::Rock(double x, double y)
+ :GameObject(x,y,BitmapStore::ROCK)
+{
+}
diff --git a/source/logic/Rock.h b/source/logic/Rock.h
new file mode 100644
index 0000000..106fd18
--- /dev/null
+++ b/source/logic/Rock.h
@@ -0,0 +1,29 @@
+#ifndef ROCK_H
+#define ROCK_H
+
+#include "../logic/GameObject.h"
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief A game object that acts as an obstacle to the player.
+*
+* Does nothing actively.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Rock: public GameObject
+{
+ public:
+ /**
+ * @brief Creates a Rock at the given coordinates.
+ *
+ * @param [in] x The x coordinate of the Rock's position.
+ * @param [in] y The y coordinate of the Rock's position.
+ */
+ Rock(double x, double y);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+};
+
+#endif // ROCK_H
diff --git a/source/logic/Smokescreen.cpp b/source/logic/Smokescreen.cpp
new file mode 100644
index 0000000..9ad4316
--- /dev/null
+++ b/source/logic/Smokescreen.cpp
@@ -0,0 +1,6 @@
+#include "Smokescreen.h"
+
+Smokescreen::Smokescreen(double x, double y)
+ :LimitedTimeObject(x,y,BitmapStore::SMOKE,SMOKE_TIME)
+{
+}
diff --git a/source/logic/Smokescreen.h b/source/logic/Smokescreen.h
new file mode 100644
index 0000000..59d1871
--- /dev/null
+++ b/source/logic/Smokescreen.h
@@ -0,0 +1,31 @@
+#ifndef SMOKESCREEN_H
+#define SMOKESCREEN_H
+
+#include "../logic/LimitedTimeObject.h"
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief GameObject that causes the EnemyCar to be delayed if they crash into it.
+*
+* After a short time, the SmokeScreen dissipates.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Smokescreen : public LimitedTimeObject
+{
+ public:
+ /**
+ * @brief Creates a Smokescreen at the given location.
+ *
+ * @param [in] x The x coordinate of the object's position.
+ * @param [in] y The y coordinate of the object's position.
+ */
+ Smokescreen(double x, double y);
+
+ //assignment and copy constructors have been left with the compiler generated versions
+ private:
+ static const int SMOKE_TIME = 60; ///< The number of frames that the Smokescreen exists before it is destroyed. 2 second at FPS=30.
+};
+
+#endif // SMOKESCREEN_H