summaryrefslogtreecommitdiff
path: root/source/presentation
diff options
context:
space:
mode:
Diffstat (limited to 'source/presentation')
-rw-r--r--source/presentation/BitmapStore.cpp238
-rw-r--r--source/presentation/BitmapStore.h140
-rw-r--r--source/presentation/ColourStore.cpp25
-rw-r--r--source/presentation/ColourStore.h60
-rw-r--r--source/presentation/GamePanel.cpp133
-rw-r--r--source/presentation/GamePanel.h117
-rw-r--r--source/presentation/InfoPanel.cpp111
-rw-r--r--source/presentation/InfoPanel.h140
-rw-r--r--source/presentation/KeyboardHandler.cpp114
-rw-r--r--source/presentation/KeyboardHandler.h83
-rw-r--r--source/presentation/Screen.cpp159
-rw-r--r--source/presentation/Screen.h159
-rw-r--r--source/presentation/ScreenPanel.cpp24
-rw-r--r--source/presentation/ScreenPanel.h98
14 files changed, 1601 insertions, 0 deletions
diff --git a/source/presentation/BitmapStore.cpp b/source/presentation/BitmapStore.cpp
new file mode 100644
index 0000000..c289ff3
--- /dev/null
+++ b/source/presentation/BitmapStore.cpp
@@ -0,0 +1,238 @@
+#include "BitmapStore.h"
+
+BitmapStore::BitmapStore(unsigned int blockWidth)
+ :_blockWidth(blockWidth)
+{
+ _bitmapFont = al_load_font("junction 02.ttf", blockWidth/6, 0);
+ if (_bitmapFont == NULL)
+ {
+ al_show_native_message_box(NULL, "Fatal error", "Fatal error", "The file 'junction 02.ttf' was not found. Ensure that it is located in the working directory.", NULL, ALLEGRO_MESSAGEBOX_ERROR);
+ throw InstallFailure();
+ }
+}
+
+BitmapStore::~BitmapStore()
+{
+ for (map<Image,ALLEGRO_BITMAP*>::iterator iter = _bitmaps.begin();
+ iter != _bitmaps.end(); ++iter)
+ {
+ al_destroy_bitmap(iter->second);
+ }
+ _bitmaps.clear();
+ al_destroy_font(_bitmapFont);
+}
+
+ALLEGRO_BITMAP* BitmapStore::getBitmap(Image image)
+{
+ map<Image,ALLEGRO_BITMAP*>::const_iterator iter = _bitmaps.find(image);
+ if (iter != _bitmaps.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ ALLEGRO_BITMAP* newImage = al_create_bitmap(_blockWidth, _blockWidth);
+ switch (image)
+ {
+ case PLAYER:
+ drawPlayerCar(newImage);
+ break;
+ case ENEMY:
+ drawEnemyCar(newImage);
+ break;
+ case CHECKPOINT:
+ drawCheckpoint(newImage);
+ break;
+ case ROCK:
+ drawRock(newImage);
+ break;
+ case MAZE_WALL:
+ drawMazeWall(newImage);
+ break;
+ case MAZE_FLOOR:
+ drawMazeFloor(newImage);
+ break;
+ case SMOKE:
+ drawSmoke(newImage);
+ break;
+ case CRASHED_CAR:
+ drawCrashedCar(newImage);
+ break;
+ case CLAIMED_CHECKPOINT:
+ drawClaimedCheckpoint(newImage);
+ break;
+ }
+
+ _bitmaps.insert(make_pair(image, newImage));
+ return newImage;
+ }
+}
+
+void BitmapStore::drawPlayerCar(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ //car body
+ al_draw_filled_rounded_rectangle(_blockWidth*0.2, 0, _blockWidth*0.8, _blockWidth*0.96, _blockWidth*0.1, _blockWidth*0.1, al_map_rgb(0,0,255));
+
+ //racing stripes
+ al_draw_filled_rectangle(_blockWidth*0.35, 0, _blockWidth*0.4, _blockWidth*0.3, al_map_rgb(255,255,255));
+ al_draw_filled_rectangle(_blockWidth*0.6, 0, _blockWidth*0.65, _blockWidth*0.3, al_map_rgb(255,255,255));
+
+ //windscreen
+ al_draw_filled_rectangle(_blockWidth*0.3, _blockWidth*0.3, _blockWidth*0.7, _blockWidth*0.5, al_map_rgb (0,0,0));
+
+ //roof
+ al_draw_rounded_rectangle(_blockWidth*0.3, _blockWidth*0.5, _blockWidth*0.7, _blockWidth*0.9, _blockWidth*0.04, _blockWidth*0.04, al_map_rgb (25,25, 112), _blockWidth*0.04);
+
+ //spoiler
+ al_draw_filled_rectangle(_blockWidth*0.2, _blockWidth*0.96, _blockWidth*0.8, _blockWidth, al_map_rgb (0,0, 225));
+ al_draw_rectangle(_blockWidth*0.2, _blockWidth*0.96, _blockWidth*0.8, _blockWidth, al_map_rgb(25,25, 112),_blockWidth*0.04);
+
+ //headlights
+ al_draw_filled_rectangle (_blockWidth*0.3,0,_blockWidth*0.35,_blockWidth*0.06, al_map_rgb(255,225,0));
+ al_draw_filled_rectangle (_blockWidth*0.65,0,_blockWidth*0.7,_blockWidth*0.06, al_map_rgb(255,225,0));
+
+ //tyres
+ al_draw_filled_rounded_rectangle (_blockWidth*0.1,_blockWidth*0.13,_blockWidth*0.2,_blockWidth*0.37,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.8,_blockWidth*0.13,_blockWidth*0.9,_blockWidth*0.37,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.1,_blockWidth*0.63,_blockWidth*0.2,_blockWidth*0.87,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.8,_blockWidth*0.63,_blockWidth*0.9,_blockWidth*0.87,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawEnemyCar(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ //car body
+ al_draw_filled_rounded_rectangle(_blockWidth*0.2, 0, _blockWidth*0.8, _blockWidth*0.96, _blockWidth*0.1, _blockWidth*0.1, al_map_rgb(255,0,0));
+
+ //racing stripes
+ al_draw_filled_rectangle(_blockWidth*0.35, 0, _blockWidth*0.4, _blockWidth*0.3, al_map_rgb(255,255,255));
+ al_draw_filled_rectangle(_blockWidth*0.6, 0, _blockWidth*0.65, _blockWidth*0.3, al_map_rgb(255,255,255));
+
+ //windscreen
+ al_draw_filled_rectangle(_blockWidth*0.3, _blockWidth*0.3, _blockWidth*0.7, _blockWidth*0.5, al_map_rgb (0,0,0));
+
+ //roof
+ al_draw_rounded_rectangle(_blockWidth*0.3, _blockWidth*0.5, _blockWidth*0.7, _blockWidth*0.9, _blockWidth*0.04, _blockWidth*0.04, al_map_rgb (25,25, 112), _blockWidth*0.04);
+
+ //spoiler
+ al_draw_filled_rectangle(_blockWidth*0.2, _blockWidth*0.96, _blockWidth*0.8, _blockWidth, al_map_rgb (0,0, 225));
+ al_draw_rectangle(_blockWidth*0.2, _blockWidth*0.96, _blockWidth*0.8, _blockWidth, al_map_rgb(25,25, 112),_blockWidth*0.04);
+
+ //headlights
+ al_draw_filled_rectangle (_blockWidth*0.3,0,_blockWidth*0.35,_blockWidth*0.06, al_map_rgb(255,225,0));
+ al_draw_filled_rectangle (_blockWidth*0.65,0,_blockWidth*0.7,_blockWidth*0.06, al_map_rgb(255,225,0));
+
+ //tyres
+ al_draw_filled_rounded_rectangle (_blockWidth*0.1,_blockWidth*0.13,_blockWidth*0.2,_blockWidth*0.37,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.8,_blockWidth*0.13,_blockWidth*0.9,_blockWidth*0.37,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.1,_blockWidth*0.63,_blockWidth*0.2,_blockWidth*0.87,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+ al_draw_filled_rounded_rectangle (_blockWidth*0.8,_blockWidth*0.63,_blockWidth*0.9,_blockWidth*0.87,_blockWidth*0.03,_blockWidth*0.03, al_map_rgb(131,139,131));
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawRock(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(131,139,131);
+ al_draw_filled_circle(_blockWidth/2, _blockWidth/2, _blockWidth/2-1, colour);
+
+ al_draw_filled_circle(_blockWidth/2, _blockWidth/2, _blockWidth/2-6, colour);
+ al_draw_filled_circle(_blockWidth/4, _blockWidth/4, _blockWidth/4-1, al_map_rgb(205,197,191));
+ al_draw_filled_circle(_blockWidth/3.2, _blockWidth/4.2, _blockWidth/4-2, al_map_rgb(205,197,191));
+ al_draw_filled_circle(_blockWidth/1.2, _blockWidth/2, _blockWidth/2-15, al_map_rgb(205,197,191));
+ al_draw_filled_circle(_blockWidth/2, _blockWidth/2, _blockWidth/2-8, al_map_rgb(205,205,193));
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawCheckpoint(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(255,255,0);
+
+ al_draw_filled_rectangle (_blockWidth*0.44, _blockWidth*0.1, _blockWidth*0.5, _blockWidth*0.9, colour);
+ al_draw_filled_rounded_rectangle (_blockWidth*0.34, _blockWidth*0.9, _blockWidth*0.6, _blockWidth*0.98, _blockWidth*0.01, _blockWidth*0.01, colour);
+ al_draw_filled_circle (_blockWidth*0.47, _blockWidth*0.14, _blockWidth*0.1, colour);
+ al_draw_filled_triangle (_blockWidth*0.44, _blockWidth*0.26, _blockWidth*0.44, _blockWidth*0.58, _blockWidth*0.8, _blockWidth*0.42, colour);
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawMazeWall(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(203,255,151);
+ al_clear_to_color(colour);
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawMazeFloor(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(0,0,0);
+ al_clear_to_color(colour);
+
+ al_set_target_bitmap(prev_draw);
+}
+void BitmapStore::drawSmoke(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(255,255,255);
+ al_draw_circle (_blockWidth/2.3, _blockWidth/2.1, _blockWidth/2-1, colour,1);
+ al_draw_circle (_blockWidth/4, _blockWidth/4, _blockWidth/4, colour,2);
+ al_draw_circle (_blockWidth/5, _blockWidth/1.5, _blockWidth/4, colour,4);
+ al_draw_circle (_blockWidth/2.5, _blockWidth/2.7, _blockWidth/3, colour,3);
+ al_draw_circle (_blockWidth/1.2, _blockWidth/1.8, _blockWidth/3.7, colour,2);
+ al_draw_circle (_blockWidth/2.8, _blockWidth/2.2, _blockWidth/6, colour,3);
+ al_draw_circle (_blockWidth/1.1, _blockWidth/1.2, _blockWidth/3, colour,2);
+ al_draw_circle (_blockWidth/1.2, _blockWidth/1.7, _blockWidth/2, colour,3);
+ al_draw_circle (_blockWidth/1.3, _blockWidth/1.3, _blockWidth/5, colour,2);
+
+ al_set_target_bitmap(prev_draw);
+}
+
+void BitmapStore::drawCrashedCar(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ al_draw_filled_rounded_rectangle(_blockWidth/3.33, _blockWidth/5, _blockWidth/1.25, _blockWidth/1.04, 5, 5, al_map_rgb (200, 200, 200));
+ al_draw_circle (_blockWidth/2.3, _blockWidth/2.1, _blockWidth/2-1, al_map_rgb (255, 0, 0),1);
+ al_draw_circle (_blockWidth/4, _blockWidth/4, _blockWidth/4, al_map_rgb (100, 100, 100),2);
+ al_draw_circle (_blockWidth/5, _blockWidth/1.5, _blockWidth/4, al_map_rgb (255, 0, 0),4);
+ al_draw_filled_rectangle(_blockWidth/2.5, _blockWidth/2, _blockWidth/1.43, _blockWidth/1.7, al_map_rgb (0,0, 0));
+ al_draw_circle (_blockWidth/2.5, _blockWidth/2.7, _blockWidth/3, al_map_rgb (100, 100, 100),3);
+ al_draw_circle (_blockWidth/1.2, _blockWidth/1.8, _blockWidth/3.7, al_map_rgb (255, 0, 0),2);
+ al_draw_rectangle(_blockWidth/3.13, _blockWidth/1.04, _blockWidth/1.25, _blockWidth, al_map_rgb (25,25, 112),1);
+ al_draw_circle (_blockWidth/2.8, _blockWidth/2.2, _blockWidth/6, al_map_rgb (255, 0, 0),3);
+ al_draw_circle (_blockWidth/1.1, _blockWidth/1.2, _blockWidth/3, al_map_rgb (100, 100, 100),2);
+ al_draw_circle (_blockWidth/1.2, _blockWidth/1.7, _blockWidth/2, al_map_rgb (100, 100, 100),3);
+ al_draw_circle (_blockWidth/1.3, _blockWidth/1.3, _blockWidth/5, al_map_rgb (255, 0, 0),2);
+
+ al_set_target_bitmap(prev_draw);
+}
+
+void BitmapStore::drawClaimedCheckpoint(ALLEGRO_BITMAP* canvas)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(canvas);
+
+ ALLEGRO_COLOR colour = al_map_rgb(255,255,255);
+ al_draw_text(_bitmapFont, colour, _blockWidth/2, _blockWidth/2, ALLEGRO_ALIGN_CENTRE , "GOTCHA");
+
+ al_set_target_bitmap(prev_draw);
+}
diff --git a/source/presentation/BitmapStore.h b/source/presentation/BitmapStore.h
new file mode 100644
index 0000000..b551698
--- /dev/null
+++ b/source/presentation/BitmapStore.h
@@ -0,0 +1,140 @@
+#ifndef BITMAPSTORE_H
+#define BITMAPSTORE_H
+
+#include <string>
+#include <map>
+using namespace std;
+
+#include <allegro5/allegro.h>
+#include <allegro5/allegro_primitives.h>
+#include <allegro5/allegro_font.h>
+#include <allegro5/allegro_ttf.h>
+#include <allegro5/allegro_native_dialog.h>
+
+#include "../logic/AllegroWrappers.h"
+
+/**
+* @brief Class for accessing images in ALLEGRO_BITMAP format and low level drawing.
+*
+* The store ensures that only one copy of identical images are created.
+* This is done through a map, that caches the images that have already been requested.
+* If an uncached image is requested, it is added to the cache before being returned.
+* The store provides an enumerated type, Image, for other classes to reference which image
+* should represent the object on the screen.
+*
+* All images are square, to allow easy rotation and placement on the screen.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class BitmapStore
+{
+ public:
+ /**
+ * @brief Constructor for creating a BitmapStore with a set image size.
+ *
+ * @param [in] blockWidth The width (and height) of an image returned by the store in pixels.
+ */
+ BitmapStore(unsigned int blockWidth);
+ /**
+ * @brief Destructor for clearing cache.
+ */
+ ~BitmapStore();
+
+ /**
+ * @brief Type used to define which image should be returned.
+ */
+ enum Image {PLAYER, ENEMY, ROCK, CHECKPOINT, MAZE_WALL, MAZE_FLOOR, SMOKE, CRASHED_CAR, CLAIMED_CHECKPOINT};
+
+ /**
+ * @brief Function to get image for drawing to the screen.
+ *
+ * @param [in] image Image to be returned.
+ * @return Requested image in ALLEGRO_BITMAP format.
+ */
+ ALLEGRO_BITMAP* getBitmap(Image image);
+
+ private:
+ /**
+ * @brief Unimplemented copy constructor, prevents copying of BitmapStore objects.
+ *
+ * Copying a BitmapStore is unneccesary as there should only be a single BitmapStore object.
+ */
+ BitmapStore(const BitmapStore& ref);
+ /**
+ * @brief Unimplemented assignment operator.
+ *
+ * @see BitmapStore(const BitmapStore& ref);
+ */
+ BitmapStore& operator=(const BitmapStore& rhs);
+
+ /**
+ * @brief Draws the image representing the player.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawPlayerCar(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing an enemy.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawEnemyCar(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing a rock.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawRock(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing a checkpoint.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawCheckpoint(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing a solid part of the maze.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawMazeWall(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing a non-solid part of the maze.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawMazeFloor(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the image representing a smokescreen.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawSmoke(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the popup that appears when a car crashes.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawCrashedCar(ALLEGRO_BITMAP* canvas);
+ /**
+ * @brief Draws the popup that appears when a checkpoint is collected.
+ *
+ * @param [out] canvas ALLEGRO_BITMAP onto which the image is drawn.
+ */
+ void drawClaimedCheckpoint(ALLEGRO_BITMAP* canvas);
+
+ AllegroDrawingInit _drawingInstalls; ///< Ensures that Allegro is initialized while an object of this class exists
+
+ ALLEGRO_FONT* _bitmapFont; ///< Font used for writing text on bitmaps.
+
+ /**
+ * @brief map containing pairs of Images (the enumerated type) and the actual images.
+ *
+ * Creates a cache for images once they have been drawn.
+ */
+ map<Image, ALLEGRO_BITMAP*> _bitmaps;
+
+ unsigned int _blockWidth; ///< The width of a square image in the store
+};
+
+#endif // BITMAPSTORE_H
diff --git a/source/presentation/ColourStore.cpp b/source/presentation/ColourStore.cpp
new file mode 100644
index 0000000..e08b6f8
--- /dev/null
+++ b/source/presentation/ColourStore.cpp
@@ -0,0 +1,25 @@
+#include "ColourStore.h"
+
+ColourStore::ColourStore()
+{
+ populateColours();
+}
+
+void ColourStore::populateColours()
+{
+ _colours[BitmapStore::PLAYER] = al_map_rgb(0,255,255);
+ _colours[BitmapStore::ENEMY] = al_map_rgb(255,0,0);
+ _colours[BitmapStore::CHECKPOINT] = al_map_rgb(0,255,0);
+ _colours[BitmapStore::MAZE_WALL] = al_map_rgb(255,255,255);
+ _colours[BitmapStore::MAZE_FLOOR] = al_map_rgb(0,0,0);
+ _transparent = al_map_rgba(0,0,0,0);
+}
+
+ALLEGRO_COLOR ColourStore::getColour(BitmapStore::Image image)
+{
+ if (_colours.find(image) != _colours.end())
+ {
+ return _colours[image];
+ }
+ else return _transparent;
+}
diff --git a/source/presentation/ColourStore.h b/source/presentation/ColourStore.h
new file mode 100644
index 0000000..8985099
--- /dev/null
+++ b/source/presentation/ColourStore.h
@@ -0,0 +1,60 @@
+#ifndef COLOURSTORE_H
+#define COLOURSTORE_H
+
+#include <allegro5/allegro.h>
+
+#include "../presentation/BitmapStore.h"
+
+/**
+* @brief Class for mapping BitmapStore images to colours for use in the minimap.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class ColourStore
+{
+ public:
+ /**
+ * @brief Creates the ColourStore object and initialises all of the colours.
+ */
+ ColourStore();
+
+ /**
+ * @brief Returns the colour associated with a given image.
+ *
+ * If no colour makes sense for the image, then when it is requested a colour
+ * with an alpha of 0 (completely transparent) is returned.
+ *
+ * @param [in] image The BitmapStore image to be associated with a colour.
+ *
+ * @return The requested colour.
+ */
+ ALLEGRO_COLOR getColour(BitmapStore::Image image);
+ private:
+ /**
+ * @brief Unimplemented copy constructor, prevents copying of ColourStore objects.
+ *
+ * Copying a ColourStore is unneccesary as there should only be a single ColourStore object.
+ */
+ ColourStore(const ColourStore& ref);
+ /**
+ * @brief Unimplemented assignment operator.
+ *
+ * @see ColourStore(const ColourStore& ref);
+ */
+ ColourStore& operator=(const ColourStore& rhs);
+
+ map<BitmapStore::Image, ALLEGRO_COLOR> _colours;
+
+ /**
+ * @brief Initialised to have an alpha of 0, and returned when the colour of an unlisted image is requested.
+ */
+ ALLEGRO_COLOR _transparent;
+
+ /**
+ * @brief Initialises all of the relevant colours.
+ */
+ void populateColours();
+};
+
+#endif // COLOURSTORE_H
diff --git a/source/presentation/GamePanel.cpp b/source/presentation/GamePanel.cpp
new file mode 100644
index 0000000..71ef290
--- /dev/null
+++ b/source/presentation/GamePanel.cpp
@@ -0,0 +1,133 @@
+#include "GamePanel.h"
+
+GamePanel::GamePanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height)
+ :ScreenPanel(back, front, x, y, width, height),
+ _mazeblockWidth(_width/BLOCKS_PER_ROW),
+ _offsetX(0),
+ _offsetY(0),
+ _bitmapStore(_mazeblockWidth)
+{
+}
+
+void GamePanel::draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(_back);
+
+ al_clear_to_color(BLANK);
+
+ float _maxOffsetX = getPanelX(maze.width()) - _width;
+ float _maxOffsetY = getPanelY(maze.height()) - _height;
+
+ if (!players.empty())
+ {
+ _offsetX = getPanelX(players.front().x()) - _width/2;
+ if (_offsetX < 0) _offsetX = 0;
+ else if (_offsetX > _maxOffsetX) _offsetX = _maxOffsetX;
+
+ _offsetY = getPanelY(players.front().y()) - _height/2;
+ if (_offsetY < 0) _offsetY = 0;
+ else if (_offsetY > _maxOffsetY) _offsetY = _maxOffsetY;
+ }
+
+ draw(maze);
+
+ for (list<PlayerCar>::const_iterator iter = players.begin(); iter != players.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<EnemyCar>::const_iterator iter = enemies.begin(); iter != enemies.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<Checkpoint>::const_iterator iter = checkpoints.begin(); iter != checkpoints.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<Rock>::const_iterator iter = rocks.begin(); iter != rocks.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<Smokescreen>::const_iterator iter = smokescreens.begin(); iter != smokescreens.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<DestroyedObjectPopup>::const_iterator iter = popups.begin(); iter != popups.end(); ++iter)
+ {
+ draw(*iter);
+ }
+
+ al_set_target_bitmap(prev_draw);
+}
+
+void GamePanel::draw(const Maze& maze)
+{
+ //only draws a parts of the maze that would appear on the screen
+ int minX = floor((_offsetX-_mazeblockWidth)/_mazeblockWidth);
+ int maxX = ceil((_offsetX+_width)/_mazeblockWidth);
+ int minY = floor((_offsetY-_mazeblockWidth)/_mazeblockWidth);
+ int maxY = ceil((_offsetY+_height)/_mazeblockWidth);
+
+ ALLEGRO_BITMAP* wallBitmap = _bitmapStore.getBitmap(BitmapStore::MAZE_WALL);
+ ALLEGRO_BITMAP* floorBitmap = _bitmapStore.getBitmap(BitmapStore::MAZE_FLOOR);
+ //used to only have one al_draw_bitmap command
+ ALLEGRO_BITMAP* currentBitmap = floorBitmap;
+ for (int x=minX; x<maxX&&x<maze.width(); ++x)
+ {
+ for (int y=minY; y<maxY&&y<maze.height(); ++y)
+ {
+ if (maze.getSolid(x,y))
+ {
+ currentBitmap = wallBitmap;
+ }
+ else
+ {
+ currentBitmap = floorBitmap;
+ }
+ al_draw_bitmap(currentBitmap, getPanelX(x)-_offsetX, getPanelY(y)-_offsetY, 0);
+ }
+ }
+}
+
+void GamePanel::draw(const GameObject& object)
+{
+ //only draws a gameobject if it would appear on the screen
+ if (object.x() < (_offsetX-_mazeblockWidth)/_mazeblockWidth) return;
+ if (object.x() > (_offsetX+_width)/_mazeblockWidth) return;
+ if (object.y() < (_offsetY-_mazeblockWidth)/_mazeblockWidth) return;
+ if (object.y() > (_offsetY+_height)/_mazeblockWidth) return;
+
+ ALLEGRO_BITMAP* bitmap = _bitmapStore.getBitmap(object.image());
+
+ float angle = 0;
+ switch(object.facing())
+ {
+ case Maze::UP:
+ angle = 0;
+ break;
+ case Maze::RIGHT:
+ angle = ALLEGRO_PI/2;
+ break;
+ case Maze::DOWN:
+ angle = ALLEGRO_PI;
+ break;
+ case Maze::LEFT:
+ angle = 3*ALLEGRO_PI/2;
+ break;
+ }
+
+ float objectX = getPanelX(object.x());
+ float objectY = getPanelY(object.y());
+ float center = _mazeblockWidth/2;
+
+ al_draw_rotated_bitmap(bitmap, center , center , objectX+center-_offsetX, objectY+center-_offsetY, angle, 0);
+}
+
+float GamePanel::getPanelX(const double& x) const
+{
+ return static_cast<float>(x*_mazeblockWidth);
+}
+float GamePanel::getPanelY(const double& y) const
+{
+ return static_cast<float>(y*_mazeblockWidth);
+}
diff --git a/source/presentation/GamePanel.h b/source/presentation/GamePanel.h
new file mode 100644
index 0000000..916ac13
--- /dev/null
+++ b/source/presentation/GamePanel.h
@@ -0,0 +1,117 @@
+#ifndef GAMEPANEL_H
+#define GAMEPANEL_H
+
+#include "../presentation/ScreenPanel.h"
+#include "../logic/Maze.h"
+#include "../logic/GameObject.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"
+
+/**
+* @brief ScreenPanel to be drawn on the screen to draw the area where the game takes place.
+*
+* This includes the scrolling maze and all of the GameObjects.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class GamePanel : public ScreenPanel
+{
+ public:
+ /**
+ * @brief Creates a GamePanel from the given back and front buffers.
+ *
+ * The sub-bitmaps that GamePanel uses are created from a rectangular region on back and front
+ * that has its top left corner at the coordinate x,y, is width long in the x direction, and
+ * height long in the y direction.
+ *
+ * @param [in] back The current back buffer of the display being sub-bitmapped.
+ * @param [in] front The current front buffer (image currently being displayed) of the display being sub-bitmapped.
+ * @param [in] x The x coordinate of the left side of the sub-bitmap in pixels.
+ * @param [in] y The x coordinate of the top of the sub-bitmap in pixels.
+ * @param [in] width The length in the x direction of the new sub-bitmap in pixels.
+ * @param [in] height The length in the y direction of the new sub-bitmap in pixels.
+ */
+ GamePanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height);
+
+
+ /**
+ * @brief Draws the given objects on the screen.
+ *
+ * The drawing is offset so that the first entry in players is in the middle of the panel.
+ * However, the offset will never be such that the drawing area will be outside of the maze.
+ *
+ * @param [in] maze The Maze that all of the objects are in.
+ * @param [in] players The list of PlayerCars to be drawn.
+ * @param [in] enemies The list of EnemyCars to be drawn.
+ * @param [in] checkpoints The list of Checkpoints to be drawn.
+ * @param [in] rocks The list of Rocks to be drawn.
+ * @param [in] smokescreens The list of Smokescreens to be drawn.
+ * @param [in] popups The list of DestroyedObjectPopups to be drawn.
+ */
+ virtual void draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups);
+ private:
+ /**
+ * @brief Copy constructor not implemented, ScreenPanels should not be copied.
+ */
+ GamePanel(const GamePanel& ref);
+ /**
+ * @brief Assignment operator not implemented, ScreenPanels should not be copied.
+ */
+ GamePanel& operator=(const GamePanel& rhs);
+ /**
+ * @brief Converts an x game coordinate value to its equivalent in pixels.
+ *
+ * Converting to the pixel coordinates happens for every object every frame. To increase
+ * performance, the parameters are passed in by constant reference instead of by value.
+ *
+ * @param [in] x The game coordinate to be converted into pixels.
+ */
+ float getPanelX(const double& x) const;
+ /**
+ * @brief Converts a y game coordinate value to its equivalent in pixels.
+ *
+ * Converting to the pixel coordinates happens for every object every frame. To increase
+ * performance, the parameters are passed in by constant reference instead of by value.
+ *
+ * @param [in] y The game coordinate to be converted into pixels.
+ */
+ float getPanelY(const double& y) const;
+
+ /**
+ * @brief Draws a Maze on the screen.
+ *
+ * Bitmaps used to represent solid and non-solid parts of the Maze are stored in the
+ * BitmapStore.
+ *
+ * @param [in] maze The Maze to be drawn.
+ */
+ void draw(const Maze& maze);
+
+ /**
+ * @brief Draws a single GameObject on the screen.
+ *
+ * The bitmap to be drawn is retrieved from the BitmapStore using the GameObject's image.
+ *
+ * @param [in] object The GameObject to be drawn.
+ */
+ void draw(const GameObject& object);
+
+ const static int BLOCKS_PER_ROW = 15; ///< The number of Maze blocks in one row shown on the panel at a time. Used to determine the scale.
+
+ unsigned int _mazeblockWidth; ///< The width of one (square) Maze block on the screen, in pixels.
+
+ float _offsetX; ///< The amount that drawing should be offset to the right, recalculated every frame.
+ float _offsetY; ///< The amount that drawing should be offset downwards, recalculated every frame.
+
+ BitmapStore _bitmapStore; ///< Used to cache ALLEGRO_BITMAPs, so that they only need to be drawn once.
+
+ AllegroInit _allegro; ///< Handles dependencies on Allegro.
+};
+
+#endif // GAMEPANEL_H
diff --git a/source/presentation/InfoPanel.cpp b/source/presentation/InfoPanel.cpp
new file mode 100644
index 0000000..b4d33fa
--- /dev/null
+++ b/source/presentation/InfoPanel.cpp
@@ -0,0 +1,111 @@
+#include "InfoPanel.h"
+
+InfoPanel::InfoPanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height)
+ :ScreenPanel(back, front, x, y, width, height),
+ _petrolHeadingY(_width/10),
+ _petrolGuageY(_petrolHeadingY + _width/10),
+ _petrolGuageHeight(_width/10),
+ _checkpointHeadingY(_petrolGuageY + _petrolGuageHeight + _width/10),
+ _checkpointValueY(_checkpointHeadingY + _width/10),
+ _miniMazeY(_checkpointValueY + _width/5),
+ _miniMazeHeight(_height - _miniMazeY),
+ _miniMazeblockWidth(0)
+{
+ _panelFont = al_load_font("junction 02.ttf", _width/10, 0);
+ if (_panelFont == NULL)
+ {
+ al_show_native_message_box(NULL, "Fatal error", "Fatal error", "The file 'junction 02.ttf' was not found. Ensure that it is located in the working directory.", NULL, ALLEGRO_MESSAGEBOX_ERROR);
+ throw InstallFailure();
+ }
+}
+
+InfoPanel::~InfoPanel()
+{
+ al_destroy_font(_panelFont);
+}
+
+void InfoPanel::draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups)
+{
+ ALLEGRO_BITMAP* prev_draw = al_get_target_bitmap();
+ al_set_target_bitmap(_back);
+
+ double petrol = 0;
+ if (!players.empty())
+ {
+ petrol = players.front().petrol();
+ }
+
+ al_clear_to_color(_colourStore.getColour(BitmapStore::MAZE_FLOOR));
+
+ //gets a mazeblock width the fits the current maze
+ _miniMazeblockWidth = min(static_cast<float>(_width)/maze.width(), static_cast<float>(_miniMazeHeight)/maze.height());
+
+ //draws petrol heading and bar
+ al_draw_text(_panelFont, al_map_rgb(255,255,255), 1, _petrolHeadingY, ALLEGRO_ALIGN_LEFT , "Petrol");
+ al_draw_filled_rectangle(0,_petrolGuageY,_width*petrol, _petrolGuageY+_petrolGuageHeight, al_map_rgb(255,128,0));
+
+ //draws checkpoints remaining heading and value
+ al_draw_text(_panelFont, al_map_rgb(255,255,255), 1, _checkpointHeadingY, ALLEGRO_ALIGN_LEFT , "Checkpoints");
+ stringstream checkpointCountString;
+ checkpointCountString << Checkpoint::checkpointCount();
+ al_draw_text(_panelFont, al_map_rgb(255,255,255), 1, _checkpointValueY, ALLEGRO_ALIGN_LEFT , checkpointCountString.str().c_str());
+
+ draw(maze);
+ for (list<PlayerCar>::const_iterator iter = players.begin(); iter != players.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<EnemyCar>::const_iterator iter = enemies.begin(); iter != enemies.end(); ++iter)
+ {
+ draw(*iter);
+ }
+ for (list<Checkpoint>::const_iterator iter = checkpoints.begin(); iter != checkpoints.end(); ++iter)
+ {
+ draw(*iter);
+ }
+
+ //restore draw target
+ al_set_target_bitmap(prev_draw);
+}
+
+void InfoPanel::draw(const Maze& maze)
+{
+ ALLEGRO_COLOR wallColour = _colourStore.getColour(BitmapStore::MAZE_WALL);
+ ALLEGRO_COLOR floorColour = _colourStore.getColour(BitmapStore::MAZE_FLOOR);
+
+ for (int x=0; x<maze.width(); ++x)
+ {
+ for (int y=0; y<maze.height(); ++y)
+ {
+ float x1 = getPanelX(x);
+ float x2 = x1 + _miniMazeblockWidth;
+ float y1 = getPanelY(y) + _miniMazeY;
+ float y2 = y1 + _miniMazeblockWidth;
+ if (maze.getSolid(x,y))
+ {
+ al_draw_filled_rectangle(x1, y1, x2, y2, wallColour);
+ }
+ else
+ {
+ al_draw_filled_rectangle(x1, y1, x2, y2, floorColour);
+ }
+ }
+ }
+}
+
+void InfoPanel::draw(const GameObject& object)
+{
+ float r = _miniMazeblockWidth/2;
+ float cx = getPanelX(object.x()) + r;
+ float cy = getPanelY(object.y()) + _miniMazeY + r;
+ al_draw_filled_circle(cx, cy, r, _colourStore.getColour(object.image()));
+}
+
+float InfoPanel::getPanelX(const double& x) const
+{
+ return static_cast<float>(x*_miniMazeblockWidth);
+}
+float InfoPanel::getPanelY(const double& y) const
+{
+ return static_cast<float>(y*_miniMazeblockWidth);
+}
diff --git a/source/presentation/InfoPanel.h b/source/presentation/InfoPanel.h
new file mode 100644
index 0000000..9af6a70
--- /dev/null
+++ b/source/presentation/InfoPanel.h
@@ -0,0 +1,140 @@
+#ifndef INFOPANEL_H
+#define INFOPANEL_H
+
+#include <allegro5/allegro.h>
+#include <allegro5/allegro_primitives.h>
+
+#include <sstream>
+using namespace std;
+
+#include "../presentation/ScreenPanel.h"
+#include "../presentation/ColourStore.h"
+#include "../logic/Maze.h"
+#include "../logic/GameObject.h"
+#include "../logic/PlayerCar.h"
+#include "../logic/EnemyCar.h"
+#include "../logic/Checkpoint.h"
+#include "../logic/AllegroWrappers.h"
+
+/**
+* @brief ScreenPanel to be drawn on the screen to give the player information.
+*
+* This includes the minimap, a scaled down version of the entire maze that does not scroll, with
+* icons to represent the PlayerCar, EnemyCars, and Checkpoints. Text is drawn to show the player the
+* number of Checkpoints that needed to be collected for victory, and a rectangle is drawn representing
+* the amount of petrol that the PlayerCar has left.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class InfoPanel : public ScreenPanel
+{
+ public:
+ /**
+ * @brief Creates an InfoPanel from the given back and front buffers.
+ *
+ * The sub-bitmaps that InfoPanel uses are created from a rectangular region on back and front
+ * that has its top left corner at the coordinate x,y, is width long in the x direction, and
+ * height long in the y direction.
+ *
+ * @param [in] back The current back buffer of the display being sub-bitmapped.
+ * @param [in] front The current front buffer (image currently being displayed) of the display being sub-bitmapped.
+ * @param [in] x The x coordinate of the left side of the sub-bitmap in pixels.
+ * @param [in] y The x coordinate of the top of the sub-bitmap in pixels.
+ * @param [in] width The length in the x direction of the new sub-bitmap in pixels.
+ * @param [in] height The length in the y direction of the new sub-bitmap in pixels.
+ */
+ InfoPanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height);
+
+ /**
+ * @brief Destructor that ensured that the font created is destroyed.
+ *
+ * The memory for the sub-bitmaps are handled by the parent class, ScreenPanel.
+ */
+ ~InfoPanel();
+
+ /**
+ * @brief Draws the InfoPanel using the given objects.
+ *
+ * Not all of the provided objects are needed for the drawing process, but they are included
+ * to give the most general drawing case. This is to support polymorphism, where the InfoPanel
+ * can be told to draw its sub-bitmap in the same manner as any other ScreenPanel.
+ *
+ * The scale of the minimap is determined at the beginning of each frame, so that it will
+ * always fit even if the maze is larger than on the last frame.
+ *
+ * @param [in] maze The Maze that all of the objects are in.
+ * @param [in] players The list of PlayerCars to be drawn.
+ * @param [in] enemies The list of EnemyCars to be drawn.
+ * @param [in] checkpoints The list of Checkpoints to be drawn.
+ * @param [in] rocks Rocks are not actually drawn.
+ * @param [in] smokescreens Smokescreens are not actually drawn.
+ * @param [in] popups DestroyedObjectPopups are not actually drawn.
+ */
+ virtual void draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups);
+ private:
+ /**
+ * @brief Copy constructor not implemented, ScreenPanels should not be copied.
+ */
+ InfoPanel(const InfoPanel& ref);
+ /**
+ * @brief Assignment operator not implemented, ScreenPanels should not be copied.
+ */
+ InfoPanel& operator=(const InfoPanel& rhs);
+ /**
+ * @brief Converts an x game coordinate value to its equivalent in pixels.
+ *
+ * Converting to the pixel coordinates happens for every object every frame. To increase
+ * performance, the parameters are passed in by constant reference instead of by value.
+ *
+ * @param [in] x The game coordinate to be converted into pixels.
+ */
+ float getPanelX(const double& x) const;
+ /**
+ * @brief Converts a y game coordinate value to its equivalent in pixels.
+ *
+ * Converting to the pixel coordinates happens for every object every frame. To increase
+ * performance, the parameters are passed in by constant reference instead of by value.
+ *
+ * @param [in] y The game coordinate to be converted into pixels.
+ */
+ float getPanelY(const double& y) const;
+
+ /**
+ * @brief Draws a Maze on the panel.
+ *
+ * The Maze is constructed of coloured squares. The colour of the squares is retrieved from
+ * the ColourStore.
+ *
+ * Unlike in the GamePanel, the entire Maze is drawn.
+ *
+ * @param [in] maze The Maze to be drawn.
+ */
+ void draw(const Maze& maze);
+
+ /**
+ * @brief Draws a single GameObject on the panel.
+ *
+ * The GameObject is represented by a coloured circle in the Maze. The colour is based on
+ * the GameObject's image and is retrieved from the ColourStore.
+ *
+ * @param [in] object The GameObject to be drawn.
+ */
+ void draw(const GameObject& object);
+
+ AllegroDrawingInit _drawing; ///< Handles dependencies on Allegro's primitive drawing functions.
+ ColourStore _colourStore; ///< Caches colours for drawing.
+
+ float _petrolHeadingY; ///< The y coordinate of the heading for the petrol guage.
+ float _petrolGuageY; ///< The y coordinate of top of the petrol guage.
+ float _petrolGuageHeight; ///< The height of the rectangle that is the petrol guage.
+ float _checkpointHeadingY; ///< The y coordinate of the heading for the number of remaining checkpoints.
+ float _checkpointValueY; ///< The y coordinate of the text stating the number of remaining checkpoints.
+ float _miniMazeY; ///< The y coordinate of the top of the Maze.
+ float _miniMazeHeight; ///< The height of the Maze.
+ float _miniMazeblockWidth; ///< The width of each Maze block being drawn.
+
+ ALLEGRO_FONT* _panelFont; ///< The font being used to write the headings and number of checkpoints remaining.
+};
+
+#endif // INFOPANEL_H
diff --git a/source/presentation/KeyboardHandler.cpp b/source/presentation/KeyboardHandler.cpp
new file mode 100644
index 0000000..b588cbe
--- /dev/null
+++ b/source/presentation/KeyboardHandler.cpp
@@ -0,0 +1,114 @@
+#include "KeyboardHandler.h"
+
+KeyboardHandler::KeyboardHandler(Maze::Direction currentFacing)
+ :_up(false),
+ _down(false),
+ _left(false),
+ _right(false),
+ _smokescreen(false),
+ _previousFacing(currentFacing)
+{
+ _keyboardEvents = al_create_event_queue();
+ al_register_event_source(_keyboardEvents, al_get_keyboard_event_source());
+}
+
+KeyboardHandler::KeyboardHandler(const KeyboardHandler& ref)
+ :_up(ref._up),
+ _down(ref._down),
+ _left(ref._left),
+ _right(ref._right),
+ _smokescreen(ref._smokescreen),
+ _previousFacing(ref._previousFacing)
+{
+ _keyboardEvents = al_create_event_queue();
+ al_register_event_source(_keyboardEvents, al_get_keyboard_event_source());
+}
+
+KeyboardHandler& KeyboardHandler::operator=(const KeyboardHandler& rhs)
+{
+ _up = rhs._up;
+ _down = rhs._down;
+ _left = rhs._left;
+ _right = rhs._right;
+ _smokescreen = rhs._smokescreen;
+ _previousFacing = rhs._previousFacing;
+
+ if (_keyboardEvents!=rhs._keyboardEvents) al_destroy_event_queue(_keyboardEvents);
+
+ _keyboardEvents = al_create_event_queue();
+ al_register_event_source(_keyboardEvents, al_get_keyboard_event_source());
+
+ return *this;
+}
+
+KeyboardHandler::~KeyboardHandler()
+{
+ al_destroy_event_queue(_keyboardEvents);
+}
+
+void KeyboardHandler::updateFlags()
+{
+ ALLEGRO_EVENT event;
+ while (al_get_next_event(_keyboardEvents, &event))
+ {
+ if (event.type==ALLEGRO_EVENT_KEY_DOWN)
+ {
+ switch (event.keyboard.keycode)
+ {
+ case UP_KEY:
+ _up = true;
+ break;
+ case DOWN_KEY:
+ _down = true;
+ break;
+ case LEFT_KEY:
+ _left = true;
+ break;
+ case RIGHT_KEY:
+ _right = true;
+ break;
+ case SMOKESCREEN_KEY:
+ _smokescreen = true;
+ break;
+ }
+ }
+ else if (event.type==ALLEGRO_EVENT_KEY_UP)
+ {
+ switch (event.keyboard.keycode)
+ {
+ case UP_KEY:
+ _up = false;
+ break;
+ case DOWN_KEY:
+ _down = false;
+ break;
+ case LEFT_KEY:
+ _left = false;
+ break;
+ case RIGHT_KEY:
+ _right = false;
+ break;
+ case SMOKESCREEN_KEY:
+ _smokescreen = false;
+ break;
+ }
+ }
+ }
+}
+
+Maze::Direction KeyboardHandler::getFacing()
+{
+ updateFlags();
+
+ if (_up) _previousFacing = Maze::UP;
+ else if (_down) _previousFacing = Maze::DOWN;
+ else if (_left) _previousFacing = Maze::LEFT;
+ else if (_right) _previousFacing = Maze::RIGHT;
+
+ return _previousFacing;
+}
+
+bool KeyboardHandler::getSmokescreen()
+{
+ return _smokescreen;
+}
diff --git a/source/presentation/KeyboardHandler.h b/source/presentation/KeyboardHandler.h
new file mode 100644
index 0000000..00d7821
--- /dev/null
+++ b/source/presentation/KeyboardHandler.h
@@ -0,0 +1,83 @@
+#ifndef KEYBOARDHANDLER_H
+#define KEYBOARDHANDLER_H
+
+#include <allegro5/allegro.h>
+
+#include "../logic/Maze.h"
+#include "../logic/AllegroWrappers.h"
+
+/**
+* @brief Class for handling keyboard related game inputs from the player.
+*
+* Written with controlling a PlayerCar in mind. The handler keeps track of the last direction
+* pushed and responds to requests from the PlayerCar for which direction it should face next,
+* and whether the player is pressing the Smokescreen button.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class KeyboardHandler
+{
+ public:
+ /**
+ * @brief Creates a KeyboardHandler with a given initial state.
+ *
+ * @param [in] currentFacing The initial value for the previous facing of the object being controlled.
+ */
+ KeyboardHandler(Maze::Direction currentFacing);
+ /**
+ * @brief Copy constructor that ensures that a copy of a KeyboardHandler will have its own event queue.
+ */
+ KeyboardHandler(const KeyboardHandler& ref);
+ /**
+ * @brief Assignment operator that ensures that an assigned KeyboardHandler will have its own event queue.
+ */
+ KeyboardHandler& operator=(const KeyboardHandler& rhs);
+ /**
+ * @brief Cleans up the keyboard event queue
+ */
+ ~KeyboardHandler();
+
+ /**
+ * @brief Gives the last direction that the player entered on the keyboard.
+ *
+ * All pending keyboard events are processed, then a key out of those currently depressed is returned.
+ * The precendence for keys held down (up, down, left, then right) is arbitrary, since the player
+ * should not be holding down more than one arrow key at a time.
+ * If no keys are currently depressed, the value returned on the last call is returned again.
+ *
+ * @return The direction that the player has chosen through pressing arrow keys.
+ */
+ Maze::Direction getFacing();
+
+ /**
+ * @brief Gives whether or not the key for creating a Smokescreen is currently pressed.
+ *
+ * @return True if a Smokescreen should be created.
+ */
+ bool getSmokescreen();
+
+ private:
+ AllegroKeyboardInit _keyboard; ///< Ensures that dependencies on the Allegro keyboard library are installed.
+
+ /**
+ * @brief Processes all pending keyboard inputs, and updates flags as appropriate.
+ */
+ void updateFlags();
+
+ bool _up; ///< True if the up arrow key is depressed.
+ bool _down; ///< True if the down arrow key is depressed.
+ bool _left; ///< True if the left arrow key is depressed.
+ bool _right; ///< True if the right arrow key is depressed.
+ bool _smokescreen; ///< True if the smokescreen key is depressed.
+ Maze::Direction _previousFacing; ///< The direction that was returned on the last call of getFacing.
+ ALLEGRO_EVENT_QUEUE* _keyboardEvents; ///< Queue for all keyboard events.
+
+ static const int UP_KEY = ALLEGRO_KEY_UP; ///< Key that must be pressed to turn up.
+ static const int DOWN_KEY = ALLEGRO_KEY_DOWN; ///< Key that must be pressed to turn down.
+ static const int LEFT_KEY = ALLEGRO_KEY_LEFT; ///< Key that must be pressed to turn left.
+ static const int RIGHT_KEY = ALLEGRO_KEY_RIGHT; ///< Key that must be pressed to turn right.
+ static const int SMOKESCREEN_KEY = ALLEGRO_KEY_SPACE; ///< Key that must be pressed to create a smokescreen.
+};
+
+#endif // KEYBOARDHANDLER_H
diff --git a/source/presentation/Screen.cpp b/source/presentation/Screen.cpp
new file mode 100644
index 0000000..0f5ba4c
--- /dev/null
+++ b/source/presentation/Screen.cpp
@@ -0,0 +1,159 @@
+#include "Screen.h"
+
+Screen::Screen(unsigned int screenWidth, unsigned int screenHeight, bool fullscreen)
+ :_exitClicked(false),
+ _screenWidth(screenWidth),
+ _screenHeight(screenHeight),
+ _gameAreaWidth(_screenWidth*0.75),
+ _infoPanelWidth(_screenWidth - _gameAreaWidth)
+{
+ if (fullscreen)
+ {
+ al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);
+ if (!resolutionSupported())
+ {
+ al_show_native_message_box(NULL, "Fatal error", "Fatal error", "The fullscreen resolution specified in config.txt is not supported by your system. Please open config.txt and change the resolution to a supported resolution, or change fullscreen to false.", NULL, ALLEGRO_MESSAGEBOX_ERROR);
+ throw BadResolution();
+ }
+ }
+ else
+ {
+ al_set_new_display_flags(ALLEGRO_WINDOWED);
+ //need to add error checking for windows that are way too big
+ }
+
+ al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);
+
+ _display = al_create_display(_screenWidth, _screenHeight);
+
+ al_hide_mouse_cursor(_display);
+ _windowEvents = al_create_event_queue();
+ al_register_event_source(_windowEvents, al_get_display_event_source(_display));
+ //used so that ESC can be pressed to exit.
+ al_register_event_source(_windowEvents, al_get_keyboard_event_source());
+
+ _font = al_load_font("junction 02.ttf", _screenWidth/10, 0);
+ if (_font == NULL)
+ {
+ al_show_native_message_box(NULL, "Fatal error", "Fatal error", "The file 'junction 02.ttf' was not found. Ensure that it is located in the working directory.", NULL, ALLEGRO_MESSAGEBOX_ERROR);
+ al_destroy_event_queue(_windowEvents);
+ al_destroy_display(_display);
+ throw InstallFailure();
+ }
+
+ ALLEGRO_BITMAP* front = al_get_backbuffer(_display);
+ al_flip_display();
+ ALLEGRO_BITMAP* back = al_get_backbuffer(_display);
+
+ _panels.push_back(new GamePanel(back, front, 0, 0, _gameAreaWidth, _screenHeight));
+ _panels.push_back(new InfoPanel(back, front, _gameAreaWidth, 0, _infoPanelWidth, _screenHeight));
+}
+
+Screen::~Screen()
+{
+ for (vector<ScreenPanel*>::iterator iter = _panels.begin(); iter!=_panels.end(); ++iter)
+ {
+ delete (*iter);
+ }
+ _panels.clear();
+
+ al_destroy_font(_font);
+ al_destroy_event_queue(_windowEvents);
+ al_destroy_display(_display);
+}
+
+string Screen::getLevel()
+{
+ string result("");
+ ALLEGRO_FILECHOOSER* filechooser = al_create_native_file_dialog(".", "Choose your level", "*.lvl",ALLEGRO_FILECHOOSER_FILE_MUST_EXIST);
+ al_show_native_file_dialog(_display, filechooser);
+ if (al_get_native_file_dialog_count(filechooser)==0)
+ {
+ _exitClicked = true;
+ }
+ else
+ {
+ result = al_get_native_file_dialog_path(filechooser, 0);
+ }
+
+ al_destroy_native_file_dialog(filechooser);
+ return result;
+}
+
+void Screen::draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups)
+{
+ for (vector<ScreenPanel*>::iterator iter = _panels.begin(); iter!=_panels.end(); ++iter)
+ {
+ (*iter)->draw(maze, players, enemies, checkpoints, rocks, smokescreens, popups);
+ }
+ flip();
+}
+
+void Screen::flip()
+{
+ al_flip_display();
+ for (vector<ScreenPanel*>::iterator iter = _panels.begin(); iter!=_panels.end(); ++iter)
+ {
+ (*iter)->flip();
+ }
+}
+
+void Screen::drawWin()
+{
+ flip();
+
+ ALLEGRO_COLOR transBlack = al_map_rgba(0,0,0,200);
+ al_draw_filled_rectangle(0,0,_screenWidth,_screenHeight, transBlack);
+
+ ALLEGRO_COLOR textColour = al_map_rgb(255,255,255);
+ al_draw_text(_font, textColour, _screenWidth/2, _screenHeight/2, ALLEGRO_ALIGN_CENTRE , "You win!");
+
+ flip();
+}
+
+void Screen::drawLoss()
+{
+ flip();
+
+ ALLEGRO_COLOR transBlack = al_map_rgba(0,0,0,200);
+ al_draw_filled_rectangle(0,0,_screenWidth,_screenHeight, transBlack);
+
+ ALLEGRO_COLOR textColour = al_map_rgb(255,255,255);
+ al_draw_text(_font, textColour, _screenWidth/2, _screenHeight/2, ALLEGRO_ALIGN_CENTRE , "You lose!");
+
+ flip();
+}
+
+bool Screen::exitClicked()
+{
+ if (_exitClicked) return true;
+
+ ALLEGRO_EVENT event;
+ while (al_get_next_event(_windowEvents, &event))
+ {
+ if (event.type==ALLEGRO_EVENT_DISPLAY_CLOSE || (event.type==ALLEGRO_EVENT_KEY_CHAR && event.keyboard.keycode==ALLEGRO_KEY_ESCAPE))
+ {
+ al_flush_event_queue(_windowEvents);
+ _exitClicked = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Screen::resolutionSupported()
+{
+ ALLEGRO_DISPLAY_MODE mode;
+ for (int i=0; i<al_get_num_display_modes(); ++i)
+ {
+ al_get_display_mode(i, &mode);
+
+ if (static_cast<unsigned int>(mode.width)==_screenWidth && static_cast<unsigned int>(mode.height)==_screenHeight)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/source/presentation/Screen.h b/source/presentation/Screen.h
new file mode 100644
index 0000000..16d8cee
--- /dev/null
+++ b/source/presentation/Screen.h
@@ -0,0 +1,159 @@
+#ifndef SCREEN_H
+#define SCREEN_H
+
+#include <list>
+#include <algorithm>
+#include <sstream>
+using namespace std;
+
+#include <allegro5/allegro.h>
+#include <allegro5/allegro_native_dialog.h>
+
+#include "../logic/Maze.h"
+#include "../logic/GameObject.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 "../presentation/GamePanel.h"
+#include "../presentation/InfoPanel.h"
+
+/**
+* @brief Exception that is thrown if the Config file requests a resolution that can not work.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class BadResolution{};
+
+/**
+* @brief Class for creating a window on the screen.
+*
+* Anything involving drawing on the screen is the responsibility of this class. Most of these
+* responsibilities are outsourced through creating ScreenPanels with more specialised tasks,
+* such as drawing specifically the area where the gameplay takes place (GamePanel) or the
+* providing the player with information (InfoPanel).
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class Screen
+{
+ public:
+ /**
+ * @brief Creates a Screen with the given width and height in pixels, and the given fullscreen setting.
+ *
+ * @param [in] screenWidth The width of the display that will be created in pixels.
+ * @param [in] screenHeight The height of the display that will be created in pixels.
+ * @param [in] fullscreen True if the game should be in fullscreen mode. False otherwise.
+ */
+ Screen(unsigned int screenWidth, unsigned int screenHeight, bool fullscreen);
+
+ /**
+ * @brief Destructor to ensure that the display and any ScreenPanels are destroyed properly.
+ */
+ ~Screen();
+
+ /**
+ * @brief Presents the player with a file dialog, requesting a level file to be selected.
+ *
+ * If the cancel button is clicked, and empty string is returned and exitClicked() will return
+ * true on its next call.
+ *
+ * @return The path of the level file, or an empty string if no file was selected.
+ */
+ string getLevel();
+
+ /**
+ * @brief Draws the given objects on the screen.
+ *
+ * The objects are passed to each of the ScreenPanels in turn, and then the buffers
+ * are flipped.
+ *
+ * @param [in] maze The Maze that all of the objects are in.
+ * @param [in] players The list of PlayerCars to be drawn.
+ * @param [in] enemies The list of EnemyCars to be drawn.
+ * @param [in] checkpoints The list of Checkpoints to be drawn.
+ * @param [in] rocks The list of Rocks to be drawn.
+ * @param [in] smokescreens The list of Smokescreens to be drawn.
+ * @param [in] popups The list of DestroyedObjectPopups to be drawn.
+ */
+ void draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups);
+
+ /**
+ * @brief Function to find if the player has chosen to exit the game.
+ *
+ * The game can be exited by clicking the x in the corner of the window, pressing the ESC key
+ * during the game, or by clicking 'cancel' on the file selection dialog.
+ *
+ * @return True if the game should be quit.
+ */
+ bool exitClicked();
+
+ /**
+ * @brief Draws a splash screen when the level has been won.
+ */
+ void drawWin();
+ /**
+ * @brief Draws a splash screen when the level has been lost.
+ */
+ void drawLoss();
+
+ private:
+ /**
+ * @brief Unimplemented copy constructor, prevents copying of Screen objects.
+ *
+ * Copying a Screen is unneccesary as there should only be a single Screen object.
+ */
+ Screen(const Screen& ref);
+ /**
+ * @brief Unimplemented assignment operator.
+ *
+ * @see Screen(const Screen& ref)
+ */
+ Screen& operator=(const Screen& rhs);
+
+ /**
+ * @brief Flips the display's buffers, as well as those for all of the ScreenPanels.
+ */
+ void flip();
+
+ /**
+ * @brief Checks the current resolution (_screenWidth and _screenHeight) against the screen's supported resolutions.
+ *
+ * Used to test if a fullscreen mode selection will launch without issue.
+ *
+ * @return True if the current resolution is supported.
+ */
+ bool resolutionSupported();
+
+ AllegroInit _allegro; ///< Ensures that Allegro has been installed, for event queues and creating the display.
+ AllegroKeyboardInit _keyboard; ///< Ensures that the keyboard has been installed, for checking for the ESC key.
+ AllegroDrawingInit _drawing; ///< Ensures that drawing operations have been installed, for drawing splash screens.
+
+ bool _exitClicked; ///< Set to true when the user chooses to quit the game.
+
+ unsigned int _screenWidth; ///< Horizontal number of pixels per row on the screen.
+ unsigned int _screenHeight; ///< Vertical number of pixels per column on the screen.
+
+ unsigned int _gameAreaWidth; ///< Width of the GamePanel created.
+ unsigned int _infoPanelWidth; ///< Width of the InfoPanel created. The InfoPanel is placed directly to the right of the GamePanel.
+
+ ALLEGRO_DISPLAY* _display; ///< The window created on the player's monitor to see the game.
+ ALLEGRO_EVENT_QUEUE* _windowEvents; ///< Events caught by the screen, checked for an exit command.
+ ALLEGRO_FONT* _font; ///< Font used in drawing splash screens.
+
+ /**
+ * @brief Polymorphic container used to encapsulate the different types of drawing to the screen.
+ *
+ * Since the memory of the ScreenPanels in the vector is allocated dynamically, it must be deallocated
+ * in the destructor.
+ */
+ vector<ScreenPanel*> _panels;
+};
+
+#endif // SCREEN_H
diff --git a/source/presentation/ScreenPanel.cpp b/source/presentation/ScreenPanel.cpp
new file mode 100644
index 0000000..db2db69
--- /dev/null
+++ b/source/presentation/ScreenPanel.cpp
@@ -0,0 +1,24 @@
+#include "ScreenPanel.h"
+
+const ALLEGRO_COLOR ScreenPanel::BLANK = al_map_rgb(0,0,0);
+
+ScreenPanel::ScreenPanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height)
+ :_width(width),
+ _height(height)
+{
+ _back = al_create_sub_bitmap(back, x, y, _width, _height);
+ _front = al_create_sub_bitmap(front, x, y, _width, _height);
+}
+
+ScreenPanel::~ScreenPanel()
+{
+ al_destroy_bitmap(_back);
+ al_destroy_bitmap(_front);
+}
+
+void ScreenPanel::flip()
+{
+ ALLEGRO_BITMAP* temp = _back;
+ _back = _front;
+ _front = temp;
+}
diff --git a/source/presentation/ScreenPanel.h b/source/presentation/ScreenPanel.h
new file mode 100644
index 0000000..13b4978
--- /dev/null
+++ b/source/presentation/ScreenPanel.h
@@ -0,0 +1,98 @@
+#ifndef SCREENPANEL_H
+#define SCREENPANEL_H
+
+#include <allegro5/allegro.h>
+
+#include "../logic/Maze.h"
+#include "../logic/GameObject.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"
+
+/**
+* @brief Parent class for panels that are drawn on the screen.
+*
+* Panels are given a sub-bitmap of the Screen bitmap, which they draw their panel specific outputs on.
+* The ScreenPanel class is subclassed to give specific drawing functions, like drawing the Maze and
+* GameObjects on the screen.
+*
+* When the object is created, the back bitmap and front bitmap should correspond to the back and front buffers
+* of the display respectively. This should be kept in sync by calling flip every time the display is flipped.
+*
+* @author Justin Wernick
+* @author David Schneider
+*/
+class ScreenPanel
+{
+ public:
+ /**
+ * @brief Creates a ScreenPanel from the given back and front buffers.
+ *
+ * The sub-bitmaps that ScreenPanel uses are created from a rectangular region on back and front
+ * that has its top left corner at the coordinate x,y, is width long in the x direction, and
+ * height long in the y direction.
+ *
+ * @param [in] back The current back buffer of the display being sub-bitmapped.
+ * @param [in] front The current front buffer (image currently being displayed) of the display being sub-bitmapped.
+ * @param [in] x The x coordinate of the left side of the sub-bitmap in pixels.
+ * @param [in] y The x coordinate of the top of the sub-bitmap in pixels.
+ * @param [in] width The length in the x direction of the new sub-bitmap in pixels.
+ * @param [in] height The length in the y direction of the new sub-bitmap in pixels.
+ */
+ ScreenPanel(ALLEGRO_BITMAP* back, ALLEGRO_BITMAP* front, int x, int y, int width, int height);
+
+ /**
+ * @brief Destructor to ensure that sub-bitmap memory is deallocated.
+ */
+ virtual ~ScreenPanel();
+
+ /**
+ * @brief Pure virtual method for drawing a collection of objects onto the panel.
+ *
+ * Implementations do not need to draw all of the objects if it is not neccesary for the
+ * type of panel, but the interface accepts all of them to be general.
+ *
+ * @param [in] maze The Maze that all of the objects are in.
+ * @param [in] players The list of PlayerCars to be drawn.
+ * @param [in] enemies The list of EnemyCars to be drawn.
+ * @param [in] checkpoints The list of Checkpoints to be drawn.
+ * @param [in] rocks The list of Rocks to be drawn.
+ * @param [in] smokescreens The list of Smokescreens to be drawn.
+ * @param [in] popups The list of DestroyedObjectPopups to be drawn.
+ */
+ virtual void draw(const Maze& maze, const list<PlayerCar>& players, const list<EnemyCar>& enemies, const list<Checkpoint>& checkpoints, const list<Rock>& rocks, const list<Smokescreen>& smokescreens, const list<DestroyedObjectPopup>& popups) = 0;
+
+ /**
+ * @brief Swaps the front and back buffers.
+ *
+ * This function should be called every time the display is flipped.
+ */
+ virtual void flip();
+
+ protected:
+ static const ALLEGRO_COLOR BLANK; ///< Colour used to clear the screen at the beginning of drawing operations.
+
+ ALLEGRO_BITMAP* _back; ///< The back buffer. Only the back buffer can be drawn to.
+
+ int _width; ///< The width of the sub-bitmaps being drawn to in pixels.
+ int _height; ///< The height of the sub-bitmaps being drawn to in pixels.
+ private:
+ /**
+ * @brief Copy constructor not implemented, ScreenPanels should not be copied.
+ */
+ ScreenPanel(const ScreenPanel& ref);
+ /**
+ * @brief Assignment operator not implemented, ScreenPanels should not be copied.
+ */
+ ScreenPanel& operator=(const ScreenPanel& rhs);
+
+ AllegroInit _allegro; ///< Handles dependencies on Allegro being initialised.
+
+ ALLEGRO_BITMAP* _front; ///< The front buffer, that is currently being shown on the screen.
+};
+
+#endif // SCREENPANEL_H