// Brain Party
// Copyright (C) 2010 Paul Hudson (http://www.tuxradar.com/brainparty)

// Brain Party is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#include <ctime>
#include "BPGame.h"
#include "SDL.h"
#include "SDL_opengl.h" 
#include "SDL_ttf.h" 
#include <string>


#include "bombhunt.h"
#include "balloonblaster.h"
#include "bpsays.h"
#include "bubbletrouble.h"
#include "cardmatch.h"
#include "connex.h"
#include "cupsnballs.h"
#include "diceoff.h"
#include "flashcounting.h"
#include "flashlight.h"
#include "iqtest.h"
#include "jewelflip.h"
#include "jeweljam.h"
#include "marbledrop.h"
#include "memoryblox.h"
#include "memorybox.h"
#include "memorymaths.h"
#include "minesweep.h"
#include "moonjump.h"
#include "nextinline.h"
#include "numbersnake.h"
#include "oddoneout.h"
#include "patchmatch.h"
#include "perfectpaths.h"
#include "routefinder.h"
#include "rps.h"
#include "scrambled.h"
#include "setfinder.h"
#include "sharpshooter.h"
#include "shortcircuitsudoku.h"
#include "shufflepuzzler.h"
#include "strangerdanger.h"
#include "symboliclogic.h"
#include "underthehat.h"
#include "untangler.h"
#include "wordsmash.h"

Texture* BPGame::sfcLogo;
bool BPGame::ShowingMessageBox;
bool BPGame::ShowingClearScores;
SpriteFont* BPGame::sfcMessageBoxText;
SpriteFont* BPGame::sfcMessageBoxTitle;

void BPGame::Init(int width, int height) {
	srand(time(0));
	
	EnableSound = true;
	EnableMusic = true;
	
	EmptyPoint = BPPoint(0,0);
	LastStateChange = 0;
	
	LastBGObject = 0;
	
	Black = new Colour(0.0f, 0.0f, 0.0f, 1.0f);
	White = new Colour(1.0f, 1.0f, 1.0f, 1.0f);
	TransparentWhite = new Colour(1.0f, 1.0f, 1.0f, 0.0f);
	
	Blue = new Colour(0.0f, 0.0f, 1.0f, 1.0f);
	Cyan = new Colour(0.0f, 1.0f, 1.0f, 1.0f);
	Green = new Colour(0.0f, 1.0f, 0.0f, 1.0f);
	Orange = new Colour(1.0f, 0.75f, 0.0f, 1.0f);
	Red = new Colour(1.0f, 0.0f, 0.0f, 1.0f);
	LightRed = new Colour(1.0f, 0.5f, 0.6f, 1.0f);
	DarkRed = new Colour(0.5f, 0.0f, 0.0f, 1.0f);
	Yellow = new Colour(1.0f, 1.0f, 0.0f, 1.0f);
	DarkGrey = new Colour(0.5f, 0.5f, 0.5f, 1.0f);
	ConnexGreen = new Colour(0.0f, 0.8f, 0.0f, 0.7f);
	
	CheatMainTaps = CheatOptionsTaps = 0;

	InTestMode = false;
	
	TickCount = 0;
	ElapsedSeconds = 0;
	AnimCounter = 0.0f;
	PractiseAnimCounter = 0.0f;
	
	PractisePageNumber = 0;
	PractisePageMoveLeft = true;
	
	sfcTestWeight = NULL;
	sfcTestJob = NULL;
	sfcHighestBrainWeight = NULL;
	
	HighestBrainWeight = -1;
	CurrentBrainWeight = -1;
	
	TestBrainJobs.Add("Fast Food Assistant");
	TestBrainJobs.Add("Fast Food Manager");
	TestBrainJobs.Add("Bingo Club Manager");
	TestBrainJobs.Add("Web Designer");
	TestBrainJobs.Add("Personal Shopper");
	TestBrainJobs.Add("Aromatherapist");
	TestBrainJobs.Add("Solicitor");
	TestBrainJobs.Add("Psychotherapist");
	TestBrainJobs.Add("Lifestyle Consultant");
	TestBrainJobs.Add("Librarian");
	TestBrainJobs.Add("Acupuncturist");
	TestBrainJobs.Add("Insurance Broker");
	TestBrainJobs.Add("Bank Manager");
	TestBrainJobs.Add("Fashion Stylist");
	TestBrainJobs.Add("Interior Designer");
	TestBrainJobs.Add("Fashion Consultant");
	TestBrainJobs.Add("Sales Assistant");
	TestBrainJobs.Add("Actor");
	TestBrainJobs.Add("Sales Executive");
	TestBrainJobs.Add("Movie Director");
	TestBrainJobs.Add("Hotel Manager");
	TestBrainJobs.Add("Botanist");
	TestBrainJobs.Add("Marketing Executive");
	TestBrainJobs.Add("Economist");
	TestBrainJobs.Add("Statistician");
	TestBrainJobs.Add("Theatre Director");
	TestBrainJobs.Add("Nurse");
	TestBrainJobs.Add("Police Officer");
	TestBrainJobs.Add("Meteorologist");
	TestBrainJobs.Add("Art Valuer");
	TestBrainJobs.Add("Museum Curator");
	TestBrainJobs.Add("Marine Biologist");
	TestBrainJobs.Add("School Teacher");
	TestBrainJobs.Add("Dentist");
	TestBrainJobs.Add("Software Engineer");
	TestBrainJobs.Add("Forensic Scientist");
	TestBrainJobs.Add("Psychiatrist");
	TestBrainJobs.Add("Archaeologist");
	TestBrainJobs.Add("Engineer");
	TestBrainJobs.Add("Investment Analyst");
	TestBrainJobs.Add("Physicist");
	TestBrainJobs.Add("Sculptor");
	TestBrainJobs.Add("Painter");
	TestBrainJobs.Add("Poet");
	TestBrainJobs.Add("Doctor");
	TestBrainJobs.Add("Composer");
	TestBrainJobs.Add("Fighter Pilot");
	TestBrainJobs.Add("Architect");
	TestBrainJobs.Add("Brain Surgeon");
	TestBrainJobs.Add("Professor");
	TestBrainJobs.Add("Philosopher");
	TestBrainJobs.Add("Astronaut");
	TestBrainJobs.Add("Nobel Prize Winner");
	TestBrainJobs.Add("Genius");
	TestBrainJobs.Add("Super Genius");
	TestBrainJobs.Add("Albert Einstein");
	
	PeopleNames.Add("Andrea");
	PeopleNames.Add("Andrew");
	PeopleNames.Add("Bob");
	PeopleNames.Add("Catherine");
	PeopleNames.Add("Dawn");
	PeopleNames.Add("Diana");
	PeopleNames.Add("Esther");
	PeopleNames.Add("Graham");
	PeopleNames.Add("Hannah");
	PeopleNames.Add("Jack");
	PeopleNames.Add("James");
	PeopleNames.Add("Jeff");
	PeopleNames.Add("Jeremy");
	PeopleNames.Add("John");
	PeopleNames.Add("Kathryn");
	PeopleNames.Add("Laura");
	PeopleNames.Add("Louise");
	PeopleNames.Add("Lyn");
	PeopleNames.Add("Mark");
	PeopleNames.Add("Matthew");
	PeopleNames.Add("Mike");
	PeopleNames.Add("Paul");
	PeopleNames.Add("Rebecca");
	PeopleNames.Add("Richard");
	PeopleNames.Add("Sandra");
	PeopleNames.Add("Scott");
	PeopleNames.Add("Tanya");
	PeopleNames.Add("Terry");
	PeopleNames.Add("Tim");
	PeopleNames.Add("William");
	
	
	sfcMiniGameUnknown = LoadBitmap("minigame_unknown", 90, 90);
	MiniGames[BALLOONBLASTER] = new BPMiniGame_Container(BALLOONBLASTER, "balloonblaster", "Balloon Blaster!", LoadBitmap("minigame_balloonblaster", 90, 90), false);
	MiniGames[BOMBHUNT] = new BPMiniGame_Container(BOMBHUNT, "bombhunt", "Bomb Hunt", LoadBitmap("minigame_bombhunt", 90, 90), false);
	MiniGames[BPSAYS] = new BPMiniGame_Container(BPSAYS, "bpsays", "Brain Party says...", LoadBitmap("minigame_bpsays", 90, 90), false);
	MiniGames[BUBBLETROUBLE] = new BPMiniGame_Container(BUBBLETROUBLE, "bubbletrouble", "Bubble Trouble", LoadBitmap("minigame_bubbletrouble", 90, 90), false);
	MiniGames[CARDMATCH] = new BPMiniGame_Container(CARDMATCH, "cardmatch", "Card Match", LoadBitmap("minigame_cardmatch", 90, 90), false);
	MiniGames[CUPSNBALLS] = new BPMiniGame_Container(CUPSNBALLS, "cupsnballs", "Cups 'n' Balls", LoadBitmap("minigame_cupsnballs", 90, 90), false);
	MiniGames[CONNEX] = new BPMiniGame_Container(CONNEX, "connex", "Connex", LoadBitmap("minigame_connex", 90, 90), false);
	MiniGames[DICEOFF] = new BPMiniGame_Container(DICEOFF, "diceoff", "Dice Off", LoadBitmap("minigame_unknown", 90, 90), true);
	MiniGames[FLASHCOUNTING] = new BPMiniGame_Container(FLASHCOUNTING, "flashcounting", "Flash Counting", LoadBitmap("minigame_flashcounting", 90, 90), false);
	MiniGames[FLASHLIGHT] = new BPMiniGame_Container(FLASHLIGHT, "flashlight", "Flashlight", LoadBitmap("minigame_flashlight", 90, 90), false);
	MiniGames[IQTEST] = new BPMiniGame_Container(IQTEST, "iqtest", "IQ Test", LoadBitmap("minigame_iqtest", 90, 90), false);
	MiniGames[JEWELFLIP] = new BPMiniGame_Container(JEWELFLIP, "jewelflip", "Jewel Flip", LoadBitmap("minigame_jewelflip", 90, 90), false);
	MiniGames[JEWELJAM] = new BPMiniGame_Container(JEWELJAM, "jeweljam", "Jewel Jam", LoadBitmap("minigame_jeweljam", 90, 90), false);
	MiniGames[MARBLEDROP] = new BPMiniGame_Container(MARBLEDROP, "marbledrop", "Marble Drop", LoadBitmap("minigame_marbledrop", 90, 90), false);
	MiniGames[MEMORYBLOX] = new BPMiniGame_Container(MEMORYBLOX, "memoryblox", "MemoryBlox", LoadBitmap("minigame_memoryblox", 90, 90), false);
	MiniGames[MEMORYBOX] = new BPMiniGame_Container(MEMORYBOX, "memorybox", "Memory Box", LoadBitmap("minigame_memorybox", 90, 90), false);
	MiniGames[MEMORYMATHS] = new BPMiniGame_Container(MEMORYMATHS, "memorymaths", "Memory Maths", LoadBitmap("minigame_memorymaths", 90, 90), false);
	MiniGames[MINESWEEP] = new BPMiniGame_Container(MINESWEEP, "minesweep", "Mine Sweep", LoadBitmap("minigame_minesweep", 90, 90), false);
	MiniGames[MOONJUMP] = new BPMiniGame_Container(MOONJUMP, "moonjump", "Moon Jump", LoadBitmap("minigame_moonjump", 90, 90), false);
	MiniGames[NEXTINLINE] = new BPMiniGame_Container(NEXTINLINE, "nextinline", "Next in Line", LoadBitmap("minigame_nextinline", 90, 90), false);
	MiniGames[NUMBERSNAKE] = new BPMiniGame_Container(NUMBERSNAKE, "numbersnake", "Number Snake", LoadBitmap("minigame_numbersnake", 90, 90), false);
	MiniGames[ODDONEOUT] = new BPMiniGame_Container(ODDONEOUT, "oddoneout", "Odd One Out", LoadBitmap("minigame_oddoneout", 90, 90), false);
	MiniGames[PATCHMATCH] = new BPMiniGame_Container(PATCHMATCH, "patchmatch", "Patch Match", LoadBitmap("minigame_patchmatch", 90, 90), false);
	MiniGames[PERFECTPATHS] = new BPMiniGame_Container(PERFECTPATHS, "perfectpaths", "Perfect Paths", LoadBitmap("minigame_perfectpaths", 90, 90), false);
	MiniGames[ROUTEFINDER] = new BPMiniGame_Container(ROUTEFINDER, "routefinder", "Route Finder", LoadBitmap("minigame_routefinder", 90, 90), false);
	MiniGames[RPS] = new BPMiniGame_Container(RPS, "rockpaperscissors", "Rock, Paper, Scissors", LoadBitmap("minigame_rps", 90, 90), false);
	MiniGames[SCRAMBLED] = new BPMiniGame_Container(SCRAMBLED, "scrambled", "Scrambled", LoadBitmap("minigame_scrambled", 90, 90), false);
	MiniGames[SETFINDER] = new BPMiniGame_Container(SETFINDER, "setfinder", "Set Finder", LoadBitmap("minigame_setfinder", 90, 90), false);
	MiniGames[SHARPSHOOTER] = new BPMiniGame_Container(SHARPSHOOTER, "sharpshooter", "Sharpshooter", LoadBitmap("minigame_sharpshooter", 90, 90), true);
	MiniGames[SHORTCIRCUITSUDOKU] = new BPMiniGame_Container(SHORTCIRCUITSUDOKU, "shortcircuitsudoku", "Short-circuit Sudoku", LoadBitmap("minigame_shortcircuitsudoku", 90, 90), false);
	MiniGames[SHUFFLEPUZZLER] = new BPMiniGame_Container(SHUFFLEPUZZLER, "shufflepuzzler", "Shuffle Puzzler", LoadBitmap("minigame_shufflepuzzler", 90, 90), true);
	MiniGames[STRANGERDANGER] = new BPMiniGame_Container(STRANGERDANGER, "strangerdanger", "Stranger Danger", LoadBitmap("minigame_unknown", 90, 90), true);
	MiniGames[SYMBOLICLOGIC] = new BPMiniGame_Container(SYMBOLICLOGIC, "symboliclogic", "Symbolic Logic", LoadBitmap("minigame_symboliclogic", 90, 90),  false);
	MiniGames[UNDERTHEHAT] = new BPMiniGame_Container(UNDERTHEHAT, "underthehat", "Under the Hat", LoadBitmap("minigame_underthehat", 90, 90), true);
	MiniGames[UNTANGLER] = new BPMiniGame_Container(UNTANGLER, "untangler", "Untangler", LoadBitmap("minigame_untangler", 90, 90), false);
	MiniGames[WORDSMASH] = new BPMiniGame_Container(WORDSMASH, "wordsmash", "Word Smash", LoadBitmap("minigame_unknown", 90, 90), true);

	sfcLogo = LoadBitmap("brainparty", 312, 227);
	ShowingMessageBox = false;
	ShowingClearScores = false;
	sfcMessageBoxTitle = NULL;
	sfcMessageBoxText = NULL;
	
	sfcTestStatus.Add(LoadBitmap("teststatus1", 320, 75));
	sfcTestStatus.Add(LoadBitmap("teststatus2", 320, 75));
	sfcTestStatus.Add(LoadBitmap("teststatus3", 320, 75));
	sfcTestStatus.Add(LoadBitmap("teststatus4", 320, 75));
	sfcTestStatus.Add(LoadBitmap("teststatus5", 320, 75));
	
	sfcResults90[FAIL] = LoadBitmap("results_fail_90", 90, 90);
	sfcResults90[BRONZE] = LoadBitmap("results_bronze_90", 90, 90);
	sfcResults90[SILVER] = LoadBitmap("results_silver_90", 90, 90);
	sfcResults90[GOLD] = LoadBitmap("results_gold_90", 90, 90);
	sfcResults90[PLATINUM] = LoadBitmap("results_platinum_90", 90, 90);

	sfcFirstRun = LoadBitmap("firstrun", 320, 480);
	sfcHelp = LoadBitmap("menu_help", 320, 480);
	sfcLoading = LoadBitmap("loading", 320, 480);
	
	sfcOptions = LoadBitmap("menu_options", 320, 480);
	sfcOptionsSoundsOff = LoadBitmap("sound_off", 320, 48);
	sfcOptionsMusicOff = LoadBitmap("music_off", 320, 48);
	
	sfcTestBackground = LoadBitmap("bg_test", 512, 512);
	sfcResultsBackground = LoadBitmap("bg_results", 512, 512);
	sfcResultsBackground2 = LoadBitmap("bg_results2", 320, 480);
	sfcHistoryBackground = LoadBitmap("history", 320, 480);
	
	sfcMarathonMode = LoadBitmap("menu_marathonmode", 320, 480);
	sfcBrainBoost = LoadBitmap("menu_brainboost", 320, 480);
	
	sfcBGObjectTypes.Add(LoadBitmap("emptycircle", 256, 256));
	sfcBGObjectTypes.Add(LoadBitmap("doubleemptycircle", 256, 256));
	sfcBGObjectTypes.Add(LoadBitmap("doublecircle", 256, 256));
	sfcBGObjectTypes.Add(LoadBitmap("circlering", 256, 256));
	sfcBGObjectTypes.Add(LoadBitmap("filledcircle", 256, 256));
	
	sfcSecretLo = LoadBitmap("secret_lo", 71, 66);
	sfcSecretHi = LoadBitmap("secret_hi", 71, 66);
	
	sfcStarTypes.Add(LoadBitmap("star_dark", 3, 3));
	sfcStarTypes.Add(LoadBitmap("star_medium", 3, 3));
	sfcStarTypes.Add(LoadBitmap("star_bright", 3, 3));

	Sounds["baa"] = LoadSound("baa");
	Sounds["baa2"] = LoadSound("baa2");
	Sounds["baa3"] = LoadSound("baa3");
	Sounds["balloon_pop"] = LoadSound("balloon_pop");
	Sounds["beep_hi"] = LoadSound("beep_hi");
	Sounds["bubble"] = LoadSound("bubble");
	Sounds["card_flip"] = LoadSound("card_flip");
	Sounds["card_flip2"] = LoadSound("card_flip2");
	Sounds["click"] = LoadSound("click");
	Sounds["correct"] = LoadSound("correct");
	Sounds["down"] = LoadSound("down");
	Sounds["explosion"] = LoadSound("explosion");
	Sounds["gem_select"] = LoadSound("gem_select");
	Sounds["glass_break"] = LoadSound("glass_break");
	Sounds["gun1"] = LoadSound("gun1");
	Sounds["gun2"] = LoadSound("gun2");
	Sounds["happy_ring"] = LoadSound("happy_ring");
	Sounds["meow"] = LoadSound("meow");
	Sounds["mouse_click"] = LoadSound("mouse_click");
	Sounds["result"] = LoadSound("result");
	Sounds["slide"] = LoadSound("slide");
	Sounds["soft_boom"] = LoadSound("soft_boom");
	Sounds["swoosh_long"] = LoadSound("swoosh_long");
	Sounds["whack"] = LoadSound("whack");
	Sounds["wrong"] = LoadSound("wrong");
	Sounds["wrong2"] = LoadSound("wrong2");
	Sounds["wrong3"] = LoadSound("wrong3");
	Sounds["zap"] = LoadSound("zap");
	
	PlaySound("mouse_click", true, false);
	
	Music = NULL;
		
	sfcMainMenu = LoadBitmap("menu_main", 320, 480);
	sfcPlayMenu = LoadBitmap("menu_play", 320, 480);
	sfcPractiseMenu = LoadBitmap("menu_practise", 320, 480);
	sfcPractiseMenuHider = LoadBitmap("menu_practise_hider", 134, 34);
	sfcProfessorTalk = LoadBitmap("professor_talk", 320, 480);
	sfcCredits = LoadBitmap("menu_credits", 320, 480);
	
	sfcBottomBar = LoadBitmap("bottombar", 320, 64);
	
	sfcQuitTest = LoadBitmap("quit_test", 320, 480);
	
	sfcResultsFail = LoadBitmap("results_fail", 320, 480);
	sfcResultsBronze = LoadBitmap("results_bronze", 320, 480);
	sfcResultsSilver = LoadBitmap("results_silver", 320, 480);
	sfcResultsGold = LoadBitmap("results_gold", 320, 480);
	sfcResultsPlatinum = LoadBitmap("results_platinum", 320, 480);
	
	sfcWhite = LoadBitmap("white", 64, 64);
	
	sfcCorrect = LoadBitmap("correct", 320, 92);
	sfcWrong = LoadBitmap("wrong", 320, 92);

	ActiveMiniGame = NULL;
	
	LoadSettings();
	
	if (FirstRun) {
		SetGameState(FIRST_RUN);
	} else {
		SetGameState(MAIN_MENU);		
	}
	
	PlayMusic("theme");
	
	
	// Hackage for stress testing!
	
//	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
//		for (int i = 0; i < 1000000; ++i) {
//			NSLog("Creating %", it->second->ShortName);
//			CreateMiniGame(UNTANGLER, false, true);
//			ActiveMiniGame->Start();
//		}
//	}
}

void BPGame::Update(float elapsed) {
	ElapsedSeconds = elapsed;
	ElapsedTickCount = floor(elapsed * 1000);
	TickCount += ElapsedTickCount;
	
	if (ShowingMessageBox) {
		return;
	}

	if (!GameStateJustChanged) {
		// some gamestates need to increment the animcounter - do that now!
		switch (GameState) {
			case MAIN_MENU_PLAY:
			case PLAY_MAIN_MENU:
			case PRACTISE_PROFESSOR:
			case MAIN_MENU_OPTIONS:
			case OPTIONS_MAIN_MENU:
			case PLAY_HISTORY:
			case HISTORY_CREDITS:
			case CREDITS_HISTORY:
			case HISTORY_PLAY:
			case TEST_RESULTS_PLAY:
			case MAIN_MENU_HELP:
			case HELP_MAIN_MENU:
				if (AnimCounter < 1.0f) {
					AnimCounter += 3.5f * elapsed;
					if (AnimCounter > 1.0f) {
						AnimCounter = 1.0f;
					}
				}
				
				break;
				
			case PLAY_PRACTISE:
			case PRACTISE_PLAY:
				if (AnimCounter < 1.0f) {
					AnimCounter += 3.5f * elapsed;
					if (AnimCounter > 1.0f) {
						AnimCounter = 1.0f;
					}
				}
				
				// don't break - we need to execute the following one too!
				
			case PRACTISE_MENU:
				if (PractiseAnimCounter < 1.0f) {
					PractiseAnimCounter += 3.5f * elapsed;
					if (PractiseAnimCounter > 1.0f) {
						PractiseAnimCounter = 1.0f;
					}
				}
				
				break;

			case TEST_STATUS:
				if (LastStateChange + 5000 < TickCount) {
					NextTest();
				}
				
				if (LastBGObject + 1000 < TickCount) {
					BGObject* obj = new BGObject();
					obj->Scale = 0.0f;
					obj->Pos = BPPoint(RandomRange(0, 320), RandomRange(0, 480));
					
					switch (RandomRange(0, 17)) {
					case 0:
					case 1:
					case 2:
					case 3:
					case 4:
					case 5:
					case 6:
					case 7:
						obj->Type = sfcBGObjectTypes[0];
						break;
						
					case 8:
					case 9:
					case 10:
					case 11:
						obj->Type = sfcBGObjectTypes[1];
						break;
						
					case 12:
					case 13:
					case 14:
						obj->Type = sfcBGObjectTypes[2];
						break;
						
					case 15:
					case 16:
						obj->Type = sfcBGObjectTypes[3];
						break;
						
					default:
						obj->Type = sfcBGObjectTypes[4];
						break;
					}
					
					BackgroundObjects.Add(obj);
					LastBGObject = TickCount;
				}
					

				for (int i = BackgroundObjects.Count - 1; i >= 0; --i) {
					BackgroundObjects[i]->Scale += 0.3f * ElapsedSeconds;
					
					if (BackgroundObjects[i]->Scale >= 1.0f) {
						BackgroundObjects.RemoveAt(i);
					}
				}
				
				
				break;
				
			case TEST_RESULTS:
				for (int i = 0; i < TestResultContainers.Count; ++i) {
					TestResultContainer* result = TestResultContainers[i];
					
					if (result->GoingUp) {
						result->FadeUpAnim += 0.2f * ElapsedSeconds;
						if (result->FadeUpAnim > 1.0f) {
							result->FadeUpAnim = 1.0f;
							result->GoingUp = false;
						}
					} else {
						result->FadeUpAnim -= 0.4f * ElapsedSeconds;
						if (result->FadeUpAnim < -1.0f) {
							result->FadeUpAnim = -1.0f;
							result->GoingUp = true;
						}
					}
				}
				
				break;
				
		}
	} else {
		GameStateJustChanged = false; // we skipped our animcounter update - now continue as normal
	}
	
	switch (GameState) {
		case PLAY_PRACTISE:
		case PRACTISE_PLAY:
			if (AnimCounter == 1.0f) {
				if (GameState == PLAY_PRACTISE) {
					SetGameState(PRACTISE_MENU);
				} else {
					SetGameState(PLAY_MENU);					
				}
			}
			// don't break - we need to update the practise screen behind the scenes too
			
		case PRACTISE_MENU:		
				for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
					it->second->X = SmoothStep(it->second->StartX, it->second->DestX, PractiseAnimCounter);
					it->second->Y = SmoothStep(it->second->StartY, it->second->DestY, PractiseAnimCounter);
				}

				break;
			
		case PLAYING:
			ActiveMiniGame->TickMiniGame();
			break;
			
		case MAIN_MENU_PLAY:
			if (AnimCounter == 1.0f) SetGameState(PLAY_MENU);
			break;
			
		case PLAY_MAIN_MENU:
			if (AnimCounter == 1.0f) SetGameState(MAIN_MENU);
			break;

		case PLAY_HISTORY:
			if (AnimCounter == 1.0f) SetGameState(HISTORY);
			break;
			
		case HISTORY_CREDITS:
			if (AnimCounter == 1.0f) SetGameState(CREDITS);
			break;
			
		case CREDITS_HISTORY:
			if (AnimCounter == 1.0f) SetGameState(HISTORY);
			break;
			
		case HISTORY_PLAY:
			if (AnimCounter == 1.0f) SetGameState(PLAY_MENU);
			break;

		case PRACTISE_PROFESSOR:
			if (AnimCounter == 1.0f) SetGameState(PROFESSOR);
			break;
			
		case MAIN_MENU_OPTIONS:
			if (AnimCounter == 1.0f) SetGameState(OPTIONS);
			break;
			
		case OPTIONS_MAIN_MENU:
			if (AnimCounter == 1.0f) SetGameState(MAIN_MENU);
			break;
			
		case TEST_RESULTS_PLAY:
			if (AnimCounter == 1.0f) SetGameState(PLAY_MENU);
			break;
			
		case MAIN_MENU_HELP:
			if (AnimCounter == 1.0f) SetGameState(HELP);
			break;
			
		case HELP_MAIN_MENU:
			if (AnimCounter == 1.0f) SetGameState(MAIN_MENU);
			break;
	}
}

void BPGame::Draw() {
	Clear(Black);
		
	switch (GameState) {
		case FIRST_RUN:
			sfcFirstRun->Draw(0, 0);
			break;
		
		case MAIN_MENU:
			RenderMainMenu(0, 0);
			break;
			
		case MAIN_MENU_PLAY:
			{
				float offval = SmoothStep(0, -320, AnimCounter);
				float onval = SmoothStep(320, 0, AnimCounter);
				
				RenderMainMenu(offval, 0.0f);
				RenderPlayMenu(onval, 0.0f);			
			}
			
			break;
			
		case PLAY_MAIN_MENU:
			{				
				float offval = SmoothStep(-320, 0, AnimCounter);
				float onval = SmoothStep(0, 320, AnimCounter);
				
				RenderMainMenu(offval, 0.0f);
				RenderPlayMenu(onval, 0.0f);			
			}
			
			break;
			
		case PLAY_HISTORY:
			{
				float offval = SmoothStep(0, -480, AnimCounter);
				float onval = SmoothStep(480, 0, AnimCounter);
				
				RenderPlayMenu(0.0f, offval);
				RenderHistory(0.0f, onval);			
			}
			
			break;
			
		case HISTORY_CREDITS:
			{
				float offval = SmoothStep(0, -480, AnimCounter);
				float onval = SmoothStep(480, 0, AnimCounter);
				
				RenderHistory(0.0f, offval);
				RenderCredits(0.0f, onval);			
			}
			
			break;

		case CREDITS_HISTORY:
			{
				float offval = SmoothStep(0, 480, AnimCounter);
				float onval = SmoothStep(-480, 0, AnimCounter);
				
				RenderCredits(0.0f, offval);
				RenderHistory(0.0f, onval);			
			}
			
			break;
			
		case HISTORY_PLAY:
			{
				float offval = SmoothStep(0, 480, AnimCounter);
				float onval = SmoothStep(-480, 0, AnimCounter);
				
				RenderHistory(0.0f, offval);
				RenderPlayMenu(0.0f, onval);			
			}
			
			break;
			
		case MAIN_MENU_OPTIONS:
			RenderOptionsMenu();
			
			glColor4f(1.0f, 1.0f, 1.0f, 1 - AnimCounter);
			RenderMainMenu(0.0f, 0.0f);
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			
			break;
			
		case OPTIONS_MAIN_MENU:
			RenderMainMenu(0.0f, 0.0f);
			
			glColor4f(1.0f, 1.0f, 1.0f, 1 - AnimCounter);
			RenderOptionsMenu();
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			
			break;
			
		case MAIN_MENU_HELP:
		{
			float onval = SmoothStep(480, 0, AnimCounter);
			
			RenderMainMenu(0.0f, 0.0f);
			RenderHelp(0.0f, onval);			
		}
			
			break;
			
		case HELP_MAIN_MENU:
		{
			float offval = SmoothStep(0, 480, AnimCounter);

			RenderMainMenu(0.0f, 0.0f);
			RenderHelp(0.0f, offval);
		}
			
			break;
			
		case TEST_RESULTS_PLAY:
			{				
				float offval = SmoothStep(0, 480, AnimCounter);
				float onval = SmoothStep(-480, 0, AnimCounter);
				
				RenderTestResults(0.0f, offval);
				RenderPlayMenu(0.0f, onval);			
			}
			
			break;
			
		case PLAY_MENU:
			RenderPlayMenu(0.0f, 0.0f);
			break;
			
		case PRACTISE_PLAY:
			RenderPractiseMenu();
			
			glColor4f(1.0f, 1.0f, 1.0f, AnimCounter);
			RenderPlayMenu(0.0f, 0.0f);
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			
			break;
			
		case PLAY_PRACTISE:
			RenderPractiseMenu();
			
			glColor4f(1.0f, 1.0f, 1.0f, 1 - AnimCounter);
			RenderPlayMenu(0.0f, 0.0f);
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			
			break;
			
		case PRACTISE_MENU:
			RenderPractiseMenu();
			break;
			
		case PROFESSOR:
			RenderProfessor();
			break;

		case PRACTISE_PROFESSOR:		
			RenderPractiseMenu();

			glColor4f(1.0f, 1.0f, 1.0f, AnimCounter);
			RenderProfessor();
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			break;
			
		case PLAYING:
			RenderPlaying();
			break;
			
		case TEST_STATUS:
			RenderTestStatus();
			break;
			
		case TEST_RESULTS:
			RenderTestResults(0.0f, 0.0f);
			break;
			
		case OPTIONS:
			RenderOptionsMenu();
			break;
			
		case HISTORY:
			RenderHistory(0.0f, 0.0f);
			break;

		case BRAINBOOST:
			RenderBrainBoost();
			break;
			
		case MARATHON:
			RenderMarathon();
			break;
			
		case CREDITS:
			RenderCredits(0.0f, 0.0f);
			break;
			
		case HELP:
			RenderHelp(0.0f, 0.0f);
			break;
	}
	
	if (ShowingMessageBox) {
		FillRectangle(Colour(0.0f, 0.0f, 0.0f, 0.85f), 0, 0, 320, 480);
		
		Colour col = Colour(1.0f, 1.0f, 1.0f, 1.0f);
		sfcLogo->Draw(160, 90, 0.0f, 0.6f, col);
		sfcMessageBoxTitle->drawAtPoint(0, 170);
		
		glColor4f(0.8f, 0.8f, 0.8f, 1.0f);
		sfcMessageBoxText->drawAtPoint(10, 205);
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	}
}

Texture* BPGame::LoadBitmap(const char*	 filename, int actualwidth, int actualheight) {
	return new Texture(filename, actualwidth, actualheight);
}

void BPGame::AddTestScore(int score) {
	if (InTestMode) {
		TestScores.Add(score);
		
		if (TestScores.Count == TestMiniGames.Count) {
			// that was the last game in this test run!
			CalculateTestResults();
			SetGameState(TEST_RESULTS);
			InTestMode = false;
			PlayMusic("results");
		} else {
			SetGameState(TEST_STATUS);
		}
	}
}

void BPGame::FillRectangle(Colour col, float x, float y, float width, float height) {
	glColor4f(col.R, col.G, col.B, col.A);
	sfcWhite->Draw(x, y, width, height);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

void BPGame::DrawImage(Texture* tex, float x, float y) {
	tex->Draw(x, y);
}

void BPGame::DrawImage(Texture* tex, float x, float y, Colour &col) {
	tex->Draw(x, y, col);
}


void BPGame::DrawImage(Texture* tex, float x, float y, float rotation, float scale, Colour &col) {
	tex->Draw(x, y, rotation, scale, col);	
}

void BPGame::Clear(Colour* col) {
	glClearColor(col->R, col->G, col->B, col->A);
	glClear(GL_COLOR_BUFFER_BIT);
}

bool BPGame::PointOverRect(int x1, int y1, int x2, int y2, int width, int height) {
	if (x1 >= x2 && x1 <= x2 + width) {
		if (y1 >= y2 && y1 <= y2 + height) {
			return true;
		}
	}
	
	return false;
}

bool BPGame::RectOverRect(int x1, int y1, int width1, int height1, int x2, int y2, int width2, int height2) {
	if (x1 + width1 < x2) return false;
	if (x1 > x2 + width2) return false;
	if (y1 + height1 < y2) return false;
	if (y1 > y2 + height2) return false;
	
	return true;
}

void BPGame::TouchStart(float x, float y) {
	if (ShowingMessageBox) {
		return;
	}
	
	TouchEvent.X = x;
	TouchEvent.Y = y;

	switch (GameState) {
		case PLAYING:
			if (ActiveMiniGame != NULL) {
				ActiveMiniGame->HandleMouseDown(TouchEvent);
			}
			
			break;
	}
}

void BPGame::TouchStop(float x, float y) {
	if (ShowingMessageBox) {
		ShowingMessageBox = false;
		ShowingClearScores = false;
		return;
	}
	
	TouchEvent.X = x;
	TouchEvent.Y = y;
	
	switch (GameState) {
		case FIRST_RUN:
			SetGameState(MAIN_MENU);
			PlaySound("mouse_click");
			break;
			
		case MAIN_MENU:
			if (PointOverRect(TouchEvent.X, TouchEvent.Y, 37, 207, 248, 78)) {
				SetGameState(MAIN_MENU_PLAY);
				PlaySound("mouse_click");
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 22, 309, 272, 58)) {
				SetGameState(MAIN_MENU_OPTIONS);
				PlaySound("mouse_click");
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 73, 370, 170, 58)) {
				SetGameState(MAIN_MENU_HELP);
				PlaySound("mouse_click");
			} else if (TouchEvent.Y < 207) {
				++CheatMainTaps;
			}
			
			break;
			
		case PLAY_MENU:
			if (PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 48, 320, 128)) {
				if (AllTestScores.Count == 0) {
					MessageBox::Show("Once you have completed your first test, this will show your brain weight.", "Brain weight");
				} else {
					MessageBox::Show("This is your current brain weight, as measured by the last test you did.", "Brain weight");
				}
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 16, 192, 288, 48)) {
				RunTest();
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 15, 252, 289, 48)) {
				if (NumUnlockedGames > 0) {
					SetGameState(PLAY_PRACTISE);
					PlaySound("mouse_click");
				} else {
					MessageBox::Show("You can't practise any games until you have completed at least one test. Go on, it won't take long!", "No games to practise");
				}
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 28, 312, 264, 48)) {
				SetGameState(PLAY_HISTORY);
				PlaySound("mouse_click");
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 74, 410, 172, 48)) {
				SetGameState(PLAY_MAIN_MENU);
				PlaySound("mouse_click");
			}
			break;
			
		case PRACTISE_MENU:
			if (PointOverRect(TouchEvent.X, TouchEvent.Y, 91, 437, 137, 27)) {
				SetGameState(PRACTISE_PLAY);
				PlaySound("mouse_click");
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 15, 384, 134, 34)) {
				// back
				if (PractiseAnimCounter == 1.0f) {
					if (PractisePageNumber != 0) {
						PractisePageNumber -= 9;
						if (PractisePageNumber < 0) PractisePageNumber = 0;
						PractisePageMoveLeft = false;
						PositionPractiseGames();
						PlaySound("mouse_click");
					}
				}
			} else if (PointOverRect(TouchEvent.X, TouchEvent.Y, 173, 383, 134, 34)) {
				// more
				if (PractiseAnimCounter == 1.0f) {
					if (PractisePageNumber + 9 < NumUnlockedGames) {
						PractisePageNumber += 9;
						PractisePageMoveLeft = true;
						PositionPractiseGames();
						PlaySound("mouse_click");
					}
				}
			} else {
				// did the player ask to play a minigame?
				int i = 0;
				int j = 0;
				int xpos = 0;
				
				for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
					if (it->second->IsSecret) continue;
					if (!it->second->Unlocked) continue;
					
					if (i < PractisePageNumber) {
						++i;
						continue;
					}
					
					xpos = i - PractisePageNumber;
					
					y = 75 + (xpos * 100);
					x = 15 + (j * 100);
					
					if (PointOverRect(TouchEvent.X, TouchEvent.Y, x, y, 90, 90)) {
						if (it->second->Unlocked) {
							PlaySound("mouse_click");
							CreateMiniGame((MiniGameType)it->second->GameCode, false, true);
						}
						
						break;
					}
					
					++j;
					
					if (j == 3) {
						j = 0;
						++i;
					}
					
					if (i > PractisePageNumber + 2) break;	
				}
			}
			break;
			
		case PROFESSOR:
			ActiveMiniGame->PlayMusic();
			ActiveMiniGame->Start();
			SetGameState(PLAYING);
			break;
			
		case PLAYING:
			ActiveMiniGame->HandleMouseUp(TouchEvent);
			break;
			
		case TEST_STATUS:
			NextTest();
			break;
			
		case TEST_RESULTS:
			if (LastStateChange + 1600 < TickCount) {
				SetGameState(TEST_RESULTS_PLAY);
				PlayMusic("theme");
			}
			
			break;
			
		case OPTIONS:
			if (PointOverRect(x, y, 21, 193, 277, 39)) {
				EnableSound = !EnableSound;
				PlaySound("click", false, true);
				SaveSettings();
			} else if (PointOverRect(x, y, 29, 259, 262, 37)) {
				EnableMusic = !EnableMusic;
				PlaySound("click");
				
				if (EnableMusic == false) {
					StopMusic();
				} else {
					PlayMusic("theme");
				}
				
				SaveSettings();	
			} else if (PointOverRect(x, y, 15, 353, 290, 31)) {
				PromptResetScores();
				PlaySound("click");
				
			} else if (PointOverRect(x, y, 0, 416, 320, 64)) {
				SetGameState(OPTIONS_MAIN_MENU);
				PlaySound("mouse_click");
			} else if (TouchEvent.Y < 193) {
				++CheatOptionsTaps;
			}
			
			break;
			
		case HISTORY:
			if (PointOverRect(x, y, 17, 45, 285, 161)) {
				MessageBox::Show("This graph shows your most recent test results.", "Your test history");
			} else if (PointOverRect(x, y, 21, 251, 285, 57)) {
				MessageBox::Show("This is the highest brain weight you have scored in any test. Each minigame is worth up to 500g, and you get five in a test, so the maximum brain weight is 2500g.", "Highest Brain Weight");
			} else if (PointOverRect(x, y, 0, 349, 74, 66)) {
				if (Secret1) {
					CreateMiniGame(DICEOFF, false, false);
				} else {
					MessageBox::Show("This secret game is unlocked when you get a Platinum score in Balloon Blaster.", "Secret locked!");
				}
			} else if (PointOverRect(x, y, 82, 349, 74, 66)) {
				if (Secret2) {
					CreateMiniGame(WORDSMASH, false, false);
				} else {
					MessageBox::Show("This secret game is unlocked when you get a Platinum score in Jewel Jam.", "Secret locked!");
				}				
			} else if (PointOverRect(x, y, 162, 349, 74, 66)) { 
				if (Secret3) {
					SetGameState(MARATHON);
					PlaySound("mouse_click");
				} else {
					MessageBox::Show("This secret area is unlocked when you get a Platinum score in Odd One Out.", "Secret locked!");
				}
			} else if (PointOverRect(x, y, 245, 349, 74, 66)) {
				if (Secret4) {
					SetGameState(BRAINBOOST);
					PlaySound("mouse_click");
				} else {
					MessageBox::Show("This secret area is unlocked when you get a Platinum score in Untangler.", "Secret locked!");
				}				
			} else if (PointOverRect(x, y, 2, 418, 110, 54)) {
				SetGameState(HISTORY_PLAY);
				PlaySound("mouse_click");
			} else if (PointOverRect(x, y, 182, 418, 136, 54)) {
				SetGameState(HISTORY_CREDITS);
				PlaySound("mouse_click");
			}
			
			break;
				
		case CREDITS:
			if (PointOverRect(x, y, 193, 203, 126, 148)) {
				// clicked on the professor!
				if (CheatMainTaps >= 5 && CheatOptionsTaps >= 5) {
					MessageBox::Show("Hey, it looks like you activated the cheat code - all the minigames and secrets have been unlocked. We hope you're proud of yourself!", "Cheat activated!");	
					
					for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
						it->second->Unlocked = true;
					}
					
					Secret1 = Secret2 = Secret3 = Secret4 = true;
					
					SaveSettings();
					
					PlaySound("meow");
				}
			}
			
			SetGameState(CREDITS_HISTORY);
			PlaySound("mouse_click");
			break;
			
		case MARATHON:
			if (PointOverRect(x, y, 12, 201, 90, 90)) {
				CreateMiniGame(BALLOONBLASTER, true, false);
			} else if (PointOverRect(x, y, 116, 201, 90, 90)) {
				CreateMiniGame(BPSAYS, true, false);
			} else if (PointOverRect(x, y, 220, 201, 90, 90)) {
				CreateMiniGame(JEWELJAM, true, false);
			} else if (PointOverRect(x, y, 12, 305, 90, 90)) {
				CreateMiniGame(SHORTCIRCUITSUDOKU, true, false);
			} else if (PointOverRect(x, y, 116, 305, 90, 90)) {
				CreateMiniGame(SYMBOLICLOGIC, true, false);
			} else if (PointOverRect(x, y, 220, 305, 90, 90)) {
				CreateMiniGame(UNTANGLER, true, false);
			} else if (PointOverRect(x, y, 0, 423, 320, 64)) {
				SetGameState(HISTORY);
				PlaySound("mouse_click");
			}
			
			break;
			
		case BRAINBOOST:
			if (PointOverRect(x, y, 53, 184, 90, 90)) {
				CreateMiniGame(SHARPSHOOTER, true, false);
			} else if (PointOverRect(x, y, 179, 184, 90, 90)) {
				CreateMiniGame(SHUFFLEPUZZLER, true, false);
			} else if (PointOverRect(x, y, 53, 305, 90, 90)) {
				CreateMiniGame(STRANGERDANGER, true, false);
			} else if (PointOverRect(x, y, 179, 305, 90, 90)) {
				CreateMiniGame(UNDERTHEHAT, true, false);
			} else if (PointOverRect(x, y, 0, 423, 320, 64)) {
				SetGameState(HISTORY);
				PlaySound("mouse_click");
			}
			
			break;
			
		case HELP:
			SetGameState(HELP_MAIN_MENU);
			PlaySound("mouse_click");
			break;
	}
}

void BPGame::TouchDrag(float x, float y) {
	if (ShowingMessageBox) {
		return;
	}
	
	TouchEvent.X = x;
	TouchEvent.Y = y;

	switch (GameState) {
		case PLAYING:
			if (ActiveMiniGame != NULL) {
				ActiveMiniGame->HandleMouseMove(TouchEvent);
			}
			
			break;
	}
}

const string TrimString(const std::string& str) {
	string::size_type First = str.find_first_not_of( ' ' );

	if (First == string::npos) {
		First = 0;
	}

	string::size_type Last = str.find_last_not_of( ' ' );
	if (Last != string::npos) {
		Last = (Last + 1) - First;
	}

  	return str.substr(First, Last);
}


void BPGame::AllocString(SpriteFont** tex, const char* str, FontSizes size, float width, float height, Alignments align, bool bold) {
	SAFE_DELETE((*tex));
	
	// it's incredibly lazy to open and close the font each time - hurray!
	TTF_Font* fnt = TTF_OpenFont("Content/freesans.ttf", size - 3); // NB: the -3 is here because the freesans.ttf font we're using is a big chunkier than the iPhone font
	
	static SDL_Color white = { 255, 255, 255, 255 };
	
	vector<string> lines; // we'll store the split lines here
	string temp(str); // the current text we're working on
	temp += " ";  // always add a space to the end of the text to enable wrapping - this gets removed later
	int n = 0; // current index
	int p = 0; // previous space

	while (n != -1) {		
		std::string strSub;
		n = temp.find(" ", p + 1 );		// -- Find the next " "
		
		std::string line_so_far = temp.substr(0, n);
		int w,h;
		
		const char* c_str = line_so_far.c_str();
		TTF_SizeText(fnt, c_str, &w, &h);
		
		if (w >= width)	{
			strSub = temp.substr(0, p);
			lines.push_back(strSub);
			
			if (n != -1) temp = temp.substr( p+1, string::npos );			
			p = 0;
		} else {
			p = n;
		}
	}

	// do the last line too
	string strSub = temp.substr(0, p);
	lines.push_back(strSub);
	
		
	// create a SpriteFont to hold all the information - this is used for drawing later
	(*tex) = new SpriteFont();
	(*tex)->Width = width;
	(*tex)->Height = height;
	(*tex)->Align = align;
	
	// render all the text
	for (int i = 0; i < lines.size(); ++i) {
		SDL_Surface* sfc = TTF_RenderText_Blended(fnt, TrimString(lines[i]).c_str(), white);
		
		if (sfc != NULL) {
			(*tex)->Surfaces.Add(new Texture(sfc));
			SDL_FreeSurface(sfc);
		}
	}
	
	TTF_CloseFont(fnt);
}

void BPGame::DrawString(SpriteFont* tex, Colour &col, float x, float y) {
	if (tex == NULL) return;
	
	glColor4f(col.R, col.G, col.B, col.A);	
	tex->drawAtPoint(x,y);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

void BPGame::DrawString(SpriteFont* tex, Colours col, float x, float y) {
	if (tex == NULL) return;	
	
	if (col == WHITE) {
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	} else if (col == BLACK) {
		glColor4f(0.0f, 0.0f, 0.0f, 1.0f);		
	}
	
	tex->drawAtPoint(x,y);
	
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

void BPGame::DrawString(SpriteFont* tex, float x, float y) {
	if (tex == NULL) return;
			
	tex->drawAtPoint(x,y);
}

int BPGame::RandomRange(int min, int max) {
	if (max < min) return min;
	
	int value = rand();
	return (value % ((max - min) + 1) + min);
}

int BPGame::RandomRangeExcept(int min, int max, int except) {
	int val = RandomRange(min, max);
	
	while (val == except) {
		val = RandomRange(min, max);
	}
	
	return val;
}

void BPGame::LoadSettings() {
	Secret1 = Secret2 = Secret3 = Secret4 = false;
	NumUnlockedGames = 0;

	ifstream ifs;
	ifs.open(".brainparty");
	
	FirstRun = false;
	
	if (ifs.is_open()) {
		string temp;
	
		int loading_stage = 0;
		int line_number = 0;
		int secret_num = 1;

		while (getline(ifs, temp)) {
			++line_number;
		
			if (temp.size() == 0) {
				++loading_stage;
			} else {
				switch (loading_stage) {
					case 0:
						// loading whether we have music and sound
						if (line_number == 1) {
							if (temp.compare("1") == 0) {
								EnableSound = true;
							} else {
								EnableSound = false;							
							}
						} else {
							if (temp.compare("1") == 0) {
								EnableMusic = true;
							} else {
								EnableMusic = false;							
							}
						}
						break;
				
					case 1:
						// loading secrets
						switch (secret_num) {
							case 1:
								if (temp.compare("1") == 0) {
									Secret1 = true;
								} else {
									Secret1 = false;							
								}
						
								break;
						
							case 2:
								if (temp.compare("1") == 0) {
									Secret2 = true;
								} else {
									Secret2 = false;							
								}
								break;
						
							case 3:
								if (temp.compare("1") == 0) {
									Secret3 = true;
								} else {
									Secret3 = false;							
								}
								break;
						
							case 4:
								if (temp.compare("1") == 0) {;
							
									Secret4 = true;
								} else {
									Secret4 = false;							
								}
								break;
						}

						++secret_num;
					
						break;
					
					case 2:
					{
						// loading test scores
						CurrentBrainWeight = atoi(temp.c_str());
						AllTestScores.Add(CurrentBrainWeight);
					
						if (CurrentBrainWeight > HighestBrainWeight) {
							HighestBrainWeight = CurrentBrainWeight;
						}
					
						break;
					}	

					
					case 3:
						const char* gamename = temp.c_str();
				
						for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
							if (it->second->IsSecret) continue;
					
							if (strcmp(gamename, it->second->ShortName) == 0) {
								it->second->Unlocked = true;
								++NumUnlockedGames;
								continue;
							}
						}
					
						break;
				}
			}
		}
	} else {
		FirstRun = true;
	}
	
	if (CurrentBrainWeight != -1) {
		ostringstream output;
		output << CurrentBrainWeight << "g";
		AllocString(&sfcTestWeight, output.str().c_str(), BRAINWEIGHT, 320, 100, CENTRED, true);
	} else {
		AllocString(&sfcTestWeight, "???", BRAINWEIGHT, 320, 100, CENTRED, true);		
	}
	
	if (HighestBrainWeight != -1) {
		ostringstream output;
		output << HighestBrainWeight << "g";
		AllocString(&sfcHighestBrainWeight, output.str().c_str(), XXLARGE, 320, 100, CENTRED, true);
	} else {
		AllocString(&sfcHighestBrainWeight, "N/A", XXLARGE, 320, 100, CENTRED, true);		
	}
}

void BPGame::SaveSettings() {
 	ofstream savefile;
 	savefile.open(".brainparty");
	savefile << EnableSound << endl;
	savefile << EnableMusic << endl;
	savefile << endl;
	
	savefile << Secret1 << endl;
	savefile << Secret2 << endl;
	savefile << Secret3 << endl;
	savefile << Secret4 << endl;
	savefile << endl;
	
	for (int i = 0; i < AllTestScores.Count; ++i) {
		savefile << AllTestScores[i] << endl;
	}
	
	savefile << endl;
	
	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
		if (it->second->Unlocked) {
			savefile << it->second->ShortName << endl;
		}
	}
	
 	savefile.close();

	LoadSettings();
}

string BPGame::SeparateThousands(int number) {
	char buffer[20];
	
	int i, digit, out_index = 0;

	for (i = number; i != 0; i /= 10) {
		digit = i % 10;

		if ((out_index + 1) % 4 == 0) {
			buffer[out_index++] = ',';
		}
		
		buffer[out_index++] = digit + '0';
	}
	
	buffer[out_index] = '\0';
	
	string retval = string(buffer);
	reverse(retval.begin(), retval.end());
	return retval;
}

string* BPGame::TicksToTime(int ticks) {
	int seconds_elapsed = floor(ticks / 1000);
	int minutes = floor(seconds_elapsed / 60.0);
	int seconds = seconds_elapsed % 60;
	
	ostringstream output;
	
	if (seconds < 10) {
		output << minutes << ":0" << seconds;
	} else {
		output << minutes << ":" << seconds;
	}
	
	string* retval = new string(output.str());
	return retval;
}

int BPGame::DivRem(int Num, int Div, int* Rem) {
	(*Rem) = Num % Div;
	return floor(Num / Div);
}

void BPGame::DrawLine(int fromx, int fromy, int tox, int toy, Colour* col, float width) {	
	GLfloat vertices[] = { fromx, fromy, tox, toy }; 

	glDisable(GL_TEXTURE_2D);
	glLineWidth(width);
	glColor4f(col->R, col->G, col->B, col->A);
	glLoadIdentity();

	glVertexPointer(2, GL_FLOAT, 0, vertices);
	glDrawArrays(GL_LINES, 0, 2);
	
	glEnable(GL_TEXTURE_2D);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

float BPGame::Clamp(float value, float minval, float maxval) {
	if (value > maxval) {
		value = maxval;
	} else if (value < minval) {
		value = minval;
	}
		
	return value;
}

float BPGame::Hermite(float value1, float tangent1, float value2, float tangent2, float amount) {
	float AmountSquared = amount * amount;
	float AmountCubed = AmountSquared * amount;

	float result = 0.0f;
	
	if (amount == 0.0f) {
		result = value1;
	} else if (amount == 1.0f) {
		result = value2;
	} else {
		result = (2 * value1 - 2 * value2 + tangent2 + tangent1) * AmountCubed + (3 * value2 - 3 * value1 - 2 * tangent1 - tangent2) * AmountSquared + tangent1 * amount + value1;
	}

	return result;
}

float BPGame::Lerp(float value1, float value2, float amount) {
	float result = Clamp(amount, 0.0f, 1.0f);
	return value1 + (value2 - value1) * result;
}

float BPGame::SmoothStep(float value1, float value2, float amount) {
	float result = Clamp(amount, 0.0f, 1.0f);
	result = Hermite(value1, 0.0, value2, 0.0, result);
	return result;
}

float BPGame::SmoothStep2(float value1, float value2, float amount) {
	float result = Clamp(amount, 0.0f, 1.0f);
	
	if (result > 0.5f) {
		result = Hermite(value2, 0.0f, value1, 0.0f, (result - 0.5f) * 2.0f);
	} else {
		result = Hermite(value1, 0.0f, value2, 0.0f, result * 2.0f);
	}
	
	return result;
}

Colour BPGame::ColourSmoothStep(Colour &from, Colour &to, float amount) {
	return Colour(SmoothStep(from.R, to.R, amount), SmoothStep(from.G, to.G, amount), SmoothStep(from.B, to.B, amount), SmoothStep(from.A, to.A, amount));
}


bool BPGame::StartsWithVowel(string* word) {
	char chr = (*word)[0];
	if (chr == 'a' || chr == 'e' || chr == 'i' || chr == 'o' || chr == 'u'){
		return true;
	}
	
	return false;
}

const char* BPGame::GetName() {
	return PeopleNames[RandomRange(0, PeopleNames.Count - 1)];
}

void BPGame::RenderMainMenu(float xoff, float yoff) {
	DrawImage(sfcMainMenu, xoff, yoff);
}

void BPGame::RenderOptionsMenu() {
	DrawImage(sfcOptions, 0, 0);
	
	if (!EnableSound) {
		DrawImage(sfcOptionsSoundsOff, 0, 188);
	}
	
	if (!EnableMusic) {
		DrawImage(sfcOptionsMusicOff, 0, 254);		
	}
}

void BPGame::RenderPlayMenu(float xoff, float yoff) {
	DrawImage(sfcPlayMenu, xoff, yoff);
	DrawString(sfcTestWeight, xoff, yoff + 58.0f);	
}

void BPGame::RenderPractiseMenu() {
	DrawImage(sfcPractiseMenu, 0, 0);
	
	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
		if (!it->second->IsShowing) {
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f - PractiseAnimCounter);
		}
		
		if (it->second->Pic == NULL) {
			DrawImage(sfcMiniGameUnknown, it->second->X, it->second->Y);
		} else {
			DrawImage(it->second->Pic, it->second->X, it->second->Y);
		}

		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	}
	
	if (PractisePageNumber == 0) {
		// hide Back
		glColor4f(1.0f, 1.0f, 1.0f, 0.85f);
		DrawImage(sfcPractiseMenuHider, 15, 384);
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	}
	
	if (PractisePageNumber + 9 >= NumUnlockedGames) {
		glColor4f(1.0f, 1.0f, 1.0f, 0.85f);
		DrawImage(sfcPractiseMenuHider, 173, 383);
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	}
}

void BPGame::RenderProfessor() {
	DrawImage(sfcProfessorTalk, 0, 0);

	ActiveMiniGame->DrawProfessorText();
}

void BPGame::RenderPlaying() {
	ActiveMiniGame->RenderMiniGame();
}

void BPGame::RenderTestStatus() {
	static float rotation = 0.0f;
	rotation += 15 * ElapsedSeconds;
	
	Clear(Black);
	DrawImage(sfcTestBackground, 160.0f, 240.0f, rotation, 1.2f, (*White));
	Colour col = Colour(1.0f, 1.0f, 1.0f, 0.2f);
	DrawImage(sfcTestBackground, 160.0f, 240.0f, -rotation, 1.2f, col);
	
	for (int i = 0; i < BackgroundObjects.Count; ++i) {
		BGObject* obj = BackgroundObjects[i];
		
		float alpha;
		
		if (obj->Scale < 0.5f) {
			// fading in
			alpha = (obj->Scale / 2.5f);
		} else {
			// fading out
			alpha = 0.4f - (obj->Scale / 2.5f);
		}
		
		alpha = Clamp(alpha, 0.0f, 1.0f);
		Colour col = Colour(1.0f, 1.0f, 1.0f, alpha);
		
		DrawImage(obj->Type, obj->Pos.X, obj->Pos.Y, 0.0f, obj->Scale, col);
	}
	
	DrawImage(sfcTestStatus[TestPosition], 0.0f, 208.0f);
}

void BPGame::RenderTestResults(float xoff, float yoff) {
	if (LastStateChange + 200 > TickCount) {
		// plain background
		DrawImage(sfcResultsBackground, xoff + 160.0f, yoff + 240.0f, 0.0f, 1.0f, (*White));
	} else if (LastStateChange + 1000 > TickCount) {
		// fade in Your Brain Weighs text
		DrawImage(sfcResultsBackground, xoff + 160.0f, yoff + 240.0f, 0.0f, 1.0f, (*White));
		Colour col = Colour(1.0f, 1.0, 1.0f, 1 - (((LastStateChange + 1000) - TickCount) / 800.0f));
		DrawImage(sfcResultsBackground2, xoff + 160.0f, yoff + 240.0f, 0.0f, 1.0f, col);
	} else if (LastStateChange + 1600 > TickCount) {
		// fade in brain weight
		DrawImage(sfcResultsBackground2, xoff + 160.0f, yoff + 240.0f, 0.0f, 1.0f, (*White));
		float alpha = 1 - (((LastStateChange + 1600) - TickCount) / 600.0f);

		glColor4f(1.0f, 1.0f, 1.0f, alpha);
		DrawString(sfcTestWeight, xoff + 0.0f, yoff + 93.0f);
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	} else {
		// show everything
		DrawImage(sfcResultsBackground2, xoff + 160.0f, yoff + 240.0f, 0.0f, 1.0f, (*White));
		DrawString(sfcTestWeight, xoff + 0.0f, yoff + 93.0f);
		DrawString(sfcTestJob, xoff + 0.0f, yoff + 200.0f);
	}
	
	float diff = 1 - (((LastStateChange + 500) - TickCount) / 500.0f);
	
	for (int i = 0; i < TestResultContainers.Count; ++i) {
		TestResultContainer* result = TestResultContainers[i];
		
		int x, y, rot;
		
		if (LastStateChange + 500 > TickCount) {
			// fading in
			x = SmoothStep(160, result->Pos.X, diff);
			y = SmoothStep(525, result->Pos.Y, diff);
			rot = SmoothStep(60 + (i * 60), 0, diff);
			Colour col = Colour(1.0f, 1.0f, 1.0f, SmoothStep(0.0f, 1.0f, diff));
			DrawImage(MiniGames[(MiniGameType)result->MiniGame]->Pic, xoff + x, yoff + y, rot, 1.0f, col);
		} else {
			x = result->Pos.X;
			y = result->Pos.Y;
			rot = 0;
			DrawImage(MiniGames[(MiniGameType)result->MiniGame]->Pic, xoff + x, yoff + y, rot, 1.0f, (*White));
			Colour col = Colour(1.0f, 1.0f, 1.0f, SmoothStep(0.0f, 1.0f, result->FadeUpAnim));
			DrawImage(sfcResults90[result->Rank], xoff + x, yoff + y, 0.0f, 1.0f, col);
		}
	}
}

void BPGame::RenderHistory(float xoff, float yoff) {
	DrawImage(sfcHistoryBackground, xoff, yoff);
	
	DrawString(sfcHighestBrainWeight, xoff, 246.0f + yoff);
	
	if (Secret1) {
		DrawImage(sfcSecretHi, 2 + xoff, 349 + yoff);
	} else {
		DrawImage(sfcSecretLo, 2 + xoff, 349 + yoff);
	}
	
	if (Secret2) {
		DrawImage(sfcSecretHi, 83 + xoff, 349 + yoff);
	} else {
		DrawImage(sfcSecretLo, 83 + xoff, 349 + yoff);
	}
	
	if (Secret3) {
		DrawImage(sfcSecretHi, 164 + xoff, 349 + yoff);
	} else {
		DrawImage(sfcSecretLo, 164 + xoff, 349 + yoff);
	}
	
	if (Secret4) {
		DrawImage(sfcSecretHi, 246 + xoff, 349 + yoff);
	} else {
		DrawImage(sfcSecretLo, 246 + xoff, 349 + yoff);
	}
	
	int graph_height = 148;
	int last_score = -1;
	
	if (ShowScores.Count == 1) {
		// just one score - draw a dot!
		int this_score = round(ShowScores[0] / 2500.0f * graph_height);
		DrawLine(52 + xoff, 201 - this_score + yoff, 56 + xoff, 201 - this_score + yoff, Red, 4.0f);
	} else {
		for (int i = 0; i < ShowScores.Count; ++i) {
			int this_score = round(ShowScores[i] / 2500.0f * graph_height);
		
			if (last_score != -1) {
				// connect the last dot to this dot
				DrawLine(54 + ((i - 1) * 30) + xoff, 201 - last_score + yoff, 54 + (i * 30) + xoff, 201 - this_score + yoff, Red, 3.0f);
			}
		
			last_score = this_score;
		}
	}
}

void BPGame::RenderBrainBoost() {
	DrawImage(sfcBrainBoost, 0, 0);
}

void BPGame::RenderMarathon() {
	DrawImage(sfcMarathonMode, 0, 0);	
}

void BPGame::RenderHelp(float xoff, float yoff) {
	DrawImage(sfcHelp, xoff, yoff);
}

void BPGame::RenderCredits(float xoff, float yoff) {
	DrawImage(sfcCredits, xoff, yoff);
}

void BPGame::CalculateTestResults() {
	// add up all our scores and see how well we did...
	TestBrainWeight = 0;
	
	for (int i = 0; i < TestScores.Count; ++i) {
		TestBrainWeight += TestScores[i];
	}
	
	if (TestBrainWeight < 500) {
		TestBrainJob = TestBrainJobs[0];
	} else if (TestBrainWeight < 600) {
		TestBrainJob = TestBrainJobs[1];
	} else if (TestBrainWeight < 650) {
		TestBrainJob = TestBrainJobs[2];
	} else if (TestBrainWeight < 700) {
		TestBrainJob = TestBrainJobs[3];
	} else if (TestBrainWeight < 750) {
		TestBrainJob = TestBrainJobs[4];
	} else if (TestBrainWeight < 800) {
		TestBrainJob = TestBrainJobs[5];
	} else if (TestBrainWeight < 850) {
		TestBrainJob = TestBrainJobs[6];
	} else if (TestBrainWeight < 900) {
		TestBrainJob = TestBrainJobs[7];
	} else if (TestBrainWeight < 950) {
		TestBrainJob = TestBrainJobs[8];
	} else if (TestBrainWeight < 1000) {
		TestBrainJob = TestBrainJobs[9];
	} else if (TestBrainWeight < 1050) {
		TestBrainJob = TestBrainJobs[10];
	} else if (TestBrainWeight < 1100) {
		TestBrainJob = TestBrainJobs[11];
	} else if (TestBrainWeight < 1150) {
		TestBrainJob = TestBrainJobs[12];
	} else if (TestBrainWeight < 1175) {
		TestBrainJob = TestBrainJobs[13];
	} else if (TestBrainWeight < 1200) {
		TestBrainJob = TestBrainJobs[14];
	} else if (TestBrainWeight < 1225) {
		TestBrainJob = TestBrainJobs[15];
	} else if (TestBrainWeight < 1250) {
		TestBrainJob = TestBrainJobs[16];
	} else if (TestBrainWeight < 1275) {
		TestBrainJob = TestBrainJobs[17];
	} else if (TestBrainWeight < 1300) {
		TestBrainJob = TestBrainJobs[18];
	} else if (TestBrainWeight < 1325) {
		TestBrainJob = TestBrainJobs[19];
	} else if (TestBrainWeight < 1350) {
		TestBrainJob = TestBrainJobs[20];
	} else if (TestBrainWeight < 1375) {
		TestBrainJob = TestBrainJobs[21];
	} else if (TestBrainWeight < 1400) {
		TestBrainJob = TestBrainJobs[22];
	} else if (TestBrainWeight < 1425) {
		TestBrainJob = TestBrainJobs[23];
	} else if (TestBrainWeight < 1450) {
		TestBrainJob = TestBrainJobs[24];
	} else if (TestBrainWeight < 1475) {
		TestBrainJob = TestBrainJobs[25];
	} else if (TestBrainWeight < 1500) {
		TestBrainJob = TestBrainJobs[26];
	} else if (TestBrainWeight < 1525) {
		TestBrainJob = TestBrainJobs[27];
	} else if (TestBrainWeight < 1550) {
		TestBrainJob = TestBrainJobs[28];
	} else if (TestBrainWeight < 1575) {
		TestBrainJob = TestBrainJobs[29];
	} else if (TestBrainWeight < 1600) {
		TestBrainJob = TestBrainJobs[30];
	} else if (TestBrainWeight < 1625) {
		TestBrainJob = TestBrainJobs[31];
	} else if (TestBrainWeight < 1650) {
		TestBrainJob = TestBrainJobs[32];
	} else if (TestBrainWeight < 1675) {
		TestBrainJob = TestBrainJobs[33];
	} else if (TestBrainWeight < 1700) {
		TestBrainJob = TestBrainJobs[34];
	} else if (TestBrainWeight < 1725) {
		TestBrainJob = TestBrainJobs[35];
	} else if (TestBrainWeight < 1750) {
		TestBrainJob = TestBrainJobs[36];
	} else if (TestBrainWeight < 1775) {
		TestBrainJob = TestBrainJobs[37];
	} else if (TestBrainWeight < 1800) {
		TestBrainJob = TestBrainJobs[38];
	} else if (TestBrainWeight < 1825) {
		TestBrainJob = TestBrainJobs[39];
	} else if (TestBrainWeight < 1850) {
		TestBrainJob = TestBrainJobs[40];
	} else if (TestBrainWeight < 1875) {
		TestBrainJob = TestBrainJobs[41];
	} else if (TestBrainWeight < 1900) {
		TestBrainJob = TestBrainJobs[42];
	} else if (TestBrainWeight < 1925) {
		TestBrainJob = TestBrainJobs[43];
	} else if (TestBrainWeight < 1950) {
		TestBrainJob = TestBrainJobs[44];
	} else if (TestBrainWeight < 1975) {
		TestBrainJob = TestBrainJobs[45];
	} else if (TestBrainWeight < 2000) {
		TestBrainJob = TestBrainJobs[46];
	} else if (TestBrainWeight < 2025) {
		TestBrainJob = TestBrainJobs[47];
	} else if (TestBrainWeight < 2050) {
		TestBrainJob = TestBrainJobs[48];
	} else if (TestBrainWeight < 2075) {
		TestBrainJob = TestBrainJobs[49];
	} else if (TestBrainWeight < 2100) {
		TestBrainJob = TestBrainJobs[50];
	} else if (TestBrainWeight < 2125) {
		TestBrainJob = TestBrainJobs[51];
	} else if (TestBrainWeight < 2150) {
		TestBrainJob = TestBrainJobs[52];
	} else if (TestBrainWeight < 2175) {
		TestBrainJob = TestBrainJobs[53];
	} else if (TestBrainWeight < 2200) {
		TestBrainJob = TestBrainJobs[54];
	} else {
		TestBrainJob = TestBrainJobs[55];
	}
	
	AllocString(&sfcTestJob, TestBrainJob, NORMAL, 320.0f, 80.0f, CENTRED, false);
	
	// add the new score to the array
	AllTestScores.Add(TestBrainWeight);
	CurrentBrainWeight = TestBrainWeight;
	
	// unlock the games we just played
	for (int i = 0; i < TestMiniGames.Count; ++i) {
		MiniGames[TestMiniGames[i]]->Unlocked = true;
	}
	
	// now prepare the results screen
	TestResultContainers.Clear();
	
	for (int i = 0; i < TestMiniGames.Count; ++i) {
		TestResultContainer* result = new TestResultContainer();
		
		ostringstream brainweight;
		brainweight << TestScores[i] << "g";
		AllocString(&result->sfcBrainWeight, brainweight.str().c_str(), NORMAL, 90.0f, 30.0f, CENTRED, true);
		
		result->MiniGame = TestMiniGames[i];
		result->Rank = BPMiniGame::GetRank(TestScores[i]);
		result->GoingUp = false;
		
		if (i < 2) {
			// first row
			result->Pos = BPPoint(110 + (i * 100), 325);
		} else {
			// second row
			result->Pos = BPPoint(60 + ((i - 2) * 100), 425);
		}
		
		result->FadeUpAnim = Lerp(-1.0f, 0.0f, i / (float)(TestMiniGames.Count - 1));
		TestResultContainers.Add(result);
	}
	
	SaveSettings();
}

void BPGame::CreateMiniGame(MiniGameType game, bool marathon, bool practise) {	
	if (ActiveMiniGame != NULL) {		
		SAFE_DELETE(ActiveMiniGame);
	}
		
	switch (game) {
		case BALLOONBLASTER:
			ActiveMiniGame = new BPMiniGame_BalloonBlaster(this);
			break;
			
		case BOMBHUNT:
			ActiveMiniGame = new BPMiniGame_BombHunt(this);
			break;
			
		case BPSAYS:
			ActiveMiniGame = new BPMiniGame_BPSays(this);
			break;
			
		case BUBBLETROUBLE:
			ActiveMiniGame = new BPMiniGame_BubbleTrouble(this);
			break;
			
		case CARDMATCH:
			ActiveMiniGame = new BPMiniGame_CardMatch(this);
			break;
			
		case CONNEX:
			ActiveMiniGame = new BPMiniGame_Connex(this);
			break;

		case CUPSNBALLS:
			ActiveMiniGame = new BPMiniGame_CupsNBalls(this);
			break;
			
		case DICEOFF:
			ActiveMiniGame = new BPMiniGame_DiceOff(this);
			break;
			
		case FLASHCOUNTING:
			ActiveMiniGame = new BPMiniGame_FlashCounting(this);
			break;
			
		case FLASHLIGHT:
			ActiveMiniGame = new BPMiniGame_Flashlight(this);
			break;
			
		case IQTEST:
			ActiveMiniGame = new BPMiniGame_IQTest(this);
			break;
			
		case JEWELFLIP:
			ActiveMiniGame = new BPMiniGame_JewelFlip(this);
			break;
			
		case JEWELJAM:
			ActiveMiniGame = new BPMiniGame_JewelJam(this);
			break;
			
		case MARBLEDROP:
			ActiveMiniGame = new BPMiniGame_MarbleDrop(this);
			break;
			
		case MEMORYBLOX:
			ActiveMiniGame = new BPMiniGame_MemoryBlox(this);
			break;
			
		case MEMORYBOX:
			ActiveMiniGame = new BPMiniGame_MemoryBox(this);
			break;
			
		case MEMORYMATHS:
			ActiveMiniGame = new BPMiniGame_MemoryMaths(this);
			break;
			
		case MINESWEEP:
			ActiveMiniGame = new BPMiniGame_MineSweep(this);
			break;
			
		case MOONJUMP:
			ActiveMiniGame = new BPMiniGame_MoonJump(this);
			break;

		case NEXTINLINE:
			ActiveMiniGame = new BPMiniGame_NextInLine(this);
			break;
			
		case NUMBERSNAKE:
			ActiveMiniGame = new BPMiniGame_NumberSnake(this);
			break;
			
		case ODDONEOUT:
			ActiveMiniGame = new BPMiniGame_OddOneOut(this);
			break;

		case PATCHMATCH:
			ActiveMiniGame = new BPMiniGame_PatchMatch(this);
			break;
			
		case PERFECTPATHS:
			ActiveMiniGame = new BPMiniGame_PerfectPaths(this);
			break;
			
		case ROUTEFINDER:
			ActiveMiniGame = new BPMiniGame_RouteFinder(this);
			break;
			
		case RPS:
			ActiveMiniGame = new BPMiniGame_RPS(this);
			break;
			
		case SCRAMBLED:
			ActiveMiniGame = new BPMiniGame_Scrambled(this);
			break;
			
		case SETFINDER:
			ActiveMiniGame = new BPMiniGame_SetFinder(this);
			break;
			
		case SHARPSHOOTER:
			ActiveMiniGame = new BPMiniGame_Sharpshooter(this);
			break;

		case SHORTCIRCUITSUDOKU:
			ActiveMiniGame = new BPMiniGame_ShortCircuitSudoku(this);
			break;
			
		case SHUFFLEPUZZLER:
			ActiveMiniGame = new BPMiniGame_ShufflePuzzler(this);
			break;
			
		case STRANGERDANGER:
			ActiveMiniGame = new BPMiniGame_StrangerDanger(this);
			break;
			
		case SYMBOLICLOGIC:
			ActiveMiniGame = new BPMiniGame_SymbolicLogic(this);
			break;
			
		case UNDERTHEHAT:
			ActiveMiniGame = new BPMiniGame_UnderTheHat(this);
			break;

		case UNTANGLER:
			ActiveMiniGame = new BPMiniGame_Untangler(this);
			break;
			
		case WORDSMASH:
			ActiveMiniGame = new BPMiniGame_WordSmash(this);
			break;
	}
	
	ActiveMiniGame->Init();
	
	if (marathon) ActiveMiniGame->SetMarathon();
	
	if (GameState == HISTORY) {
		ActiveMiniGame->ReturnType = RT_SECRET;
	} else if (GameState == BRAINBOOST) {
		ActiveMiniGame->ReturnType = RT_BRAINBOOST;
	}
	
	if (practise) {
		SetGameState(PRACTISE_PROFESSOR);
	} else {
		SetGameState(PROFESSOR);		
	}
}

void BPGame::SetGameState(GameStates state) {
	GameState = state;
	AnimCounter = 0.0f;
	GameStateJustChanged = true;
	
	if (state != TEST_RESULTS_PLAY) {
		// don't reset LastStateChange when going from test results back to the play menu, as that resets the
		// animation on the results screen
		LastStateChange = TickCount;
	}
	
	switch (state) {
		case PLAY_PRACTISE:
			PractisePageNumber = 0;
			PractisePageMoveLeft = true;
			
			for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
				it->second->IsShowing = false;
			}
			
			PositionPractiseGames();
			
			break;
			
		case HISTORY:
			ShowScores.Clear();
			
			if (AllTestScores.Count > 8) {
				for (int i = AllTestScores.Count - 8; i < AllTestScores.Count; ++i) {
					ShowScores.Add(AllTestScores[i]);
				}				
			} else {
				for (int i = 0; i < AllTestScores.Count; ++i) {
					ShowScores.Add(AllTestScores[i]);
				}
			}
			
			break;
			
		case PROFESSOR:
			PlayMusic("professor");
			break;
			
		case TEST_STATUS:
			PlaySound("happy_ring");
			break;
	}
}

void BPGame::PositionPractiseGames() {
	PractiseAnimCounter = 0.0f;
	
	// get all minigames off the screen
	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
		if (it->second->IsShowing) {
			// these are visible - make them scroll offscreen neatly
			it->second->StartX = it->second->X;
			it->second->StartY = it->second->Y;
			
			if (PractisePageMoveLeft) {
				it->second->DestX = -100;
			} else {
				it->second->DestX = 320;
			}
			
			it->second->DestY = 175;
		} else {
			it->second->Y = it->second->StartY = it->second->DestY = 175;			

			if (PractisePageMoveLeft) {
				it->second->X = it->second->StartX = it->second->DestX = -100;
			} else {
				it->second->X = it->second->StartX = it->second->DestX = 320;				
			}
		}
		
		it->second->IsShowing = false;
	}
	
	BPPList<BPPoint*> PractisePositions;
	
	for (int row = 0; row < 3; ++row) {
		for (int col = 0; col < 3; ++col) {
			PractisePositions.Add(new BPPoint(15 + (col * 100), 75 + (row * 100)));
		}
	}
		
	int i = 0;
	int j = 0;
	
	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
		if (it->second->IsSecret) continue;
		if (!it->second->Unlocked) continue;
		
		if (i < PractisePageNumber) {
			++i;
			it->second->IsShowing = false;
			continue;
		}
		
		it->second->DestX = PractisePositions[j]->X;
		it->second->DestY = PractisePositions[j]->Y;
		
		it->second->StartY = PractisePositions[j]->Y;
		
		if (PractisePageMoveLeft) {
			it->second->StartX = 320;
		} else {
			it->second->StartX = -100;
		}
		
		it->second->IsShowing = true;
		
		++i;
		++j;
		
		if (i >= PractisePageNumber + 9) break;
	}
	
	PractisePositions.Clear();
}

void BPGame::RunTest() {
	StopMusic();
	
	TestMiniGames.Clear();
	TestScores.Clear();
	TestPosition = 0;
	
	BPList<MiniGameType> SeenMiniGames;
	BPList<MiniGameType> UnseenMiniGames;
	
	// add them all to an array, then shuffle the array
	for (map<MiniGameType, BPMiniGame_Container*>::const_iterator it = MiniGames.begin(); it != MiniGames.end(); ++it) {
		if (it->second->IsSecret) continue; // don't allow hidden minigames into the test
		
		if (it->second->Unlocked) {
			SeenMiniGames.Add(it->first);
		} else {
			UnseenMiniGames.Add(it->first);			
		}
	}
	
	SeenMiniGames.Shuffle();
	UnseenMiniGames.Shuffle();
	
	int to_add;
	
	if (SeenMiniGames.Count == 0) {
		// we've seen no games yet; add five unseen games
		to_add = 5;
	} else {
		to_add = min(UnseenMiniGames.Count, 4);
	}
	
	for (int i = 0; i < to_add; ++i) {
		TestMiniGames.Add(UnseenMiniGames[i]);
	}
	
	// figure out how many seen games we need to add, and add them
	to_add = 5 - to_add;
	for (int i = 0; i < to_add; ++i) {
		TestMiniGames.Add(SeenMiniGames[i]);
	}
	
	// shuffle the list so that the seen/unseen games come in any order
	TestMiniGames.Shuffle();
	
	InTestMode = true;
	SetGameState(TEST_STATUS);
}

void BPGame::NextTest() {
	if (TestPosition == TestMiniGames.Count) {
		// use this for quick testing
//	if (true) {
//		TestScores.Add(150);
//		TestScores.Add(250);
//		TestScores.Add(350);
//		TestScores.Add(450);
//		TestScores.Add(500);
		CalculateTestResults();
		SetGameState(TEST_RESULTS);
		
		InTestMode = false;
		
		PlayMusic("results");

		return;
	}
	
	CreateMiniGame(TestMiniGames[TestPosition], false, false);
	++TestPosition;
}

void BPGame::PlaySound(const char* sound, bool preload_only, bool force_play) {

	if (!EnableSound && !force_play) return;
	
	if (Sounds.find(sound) != Sounds.end()) {
		if (preload_only) {
			// do nothing - this is only for iPhone
		} else {
			Mix_PlayChannel(-1, Sounds[sound], 0);
		}
	} else {
		printf("Error: sound %s doesn't exist.\n", sound);
	}
}

void BPGame::PlayMusic(const char* name) {
	if (!EnableMusic) return;
	
	if (Music != NULL) {
		Mix_HaltMusic();
		Mix_FreeMusic(Music);
		Music = NULL;
	}
	
	string* file = new string(name);
	file->insert(0, "Content/");
	file->append(".ogg");
	
	Music = Mix_LoadMUS(file->c_str());
	Mix_PlayMusic(Music, -1);

	delete(file);
}

void BPGame::StopMusic() {
	if (Music != NULL) {
		Mix_HaltMusic();
		Mix_FreeMusic(Music);
		Music = NULL;
	}
}

bool BPGame::FileExists(const char * filename) {
    if (FILE * file = fopen(filename, "r")) {
        fclose(file);
        return true;
    }
    return false;
}

Mix_Chunk* BPGame::LoadSound(const char* filename, const char* extension) {
	string* file = new string(filename);
	file->insert(0, "Content/");
	file->append(".wav");
	
	Mix_Chunk* retval = Mix_LoadWAV(file->c_str());
	if (retval == NULL) printf("Warning: couldn't load file %s.\n", filename);
	delete file;
	
	return retval;
}

void BPGame::CancelTest() {
	GameState = PLAY_MENU;
	TestMiniGames.Clear();
	TestScores.Clear();
	TestPosition = 0;
	
	InTestMode = false;
	
	PlayMusic("theme");
}

void BPGame::Accelerate(float x, float y, float z) {
	Acceleration[0] = x;
	Acceleration[1] = y;
	Acceleration[2] = z;
}

void BPGame::PromptResetScores() {
	ShowingClearScores = true;
	MessageBox::Show("Are you sure you want to wipe all your test scores so far? Press Y on your keyboard now to wipe your scores. Click the mouse or press any other key cancel.", "Clear scores?");
}

void BPGame::ExecuteResetScores() {
	AllTestScores.Clear();
	SaveSettings();
	MessageBox::Show("Your test scores have been wiped and you have a clean slate - good luck!", "Scores wiped!");
}

Colour* BPGame::ColorLerp(Colour* from, Colour* to, float amount) {
	Colour* col = new Colour(Lerp(from->R, to->R, amount), Lerp(from->G, to->G, amount), Lerp(from->B, to->B, amount), Lerp(from->A, to->A, amount));
	return col;
}