From 75da1dc0d137d5ed64eee56c97ca9d2444379297 Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Fri, 31 Aug 2012 17:32:26 +0200 Subject: [PATCH] Initial code import (0.5.91-2 from Fremantle) --- BGObject.cpp | 19 + BGObject.h | 37 + BPGame.cpp | 2421 ++++++++++++++++++++++++++++++++++++++++ BPGame.h | 310 +++++ BPList.h | 231 ++++ BPPoint.cpp | 42 + BPPoint.h | 39 + COPYING | 674 +++++++++++ CREDITS | 31 + Colour.cpp | 32 + Colour.h | 32 + Makefile | 32 + MessageBox.cpp | 25 + MessageBox.h | 31 + MiniGameContainer.h | 50 + Minigame.cpp | 496 ++++++++ Minigame.h | 131 +++ README | 73 ++ SDLMain.h | 26 + SDLMain.m | 370 ++++++ SpriteFont.cpp | 50 + SpriteFont.h | 39 + TestResultContainer.h | 40 + Texture.cpp | 228 ++++ Texture.h | 59 + WordList.h | 78 ++ balloonblaster.cpp | 361 ++++++ balloonblaster.h | 83 ++ bombhunt.cpp | 214 ++++ bombhunt.h | 66 ++ bpsays.cpp | 318 ++++++ bpsays.h | 63 ++ brainparty.desktop | 7 + brainparty.png | Bin 0 -> 10540 bytes bubbletrouble.cpp | 267 +++++ bubbletrouble.h | 85 ++ cardmatch.cpp | 220 ++++ cardmatch.h | 65 ++ connex.cpp | 211 ++++ connex.h | 66 ++ cupsnballs.cpp | 289 +++++ cupsnballs.h | 102 ++ debian/brainparty.docs | 3 + debian/changelog | 33 + debian/compat | 1 + debian/control | 127 +++ debian/copyright | 71 ++ debian/dirs | 2 + debian/rules | 67 ++ diceoff.cpp | 328 ++++++ diceoff.h | 96 ++ flashcounting.cpp | 306 +++++ flashcounting.h | 74 ++ flashlight.cpp | 233 ++++ flashlight.h | 64 ++ iqtest.cpp | 292 +++++ iqtest.h | 63 ++ jewelflip.cpp | 365 ++++++ jewelflip.h | 70 ++ jeweljam.cpp | 510 +++++++++ jeweljam.h | 81 ++ main.cpp | 180 +++ marbledrop.cpp | 223 ++++ marbledrop.h | 61 + memoryblox.cpp | 249 +++++ memoryblox.h | 59 + memorybox.cpp | 271 +++++ memorybox.h | 72 ++ memorymaths.cpp | 408 +++++++ memorymaths.h | 75 ++ minesweep.cpp | 222 ++++ minesweep.h | 72 ++ moonjump.cpp | 213 ++++ moonjump.h | 72 ++ nextinline.cpp | 297 +++++ nextinline.h | 59 + numbersnake.cpp | 272 +++++ numbersnake.h | 62 + oddoneout.cpp | 223 ++++ oddoneout.h | 63 ++ patchmatch.cpp | 312 ++++++ patchmatch.h | 79 ++ perfectpaths.cpp | 443 ++++++++ perfectpaths.h | 102 ++ routefinder.cpp | 318 ++++++ routefinder.h | 66 ++ rps.cpp | 193 ++++ rps.h | 82 ++ scrambled.cpp | 476 ++++++++ scrambled.h | 99 ++ setfinder.cpp | 490 ++++++++ setfinder.h | 102 ++ sharpshooter.cpp | 240 ++++ sharpshooter.h | 70 ++ shortcircuitsudoku.cpp | 366 ++++++ shortcircuitsudoku.h | 89 ++ shufflepuzzler.cpp | 245 ++++ shufflepuzzler.h | 70 ++ strangerdanger.cpp | 312 ++++++ strangerdanger.h | 71 ++ symboliclogic.cpp | 351 ++++++ symboliclogic.h | 102 ++ underthehat.cpp | 284 +++++ underthehat.h | 81 ++ untangler.cpp | 284 +++++ untangler.h | 65 ++ wordsmash.cpp | 598 ++++++++++ wordsmash.h | 105 ++ 108 files changed, 20047 insertions(+) create mode 100644 BGObject.cpp create mode 100644 BGObject.h create mode 100644 BPGame.cpp create mode 100644 BPGame.h create mode 100644 BPList.h create mode 100644 BPPoint.cpp create mode 100644 BPPoint.h create mode 100644 COPYING create mode 100644 CREDITS create mode 100644 Colour.cpp create mode 100644 Colour.h create mode 100644 Makefile create mode 100644 MessageBox.cpp create mode 100644 MessageBox.h create mode 100644 MiniGameContainer.h create mode 100644 Minigame.cpp create mode 100644 Minigame.h create mode 100644 README create mode 100644 SDLMain.h create mode 100644 SDLMain.m create mode 100644 SpriteFont.cpp create mode 100644 SpriteFont.h create mode 100644 TestResultContainer.h create mode 100644 Texture.cpp create mode 100644 Texture.h create mode 100644 WordList.h create mode 100644 balloonblaster.cpp create mode 100644 balloonblaster.h create mode 100644 bombhunt.cpp create mode 100644 bombhunt.h create mode 100644 bpsays.cpp create mode 100644 bpsays.h create mode 100644 brainparty.desktop create mode 100644 brainparty.png create mode 100644 bubbletrouble.cpp create mode 100644 bubbletrouble.h create mode 100644 cardmatch.cpp create mode 100644 cardmatch.h create mode 100644 connex.cpp create mode 100644 connex.h create mode 100644 cupsnballs.cpp create mode 100644 cupsnballs.h create mode 100644 debian/brainparty.docs create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dirs create mode 100755 debian/rules create mode 100644 diceoff.cpp create mode 100644 diceoff.h create mode 100644 flashcounting.cpp create mode 100644 flashcounting.h create mode 100644 flashlight.cpp create mode 100644 flashlight.h create mode 100644 iqtest.cpp create mode 100644 iqtest.h create mode 100644 jewelflip.cpp create mode 100644 jewelflip.h create mode 100644 jeweljam.cpp create mode 100644 jeweljam.h create mode 100644 main.cpp create mode 100644 marbledrop.cpp create mode 100644 marbledrop.h create mode 100644 memoryblox.cpp create mode 100644 memoryblox.h create mode 100644 memorybox.cpp create mode 100644 memorybox.h create mode 100644 memorymaths.cpp create mode 100644 memorymaths.h create mode 100644 minesweep.cpp create mode 100644 minesweep.h create mode 100644 moonjump.cpp create mode 100644 moonjump.h create mode 100644 nextinline.cpp create mode 100644 nextinline.h create mode 100644 numbersnake.cpp create mode 100644 numbersnake.h create mode 100644 oddoneout.cpp create mode 100644 oddoneout.h create mode 100644 patchmatch.cpp create mode 100644 patchmatch.h create mode 100644 perfectpaths.cpp create mode 100644 perfectpaths.h create mode 100644 routefinder.cpp create mode 100644 routefinder.h create mode 100644 rps.cpp create mode 100644 rps.h create mode 100644 scrambled.cpp create mode 100644 scrambled.h create mode 100644 setfinder.cpp create mode 100644 setfinder.h create mode 100644 sharpshooter.cpp create mode 100644 sharpshooter.h create mode 100644 shortcircuitsudoku.cpp create mode 100644 shortcircuitsudoku.h create mode 100644 shufflepuzzler.cpp create mode 100644 shufflepuzzler.h create mode 100644 strangerdanger.cpp create mode 100644 strangerdanger.h create mode 100644 symboliclogic.cpp create mode 100644 symboliclogic.h create mode 100644 underthehat.cpp create mode 100644 underthehat.h create mode 100644 untangler.cpp create mode 100644 untangler.h create mode 100644 wordsmash.cpp create mode 100644 wordsmash.h diff --git a/BGObject.cpp b/BGObject.cpp new file mode 100644 index 0000000..cd80a46 --- /dev/null +++ b/BGObject.cpp @@ -0,0 +1,19 @@ +// 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 "BGObject.h" + diff --git a/BGObject.h b/BGObject.h new file mode 100644 index 0000000..a4f3c5d --- /dev/null +++ b/BGObject.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef __BGOBJECT_H__ +#define __BGOBJECT_H__ + +#include "BPPoint.h" +#include "Colour.h" +#include "Texture.h" + +class BGObject { +public: + BPPoint Pos; + float Scale; + Colour Col; + Texture* Type; + + BGObject() { + Col = Colour(1.0f, 1.0f, 1.0f, 1.0f); + } +}; + +#endif diff --git a/BPGame.cpp b/BPGame.cpp new file mode 100644 index 0000000..63a8ae1 --- /dev/null +++ b/BPGame.cpp @@ -0,0 +1,2421 @@ +// 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 +#include "BPGame.h" +#include "SDL.h" + +#include +#include + +#include "SDL_ttf.h" +#include + + +#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::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::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; + } + + ConvertCoordinates(x, y); + 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; + } + + ConvertCoordinates(x, y); + 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 (PointOverRect(TouchEvent.X, TouchEvent.Y, 260, 0, 60, 60)) { + SetGameState(DO_QUIT); + } 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::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::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; + } + + ConvertCoordinates(x, y); + 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("/opt/brainparty/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 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("/home/user/.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::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("/home/user/.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::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::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::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::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 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::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 SeenMiniGames; + BPList UnseenMiniGames; + + // add them all to an array, then shuffle the array + for (map::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, "/opt/brainparty/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, "/opt/brainparty/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; +} diff --git a/BPGame.h b/BPGame.h new file mode 100644 index 0000000..8bd3621 --- /dev/null +++ b/BPGame.h @@ -0,0 +1,310 @@ +// 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. + +#ifndef __BPGAME_H__ +#define __BPGAME_H__ + +#define SAFE_DELETE(a) { delete(a); (a) = 0; } +#define TIMERREDRAW 1000 + +#include +#include +#include +#include +#include +#include +#include + +#include "SDL.h" + +#include +#include + +#include "SDL_mixer.h" + +#include "Texture.h" +#include "Colour.h" +#include "BPPoint.h" +#include "MessageBox.h" +#include "BPList.h" +#include "Minigame.h" +#include "MiniGameContainer.h" +#include "TestResultContainer.h" +#include "SpriteFont.h" +#include "BGObject.h" + +enum GameStates { FIRST_RUN, MAIN_MENU, ABOUT, PLAY_MENU, PRACTISE_MENU, PLAYING, TEST_STATUS, TEST_RESULTS, PROFESSOR, OPTIONS, HELP, HISTORY, CREDITS, STORE, BRAINBOOST, MARATHON, MAIN_MENU_PLAY, PLAY_MAIN_MENU, PLAY_PRACTISE, PRACTISE_PLAY, PLAY_HISTORY, HISTORY_CREDITS, CREDITS_HISTORY, HISTORY_PLAY, TEST_RESULTS_PLAY, PRACTISE_PROFESSOR, MAIN_MENU_OPTIONS, OPTIONS_MAIN_MENU, MAIN_MENU_HELP, HELP_MAIN_MENU, DO_QUIT }; +enum MiniGameType { BALLOONBLASTER, BOMBHUNT, BPSAYS, BUBBLETROUBLE, CARDMATCH, CONNEX, CUPSNBALLS, DICEOFF, FLASHCOUNTING, FLASHLIGHT, IQTEST, JEWELFLIP, JEWELJAM, MARBLEDROP, MEMORYBLOX, MEMORYBOX, MEMORYMATHS, MINESWEEP, MOONJUMP, NEXTINLINE, NUMBERSNAKE, ODDONEOUT, PATCHMATCH, PERFECTPATHS, ROUTEFINDER, RPS, SCRAMBLED, SETFINDER, SHARPSHOOTER, SHORTCIRCUITSUDOKU, SHUFFLEPUZZLER, STRANGERDANGER, SYMBOLICLOGIC, UNDERTHEHAT, UNTANGLER, WORDSMASH }; +enum FontSizes { TINY = 16, XSMALL = 19, SMALL = 22, NORMAL = 24, LARGE = 30, XLARGE = 40, XXLARGE = 50, MEGA = 72, BRAINWEIGHT = 82, ALMOSTEPIC = 90, EPIC = 96, LARGEST = 120 }; +enum Colours { WHITE, BLACK, NOCOLOUR }; + +class BPMiniGame; +class SpriteFont; + +struct cmp_str { + bool operator()(char const *a, char const *b) { + return std::strcmp(a, b) < 0; + } +}; + +class BPGame { +public: + int GLViewRenderbuffer; + int GLViewFramebuffer; + + int TickCount; // number of ticks ever + int ElapsedTickCount; // number of ticks since last update + float ElapsedSeconds; + + int CheatMainTaps; + int CheatOptionsTaps; + + bool EnableSound; + bool EnableMusic; + + int NumUnlockedGames; + + BPPoint EmptyPoint; + + Colour* Black; + Colour* White; + Colour* TransparentWhite; + Colour* Blue; + Colour* Cyan; + Colour* Green; + Colour* ConnexGreen; + Colour* Orange; + Colour* Red; + Colour* LightRed; + Colour* DarkRed; + Colour* Yellow; + Colour* DarkGrey; + + static Texture* sfcLogo; + static bool ShowingMessageBox; + static bool ShowingClearScores; + static SpriteFont* sfcMessageBoxTitle; + static SpriteFont* sfcMessageBoxText; + + Texture* sfcMainMenu; + Texture* sfcPlayMenu; + Texture* sfcPractiseMenu; + Texture* sfcPractiseMenuHider; + Texture* sfcProfessorTalk; + Texture* sfcCredits; + Texture* sfcOptions; + Texture* sfcOptionsSoundsOff; + Texture* sfcOptionsMusicOff; + Texture* sfcHistoryBackground; + Texture* sfcMarathonMode; + Texture* sfcBrainBoost; + Texture* sfcFirstRun; + Texture* sfcHelp; + + Texture* sfcLoading; + + Texture* sfcSecretHi; + Texture* sfcSecretLo; + + Texture* sfcResultsFail; + Texture* sfcResultsBronze; + Texture* sfcResultsSilver; + Texture* sfcResultsGold; + Texture* sfcResultsPlatinum; + + Texture* sfcBottomBar; + Texture* sfcMinigameHelp; + Texture* sfcMinigameBack; + Texture* sfcQuitTest; + + Texture* sfcWhite; + + Texture* sfcMiniGameUnknown; + + Texture* sfcTestBackground; + Texture* sfcResultsBackground; + Texture* sfcResultsBackground2; + map sfcResults90; + + BPList sfcBGObjectTypes; + BPList BackgroundObjects; + int LastBGObject; + + BPList sfcStarTypes; + + Mix_Music* Music; + map Sounds; + + Texture* sfcCorrect; + Texture* sfcWrong; + + BPPList sfcTestStatus; + BPPList TestResultContainers; + + GameStates GameState; + int LastStateChange; + bool GameStateJustChanged; // used to skip one frame of anim updates to avoid jumping after long game changes such as starting a minigame + + float AnimCounter; + float PractiseAnimCounter; + + BPMiniGame* ActiveMiniGame; + string* CurrentMiniGame; + + int PractisePageNumber; + bool PractisePageMoveLeft; // true if items should be moving right to left + + bool FirstRun; + bool Secret1; + bool Secret2; + bool Secret3; + bool Secret4; + + void Init(int width, int height); + void Update(float elapsed); + void Draw(); + + BPPoint TouchEvent; + + BPPList TestBrainJobs; + BPPList PeopleNames; + + map MiniGames; + + BPList AllTestScores; + BPList ShowScores; + BPList TestMiniGames; + BPList TestScores; + + SpriteFont* sfcTestWeight; + SpriteFont* sfcTestJob; + SpriteFont* sfcHighestBrainWeight; + + int TestPosition; + bool InTestMode; + + int CurrentBrainWeight; + int HighestBrainWeight; + int TestBrainWeight; + const char* TestBrainJob; + + float Acceleration[3]; + + inline void ConvertCoordinates(float &x, float &y) { + /** + * This converts the physical (screen) coordinates to the + * game world coordinates. We've rotated the screen by 90 + * degrees, and the resolution is also different. We then + * center the scaled image on the screen (the "-26"). The + * "1.666/1.6" is here, because the screen has a ratio of + * 480/800 and the world 480/320. + * + * world: screen: + * a--b b--d + * | | | | + * c--d a--c + * 320x480 800x480 + * + * The world is centered on the screen, so the world takes + * up 720 pixels height on the screen - 40 pixels are black + * border at the top (screen: left) and bottom (screen: right). + * + * These 40 pixels are ~ 26 pixels in world coordinates: + * offset_in_world = (40 * 480 / 800) * 1.666 / 1.5 + **/ + float tmp = (480.-y)*320./480.; + y = ((x*480./800.)*1.666/1.5)-26; + x = tmp; + } + void TouchStart(float x, float y); + void TouchStop(float x, float y); + void TouchDrag(float x, float y); + void Accelerate(float x, float y, float z); + + Texture* LoadBitmap(const char* filename, int actualwidth = 100, int actualheight = 100); + Mix_Chunk* LoadSound(const char* filename, const char* extension = "wav"); + void PlaySound(const char* sound, bool preload_only = false, bool force_play = false); + void AddTestScore(int score); + void AddPlatinumGame(); + + void FillRectangle(Colour col, float x, float y, float width, float height); + void DrawImage(Texture* tex, float x, float y); + void DrawImage(Texture* tex, float x, float y, Colour &col); + void DrawImage(Texture* tex, float x, float y, float rotation, float scale, Colour &col); + static void AllocString(SpriteFont** tex, const char* str, FontSizes size, float width, float height, Alignments align, bool bold = false); + void DrawString(SpriteFont* tex, Colour &col, float x, float y); + void DrawString(SpriteFont* tex, Colours col, float x, float y); + void DrawString(SpriteFont* tex, float x, float y); + void Clear(Colour* col); + bool PointOverRect(int x1, int y1, int x2, int y2, int width, int height); + bool RectOverRect(int x1, int y1, int width1, int height1, int x2, int y2, int width2, int height2); + int RandomRange(int min, int max); + int RandomRangeExcept(int min, int max, int except); + string SeparateThousands(int number); + string* TicksToTime(int ticks); + int DivRem(int Num, int Div, int* Rem); + void DrawLine(int fromx, int fromy, int tox, int toy, Colour* col, float width); + float Hermite(float value1, float tangent1, float value2, float tangent2, float amount); + float Lerp(float value1, float value2, float amount); + float SmoothStep(float value1, float value2, float amount); + float SmoothStep2(float value1, float value2, float amount); + Colour ColourSmoothStep (Colour &from, Colour &to, float amount); + float Clamp(float value, float minval, float maxval); + bool StartsWithVowel(string* word); + const char* GetName(); + Colour* ColorLerp (Colour* from, Colour* to, float amount); + + void LoadSettings(); + void SaveSettings(); + + void RenderMainMenu(float xoff, float yoff); + void RenderOptionsMenu(); + void RenderPlayMenu(float xoff, float yoff); + void RenderPractiseMenu(); + void RenderProfessor(); + void RenderPlaying(); + void RenderTestStatus(); + void RenderTestResults(float xoff, float yoff); + void RenderHistory(float xoff, float yoff); + void RenderBrainBoost(); + void RenderMarathon(); + void RenderCredits(float xoff, float yoff); + void RenderHelp(float xoff, float yoff); + + void CalculateTestResults(); + void CreateMiniGame(MiniGameType game, bool marathon, bool practise); + void SetGameState(GameStates state); + void PositionPractiseGames(); + + void RunTest(); + void NextTest(); + void CancelTest(); + + void PlayMusic(const char* name); + void StopMusic(); + + void PromptResetScores(); + void ExecuteResetScores(); + + bool FileExists(const char* filename); + + static bool IsNull(void* somepointer) { return (somepointer == NULL); } + + vector Split(const string& s); +}; + +#endif diff --git a/BPList.h b/BPList.h new file mode 100644 index 0000000..5c986cb --- /dev/null +++ b/BPList.h @@ -0,0 +1,231 @@ +// 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. + +#ifndef __BPList_H__ +#define __BPList_H__ + +#include +#include + +#include "Texture.h" +#include "Colour.h" +#include "BPPoint.h" + +using namespace std; + +template +class BPList { +private: + vector data; + +public: + int Count; + BPList(); + ~BPList(); + void Add(T item); + bool Contains(T item); + void Clear(); + int IndexOf(T item); + void Insert(int pos, T item); + void Remove(T item); + void RemoveAt(int pos); + void SetValue(int pos, T val); + void Shuffle(); + T operator[] (int position); +}; + +template +BPList::BPList() { + Count = 0; +} + +template +BPList::~BPList() { + +} + +template +void BPList::Add(T item) { + data.push_back(item); + ++Count; +} + +template +bool BPList::Contains(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + return result != data.end(); +} + +template +void BPList::Clear() { + data.clear(); + Count = 0; +} + +template +T BPList::operator[] (int position) { + return data[position]; +} + +template +void BPList::Insert(int pos, T item) { + data.insert(data.begin() + pos, item); + ++Count; +} + +template +void BPList::Shuffle() { + random_shuffle(data.begin(), data.end()); +} + +template +void BPList::Remove(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + if (result != data.end()) { + data.erase(result); + --Count; + } +} + +template +void BPList::RemoveAt(int pos) { + data.erase(data.begin() + pos); + --Count; +} + +template +int BPList::IndexOf(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + + if (result == data.end()) return -1; + + return result - data.begin(); +} + +template +void BPList::SetValue(int pos, T val) { + data[pos] = val; +} + + + + + + + +template +class BPPList { +private: + vector data; + +public: + int Count; + BPPList(); + ~BPPList(); + void Add(T item); + bool Contains(T item); + void Clear(bool safe_delete); + int IndexOf(T item); + void Insert(int pos, T item); + void Remove(T item); + void RemoveAt(int pos); + void SetValue(int pos, T val); + void Shuffle(); + T operator[] (int position); +}; + +template +BPPList::BPPList() { + Count = 0; +} + +template +BPPList::~BPPList() { + +} + +template +void BPPList::Add(T item) { + data.push_back(item); + ++Count; +} + +template +bool BPPList::Contains(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + return result != data.end(); +} + +template +void BPPList::Clear(bool safe_delete = true) { + if (safe_delete) { + for (int i = data.size() - 1; i >= 0; --i) { + T item = data[i]; + SAFE_DELETE(item); + } + } + + data.clear(); + Count = 0; +} + +template +T BPPList::operator[] (int position) { + return data[position]; +} + +template +void BPPList::Insert(int pos, T item) { + data.insert(data.begin() + pos, item); + ++Count; +} + +template +void BPPList::Shuffle() { + random_shuffle(data.begin(), data.end()); +} + +template +void BPPList::Remove(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + if (result != data.end()) { + data.erase(result); + --Count; + } +} + +template +void BPPList::RemoveAt(int pos) { + data.erase(data.begin() + pos); + --Count; +} + +template +int BPPList::IndexOf(T item) { + typename std::vector::iterator result = find(data.begin(), data.end(), item); + + if (result == data.end()) return -1; + + return result - data.begin(); +} + +template +void BPPList::SetValue(int pos, T val) { + data[pos] = val; +} + +#endif diff --git a/BPPoint.cpp b/BPPoint.cpp new file mode 100644 index 0000000..286e184 --- /dev/null +++ b/BPPoint.cpp @@ -0,0 +1,42 @@ +// 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 "BPPoint.h" + +BPPoint::BPPoint() { + X = 0; + Y = 0; +} + +BPPoint::BPPoint(float x, float y) { + X = x; + Y = y; +} + +bool BPPoint::operator!= (BPPoint to) { + return this->X != to.X || this->Y != to.Y; +} + +float BPPoint::Distance(BPPoint from, BPPoint to) { + return sqrt(DistanceSquared(from, to)); +} + +float BPPoint::DistanceSquared(BPPoint from, BPPoint to) { + return (from.X - to.X) * (from.X - to.X) + (from.Y - to.Y) * (from.Y - to.Y); +} + +BPPoint BPPoint::Zero = BPPoint(); diff --git a/BPPoint.h b/BPPoint.h new file mode 100644 index 0000000..1a0aa07 --- /dev/null +++ b/BPPoint.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef __BPPoint_H__ +#define __BPPoint_H__ + +#include "math.h" + +class BPPoint { +public: + float X; + float Y; + + static BPPoint Zero; + + BPPoint(); + BPPoint(float x, float y); + + static float Distance(BPPoint from, BPPoint to); + static float DistanceSquared(BPPoint from, BPPoint to); + + bool operator!= (BPPoint to); +}; + +#endif diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..1d39b3c --- /dev/null +++ b/CREDITS @@ -0,0 +1,31 @@ + + Programming + =========== + Paul Hudson + + Art + === + Paul Hudson + Scott Ewart + + Music + ===== + electricity.ogg - "Electricity" by Alexander Blu; CC-BY-SA 3.0. + morningwave.ogg - "RedQuadro - Morning Wave" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + highway.ogg - "Max Loginov - Highway" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + nevertoolate.ogg - "J-Sun Never Too Late" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + chekolake.ogg - "Al. Galizdra - Cheko Lake" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + starmarch.ogg - "Newalks - StarMarch" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + + Music from Soundsnap + ==================== + ambient.ogg + brainrace.ogg + lively.ogg + professor.ogg + results.ogg + theme.ogg + + Soundsnap's licence allows all use, commercial and non-commercial, as long as you + do not "Resell or distribute the sounds 'as they are'. For example, you cannot download + and sell them as part of a CD library." diff --git a/Colour.cpp b/Colour.cpp new file mode 100644 index 0000000..82a0521 --- /dev/null +++ b/Colour.cpp @@ -0,0 +1,32 @@ +// 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 "Colour.h" + +Colour::Colour(float r, float g, float b, float a) { + R = r; + G = g; + B = b; + A = a; +} + +Colour::Colour() { + R = 0; + G = 0; + B = 0; + A = 1; +} diff --git a/Colour.h b/Colour.h new file mode 100644 index 0000000..f0bd13f --- /dev/null +++ b/Colour.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef __BPCOLOUR_H__ +#define __BPCOLOUR_H__ + +class Colour { +public: + float R; + float G; + float B; + float A; + + Colour(); + Colour(float r, float g, float b, float a); +}; + +#endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ae75393 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +MACHINE= $(shell uname -s) +OBJFILES := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) + +ifeq ($(MACHINE),Darwin) + INCLUDES = -I/Library/Frameworks/SDL.framework/Headers -I/Library/Frameworks/SDL_image.framework/Headers -I/Library/Frameworks/SDL_mixer.framework/Headers -I/Library/Frameworks/SDL_ttf.framework/Headers -I/System/Library/Frameworks/OpenGL.framework/Headers + LIBS = -m32 -framework SDL -framework SDL_image -framework SDL_mixer -framework SDL_ttf -framework Cocoa -framework OpenGL + + # SDL isn't in a great state on 64-bit Macs, so force 32-bit for now + CXXFLAGS = -g -c -m32 -Wno-deprecated + + # to compile on OS X you need to include this Objective C file + OSXCOMPAT = SDLMain.m +else + INCLUDES = `sdl-config --cflags` -I/usr/X11R6/include + LIBS = `sdl-config --libs` -lGLES_CM -lSDL_gles -lSDL_mixer -lSDL_ttf -lSDL_gfx -lSDL_image + CXXFLAGS = -O2 -c -Wno-deprecated + OSXCOMPAT = +endif + +# object files have corresponding source files +CXX = g++ + +all: brainparty + +brainparty: $(OBJFILES) + $(CXX) -o brainparty $(INCLUDES) $(OSXCOMPAT) $(OBJFILES) $(LIBS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ $< + +clean: + rm -f brainparty *.o diff --git a/MessageBox.cpp b/MessageBox.cpp new file mode 100644 index 0000000..8229877 --- /dev/null +++ b/MessageBox.cpp @@ -0,0 +1,25 @@ +// 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 "MessageBox.h" + +bool MessageBox::Show(const char* text, const char* title) { + BPGame::AllocString(&BPGame::sfcMessageBoxTitle, title, NORMAL, 320, 480, CENTRED); + BPGame::AllocString(&BPGame::sfcMessageBoxText, text, XSMALL, 300, 480, LEFT); + + BPGame::ShowingMessageBox = true; +} diff --git a/MessageBox.h b/MessageBox.h new file mode 100644 index 0000000..9ba2c40 --- /dev/null +++ b/MessageBox.h @@ -0,0 +1,31 @@ +// 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. + +#ifndef __MESSAGEBOX_H__ +#define __MESSAGEBOX_H__ + +#include +#include "BPGame.h" + +using namespace std; + +class MessageBox { +public: + static bool Show(const char* text, const char* title); +}; + +#endif diff --git a/MiniGameContainer.h b/MiniGameContainer.h new file mode 100644 index 0000000..4b6099d --- /dev/null +++ b/MiniGameContainer.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef __BPMINIGAME_H__ +#define __BPMINIGAME_H__ + +class BPMiniGame_Container { +public: + int GameCode; + const char* ShortName; + const char* FriendlyName; + Texture* Pic; + bool IsShowing; // is this currently visible on the practise screen? + bool IsSecret; + bool Unlocked; + + float X; + float Y; + + float StartX; + float StartY; + + float DestX; + float DestY; + + BPMiniGame_Container(int gamecode, const char* shortname, const char* friendlyname, Texture* pic, bool issecret) { + ShortName = shortname; + FriendlyName = friendlyname; + Pic = pic; + GameCode = gamecode; + IsSecret = issecret; + Unlocked = false; + } +}; + +#endif diff --git a/Minigame.cpp b/Minigame.cpp new file mode 100644 index 0000000..b82cf9b --- /dev/null +++ b/Minigame.cpp @@ -0,0 +1,496 @@ +// 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 "Minigame.h" + +BPMiniGame::BPMiniGame(BPGame* game) { + TheGame = game; + + ReturnType = RT_NORMAL; + + FinishedTime = FinalWeight = 0; + + FinalGrade = NULL; + + sfcGameTitle = NULL; + sfcGameHelp = NULL; + sfcFinalWeight = NULL; + sfcFinalGrade = NULL; + + MarathonMode = false; + + WantToQuit = -1; +} + +void BPMiniGame::Init() { + TheGame->AllocString(&sfcGameTitle, GameTitle, LARGE, 276, 46, LEFT, true); + TheGame->AllocString(&sfcGameHelp, GameHelp, XSMALL, 276, 170, LEFT); +} + +BPMiniGame::~BPMiniGame() { +// SAFE_DELETE(GameTitle); +// SAFE_DELETE(sfcGameTitle); +// SAFE_DELETE(GameHelp); +// SAFE_DELETE(sfcGameHelp); +// SAFE_DELETE(GameHelp2); +// SAFE_DELETE(FinalGrade); + +// SAFE_DELETE(sfcFinalWeight); +// SAFE_DELETE(sfcFinalGrade); +} + +RankTypes BPMiniGame::GetRank(int Weight) { + if (Weight < 50) { + return FAIL; + } else if (Weight < 150) { + return BRONZE; + } else if (Weight < 350) { + return SILVER; + } else if (Weight < 450) { + return GOLD; + } else { + return PLATINUM; + } +} + +const char* BPMiniGame::GetGrade(int Weight) { + if (Weight < 50) { + return "F"; + } else if (Weight < 50) { + return "D-"; + } else if (Weight < 100) { + return "D"; + } else if (Weight < 125) { + return "D+"; + } else if (Weight < 150) { + return "D++"; + } else if (Weight < 175) { + return "C-"; + } else if (Weight < 200) { + return "C"; + } else if (Weight < 225) { + return "C+"; + } else if (Weight < 250) { + return "C++"; + } else if (Weight < 275) { + return "B-"; + } else if (Weight < 300) { + return "B"; + } else if (Weight < 325) { + return "B+"; + } else if (Weight < 350) { + return "B++"; + } else if (Weight < 375) { + return "A-"; + } else if (Weight < 400) { + return "A"; + } else if (Weight < 425) { + return "A+"; + } else if (Weight < 450) { + return "A++"; + } else { + return "A*"; + } +} + +void BPMiniGame::TickMiniGame() { + Tick(); +} + +void BPMiniGame::RenderMiniGame() { + if (FinishedTime != 0 && FinishedTime + 250 < this->TheGame->TickCount) { + RenderScore(); + } else { + Render(); + + TheGame->DrawImage(TheGame->sfcBottomBar, 0, 416); + + if (WantToQuit != -1) { + float diff = TheGame->TickCount - WantToQuit; + diff /= 200.0f; + diff = TheGame->Clamp(diff, 0.0f, 1.0f); + + Colour col = Colour(1.0f, 1.0f, 1.0f, TheGame->SmoothStep(0.0f, 1.0f, diff)); + + TheGame->DrawImage(TheGame->sfcQuitTest, 0, 0, col); + } + } +} + +void BPMiniGame::RenderScore() { + switch (FinalRank) { + case FAIL: + TheGame->DrawImage(TheGame->sfcResultsFail, 0, 0); + break; + + case BRONZE: + TheGame->DrawImage(TheGame->sfcResultsBronze, 0, 0); + break; + + case SILVER: + TheGame->DrawImage(TheGame->sfcResultsSilver, 0, 0); + break; + + case GOLD: + TheGame->DrawImage(TheGame->sfcResultsGold, 0, 0); + break; + + case PLATINUM: + TheGame->DrawImage(TheGame->sfcResultsPlatinum, 0, 0); + break; + + default: // no score has been assigned yet; just fill it in black + TheGame->Clear(TheGame->Black); + return; + } + + TheGame->DrawString(sfcFinalWeight, WHITE, 0, 10); + TheGame->DrawString(sfcFinalGrade, WHITE, 0, 50); +} + +void BPMiniGame::Success() { + TheGame->StopMusic(); + + // minigame was completed successfully! + if (FinishedTime == 0) { + FinishedTime = TheGame->TickCount; + CalculateResult(); + + if (FinalRank == FAIL) { + TheGame->PlaySound("glass_break"); + } else if (FinalRank == PLATINUM) { + TheGame->PlaySound("result"); + + if (TheGame->Secret1 == false && strcmp(GameTitle, "Balloon Blaster") == 0) { + // they just unlocked a secret + MessageBox::Show("Congratulations on getting a platinum rating in Balloon Blaster - as a special bonus you've also unlocked a secret! Visit the Results screen to see what you've unlocked.", "Congratulations!"); + TheGame->Secret1 = true; + TheGame->SaveSettings(); + } else if (TheGame->Secret2 == false && strcmp(GameTitle, "Jewel Jam") == 0) { + // they just unlocked a secret + MessageBox::Show("Congratulations on getting a platinum rating in Jewel Jam - as a special bonus you've also unlocked a secret! Visit the Results screen to see what you've unlocked.", "Congratulations!"); + TheGame->Secret2 = true; + TheGame->SaveSettings(); + } else if (TheGame->Secret3 == false && strcmp(GameTitle, "Odd One Out") == 0) { + // they just unlocked a secret + MessageBox::Show("Congratulations on getting a platinum rating in Odd One Out - as a special bonus you've also unlocked a secret! Visit the Results screen to see what you've unlocked.", "Congratulations!"); + TheGame->Secret3 = true; + TheGame->SaveSettings(); + } else if (TheGame->Secret4 == false && strcmp(GameTitle, "Untangler") == 0) { + // they just unlocked a secret + MessageBox::Show("Congratulations on getting a platinum rating in Untangler - as a special bonus you've also unlocked a secret! Visit the Results screen to see what you've unlocked.", "Congratulations!"); + TheGame->Secret4 = true; + TheGame->SaveSettings(); + } + } else { + TheGame->PlaySound("result"); + } + } +} + +void BPMiniGame::Failure() { + TheGame->StopMusic(); + + // minigame was not completed successfully! + if (FinishedTime == 0) { + FinishedTime = TheGame->TickCount; + FinalWeight = 0; + FinalRank = GetRank(0); + FinalGrade = GetGrade(0); + + TheGame->PlaySound("glass_break"); + } +} + +void BPMiniGame::CalculateResult() { + FinalWeight = GetWeight(); + FinalRank = GetRank(FinalWeight); + FinalGrade = GetGrade(FinalWeight); + + ostringstream brainweight; + brainweight << "Brain weight: " << FinalWeight << "g"; + TheGame->AllocString(&sfcFinalWeight, brainweight.str().c_str(), LARGE, 320, 120, CENTRED); + + ostringstream finalgrade; + finalgrade << "Grade: " << FinalGrade; + TheGame->AllocString(&sfcFinalGrade, finalgrade.str().c_str(), LARGE, 320, 80, CENTRED); +} + +void BPMiniGame::HandleMouseUp(BPPoint e) { + TouchEvent = e; + + if (FinishedTime != 0) { + if (FinishedTime + 900 < TheGame->TickCount) { + // showing results screen; this delay is important because it stops people skipping the results screen by accident + ContinueGame(); + } else { + // do nothing; waiting for results screen to appear + } + } else { + if (WantToQuit != -1) { + // player has asked to quit the test - check their confirmation result + if (TheGame->PointOverRect(e.X, e.Y, 0, 274, 320, 65)) { + // don't quit! + WantToQuit = -1; + TheGame->PlaySound("mouse_click"); + } else if (TheGame->PointOverRect(e.X, e.Y, 0, 370, 320, 65)) { + TheGame->CancelTest(); + TheGame->PlaySound("mouse_click"); + } + } else { + if (e.Y > 416 && (BackDown || HelpDown)) { + // user clicked on either Back or Help; if they didn't mouse down + mouse up on back or help, pass the click to the mini game for handling + if (e.X < 89) { + if (BackDown) { + // only go back if we have mouse down + mouse up + if (TheGame->InTestMode) { + WantToQuit = TheGame->TickCount; + } else { + GoBack(); + } + + TheGame->PlaySound("mouse_click"); + } + } else if (e.X > 228) { + if (HelpDown) { + ShowHelp(); // only go back if we have mouse down + mouse up + TheGame->PlaySound("mouse_click"); + } + } + } else { + OnMouseUp(); + } + + BackDown = false; + HelpDown = false; + } + } +} + +void BPMiniGame::HandleMouseDown(BPPoint e) { + if (WantToQuit != -1) return; // user is trying to quit; ignore these taps + + TouchEvent = e; + + if (FinishedTime != 0) return; // ignore these clicks + + if (e.Y > 416) { + // user clicked on either Back or Help + if (e.X < 89) { + BackDown = true; + } else if (e.X > 228) { + HelpDown = true; + } + } else { + // Uncomment to quickly test whizzing through games + //Success(); + //return; + OnMouseDown(); + } +} + +void BPMiniGame::HandleMouseMove(BPPoint e) { + if (WantToQuit != -1) return; // user is trying to quit; ignore these taps + + TouchEvent = e; + + if (e.Y < 416) { + OnMouseMove(); + } +} + +int BPMiniGame::DivRem(int Num, int Div, int &Rem) { + Rem = Num % Div; + return (int)floor(Num / Div); +} + +void BPMiniGame::ShowHelp() { + MessageBox::Show(GameHelp2, GameTitle); +} + +void BPMiniGame::GoBack() { + switch (ReturnType) { + case RT_NORMAL: + if (MarathonMode) { + TheGame->GameState = MARATHON; + TheGame->PlayMusic("theme"); + } else { + if (TheGame->InTestMode) { + TheGame->GameState = TEST_STATUS; + } else { + TheGame->GameState = PRACTISE_MENU; + TheGame->PlayMusic("theme"); + } + } + + break; + + case RT_SECRET: + TheGame->GameState = HISTORY; + TheGame->PlayMusic("theme"); + + break; + + case RT_BRAINBOOST: + TheGame->GameState = BRAINBOOST; + TheGame->PlayMusic("theme"); + break; + } +} + +void BPMiniGame::ContinueGame() { + if (TheGame->InTestMode) { + TheGame->AddTestScore(FinalWeight); + } else { + GoBack(); + } +} + +int BPMiniGame::Round(double num) { + return (int)round(num); +} + +int BPMiniGame::MinMax(int num) { + if (num <= 0) return 0; + + float fnum = num; + + if (fnum > 500) return 500; + + return round(fnum); +} + +void BPMiniGame::SetMarathon() { + // do nothing by default +} + +void BPMiniGame::DrawProfessorText() { + TheGame->DrawString(sfcGameTitle, BLACK, 23, 20); + TheGame->DrawString(sfcGameHelp, BLACK, 23, 53); +} + +bool BPMiniGame::RedrawClock() { + // used to redraw clocks in minigames that use them at a regular pace + static int ClockTime = 0; + + ClockTime += TheGame->ElapsedTickCount; + + if (ClockTime > TIMERREDRAW) { + ClockTime -= TIMERREDRAW; + return true; + } else { + return false; + } +} + + +void BPMiniGame::RenderCorrect() { + TheGame->DrawImage(TheGame->sfcCorrect, 0, 172); +} + +void BPMiniGame::RenderWrong() { + TheGame->DrawImage(TheGame->sfcWrong, 0, 172); +} + +void BPMiniGame::PlayMusic() { + switch (MiniGameType) { + case ACTION: + switch (TheGame->RandomRange(0, 3)) { + case 0: + TheGame->PlayMusic("starmarch"); + break; + + case 1: + TheGame->PlayMusic("nevertoolate"); + break; + + case 2: + TheGame->PlayMusic("chekolake"); + break; + + case 3: + TheGame->PlayMusic("highway"); + break; + } + + break; + + case LIVELY: + switch (TheGame->RandomRange(0, 2)) { + case 0: + TheGame->PlayMusic("lively"); + break; + + case 1: + TheGame->PlayMusic("electricity"); + break; + + case 2: + TheGame->PlayMusic("nevertoolate"); + break; + } + + break; + + case PUZZLE: + switch (TheGame->RandomRange(0, 2)) { + case 0: + TheGame->PlayMusic("ambient"); + break; + + case 1: + TheGame->PlayMusic("brainrace"); + break; + + case 2: + TheGame->PlayMusic("morningwave"); + break; + } + + break; + } +} + +void BPMiniGame::GenerateStarfield(BPPList &list) { + for (int i = 0; i < 100; ++i) { + BPMiniGame_BGStar* star = new BPMiniGame_BGStar(); + star->Type = TheGame->RandomRange(0, 2); + star->Pos = BPPoint(TheGame->RandomRange(0, 320), TheGame->RandomRange(0, 416)); + star->Speed = (star->Type + TheGame->RandomRange(0, 2)) / 3.0f; + if (star->Speed < 0.2f) star->Speed = 0.2f; + list.Add(star); + } +} + +void BPMiniGame::UpdateStarfield(BPPList &list) { + for (int i = 0; i < list.Count; ++i) { + BPMiniGame_BGStar* star = list[i]; + + star->Pos.X -= star->Speed; + if (star->Pos.X < -2) { + star->Pos = BPPoint(322, TheGame->RandomRange(0, 416)); + } + } +} + +void BPMiniGame::DrawStarfield(BPPList &list) { + for (int i = 0; i < 100; ++i) { + BPMiniGame_BGStar* star = list[i]; + + TheGame->DrawImage(TheGame->sfcStarTypes[star->Type], star->Pos.X, star->Pos.Y); + } +} diff --git a/Minigame.h b/Minigame.h new file mode 100644 index 0000000..1d14fd2 --- /dev/null +++ b/Minigame.h @@ -0,0 +1,131 @@ +// 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. + +#ifndef __MINIGAME_H__ +#define __MINIGAME_H__ + +#include +#include +#include +#include + +#include "BPGame.h" +#include "BPPoint.h" +#include "SpriteFont.h" +#include "BPList.h" + +enum RankTypes { NONE, FAIL, BRONZE, SILVER, GOLD, PLATINUM }; // "NONE" is required, otherwise the default is "FAIL" +enum MiniGameStates { SHOWING, GUESSING, PAUSING, THINKING, WAITING, CHANGING, MOVES, MOVING, REMEMBER, CORRECT, WRONG, SUCCESS, FAILURE, FADE_IN, FADE_OUT }; +enum MiniGameTypes { PUZZLE, LIVELY, ACTION }; +enum ReturnTypes { RT_NORMAL, RT_SECRET, RT_BRAINBOOST }; + +class BPGame; +class SpriteFont; + + +class BPMiniGame_BGStar { +public: + int Type; + float Speed; + BPPoint Pos; +}; + + + +class BPMiniGame { +public: + ReturnTypes ReturnType; // controls which game screens return to y + int FinishedTime; // set to Environment.TickCount when game should no longer have Tick() called + const char* GameTitle; + const char* GameHelp; + const char* GameHelp2; + + SpriteFont* sfcGameTitle; + SpriteFont* sfcGameHelp; + MiniGameTypes MiniGameType; + + + int FinalWeight; + RankTypes FinalRank; + const char* FinalGrade; + + SpriteFont* sfcFinalWeight; + SpriteFont* sfcFinalGrade; + + BPPoint TouchEvent; + + BPGame* TheGame; + + BPMiniGame(BPGame* game); + virtual ~BPMiniGame(); + void Init(); // called after the constructor so that any descendent constructors have finished + virtual void Start() = 0; + virtual int GetWeight() = 0; + virtual void SetMarathon(); + static RankTypes GetRank(int Weight); + const char* GetGrade(int Weight); + void TickMiniGame(); + void RenderMiniGame(); + void RenderScore(); + + virtual void OnMouseDown() = 0; + virtual void OnMouseUp() = 0; + virtual void OnMouseMove() = 0; + + void HandleMouseUp(BPPoint e); + void HandleMouseDown(BPPoint e); + void HandleMouseMove(BPPoint e); + + void DrawProfessorText(); + void PlayMusic(); + +protected: + static const int MiniGameWidth = 320; + static const int MiniGameHalfWidth = 160; + static const int MiniGameHeight = 480; // note: needs to be screen height minus the height of the info bar + static const int MiniGameHalfHeight = 240; + + bool RedrawClock(); + + virtual void Render() = 0; + virtual void Tick() = 0; + + void Success(); + void Failure(); + void CalculateResult(); + void ContinueGame(); + void GoBack(); + void ShowHelp(); + int DivRem(int Num, int Div, int &Rem); + int Round(double num); + int MinMax(int num); + void RenderCorrect(); + void RenderWrong(); + void GenerateStarfield(BPPList &list); + void UpdateStarfield(BPPList &list); + void DrawStarfield(BPPList &list); + + bool MarathonMode; // used by some games to hide/stop the timer so players can play as long as they want to + +private: + bool InTick; + bool BackDown; + bool HelpDown; + int WantToQuit; +}; + +#endif diff --git a/README b/README new file mode 100644 index 0000000..8b3a912 --- /dev/null +++ b/README @@ -0,0 +1,73 @@ + + Brain Party + =========== + + Brain Party is a puzzle-solving, brain-stretching game that comes with + 36 minigames designed to push your brain to its limits, testing memory, + logic, mathematics, reactions and more. + + This game was written for Apple's iPhone, and you can buy it in the + App Store for $0.99 - in fact, doing so helps support me a little! + + Because I used non-free music in the iPhone release, this version uses + new music - see the CREDITS file for more information. + + + Compiling from source + ===================== + + I haven't used any fancy tools, so all you need to do to build Brain Party + is run "make" and give it a few minutes. If you have a multi-core CPU, try + "make -j4" to get a speed boost. + + You will need the development libraries for SDL, SDL Mixer, SDL TTF, SDL GFX, + SDL Image and OpenGL. Obviously you will also need a C++ compiler like G++. + + + Version history + =============== + + v0.1 - March 5th 2010 + - First release. + + + Reading the source + ================== + + Brain Party was originally written for Linux in C#, using Mono and SDL.NET. + It was then ported to Windows Mobile 5.0, which also uses C#, but has only + the .NET Compact Framework. From there, it was ported to the Xbox 360 (and + that port is still available through the Microsoft Indie Games store), which + also uses C# and the .NET Compact Framework, plus the XNA Framework. + + From there, the game was ported to the iPhone, which meant rewriting it in + Objective C++ and OpenGL. Some of the code was taken from the X360 version + as it had been polished; other code was taken from the WinMo code, because + it was already built for touchscreens. + + The Brain Party you have now descends from the iPhone build. All the code + that was Objective C++ is now plain C++. + + The reason I'm telling you all this is because, if you look at the code, + you'll see it's packed with ideosyncrasies. That's because the ports I've + done were usually done in less than a week, so rather than rewriting + everything from scratch I've usually just implemented shims that accomplish + the same thing. In other places, I've sorely missed functionality that was + available elsewhere, so I've re-implemented it myself. + + So, the advice is this: look at the code if you want to. But, whatever you + do, don't learn from it - it's the dirtiest C++ you'll ever see. + + This will be rectified over time. Possibly. + + + Contributing + ============ + + If you'd like to contribute to Brain Party, great! Get in touch with me at + paul.hudson@gmail.com and we'll talk. Code, graphics, sound effects, music, + etc are all welcome. If you spot any bugs, drop me a line with some sort of + helpful error message - ie, what I have to do to reproduce the error - and + I'll get on the case. + + diff --git a/SDLMain.h b/SDLMain.h new file mode 100644 index 0000000..9de5250 --- /dev/null +++ b/SDLMain.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef _SDLMain_h_ +#define _SDLMain_h_ + +#import + +@interface SDLMain : NSObject +@end + +#endif diff --git a/SDLMain.m b/SDLMain.m new file mode 100644 index 0000000..72a38cb --- /dev/null +++ b/SDLMain.m @@ -0,0 +1,370 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#include "SDL.h" +#include "SDLMain.h" +#include /* for MAXPATHLEN */ +#include + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication(SDL_Missing_Methods) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +/* Use this flag to determine whether we use SDLMain.nib or not */ +#define SDL_USE_NIB_FILE 0 + +/* Use this flag to determine whether we use CPS (docking) or not */ +#define SDL_USE_CPS 1 +#ifdef SDL_USE_CPS +/* Portions of CPS.h */ +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); +extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); + +#endif /* SDL_USE_CPS */ + +static int gArgc; +static char **gArgv; +static BOOL gFinderLaunch; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + return @"Brain Party"; +} + +#if SDL_USE_NIB_FILE +/* A helper category for NSString */ +@interface NSString (ReplaceSubString) +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; +@end +#endif + +@interface NSApplication (SDLApplication) +@end + +@implementation NSApplication (SDLApplication) +/* Invoked from the Quit menu item */ +- (void)terminate:(id)sender +{ + /* Post a SDL_QUIT event */ + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} +@end + +/* The main class of the application, the application's delegate */ +@implementation SDLMain + +/* Set the working directory to the .app's parent directory */ +- (void) setupWorkingDirectory:(BOOL)shouldChdir +{ + if (shouldChdir) + { + char parentdir[MAXPATHLEN]; + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); + if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { + chdir(parentdir); /* chdir to the binary app's parent */ + } + CFRelease(url); + CFRelease(url2); + } +} + +#if SDL_USE_NIB_FILE + +/* Fix menu to contain the real app name instead of "SDL App" */ +- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName +{ + NSRange aRange; + NSEnumerator *enumerator; + NSMenuItem *menuItem; + + aRange = [[aMenu title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; + + enumerator = [[aMenu itemArray] objectEnumerator]; + while ((menuItem = [enumerator nextObject])) + { + aRange = [[menuItem title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; + if ([menuItem hasSubmenu]) + [self fixMenu:[menuItem submenu] withAppName:appName]; + } +} + +#else + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDLMain *sdlMain; + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + +#ifdef SDL_USE_CPS + { + CPSProcessSerNum PSN; + /* Tell the dock about us */ + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [NSApplication sharedApplication]; + } +#endif /* SDL_USE_CPS */ + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + /* Create SDLMain and make it the app delegate */ + sdlMain = [[SDLMain alloc] init]; + [NSApp setDelegate:sdlMain]; + + /* Start the main event loop */ + [NSApp run]; + + [sdlMain release]; + [pool release]; +} + +#endif + + +/* + * Catch document open requests...this lets us notice files when the app + * was launched by double-clicking a document, or when a document was + * dragged/dropped on the app's icon. You need to have a + * CFBundleDocumentsType section in your Info.plist to get this message, + * apparently. + * + * Files are added to gArgv, so to the app, they'll look like command line + * arguments. Previously, apps launched from the finder had nothing but + * an argv[0]. + * + * This message may be received multiple times to open several docs on launch. + * + * This message is ignored once the app's mainline has been called. + */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + const char *temparg; + size_t arglen; + char *arg; + char **newargv; + + if (!gFinderLaunch) /* MacOS is passing command line args. */ + return FALSE; + + if (gCalledAppMainline) /* app has started, ignore this document. */ + return FALSE; + + temparg = [filename UTF8String]; + arglen = SDL_strlen(temparg) + 1; + arg = (char *) SDL_malloc(arglen); + if (arg == NULL) + return FALSE; + + newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); + if (newargv == NULL) + { + SDL_free(arg); + return FALSE; + } + gArgv = newargv; + + SDL_strlcpy(arg, temparg, arglen); + gArgv[gArgc++] = arg; + gArgv[gArgc] = NULL; + return TRUE; +} + + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Set the working directory to the .app's parent directory */ + [self setupWorkingDirectory:gFinderLaunch]; + +#if SDL_USE_NIB_FILE + /* Set the main menu to contain the real app name instead of "SDL App" */ + [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; +#endif + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + status = SDL_main (gArgc, gArgv); + + /* We're done, thank you for playing */ + exit(status); +} +@end + + +@implementation NSString (ReplaceSubString) + +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString +{ + unsigned int bufferSize; + unsigned int selfLen = [self length]; + unsigned int aStringLen = [aString length]; + unichar *buffer; + NSRange localRange; + NSString *result; + + bufferSize = selfLen + aStringLen - aRange.length; + buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); + + /* Get first part into buffer */ + localRange.location = 0; + localRange.length = aRange.location; + [self getCharacters:buffer range:localRange]; + + /* Get middle part into buffer */ + localRange.location = 0; + localRange.length = aStringLen; + [aString getCharacters:(buffer+aRange.location) range:localRange]; + + /* Get last part into buffer */ + localRange.location = aRange.location + aRange.length; + localRange.length = selfLen - localRange.location; + [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; + + /* Build output string */ + result = [NSString stringWithCharacters:buffer length:bufferSize]; + + NSDeallocateMemoryPages(buffer, bufferSize); + + return result; +} + +@end + + + +#ifdef main +# undef main +#endif + + +/* Main entry point to executable - should *not* be SDL_main! */ +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { + gArgv = (char **) SDL_malloc(sizeof (char *) * 2); + gArgv[0] = argv[0]; + gArgv[1] = NULL; + gArgc = 1; + gFinderLaunch = YES; + } else { + int i; + gArgc = argc; + gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + gArgv[i] = argv[i]; + gFinderLaunch = NO; + } + +#if SDL_USE_NIB_FILE + NSApplicationMain (argc, argv); +#else + CustomApplicationMain (argc, argv); +#endif + return 0; +} + diff --git a/SpriteFont.cpp b/SpriteFont.cpp new file mode 100644 index 0000000..a027068 --- /dev/null +++ b/SpriteFont.cpp @@ -0,0 +1,50 @@ +// 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 "SpriteFont.h" + +SpriteFont::SpriteFont() { + +} + +SpriteFont::~SpriteFont() { + Surfaces.Clear(); +} + +void SpriteFont::drawAtPoint(float x, float y) { + int height = 0; + + for (int i = 0; i < Surfaces.Count; ++i) { + Texture* tex = Surfaces[i]; + + switch (Align) { + case LEFT: + tex->Draw(x, y + height); + break; + + case CENTRED: + tex->Draw(x + (Width / 2.0f) - (tex->HalfWidth), y + height); + break; + + case RIGHT: + tex->Draw(x + Width - tex->Width, y + height); + break; + } + + height += tex->Height; + } +} diff --git a/SpriteFont.h b/SpriteFont.h new file mode 100644 index 0000000..6902a61 --- /dev/null +++ b/SpriteFont.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef __SPRITEFONT_H__ +#define __SPRITEFONT_H__ + +enum Alignments { CENTRED, LEFT, RIGHT }; + +#include "BPGame.h" +#include "BPList.h" +#include "Texture.h" + +class SpriteFont { +public: + BPPList Surfaces; + int Width; + int Height; + Alignments Align; + + SpriteFont(); + ~SpriteFont(); + void drawAtPoint(float x, float y); +}; + +#endif diff --git a/TestResultContainer.h b/TestResultContainer.h new file mode 100644 index 0000000..c236290 --- /dev/null +++ b/TestResultContainer.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef __TESTRESULTCONTAINER_H__ +#define __TESTRESULTCONTAINER_H__ + +#include "BPPoint.h" +#include "Minigame.h" +#include "SpriteFont.h" + +class TestResultContainer { +public: + int MiniGame; + int Rank; + SpriteFont* sfcBrainWeight; + BPPoint Pos; + BPPoint TextPos; + float FadeUpAnim; + bool GoingUp; + + TestResultContainer() { + sfcBrainWeight = NULL; + } +}; + +#endif diff --git a/Texture.cpp b/Texture.cpp new file mode 100644 index 0000000..0c4c2ac --- /dev/null +++ b/Texture.cpp @@ -0,0 +1,228 @@ +// 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 "Texture.h" +#include "SDL_image.h" + +Texture::Texture(SDL_Surface* surface) { + int po2width = Texture::NextPO2(surface->w); + int po2height = Texture::NextPO2(surface->h); + + if (Width != po2width or Height != po2height) { + SDL_Surface* tmpsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, po2width, po2height, 32, + #if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 + #else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF + #endif + ); + + SDL_SetAlpha(surface, 0, 0); + SDL_BlitSurface(surface, NULL, tmpsurface, NULL); + InitWithSurface(tmpsurface, surface->w, surface->h); + SDL_FreeSurface(tmpsurface); + } else { + InitWithSurface(surface, surface->w, surface->h); + } +} + +Texture::Texture(const char* filename, float actualwidth, float actualheight) { + string file = filename; + file.insert(0, "/opt/brainparty/Content/"); + file.append(".png"); + + SDL_Surface *surface = IMG_Load(file.c_str()); + if ((surface->w & (surface->w - 1)) != 0) printf("warning: %s's width is not a power of 2\n", file.c_str()); + if ((surface->h & (surface->h - 1)) != 0) printf("warning: %s's height is not a power of 2\n", file.c_str()); + + InitWithSurface(surface, actualwidth, actualheight); + + SDL_FreeSurface(surface); +} + +void Texture::InitWithSurface(SDL_Surface* surface, float actualwidth, float actualheight) { + Width = surface->w; + Height = surface->h; + + GLenum texture_format; + int colors = surface->format->BytesPerPixel; + + if (colors == 4) { + if (surface->format->Rmask == 0x000000ff) { + texture_format = GL_RGBA; + } else { + cout << "BGRA unsupported :(" << endl; + texture_format = GL_RGBA; + //texture_format = GL_BGRA; + } + } else if (colors == 3) { + if (surface->format->Rmask == 0x000000ff) { + texture_format = GL_RGB; + } else { + cout << "BGR unsupported :(" << endl; + texture_format = GL_RGB; + //texture_format = GL_BGR; + } + } else { + // this shouldn't ever happen + cout << "Problem loading texture!" << endl; + } + + glGenTextures(1, &this->Surface); + + glBindTexture(GL_TEXTURE_2D, Surface); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, texture_format, Width, Height, 0, texture_format, GL_UNSIGNED_BYTE, surface->pixels); + + WidthRatio = actualwidth / Width; + HeightRatio = actualheight / Height; + + // now store the real width and height away to be used later. + Width = actualwidth; + Height = actualheight; + HalfWidth = actualwidth / 2; + HalfHeight = actualheight / 2; + + TexCoords[0] = 0.0; + TexCoords[1] = HeightRatio; + + TexCoords[2] = WidthRatio; + TexCoords[3] = HeightRatio; + + TexCoords[4] = 0.0; + TexCoords[5] = 0.0; + + TexCoords[6] = WidthRatio; + TexCoords[7] = 0.0; + + Vertices[0] = 0.0f; + Vertices[1] = actualheight; + Vertices[2] = 0.0f; + + Vertices[3] = actualwidth; + Vertices[4] = actualheight; + Vertices[5] = 0.0f; + + Vertices[6] = 0.0f; + Vertices[7] = 0.0f; + Vertices[8] = 0.0f; + + Vertices[9] = actualwidth; + Vertices[10] = 0.0f; + Vertices[11] = 0.0f; +} + +Texture::~Texture() { + glDeleteTextures(1, &this->Surface); +} + +void Texture::Draw(float x, float y) { + if (y + Height < 0) return; + if (y > 480) return; + + glBindTexture(GL_TEXTURE_2D, Surface); + + glLoadIdentity(); + + glTranslatef(x, y, 0.0f); + + glVertexPointer(3, GL_FLOAT, 0, Vertices); + glTexCoordPointer(2, GL_FLOAT, 0, TexCoords); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +void Texture::Draw(float x, float y, Colour &col) { + glBindTexture(GL_TEXTURE_2D, Surface); + + glLoadIdentity(); + + glTranslatef(x, y, 0.0f); + glColor4f(col.R, col.G, col.B, col.A); + + glVertexPointer(3, GL_FLOAT, 0, Vertices); + glTexCoordPointer(2, GL_FLOAT, 0, TexCoords); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +} + +void Texture::Draw(float x, float y, float width, float height) { + glBindTexture(GL_TEXTURE_2D, Surface); + + glLoadIdentity(); + glTranslatef(x, y, 0.0f); + + float temp_vertices[] = { 0.0f, height, 0.0f, + width, height, 0.0f, + 0.0f, 0.0f, 0.0f, + width, 0.0f, 0.0f + }; + + glVertexPointer(3, GL_FLOAT, 0, temp_vertices); + glTexCoordPointer(2, GL_FLOAT, 0, TexCoords); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +void Texture::Draw(float x, float y, float rotation, float scale, Colour &col) { + float texwidth = Width * scale; + float texheight = Height * scale; + + float originx = texwidth / 2; + float originy = texheight / 2; + + glBindTexture(GL_TEXTURE_2D, Surface); + + glLoadIdentity(); + + glTranslatef(x, y, 0.0f); + glRotatef(rotation, 0, 0, 1); + glColor4f(col.R, col.G, col.B, col.A); + + float temp_vertices[] = { + -originx, texheight - originy, 0.0f, + texwidth - originx, texheight - originy, 0.0f, + -originx, -originy, 0.0f, + texwidth - originx, -originy, 0.0f + }; + + glVertexPointer(3, GL_FLOAT, 0, temp_vertices); + glTexCoordPointer(2, GL_FLOAT, 0, TexCoords); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +} + +int Texture::NextPO2(int num) { + int value = 1; + + while (value < num) { + value <<= 1; + } + return value; +} diff --git a/Texture.h b/Texture.h new file mode 100644 index 0000000..6ac6a43 --- /dev/null +++ b/Texture.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef __TEXTURE_H__ +#define __TEXTURE_H__ + +#include +#include +#include + +#include +#include "SDL.h" + +#include +#include + +#include "Colour.h" + +using namespace std; + +class Texture { +public: + GLuint Surface; + float Width; + float Height; + float HalfWidth; + float HalfHeight; + float WidthRatio; + float HeightRatio; + + GLfloat TexCoords[8]; + GLfloat Vertices[12]; + + Texture(SDL_Surface* surface); + Texture(const char* filename, float actualwidth, float actualheight); + ~Texture(); + void InitWithSurface(SDL_Surface* surface, float actualwidth, float actualheight); + void Draw(float x, float y); + void Draw(float x, float y, Colour &col); + void Draw(float x, float y, float width, float height); + void Draw(float x, float y, float rotation, float scale, Colour &col); + static int NextPO2(int num); +}; + +#endif diff --git a/WordList.h b/WordList.h new file mode 100644 index 0000000..972764f --- /dev/null +++ b/WordList.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef __WORDLIST_H__ +#define __WORDLIST_H__ + +#include +#include + +using namespace std; +using namespace __gnu_cxx; + +namespace __gnu_cxx +{ + template<> struct hash< std::string > + { + size_t operator()( const std::string& x ) const + { + return hash< const char* >()( x.c_str() ); + } + }; +} + +typedef hash_set > string_hash_set; + +class WordList { +private: + string_hash_set Words; + +public: + WordList(const char* file) { + FILE *fp; + char filebuffer[255]; + + if ((fp = fopen(file, "r")) != NULL) { + + while(!feof(fp)) { + fgets(filebuffer, 255, fp); + char* word = new char[255]; + sscanf(filebuffer, "%s", word); + Words.insert(word); + delete[] word; + } + + fclose(fp); + } + } + + ~WordList() { + Words.clear(); + } + + bool Contains(std::string string) { + string_hash_set::const_iterator it = Words.find(string); + + if (it != Words.end()) { + return true; + } else { + return false; + } + } +}; + +#endif diff --git a/balloonblaster.cpp b/balloonblaster.cpp new file mode 100644 index 0000000..b77aaea --- /dev/null +++ b/balloonblaster.cpp @@ -0,0 +1,361 @@ +// 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 "Minigame.h" +#include "balloonblaster.h" + +BPMiniGame_BalloonBlaster::BPMiniGame_BalloonBlaster(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("clouds", 320, 480); + + Balloons = new vector* >(); + BalloonTypes = new BPPList(); + sfcBalloonBlack = TheGame->LoadBitmap("balloon_black", 32, 45); + sfcPuff = TheGame->LoadBitmap("puff", 61, 61); + + TimeStarted = 0; + TimePassed = 0; + SmartBombTime = -1; + SuccessTime = -1; + + DisappearTime = 200; + + Score = 0; + sfcScoreStr = sfcClock = NULL; + SetScore(); + + GameTitle = "Balloon Blaster"; + GameHelp = "Pop balloons by tapping them - get three or more of the same colour to score. But watch out: pop one and you lose straight away, pop two and you lose lots of points. What's more, you have only two minutes!"; + GameHelp2 = "The key to scoring high is to get as many similar-coloured balloons together as possible before popping them - even if that means popping one or two smaller groups first. Remember: if you're going to score lots of points then losing a few thousand first is a good investment!"; + + MiniGameType = LIVELY; + + BalloonTypes->Add(TheGame->LoadBitmap("balloon_blue", 32, 45)); + BalloonTypes->Add(TheGame->LoadBitmap("balloon_red", 32, 45)); + BalloonTypes->Add(TheGame->LoadBitmap("balloon_purple", 32, 45)); + BalloonTypes->Add(TheGame->LoadBitmap("balloon_green", 32, 45)); + + for (int j = 0; j < 7; ++j) { + vector* row = new vector(); + + for (int i = 0; i < 9; ++i) { + BPMiniGame_BalloonBlaster_Balloon* balloon = new BPMiniGame_BalloonBlaster_Balloon(); + balloon->X = 4 + (i * 35); + balloon->Y = MiniGameHeight + (j * 50); + balloon->DestY = 10 + (j * 50); + + balloon->Colour = TheGame->RandomRange(0, BalloonTypes->Count - 1); + row->push_back(balloon); + } + + Balloons->push_back(row); + } +} + +BPMiniGame_BalloonBlaster::~BPMiniGame_BalloonBlaster() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBalloonBlack); + SAFE_DELETE(sfcPuff); + + vector* row; + BPMiniGame_BalloonBlaster_Balloon* balloon; + + for (int i = 0; i < Balloons->size(); ++i) { + row = (*Balloons)[i]; + + for (int j = 0; j < row->size(); ++j) { + balloon = (*row)[j]; + SAFE_DELETE(balloon); + } + + row->clear(); + SAFE_DELETE(row); + }; + + Balloons->clear(); + SAFE_DELETE(Balloons); + + SAFE_DELETE(sfcScoreStr); + SAFE_DELETE(sfcClock); + + BalloonTypes->Clear(); + SAFE_DELETE(BalloonTypes); +} + +void BPMiniGame_BalloonBlaster::OnMouseUp() { + +} + +void BPMiniGame_BalloonBlaster::OnMouseMove() { + +} + +void BPMiniGame_BalloonBlaster::OnMouseDown() { + vector* row; + BPMiniGame_BalloonBlaster_Balloon* balloon; + + for (int i = 0; i < Balloons->size(); ++i) { + row = (*Balloons)[i]; + + for (int j = 0; j < row->size(); ++j) { + balloon = (*row)[j]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, balloon->X, balloon->Y, 32, 45)) { + if (balloon->Colour == -1) { + TheGame->PlaySound("explosion"); + int ScoreAdd = SmartBomb(balloon, i, j); + ScoreAdd = floor(pow(2.0f, min(16, ScoreAdd))); + ModifyScore(ScoreAdd); + } else { + int ScoreAdd = MatchBalloon(balloon, i, j); + + switch (ScoreAdd) { + case 0: + break; + case 1: + Failure(); + break; + case 2: + TheGame->PlaySound("balloon_pop"); + ModifyScore(-100); + break; + default: + TheGame->PlaySound("balloon_pop"); + ModifyScore(floor(pow(2.0f, min(16, ScoreAdd)))); + break; + } + } + + return; + } + } + } +} + +int BPMiniGame_BalloonBlaster::SmartBomb(BPMiniGame_BalloonBlaster_Balloon* balloon, int row, int col) { + // this matches all adjacent balloons + int ThisScore = 0; + + if (balloon->MatchTime == -1) { + balloon->MatchTime = TheGame->TickCount; + + if (row > 0) { + if (col > 0) ThisScore += MatchBalloon((*(*Balloons)[row - 1])[col - 1], row - 1, col - 1); + ThisScore += MatchBalloon((*(*Balloons)[row - 1])[col], row - 1, col); + if (col < 8) ThisScore += MatchBalloon((*(*Balloons)[row - 1])[col + 1], row - 1, col + 1); + } + + if (col > 0) ThisScore += MatchBalloon((*(*Balloons)[row])[col - 1], row, col - 1); + if (col < 8) ThisScore += MatchBalloon((*(*Balloons)[row])[col + 1], row, col + 1); + + if (row < 5) { + if (col > 0) ThisScore += MatchBalloon((*(*Balloons)[row + 1])[col - 1], row + 1, col - 1); + ThisScore += MatchBalloon((*(*Balloons)[row + 1])[col], row + 1, col); + if (col < 8) ThisScore += MatchBalloon((*(*Balloons)[row + 1])[col + 1], row + 1, col + 1); + } + } + + // used to give a quick white flash on the screen + SmartBombTime = TheGame->TickCount + 100; + + return ThisScore; +} + +void BPMiniGame_BalloonBlaster::ModifyScore(int adjust) { + Score += adjust; + + if (Score < 0) Score = 0; + + SetScore(); +} + +void BPMiniGame_BalloonBlaster::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_BalloonBlaster::GetWeight() { + return MinMax(Score / 490); +} + +void BPMiniGame_BalloonBlaster::Render() { + if (SmartBombTime > TheGame->TickCount) { + // give a quick white flash for the smart bomb + TheGame->Clear(TheGame->White); + return; + } + + TheGame->DrawImage(sfcBackground, 0, 0); + + BPMiniGame_BalloonBlaster_Balloon* balloon; + + for (int b = 0; b < Balloons->size(); ++b) { + vector* row = (*Balloons)[b]; + + for (int i = row->size() - 1; i >= 0; --i) { + balloon = (*row)[i]; + + if (balloon->MatchTime != -1) { + float diff = TheGame->TickCount - balloon->MatchTime; + + if (diff <= DisappearTime) { + float step = diff / DisappearTime; // get a value between 0 and 1 + Colour col = Colour(1.0f, 1.0f, 1.0f, 1 - step); + + if (balloon->Colour == -1) { + TheGame->DrawImage(sfcBalloonBlack, balloon->X + sfcBalloonBlack->HalfWidth, balloon->Y + sfcBalloonBlack->HalfHeight, 0.0f, 1.0f + step, col); + } else { + TheGame->DrawImage((*BalloonTypes)[balloon->Colour], balloon->X + sfcBalloonBlack->HalfWidth, balloon->Y + sfcBalloonBlack->HalfHeight, 0.0f, 1.0f + step, col); + } + + TheGame->DrawImage(sfcPuff, balloon->X + sfcBalloonBlack->HalfWidth, balloon->Y + sfcBalloonBlack->HalfHeight, 0.0f, 1.0f, col); + } + } else { + if (balloon->Colour == -1) { + TheGame->DrawImage(sfcBalloonBlack, balloon->X + + sfcBalloonBlack->HalfWidth, balloon->Y + sfcBalloonBlack->HalfHeight, 0.0f, 1.0f, (*TheGame->White)); + } else { + TheGame->DrawImage((*BalloonTypes)[balloon->Colour], balloon->X + sfcBalloonBlack->HalfWidth, balloon->Y + sfcBalloonBlack->HalfHeight, 0.0f, 1.0f, (*TheGame->White)); + } + } + } + } + + if (!MarathonMode) { + TimePassed = TheGame->TickCount - TimeStarted; + TimePassed = 120000 - TimePassed; + + if (TimePassed <= 0) { + if (SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + Success(); + } + } + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), LARGE, 80, 47, LEFT); + } + + TheGame->DrawString(sfcClock, BLACK, 235, 366); + } + + TheGame->DrawString(sfcScoreStr, BLACK, 15, 367); +} + +void BPMiniGame_BalloonBlaster::Tick() { + vector* row; + BPMiniGame_BalloonBlaster_Balloon* balloon; + BPMiniGame_BalloonBlaster_Balloon* copyballoon; + + int MatchTimeout = TheGame->TickCount - DisappearTime; + + for (int i = 0; i < Balloons->size(); ++i) { + row = (*Balloons)[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + balloon = (*row)[j]; + + if (balloon->Y != balloon->DestY) { + ++balloon->YSpeed; + balloon->Y -= balloon->YSpeed; + if (balloon->Y < balloon->DestY) { + balloon->Y = balloon->DestY; + balloon->YSpeed = 0; + } + } + + if (balloon->MatchTime != -1 && balloon->MatchTime < MatchTimeout) { + SAFE_DELETE((*row)[j]); + + row->erase(row->begin() + j); + + // move all balloons up below me + + for (int k = i + 1; k < Balloons->size(); ++k) { + copyballoon = (*(*Balloons)[k])[j]; + (*Balloons)[k]->erase((*Balloons)[k]->begin() + j); + + (*Balloons)[k - 1]->insert((*Balloons)[k - 1]->begin() + j, copyballoon); + copyballoon->DestY = 10 + ((k - 1) * 50); + } + + BPMiniGame_BalloonBlaster_Balloon* newballoon = new BPMiniGame_BalloonBlaster_Balloon(); + newballoon->X = 4 + (j * 35); + newballoon->Y = MiniGameHeight; + newballoon->DestY = 310; + + if (TheGame->RandomRange(0, 100) == 56) { + newballoon->Colour = -1; + } else { + newballoon->Colour = TheGame->RandomRange(0, BalloonTypes->Count - 1); + } + + (*Balloons)[Balloons->size() - 1]->insert((*Balloons)[Balloons->size() - 1]->begin() + j, newballoon); + } + } + } +} + +int BPMiniGame_BalloonBlaster::MatchBalloon(BPMiniGame_BalloonBlaster_Balloon* balloon, int row, int col) { + int ThisScore = 0; + + if (balloon->MatchTime == -1) { + balloon->MatchTime = TheGame->TickCount; + + ThisScore = 1; + + // now match all other identical balloons around it + if (row > 0) { + if ((*(*Balloons)[row - 1])[col]->Colour == balloon->Colour && (*(*Balloons)[row - 1])[col]->MatchTime == -1) { + ThisScore += MatchBalloon((*(*Balloons)[row - 1])[col], row - 1, col); + } + } + + if (row < 6) { + if ((*(*Balloons)[row + 1])[col]->Colour == balloon->Colour && (*(*Balloons)[row + 1])[col]->MatchTime == -1) { + ThisScore += MatchBalloon((*(*Balloons)[row + 1])[col], row + 1, col); + } + } + + if (col > 0) { + if ((*(*Balloons)[row])[col - 1]->Colour == balloon->Colour && (*(*Balloons)[row])[col - 1]->MatchTime == -1) { + ThisScore += MatchBalloon((*(*Balloons)[row])[col - 1], row, col - 1); + } + } + + if (col < 8) { + if ((*(*Balloons)[row])[col + 1]->Colour == balloon->Colour && (*(*Balloons)[row])[col + 1]->MatchTime == -1) { + ThisScore += MatchBalloon((*(*Balloons)[row])[col + 1], row, col + 1); + } + } + } + + return ThisScore; +} + +void BPMiniGame_BalloonBlaster::SetMarathon() { + MarathonMode = true; + GameHelp = "Pop balloons by tapping them - get three or more of the same colour to score. But watch out: popping one or two is bad!"; +} + +void BPMiniGame_BalloonBlaster::SetScore() { + if (Score > 0) { + ostringstream str; + string score_str = TheGame->SeparateThousands(Score); + str << "Score: " << score_str; + TheGame->AllocString(&sfcScoreStr, str.str().c_str(), LARGE, 250, 50, LEFT); + } else { + TheGame->AllocString(&sfcScoreStr, "Score: 0", LARGE, 250, 50, LEFT); + } +} diff --git a/balloonblaster.h b/balloonblaster.h new file mode 100644 index 0000000..0df70c8 --- /dev/null +++ b/balloonblaster.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef __BALLOONBLASTER_H__ +#define __BALLOONBLASTER_H__ + +#include "Minigame.h" + +class BPMiniGame_BalloonBlaster_Balloon { +public: + int X; + int Y; + int DestY; + int YSpeed; + int Colour; + int MatchTime; + + BPMiniGame_BalloonBlaster_Balloon() { + MatchTime = -1; + YSpeed = 0; + } + + ~BPMiniGame_BalloonBlaster_Balloon() { + YSpeed = 0; + } +}; + + + +class BPMiniGame_BalloonBlaster : public BPMiniGame { +public: + BPMiniGame_BalloonBlaster(BPGame* game); + ~BPMiniGame_BalloonBlaster(); +private: + Texture* sfcBackground; + + vector*>* Balloons; + BPPList* BalloonTypes; + Texture* sfcBalloonBlack; + Texture* sfcPuff; + + int TimeStarted; + int TimePassed; + + int SmartBombTime; + + int SuccessTime; + int Score; + float DisappearTime; + + SpriteFont* sfcScoreStr; + + SpriteFont* sfcClock; + + void SetScore(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + int SmartBomb(BPMiniGame_BalloonBlaster_Balloon* balloon, int row, int col); + void ModifyScore(int adjust); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + int MatchBalloon(BPMiniGame_BalloonBlaster_Balloon* balloon, int row, int col); + void SetMarathon(); +}; + +#endif diff --git a/bombhunt.cpp b/bombhunt.cpp new file mode 100644 index 0000000..c9b4b42 --- /dev/null +++ b/bombhunt.cpp @@ -0,0 +1,214 @@ +// 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 "bombhunt.h" +#include "Minigame.h" +#include "BPList.h" + +BPMiniGame_BombHunt::BPMiniGame_BombHunt(BPGame* game) : BPMiniGame(game) { + SuccessTime = -1; + GuessesLeft = 12; + LastNumCorrect = 0; + LastGuessTime = 0; + TimeStarted = 0; + + sfcLastNumCorrect = sfcGuessesLeft = NULL; + + Selected1 = Selected2 = Selected3 = NULL; + + GameTitle = "Bomb Hunt"; + GameHelp = "There's a bomb, and you have to diffuse it! You have 12 tries to figure out which three gems make up the bomb, and I'll tell you how many of your gem guesses were correct. All three gems in the bomb are different!"; + GameHelp2 = "To make a guess, just tap any three bomb components. If you get it wrong, you'll be told how many of those gems were correct. For example, if you tap red, green and blue gems and Brain Party says that you got 1 correct, it means that two of those gems were wrong."; + + MiniGameType = PUZZLE; + + sfcBackground = TheGame->LoadBitmap("bombhunt", 320, 480); + + Components.Add(TheGame->LoadBitmap("gem1", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem2", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem3", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem4", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem5", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem6", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem7", 64, 64)); + Components.Add(TheGame->LoadBitmap("gem8", 64, 64)); + + Components.Shuffle(); + + BombItems.Add(Components[0]); + BombItems.Add(Components[1]); + BombItems.Add(Components[2]); + + Components.Shuffle(); +} + +BPMiniGame_BombHunt::~BPMiniGame_BombHunt() { + SAFE_DELETE(sfcBackground); + + SAFE_DELETE(sfcLastNumCorrect); + SAFE_DELETE(sfcGuessesLeft); + + BombItems.Clear(false); + Components.Clear(); +} + +void BPMiniGame_BombHunt::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_BombHunt::GetWeight() { + int time = floor((TheGame->TickCount - TimeStarted) / 1000); + return MinMax(600 - round(time * 20)); +} + +void BPMiniGame_BombHunt::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < 8; ++i) { + if (i < 4) { + TheGame->DrawImage(Components[i], 23 + (i * 70), 77); + } else { + TheGame->DrawImage(Components[i], 23 + ((i - 4) * 70), 147); + } + } + + if (Selected1 != NULL) TheGame->DrawImage(Selected1, 48, 257); + if (Selected2 != NULL) TheGame->DrawImage(Selected2, 128, 257); + if (Selected3 != NULL) TheGame->DrawImage(Selected3, 208, 257); + + if (SuccessTime == -1) { + if (LastGuessTime != 0) { + TheGame->DrawString(sfcLastNumCorrect, WHITE, 0, 350); + } else { + TheGame->DrawString(sfcGuessesLeft, WHITE, 0, 350); + } + } +} + +void BPMiniGame_BombHunt::Tick() { + if (SuccessTime != -1) { + if (SuccessTime + 250 < TheGame->TickCount) { + Success(); + } + } else { + // this player has used all their chances! + if (GuessesLeft == 0) { + // short delay so the Guesses Left text can be updated + if (LastGuessTime + 250 < TheGame->TickCount) { + Failure(); + } + } + } + + if (LastGuessTime != 0) { + if (LastGuessTime + 500 < TheGame->TickCount) { + ClearForNextTry(); + } + } +} + +void BPMiniGame_BombHunt::ClearForNextTry() { + Selected1 = NULL; + Selected2 = NULL; + Selected3 = NULL; + + LastGuessTime = 0; +} + +void BPMiniGame_BombHunt::OnMouseUp() { + if (LastGuessTime != 0) ClearForNextTry(); // speed up the guessing time when you tap the screen + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 23, 77, 64, 64)) SelectItem(Components[0]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 93, 77, 64, 64)) SelectItem(Components[1]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 163, 77, 64, 64)) SelectItem(Components[2]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 233, 77, 64, 64)) SelectItem(Components[3]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 23, 147, 64, 64)) SelectItem(Components[4]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 93, 147, 64, 64)) SelectItem(Components[5]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 163, 147, 64, 64)) SelectItem(Components[6]); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 233, 147, 64, 64)) SelectItem(Components[7]); + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 231, 240, 20)) { + if (GuessesLeft > 1) { + //MessageBox.Show("You only have " + GuessesLeft + " more tries to figure out which three items make up the bomb - good luck!", GameTitle); + } else { + //MessageBox.Show("You only have one more try to figure out which three items make up the bomb - you'd better guess correctly!", GameTitle); + } + } + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 28, 243, 263, 90)) { + MessageBox::Show("This is your guessing area. As you tap on the gems above, they'll be placed here so you can see which ones you have chosen.", GameTitle); + } +} + +void BPMiniGame_BombHunt::OnMouseDown() { + +} + +void BPMiniGame_BombHunt::OnMouseMove() { + +} + +void BPMiniGame_BombHunt::SelectItem(Texture* item) { + if (Selected1 == NULL) { + TheGame->PlaySound("gem_select"); + Selected1 = item; + return; + } else if (Selected2 == NULL && Selected1 != item) { + TheGame->PlaySound("gem_select"); + Selected2 = item; + return; + } else if (Selected3 == NULL && Selected1 != item && Selected2 != item) { + Selected3 = item; + CheckAnswer(); + return; + } + + if (Selected1 == item || Selected2 == item || Selected3 == item) { + MessageBox::Show("You've chosen that item already! Remember, the bomb is made up of three different components.", "Oops!"); + } +} + +void BPMiniGame_BombHunt::CheckAnswer() { + --GuessesLeft; + + LastNumCorrect = 0; + + if (BombItems.Contains(Selected1)) ++LastNumCorrect; + if (BombItems.Contains(Selected2)) ++LastNumCorrect; + if (BombItems.Contains(Selected3)) ++LastNumCorrect; + + if (LastNumCorrect == 3) { + TheGame->PlaySound("gem_select"); + SuccessTime = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong2"); + LastGuessTime = TheGame->TickCount; + } + + ostringstream numcorrect; + numcorrect << "You got " << LastNumCorrect << " correct."; + TheGame->AllocString(&sfcLastNumCorrect, numcorrect.str().c_str(), NORMAL, 320, 50, CENTRED); + + ostringstream guesses; + + if (GuessesLeft <= 1) { + TheGame->AllocString(&sfcGuessesLeft, "1 guess remaining.", NORMAL, 320, 50, CENTRED); + } else { + guesses << GuessesLeft << " guesses remaining."; + TheGame->AllocString(&sfcGuessesLeft, guesses.str().c_str(), NORMAL, 320, 50, CENTRED); + } + +} diff --git a/bombhunt.h b/bombhunt.h new file mode 100644 index 0000000..43715cf --- /dev/null +++ b/bombhunt.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef __BOMBHUNT_H__ +#define __BOMBHUNT_H__ + +#include "Minigame.h" + +class BPMiniGame_BombHunt: public BPMiniGame { + +private: + Texture* sfcBackground; + + BPPList Components; + BPPList BombItems; + + Texture* Selected1; + Texture* Selected2; + Texture* Selected3; + + int SuccessTime; + int LastGuessTime; + + int GuessesLeft; + int LastNumCorrect; + + SpriteFont* sfcLastNumCorrect; + SpriteFont* sfcGuessesLeft; + + int TimeStarted; + +protected: + void Start(); + int GetWeight(); + void Render(); + void Tick(); + + virtual void OnMouseDown(); + virtual void OnMouseUp(); + virtual void OnMouseMove(); + + void ClearForNextTry(); + void MouseClick(BPPoint e); + void SelectItem(Texture* item); + void CheckAnswer(); + +public: + BPMiniGame_BombHunt(BPGame* game); + ~BPMiniGame_BombHunt(); +}; + +#endif diff --git a/bpsays.cpp b/bpsays.cpp new file mode 100644 index 0000000..7691796 --- /dev/null +++ b/bpsays.cpp @@ -0,0 +1,318 @@ +// 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 "bpsays.h" +#include "Minigame.h" + +BPMiniGame_BPSays::BPMiniGame_BPSays(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("bpsays", 320, 480); + sfcBackgroundChoosing = TheGame->LoadBitmap("bpsays_choosing", 320, 480); + sfcCircle = TheGame->LoadBitmap("white_circle", 64, 64); + + Sequence = new BPList(); + Numbers = new BPPList(); + + Speed = NumCorrect = NumErrors = SequenceCounter = SelectedNumber = NumTries = TimeStarted = 0; + Countdown = -1; + Repeating = false; + + GameTitle = "Brain Party Says"; + GameHelp = "Watch the colours I'll show you, then copy the sequence yourself. I'll make things harder by showing you words that are intentionally wrong - ignore those words and focus on the colours you see!"; + GameHelp2 = "The key in this game is to focus on the colours, not the words. That is, Brain Party might show a colour like Red, but then show the text \"Blue\" to try to put you off. You need to ignore the text and look at the colour: the correct answer is Red, not Blue."; + + MiniGameType = PUZZLE; + + Numbers->Add(TheGame->LoadBitmap("1", 215, 215)); + Numbers->Add(TheGame->LoadBitmap("2", 215, 215)); + Numbers->Add(TheGame->LoadBitmap("3", 215, 215)); + + ColourList.Add(Colour(0.0f, 0.0f, 1.0f, 1.0f)); + ColourList.Add(Colour(0.0f, 1.0f, 0.0f, 1.0f)); + ColourList.Add(Colour(1.0f, 0.75f, 0.0f, 1.0f)); + ColourList.Add(Colour(1.0f, 0.0f, 0.0f, 1.0f)); + + SpriteFont* tex = NULL; TheGame->AllocString(&tex, "Blue", ALMOSTEPIC, 320, 297, CENTRED, true); + ColourWords.Add(tex); + + SpriteFont* tex2 = NULL; TheGame->AllocString(&tex2, "Green", ALMOSTEPIC, 320, 297, CENTRED, true); + ColourWords.Add(tex2); + + SpriteFont* tex3 = NULL; TheGame->AllocString(&tex3, "Yellow", ALMOSTEPIC, 320, 297, CENTRED, true); + ColourWords.Add(tex3); + + SpriteFont* tex4 = NULL; TheGame->AllocString(&tex4, "Red", ALMOSTEPIC, 320, 297, CENTRED, true); + ColourWords.Add(tex4); + + GameState = 2; + Speed = 700; +} + +BPMiniGame_BPSays::~BPMiniGame_BPSays() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBackgroundChoosing); + SAFE_DELETE(sfcCircle); + + ColourWords.Clear(); + + Sequence->Clear(); + SAFE_DELETE(Sequence); + + Numbers->Clear(); + SAFE_DELETE(Numbers); +} + +void BPMiniGame_BPSays::Start() { + Countdown = TheGame->TickCount; + TimeStarted = TheGame->TickCount; + TheGame->PlaySound("beep_hi"); +} + +int BPMiniGame_BPSays::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(570 - (NumErrors * 50) - (TimePassed / 0.9)); +} + +void BPMiniGame_BPSays::Render() { + if (Countdown == -1) return; // don't do anything until the game begins! + + switch (GameState) { + case 0: + case 1: + case 2: + // counting down + TheGame->DrawImage(sfcBackground, 0, 0); + + { + float diff = (Countdown + Speed) - TheGame->TickCount; + diff /= Speed; + + diff = TheGame->Clamp(diff, 0.0f, 1.0f); + + Colour fade = Colour(1.0f, 1.0f, 1.0f, diff); + + TheGame->DrawImage((*Numbers)[GameState], MiniGameHalfWidth - 107, MiniGameHalfHeight - 107, fade); + } + + if (Countdown + Speed < TheGame->TickCount) { + --GameState; + Countdown = TheGame->TickCount; + + if (GameState == -1) { + GameState = 4; + + // if we're repeating, don't add to the sequence + if (Repeating) { + Repeating = false; + } else { + GenerateSequence(); + } + } else { + TheGame->PlaySound("beep_hi"); + } + } + break; + + case 4: + // showing a sequence + TheGame->FillRectangle(ColourList[(*Sequence)[SequenceCounter]], 0, 0, 320, 480); + + if (SequenceCounter % 2 == 0) { + TheGame->DrawString(ColourWords[(SequenceCounter + Sequence->Count) % ColourWords.Count], WHITE, 0, 0); + TheGame->DrawString(ColourWords[(SequenceCounter + Sequence->Count) % ColourWords.Count], WHITE, 0, 200); + } else { + TheGame->DrawString(ColourWords[(SequenceCounter + Sequence->Count) % ColourWords.Count], WHITE, 0, 100); + TheGame->DrawString(ColourWords[(SequenceCounter + Sequence->Count) % ColourWords.Count], WHITE, 0, 300); + } + + if (Countdown + Speed < TheGame->TickCount) { + ++SequenceCounter; + Countdown = TheGame->TickCount; + } + + if (SequenceCounter == Sequence->Count) { + GameState = 5; + SequenceCounter = 0; + SelectedNumber = -1; + } + + break; + + case 5: + // players copying the sequence + + TheGame->DrawImage(sfcBackgroundChoosing, 0, 0); + { + Colour col = Colour(1.0f, 1.0f, 1.0f, 0.6f); + + switch (SelectedNumber) { + case 0: + TheGame->DrawImage(sfcCircle, 128, 292, col); + break; + case 1: + TheGame->DrawImage(sfcCircle, 35, 200, col); + break; + case 2: + TheGame->DrawImage(sfcCircle, 221, 200, col); + break; + case 3: + TheGame->DrawImage(sfcCircle, 128, 106, col); + break; + } + } + + break; + + case 6: + // player made a mistake; show the oops message then repeat the sequence + + Repeating = true; + + TheGame->DrawImage(sfcBackground, 0, 0); + RenderWrong(); + + if (Countdown + Speed < TheGame->TickCount) { + SequenceCounter = 0; + GameState = 2; + Countdown = TheGame->TickCount; + TheGame->PlaySound("beep_hi"); + } + + break; + + case 7: + // player was correct; show the correct message then continue + TheGame->DrawImage(sfcBackgroundChoosing, 0, 0); + + { + Colour col = Colour(1.0f, 1.0f, 1.0f, 0.6f); + + switch (SelectedNumber) { + case 0: + TheGame->DrawImage(sfcCircle, 128, 292, col); + break; + case 1: + TheGame->DrawImage(sfcCircle, 35, 200, col); + break; + case 2: + TheGame->DrawImage(sfcCircle, 221, 200, col); + break; + case 3: + TheGame->DrawImage(sfcCircle, 128, 106, col); + break; + } + } + + RenderCorrect(); + + if (Countdown + Speed < TheGame->TickCount) { + GameState = 2; + TheGame->PlaySound("beep_hi"); + SelectedNumber = -1; + Countdown = TheGame->TickCount; + } + + break; + } +} + +void BPMiniGame_BPSays::GenerateSequence() { + SequenceCounter = 0; + + if (NumCorrect == 0) { + Sequence->Add(TheGame->RandomRange(0, ColourList.Count - 1)); + } else { + // don't repeat the same colour twice in a row + // otherwise it's hard to spot! + Sequence->Add(TheGame->RandomRangeExcept(0, ColourList.Count - 1, (*Sequence)[Sequence->Count - 1])); + } +} + +void BPMiniGame_BPSays::Tick() { + if (NumTries >= 8 && !MarathonMode) { + Success(); + } +} + +void BPMiniGame_BPSays::OnMouseDown() { + +} + +void BPMiniGame_BPSays::OnMouseMove() { + +} + +void BPMiniGame_BPSays::OnMouseUp() { + if (GameState != 5) { + // player is copying the sequence + return; + } + + BPPoint blue = BPPoint(159, 374); + BPPoint green = BPPoint(16, 232); + BPPoint yellow = BPPoint(302, 232); + BPPoint red = BPPoint(159, 88); + + SelectedNumber = 0; + float distance = BPPoint::Distance(TouchEvent, blue); + + if (BPPoint::Distance(TouchEvent, green) < distance) { + distance = BPPoint::Distance(TouchEvent, green); + SelectedNumber = 1; + } + + if (BPPoint::Distance(TouchEvent, yellow) < distance) { + distance = BPPoint::Distance(TouchEvent, yellow); + SelectedNumber = 2; + } + + if (BPPoint::Distance(TouchEvent, red) < distance) { + distance = BPPoint::Distance(TouchEvent, red); + SelectedNumber = 3; + } + + if ((*Sequence)[SequenceCounter] == SelectedNumber) { + ++SequenceCounter; + TheGame->PlaySound("gem_select"); + } else { + Wrong(); + } + + if (SequenceCounter == Sequence->Count) { + LevelUp(); + } +} + +void BPMiniGame_BPSays::LevelUp() { + ++NumTries; + GameState = 7; + ++NumCorrect; + Speed -= 25; + Countdown = TheGame->TickCount; +} + +void BPMiniGame_BPSays::Wrong() { + ++NumTries; + TheGame->PlaySound("wrong"); + SelectedNumber = -1; + GameState = 6; + Countdown = TheGame->TickCount; + ++NumErrors; +} + +void BPMiniGame_BPSays::SetMarathon() { + MarathonMode = true; +} diff --git a/bpsays.h b/bpsays.h new file mode 100644 index 0000000..7c7fd3a --- /dev/null +++ b/bpsays.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef __BPSAYS_H__ +#define __BPSAYS_H__ + +#include "Minigame.h" + +class BPMiniGame_BPSays : public BPMiniGame { +public: + ~BPMiniGame_BPSays(); + BPMiniGame_BPSays(BPGame* game); + void Start(); + int GetWeight(); + void Render(); + void GenerateSequence(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void LevelUp(); + void Wrong(); + void SetMarathon(); +protected: + Texture* sfcBackground; + Texture* sfcBackgroundChoosing; + Texture* sfcCircle; + + BPList ColourList; + BPPList ColourWords; + BPList* Sequence; + BPPList* Numbers; + + int GameState; + int Speed; + int Countdown; + int NumCorrect; + int NumErrors; + int SequenceCounter; + int SelectedNumber; + + int NumTries; + + bool Repeating; + + int TimeStarted; +}; + +#endif diff --git a/brainparty.desktop b/brainparty.desktop new file mode 100644 index 0000000..a05f167 --- /dev/null +++ b/brainparty.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=Brain Party +Exec=/opt/brainparty/brainparty +Icon=brainparty +Terminal=false +Type=Application +Categories=Game;LogicGame; diff --git a/brainparty.png b/brainparty.png new file mode 100644 index 0000000000000000000000000000000000000000..71b543aa720e38ff26ff8f0876f64b60a31daf52 GIT binary patch literal 10540 zcmV+{Dbv=8P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0016WNkl!av&0-8B7#fb@ z$AtG-9xnt3Fpz)~5|Z#(OkM&wfE^nQ*s%d`c#*Y@7HgNaj7GC}Pp@~YTlM~!mLrTH z#0HadQs>-rr+d2l*6&-tt-ils31bZZormPV`S@=SfEXGYdZC_`0(yYH=Dnl&o&XHs z14UpQI10=F>eoFf`}zLBn||+adHCKCBcDdD^`)QZBJCH#rWvR(VF4pY`H$OCDF-!z}6K5>!*|IOX#0p z;>pJ=oIKh}p+m9k{IfX!;x2Cc=6im#0Q|ghqH%Zoi33~!oR79v_qMk!zWNnC3pQSO zZa$ywm$^cVZA(jNZH#TX!uK0`d~#7TdU9OfcmL?(-Mhm}YHG{YeIr|c1X~~KpZ|`j z|B@Q;>`|6aIpvT+;B}p?%U^fpib z{^E~(_=bgp3tdTA2491Y=S*IRrs+Rxm2H6UjL>gufDP`Yfe1PfyejKwR}D8%NL_i z*e1nX;-ydfIo(tSjj{^FI*pw)uw(;^7#$0+JftU4R*FA-|6h#k-m`Q1l8b()C!5Q# zYxkbo?p^!FJa6uM>a{;L1Xw``V3{T)y_@uehpj=$xhbY+g{Wgl6ja7)ur) zYR8B1EDK9y5pD{Zkr*MsWSUEUnie#Jpa3FclW2i!38aw-Z7_rgD*_py6yJc#vZ!=8$ZxG`Z}-`X#6q)S-rizzwD~72Hv{i!mHo@fe(D> zqU+xHn%;r_Hcy)n6$x@~BBI8)AdzJAeORehtZV^cd7wRnP9uzkz(N@NXD*D5g~1U9 zX$-<>5C$O}jB)2I&S*}S>&)w2E<}_@#Q|>0r~fQRUV6!f79;uw$Hyj?)T+|}CxQB} z0)P(SnwCPxAH38=&=`~nP)UT9*j#+>n#|Dpjs3g! z?_XY=I_Uw&fb#PJpbvQSf`u2p>!zFj*M@6fdwE9~`BEhnER!I#z$6mKPEnmaMAnK( zryS798~~!J`a)m?h+nwzG9WC>Gtt2ynx-O*L5KuM@EniwOdaVKuu~q9iIGHDQHUK_ zba&;Qm%i#HT_eY)21kw`Z8JuXHM2_np8>!I&Xv-=e(ADTT=$v3`m?iFF6;KHwJMg> zm?XiL7Dj12&!sd|!PHLB+2LWdLYic5!bZ;lVH83qb1VAu-4TK&aMB=5f*_uI&&&yr z(KteZN~qO*QiT>`AyC?2NgJJnL`vgWIdSF7u4u1R;=uz4_Vy)OO#)+Q0^v*mc)&(M z=8vxXt&hF-&p!FO1*u$3MwK)I7h@cxv5`h1r9dSC6O$+DYqM}<+>CN^ZY7OD=y=Y< zS^rVmA)4VZf+S`xZZ<=tF>|kt0p*j3Q5dveZIlmwsuswwIKzd*-tW<`X*)o}2 zD@M5JM1bvJbOYP2phZ9shO9qtU8<{laNgD}+k4}Lz65ve@5c@is4KnCALh-WLdoo0re{DKjYUMtM6{$+wB}QE7A&%! z3o1sd7Bc6I{A6;GT49xE#pLP%kVgr`zVuU0}6f?5{ zqPwnUIvzM}R z$pG1O3MnD&Iiy^VTCK*^)D-o)Pdb~Tudj>w{hhS7<|&p+j2t;aXKS96BQerJrdv2P zI>V8XN#^zSvG$ymEbQ;ac3ftQGX#wq7(+gj#_=44v~eAW&h8Gn`}=tM_$Vq)Xv?{v z6ATH4XfEf}7Djs*Wnozk%a*OoJow-yF9@qUg%D%*ob8!`?(V@AS6qH+zFsNLg}OMz zXb-eR*bbpdz(Ap`g{|9mGrzx^!M+YuqA^=i`9;D-uJ*sNMU$e!H?80=b1%Bhn?jXCSq7HAh?9E21!{FuQ- z%e)O6u3CA|y|=E}v17+JIXpZpk|b$~qd42%o*{{2h+{BnE*5Jri3PTYkuICJY^PW( zv0$(tV?d&D9EYjtDgOFzKhO8S_dTY@#xRjjP?}<1YmTM;J^bXpd)e}npOA3{Jp%)f z?`Fq|8k3>T(a9={SDZs8m14G3dAHIsV)3mj<5`+O?{NfkbyJruX zOd6HUIZC4yOP4O8v$KOsFT8-A+qST6%aeGv4M;2-a`_x5PK@!uj~_xO0)Y$0#-K>7 z3bC9<$r88`#nKE(lEi=|0XaK6J5ws1IQ*SEzw4*m9%AJoq(l*upyqP4MA6pTia?Of zc-R88F(4#QJpL$KHg6&ArBJcLXid-vXvya(Kr_HknP*H@cn!42c?kGX8ync zYu2v8OQ*4b$DeqdO^-i8I-N!-g%pBDqd`k+D=n?9G#Ygd9@x+BojbYnuDei4jOPke z1o?amNgUDI(uO0`s92yAfgvW0s|W{#o#Xhidh+8R-ap-FG)4j22AG+dsX30b`J3PR z(z<0!mo3_`VYL%PGYFxOmP4WyB1#yXKaUH}KNrbd`l$@q#_;6!VZtcFbrK?#V6-8M zH46rpP_Fts`Q(!vI(&dI3aB4E#Kd@!Og6)!zIGP&En#9}3JpBDbtjQZ&_=fm4mFjE>8Wm(* z#}yhCH1NZSshJsKBdPfjL9Fp3jj&yg96P}scYT*_JDx&YHkRWuTPjhUnZ}loa=>rQ z(wfg;3ys#%Gs&JYE`kio@8E{t|4?nqmT&Ga7K=|*DwSEl$mS=5D2gUZrP5A(DVb@XW3WHUWzIgVfqD^8(w1v?W`4SZr1V#Hkb=ISgePKaV3qXJMqmI;_XIl|1$ z6t?RTCkCY=>S2tOl1h1&?v$or1!&b^HjIh20HsJ&Oc?kGG)l!BJARbX>?}fp2qThM zqpdW?N}&yC4Uz97Er}5^7@tIkgz+rtte{aZm_L5^4Yfxex$VH@el47O8;zbJys#Q=59Y=&g zgD7mwks~B%72|{r;zpHxp@sf|0i*!OB!tx}`AnWDN~3j}NKcdR5R8t-=K8l^U)?c$ z-+`&AsfX+J`gX1LC{P2G92y!jK-?rXkB4D6JUu=Aqs`lI-~ZnCy}NeLcSG&i>(EX> z7%2*F8|`j86GzKPAQE*hzVbqpEvWf1v9YO#ibxq6VTef-QY%#CQ>g`Pyx=@+OQ4k@ zpUFU+(9_<=>{O9UF1irmWvTfgN<$oLqCgSHAu0~gNrXyjR7zun(^`R zqzQydl0?OEoM=6h+P3wH%+zGBbIFU>Se6qL*6U=g0@D*k+B#gEw4k$RK7M3);Gsw6 z_S3F6=fn~X1`-uhnweth>T`JCpS+i2M~^em)k;_11|5@1Wf`3+Gcd4#bT-2eA9w`I zvQQ?$5(2F?#wb)2F+DxOvXv|O;QRkK`CI{^6Q16GkQOgPN;r5q3z^Gu$88TsfAPti zD<{Wx>@60H4@Xh7waFZhG+Cl}XlTehvm1Oi2ui6a2m(KjtK#V+TXOpkRJ_&atP=CO zbHcBbaJ`sfd4!Ihc{H@ki_SY2AuP6T-$Aw3zz+hVI3@@JsHJ0P?#HJ_l8+H63AdDial`3`LXYIL{@UcJtIQ{d_ z0^sSR2dGzzWOE*_=VG<^eB~c+i*NnnXD7$U5A7(I%a27-w5=)piPMGuneFiS__%2j z1`!Zxt)nmueWmKj*vVb;k%u027cE}mEL_+n9Va9-bxw{I>FydpSb~erJC`+U)?$Q3 zTU$GQ{r#+1v7EQP^{u?;hCe1yhT-9zoW0~MT+0ypbt<(6k+I0-3KXj$C&p*^t!rM+ z`t|D(wnHYDqo;2kOP8(Sb-#Tb@4oT9^mlinfsxT8967d+d1v*J2n#!tfIG*(;Y(Yb2aF zS>qdD|3>)3d+t10EEc!bYPBbnQhR`>n>Js2w(w`9#Cm&s86O|lO;ZS)&LYrS2T>F? zk|ff*ckgfyAFg?8&sl4)UEMEqZJI;JN2t_&q68XIOb{FDVN9u9XWzbq92*&B-uyo1 z^|Yh?8jZS7B2y?UO&BRutgxgYm(5b1EpudKjOj`PZMpc`P^yPamIEe_9p%XWU0Ax# zy5&pBI5~D~Ii_#A>CbDMH$8B4dV2baYPI^f)_PY{`0*w^o;+Ll|FAGJJUnbQIl|T^ zLp%s9NvG4RJ32bnoxSqh1z-A`_vX%8RA8o*<=CXA(r6GQ5^W@56l0>0LN-HJXFF}} zIXn>}!#dLTn2iM|r)%`}_K>zUB2qZEOL2CVP-{+37MZC9WLw+N!U72kQnSqO%F~%k zV~G@>|LcEFzIONb%11{IA1W4$TjDt0)wK5`z;rXj>oW=eOI3!3hlk~9CehUdVX^0V ztGm0qH!hsNe95=}-_5D+K3m``5?vq(GH7X`%mh=FNy20v(wT>H%Vb=Of+t9VI(`s> z?GnZsFPBB?UV^}2+2i<)QH=8GDCB6(XOP+;jE$r6nA8ZO&E-!&_%G4F-1)tk(rjsO zxm@1XRQUr^5=@?5)VPqB2(#Kc7^g!|tU;X;b^vv{@a=H9ik|aBuD}Nj){W8M;(&h1? zp&>IoJe-_LADg}*Y&06t^z<}4%_jZ!cLa<6>DDh~Gbw?Vhmeee>cWZ&v}a;k3bTl$ zg{;@Z$gu<5bn~Z~m@H!34oWF5ICnK~dDAOcy2#*(87$ocaW^`Q5biioL&zMWvz_nU zc1+*=*;~t%dU;p1T767w{nTmpUIU_^r}EFWHppoSlR54 z{rW*C*IC8X=OLmzxKluYjB*H3qmc4gd+8M%IdYWS@BBU!Q`6MTv;561ALG)iH{eww zWHb-7jb%+CtQtCTk*O?X6o2=3$Mj8~{6x7CjP5FxN%7CXIt2q7v6Pe5!VY?rVR(mS}2v}aM7 zndDO+`yhRN-I$0>B zG&H22i!9FxfHUL^kufHU$_b)TsLbO1Lya!_=ftYws%3xkW5oHYDaPILF?5s6-OY&bqF>CrOfFrPQe{ zZSs6%cwPViyy}YAux;m7Jvca+>hA8o@T`G_ule0~zIWaG{^z?|J9;w0s5VZF1U&NS z7S6q7J%#ojEH8_d%HS0`u)G{rwjC?oO17<=bjw-H&bsX1GtS`h#V8Z8V)X*+imR{6 zP0zNsO^qGQx3=bvW-^%r<#IXt&)WR29Dv@=evFl5Q>oM|d*_{f{Y@YF@C9%9?aK>} z5<eLGtmNZ*Bx(+GvqO8AClD<9B{Nf#^ZH#%zIV+Z4^#|{` zrf2lXHj-L^>tu1#n)`qFSh97~edPrMFSZviZV^hQk@BZCKnO8cS&AYad+c%k<+gw3 z2jBk=U;NTn*s^UKufFCg+p?VKt~#g?Y}@v# z7aRbsb(d?qFMZn^uRSZ9ibZ=SWM*V9r4tqWn&PWp{&Jvot$NKHHl{#hlS1(Cf}hd= zAq3TGl?NVpfLg7J>!b*L#rVWHOP3B>3kH|D_4@409k+cus!ShZVq^*tPDcX7 z1|hAVT6xQwn{$#R*p5Rclc6{>ix3X2?JcBTM_h2;#1)jLgFt_>@; zz_)n$%hnYvX)W=6-+RHDrH6-y#p>0oy9?QTduvNp5(St5>KIq1uq-^^_nmY){eo$LjH0M<_~>XO(845{Q6f2mXfJX2;1M2t z{6|HgvTW&!Y$27PkQmFFYm9%E{YxoHrBYnD;X=+k{~`t#E~2-)K+5*8NVEP$=cGD2 zdkdvn<;X`r^Ki|HhHw%erAx%#49CZ2;(8+}&(6-ipgus9N~IHrjvd(d#ar+7O=i%D z%nE|2jX(Rum+KY(;a*EJ>LCy=vsi~=o5aNl?-}-@n7RtnU;WjoNs%T7A5=wKW`=zp}7+@qAmEBDS#So+N%+;iQztg9Y{CJPGd=u_~pvP$oGG6Pq$~=Y1eL-wk1WPV!kDh~2!ODtA364S3E+W}0+)3djS6 zrrrjCpUq~wa=F~)mSw#}2rHO)jwH~*GGJadppoOY^0ODU_s;W&=(DV0i( zgkcyqW2)6;sVhx7EPf$1fT5uwJv=-dG-3L>h-!+uh(}pUoU6^B+sBl`m|{D zGXOQeUjhyTdz#OwHdX!^U-a{;KELimQ$Ld&HR*G^sTK2pfo3r!^DF?&vo3iW2+#ii zDN{RgiuQiKB*?Gj*xYH8I#oXyZ{A0m&pDF@oVvu%$w^K%Z#0^A9`P$4f?xeOrOH-Q uaO(`M;pUf^&*`6erf=aFeD^tx|33h;7sR}2%41gm0000LoadBitmap("bubbletrouble", 320, 480); + sfcRedCross = TheGame->LoadBitmap("red_cross", 64, 64); + + DirectionPos = 0; + MaxNumber = 0; + TimeStarted = 0; + LastCreatedTime = 0; + SuccessTime = -1; + + NumWrong = 0; + + GameTitle = "Bubble Trouble"; + GameHelp = "You'll be shown bubbles with numbers on - you need to tap the lowest-numbered bubbles first. Get it wrong or take too long, and I'll create another bubble, so be careful!"; + GameHelp2 = "The key here is to take your time: the lowest bubble may be obscured by another bubble for a second or two, so take your time and make sure you have the right bubble before tapping it!"; + + MiniGameType = LIVELY; + + BubbleTypes.Add(TheGame->LoadBitmap("marble_blue", 64, 64)); + BubbleTypes.Add(TheGame->LoadBitmap("marble_green", 64, 64)); + BubbleTypes.Add(TheGame->LoadBitmap("marble_purple", 64, 64)); + BubbleTypes.Add(TheGame->LoadBitmap("marble_red", 64, 64)); + BubbleTypes.Add(TheGame->LoadBitmap("marble_yellow", 64, 64)); + + for (int i = 0; i < 360; ++i) { + if (i < 10) continue; + if (i >= 350) continue; + if (i > 80 and i < 100) continue; + if (i > 170 and i < 190) continue; + if (i > 260 and i < 280) continue; + Directions.Add(i); + } + + Directions.Shuffle(); + + MaxNumber = 0; +} + +BPMiniGame_BubbleTrouble::~BPMiniGame_BubbleTrouble() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcRedCross); + + BubbleTypes.Clear(); + + Bubbles.Clear(); + + Directions.Clear(); +} + +void BPMiniGame_BubbleTrouble::CreateBubble(bool initial) { + int TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + + // don't create any more bubbles after 90 seconds, otherwise it's just too hard + if (TimePassed > 90) return; + + if (Bubbles.Count > 12) { + // we already have lots of bubbles - don't create any more! + return; + } + + MaxNumber += TheGame->RandomRange(1, 4); + BPMiniGame_BubbleTrouble_Bubble* bubble = new BPMiniGame_BubbleTrouble_Bubble(); + + bubble->X = TheGame->RandomRange(0, MiniGameWidth - BubbleTypes[0]->Width); + bubble->Y = TheGame->RandomRange(0, MiniGameHeight - BubbleTypes[0]->Width); + + if (initial) { + bubble->CreationTime = -1000; + } else { + bubble->CreationTime = TheGame->TickCount; + } + + bubble->Speed = TheGame->RandomRange(25, 50); + bubble->Type = TheGame->RandomRange(0, BubbleTypes.Count - 1); + bubble->Number = MaxNumber; + ChooseBubbleDirection(bubble); + + ostringstream number; + number << bubble->Number; + TheGame->AllocString(&bubble->sfcNumberStr, number.str().c_str(), LARGE, 64, 64, CENTRED, true); + + Bubbles.Add(bubble); + + LastCreatedTime = TheGame->TickCount; +} + +void BPMiniGame_BubbleTrouble::ChooseBubbleDirection(BPMiniGame_BubbleTrouble_Bubble* bubble) { + ++DirectionPos; + if (DirectionPos == Directions.Count) DirectionPos = 0; + bubble->Direction = Directions[DirectionPos]; + + SetBubbleDirection(bubble); +} + +void BPMiniGame_BubbleTrouble::SetBubbleDirection(BPMiniGame_BubbleTrouble_Bubble* bubble) { + bubble->XMove = cos(bubble->Direction * M_PI / 180) * bubble->Speed; + bubble->YMove = sin(bubble->Direction * M_PI / 180) * bubble->Speed; +} + +void BPMiniGame_BubbleTrouble::Start() { + LastCreatedTime = TheGame->TickCount; + TimeStarted = TheGame->TickCount; + + for (int i = 0; i < 15; ++i) { + CreateBubble(true); + } +} + +int BPMiniGame_BubbleTrouble::GetWeight() { + int TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(550 - (NumWrong * 50) - floor(TimePassed * 4)); +} + +void BPMiniGame_BubbleTrouble::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + BPMiniGame_BubbleTrouble_Bubble* bubble; + + for (int i = 0; i < Bubbles.Count; ++i) { + bubble = Bubbles[i]; + + if (bubble->CreationTime + 350.0f > TheGame->TickCount) { + // fade this bubble in + float diff = (bubble->CreationTime + 350.0f) - TheGame->TickCount; + diff /= 250.0f; + diff = 1 - diff; + + glColor4f(1.0f, 1.0f, 1.0f, diff * 0.8f); + TheGame->DrawImage(BubbleTypes[bubble->Type], bubble->X, bubble->Y); + + glColor4f(0.0f, 0.0f, 0.0f, diff); + TheGame->DrawString(bubble->sfcNumberStr, NOCOLOUR, bubble->X, bubble->Y + 15); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } else { + glColor4f(1.0f, 1.0f, 1.0f, 0.8f); + TheGame->DrawImage(BubbleTypes[bubble->Type], bubble->X, bubble->Y); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + TheGame->DrawString(bubble->sfcNumberStr, BLACK, bubble->X, bubble->Y + 15); + } + + if (bubble->LastWrong + 250 > TheGame->TickCount) { + TheGame->DrawImage(sfcRedCross, bubble->X, bubble->Y); + } + } +} + +void BPMiniGame_BubbleTrouble::Tick() { + if (SuccessTime == -1) { + if (LastCreatedTime + 3500 < TheGame->TickCount) { + CreateBubble(false); + } + } else { + if (SuccessTime + 500 < TheGame->TickCount) { + Success(); + } + } + + BPMiniGame_BubbleTrouble_Bubble* bubble; + + for (int i = 0; i < Bubbles.Count; ++i) { + bubble = Bubbles[i]; + + bubble->X += bubble->XMove * TheGame->ElapsedSeconds; + bubble->Y += bubble->YMove * TheGame->ElapsedSeconds; + + bool fixdirection = false; + + if (bubble->X > MiniGameWidth - BubbleTypes[0]->Width) { + bubble->X = MiniGameWidth - BubbleTypes[0]->Width; + bubble->Direction = 180 - bubble->Direction; + fixdirection = true; + } else if (bubble->X < 0) { + bubble->X = 0; + bubble->Direction = 180 - bubble->Direction; + fixdirection = true; + } + + if (bubble->Y > 416 - BubbleTypes[0]->Height) { + bubble->Y = 416 - BubbleTypes[0]->Height; + bubble->Direction = 360 - bubble->Direction; + fixdirection = true; + } else if (bubble->Y < 0) { + bubble->Y = 0; + bubble->Direction = 360 - bubble->Direction; + fixdirection = true; + } + + if (fixdirection) { + if (bubble->Direction >= 360) bubble->Direction = bubble->Direction - 360; + if (bubble->Direction < 0) bubble->Direction = 360 - abs(bubble->Direction); + SetBubbleDirection(bubble); + } + } +} + +void BPMiniGame_BubbleTrouble::OnMouseUp() { + for (int i = 0; i < Bubbles.Count; ++i) { + BPMiniGame_BubbleTrouble_Bubble* bubble = Bubbles[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, round(bubble->X), Round(bubble->Y), BubbleTypes[0]->Width, BubbleTypes[0]->Height)) { + ClickBubble(bubble); + return; + } + } +} + +void BPMiniGame_BubbleTrouble::OnMouseDown() { + +} + +void BPMiniGame_BubbleTrouble::OnMouseMove() { + +} + +void BPMiniGame_BubbleTrouble::ClickBubble(BPMiniGame_BubbleTrouble_Bubble* clicked) { + BPMiniGame_BubbleTrouble_Bubble* bubble; + + for (int i = 0; i < Bubbles.Count; ++i) { + bubble = Bubbles[i]; + + if (bubble->Number < clicked->Number) { + TheGame->PlaySound("wrong3"); + clicked->LastWrong = TheGame->TickCount; + ++NumWrong; + + if (Bubbles.Count > 12) { + // we already have lots of bubbles - don't create any more! + return; + } + + CreateBubble(false); + ChooseBubbleDirection(bubble); + + return; + } + } + + TheGame->PlaySound("bubble"); + + // if we're still here, this bubble was the correct one! + Bubbles.Remove(clicked); + + if (Bubbles.Count == 0) SuccessTime = TheGame->TickCount; +} diff --git a/bubbletrouble.h b/bubbletrouble.h new file mode 100644 index 0000000..d0341df --- /dev/null +++ b/bubbletrouble.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef __BUBBLETROUBLE_H__ +#define __BUBBLETROUBLE_H__ + +#include "Minigame.h" + +class BPMiniGame_BubbleTrouble_Bubble { +public: + float X; + float Y; + float XMove; + float YMove; + int Direction; + int Speed; + int Type; + int Number; + SpriteFont* sfcNumberStr; + + int LastWrong; + int CreationTime; + + BPMiniGame_BubbleTrouble_Bubble() { + X = Y = XMove = YMove = 0; + Direction = Speed = Type = Number = 0; + LastWrong = -1; + sfcNumberStr = NULL; + } + + ~BPMiniGame_BubbleTrouble_Bubble() { + SAFE_DELETE(sfcNumberStr); + } +}; + + +class BPMiniGame_BubbleTrouble : public BPMiniGame { +public: + BPMiniGame_BubbleTrouble(BPGame* game); + ~BPMiniGame_BubbleTrouble(); +protected: + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void Render(); + int GetWeight(); + void Start(); + void ChooseBubbleDirection(BPMiniGame_BubbleTrouble_Bubble* bubble); + void SetBubbleDirection(BPMiniGame_BubbleTrouble_Bubble* bubble); +private: + Texture* sfcBackground; + Texture* sfcRedCross; + + BPPList BubbleTypes; + BPPList Bubbles; + BPList Directions; + int DirectionPos; + int MaxNumber; + + int TimeStarted; + int LastCreatedTime; + int SuccessTime; + + int NumWrong; + + void ClickBubble(BPMiniGame_BubbleTrouble_Bubble* clicked); + void CreateBubble(bool initial); +}; + +#endif diff --git a/cardmatch.cpp b/cardmatch.cpp new file mode 100644 index 0000000..8c40850 --- /dev/null +++ b/cardmatch.cpp @@ -0,0 +1,220 @@ +// 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 "cardmatch.h" +#include "Minigame.h" + + +BPMiniGame_CardMatch::BPMiniGame_CardMatch(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("cardmatch", 320, 480); + sfcCardBack = TheGame->LoadBitmap("card_unknown", 64, 64); + + CardTypes = new BPPList(); + Cards = new BPPList(); + + Locked = false; // true to stop the player making any more moves + RemovalSpeed = 500; + + NumErrors = TimeStarted = 0; + + GameTitle = "Card Match"; + GameHelp = "This is a game kids have been playing for years, so you'll find it a cinch - just match pairs of cards on the table as fast as you can."; + GameHelp2 = "When you see the grid of cards, try tapping the first two, but make sure you remember what is shown on the cards. Each card has at least one other like it, and when you tap them both they disappear. Your job is to keep trying all the cards until you have matched them all."; + + MiniGameType = PUZZLE; + + CardTypes->Add(TheGame->LoadBitmap("card_1", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_2", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_3", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_4", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_5", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_6", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_7", 64, 64)); + CardTypes->Add(TheGame->LoadBitmap("card_8", 64, 64)); + + BPList* nums = new BPList(); + int newnum; + + for (int i = 0; i < 15; ++i) { // we need to insert fifteen pairs of numbers + newnum = TheGame->RandomRange(0, 7); + nums->Add(newnum); + nums->Add(newnum); + } + + nums->Shuffle(); + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 6; ++j) { + BPMiniGame_CardMatch_Card* card = new BPMiniGame_CardMatch_Card(); + + newnum = TheGame->RandomRange(0, nums->Count - 1); + card->Type = (*nums)[newnum]; + nums->RemoveAt(newnum); + + card->IsShowing = false; + card->X = 2 + (i * 63); + card->Y = 18 + (j * 63); + + Cards->Add(card); + } + } +} + +BPMiniGame_CardMatch::~BPMiniGame_CardMatch() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcCardBack); + + CardTypes->Clear(); + SAFE_DELETE(CardTypes); + + Cards->Clear(); + SAFE_DELETE(Cards); +} + +void BPMiniGame_CardMatch::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_CardMatch::GetWeight() { + int TimePassed = TheGame->TickCount - TimeStarted; + return MinMax(650 - (NumErrors * 15) - (TimePassed / 1000.0f)); +} + +void BPMiniGame_CardMatch::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Cards->Count; ++i) { + BPMiniGame_CardMatch_Card* card = (*Cards)[i]; + + if (card->IsShowing) { + if (card->MatchTime == 0) { + // hasn't been matched yet + TheGame->DrawImage((*CardTypes)[card->Type], card->X + 32, card->Y + 32, 0.0f, 1.0f, (*TheGame->White)); + } else { + // has been matched - make it fade away + int diffticks = TheGame->TickCount - card->MatchTime; + + if (diffticks <= RemovalSpeed) { + float fticks = ((float)diffticks / RemovalSpeed); // gives a number between 0 and 1, for colours + Colour drawcol = Colour(1.0f, 1.0f, 1.0f, 1 - fticks); + + TheGame->DrawImage((*CardTypes)[card->Type], card->X + 32, card->Y + 32, (1 - fticks) * 360, 1 - fticks, drawcol); + } + } + } else { + TheGame->DrawImage(sfcCardBack, card->X + 32, card->Y + 32, 0.0f, 1.0f, (*TheGame->White)); + } + } +} + +void BPMiniGame_CardMatch::Tick() { + CleanUpCards(false); + + if (Cards->Count == 0) { + Success(); + } +} + +void BPMiniGame_CardMatch::CleanUpCards(bool force) { + for (int i = Cards->Count - 1; i >= 0; --i) { + BPMiniGame_CardMatch_Card* card = (*Cards)[i]; + + if (card->MatchTime != 0) { + if (force || card->MatchTime + 500 < TheGame->TickCount) { + Cards->RemoveAt(i); + Locked = false; + } + } + + if (card->ShowStartTime != 0) { + if (force || card->ShowStartTime + 500 < TheGame->TickCount) { + card->ShowStartTime = 0; + card->IsShowing = false; + Locked = false; + } + } + } +} + +void BPMiniGame_CardMatch::OnMouseDown() { + +} + +void BPMiniGame_CardMatch::OnMouseMove() { + +} + +void BPMiniGame_CardMatch::OnMouseUp() { + if (Locked) { + CleanUpCards(true); + return; + } + + for (int i = 0; i < Cards->Count; ++i) { + BPMiniGame_CardMatch_Card* card = (*Cards)[i]; + + if (card->X <= TouchEvent.X && card->X + sfcCardBack->Width >= TouchEvent.X) { + if (card->Y <= TouchEvent.Y && card->Y + sfcCardBack->Height >= TouchEvent.Y) { + + // this card has already been matched; skip it + if (card->MatchTime != 0) continue; + + if (card->IsShowing) { + card->IsShowing = false; + } else { + TheGame->PlaySound("card_flip"); + card->IsShowing = true; + } + } + } + } + + CheckSelectedCards(); +} + + +void BPMiniGame_CardMatch::CheckSelectedCards() { + // the selected cards + BPMiniGame_CardMatch_Card* typefirst = NULL; + BPMiniGame_CardMatch_Card* typesecond = NULL; + + for (int i = 0; i < Cards->Count; ++i) { + BPMiniGame_CardMatch_Card* card = (*Cards)[i]; + + if (card->IsShowing) { + if (typefirst == NULL) { + typefirst = card; + } else { + typesecond = card; + } + } + } + + if (typefirst != NULL && typesecond != NULL) { + if (typefirst->Type == typesecond->Type) { + typefirst->MatchTime = TheGame->TickCount; + typesecond->MatchTime = TheGame->TickCount; + } else { + typefirst->ShowStartTime = TheGame->TickCount; + typesecond->ShowStartTime = TheGame->TickCount; + + ++NumErrors; + } + + Locked = true; + } +} diff --git a/cardmatch.h b/cardmatch.h new file mode 100644 index 0000000..e2653f2 --- /dev/null +++ b/cardmatch.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef __CARDMATCH_H__ +#define __CARDMATCH_H__ + +#include "Minigame.h" + +class BPMiniGame_CardMatch_Card { +public: + int X; + int Y; + int Type; + bool IsShowing; + int MatchTime; + int ShowStartTime; + + BPMiniGame_CardMatch_Card() { + X = Y = Type = MatchTime = ShowStartTime = 0; + IsShowing = false; + } +}; + +class BPMiniGame_CardMatch : public BPMiniGame { +public: + ~BPMiniGame_CardMatch(); + BPMiniGame_CardMatch(BPGame* game); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void CleanUpCards(bool force); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void CheckSelectedCards(); +private: + Texture* sfcBackground; + Texture* sfcCardBack; + + float RemovalSpeed; + + BPPList* CardTypes; + BPPList* Cards; + + bool Locked; // true to stop the player making any more moves + int NumErrors; + int TimeStarted; +}; + +#endif diff --git a/connex.cpp b/connex.cpp new file mode 100644 index 0000000..a81f814 --- /dev/null +++ b/connex.cpp @@ -0,0 +1,211 @@ +// 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 "connex.h" +#include "Minigame.h" + +BPMiniGame_Connex::BPMiniGame_Connex(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("connex", 320, 480); + sfcCard = TheGame->LoadBitmap("card_blank", 64, 64); + + Letters = new BPList(); + Objects = new BPPList(); + CompleteLetters = TimeStarted = 0; + + LastLetter = -1; + + GameTitle = "Connex"; + GameHelp = "Find and tap on boxes to connect them together. But here's the catch - you need to connect A to 1, 1 to B, B to 2, 2 to C and so on, alternating between letters and numbers. It's harder than it sounds!"; + GameHelp2 = "This game tests your ability to remember the positions of boxes as you play. That is, while you're looking for the first box (\"A\"), you need to try to remember what the other boxes are that are around A so that when you have to find them you know where to look."; + + MiniGameType = PUZZLE; + + Letters->Add("A"); + Letters->Add("1"); + Letters->Add("B"); + Letters->Add("2"); + Letters->Add("C"); + Letters->Add("3"); + Letters->Add("D"); + Letters->Add("4"); + Letters->Add("E"); + Letters->Add("5"); + Letters->Add("F"); + Letters->Add("6"); + Letters->Add("G"); + Letters->Add("7"); + Letters->Add("H"); + Letters->Add("8"); + Letters->Add("I"); + Letters->Add("9"); + Letters->Add("J"); + Letters->Add("10"); + + CompleteLetters = 0; + + BPList* shuffledletters = new BPList(); + + for (int i = 0; i < Letters->Count; ++i) { + shuffledletters->Add((*Letters)[i]); + } + + shuffledletters->Shuffle(); + + for (int i = 0; i < shuffledletters->Count; ++i) { + BPMiniGame_Connex_Object* obj = new BPMiniGame_Connex_Object(); + obj->Text = (*shuffledletters)[i]; + TheGame->AllocString(&obj->sfcText, obj->Text, XLARGE, 64, 64, CENTRED, true); + + int xpos; + int ypos; + + ypos = TheGame->DivRem(i, 4, &xpos); + + obj->X = 8 + (xpos * 80); + obj->Y = 13 + (ypos * 80); + + Objects->Add(obj); + } + + SAFE_DELETE(shuffledletters); +} + +BPMiniGame_Connex::~BPMiniGame_Connex() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcCard); + + Letters->Clear(); + SAFE_DELETE(Letters); + + Objects->Clear(); + SAFE_DELETE(Objects); +} + +void BPMiniGame_Connex::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_Connex::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(560 - floor(TimePassed * 6)); +} + +void BPMiniGame_Connex::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Objects->Count; ++i) { + BPMiniGame_Connex_Object* obj = (*Objects)[i]; + TheGame->DrawImage(sfcCard, obj->X, obj->Y); + + if ((*Letters)[LastLetter] == obj->Text) { + // this is the current box - highlight it! + TheGame->FillRectangle((*TheGame->Yellow), obj->X + 3, obj->Y + 3, 58, 58); + } + } + + for (int i = 0; i < CompleteLetters; ++i) { + BPMiniGame_Connex_Object* from; + BPMiniGame_Connex_Object* to; + + for (int n = 0; n < Objects->Count; ++n) { + if ((*Objects)[n]->Text == (*Letters)[i]) { + from = (*Objects)[n]; + break; + } + } + + for (int n = 0; n < Objects->Count; ++n) { + if ((*Objects)[n]->Text == (*Letters)[i + 1]) { + to = (*Objects)[n]; + break; + } + } + + TheGame->DrawLine(from->X + 32, from->Y + 32, to->X + 32, to->Y + 32, TheGame->ConnexGreen, 4.0f); + } + + for (int i = 0; i < Objects->Count; ++i) { + BPMiniGame_Connex_Object* obj = (*Objects)[i]; + TheGame->DrawString(obj->sfcText, BLACK, obj->X, obj->Y + 11); + } + + // no room for the timer! +// int TimePassed = TheGame->TickCount - TimeStarted; +// TheGame->DrawString(TheGame->TicksToTime(TimePassed), NORMAL, WHITE, 0, 267, 320, 40, CENTRED); +} + +void BPMiniGame_Connex::Tick() { + if (CompleteLetters == Letters->Count - 1) { + Success(); + } +} + +void BPMiniGame_Connex::OnMouseDown() { + +} + +void BPMiniGame_Connex::OnMouseMove() { + +} + +void BPMiniGame_Connex::OnMouseUp() { + if (CompleteLetters == Letters->Count - 1) { + // this game is finished! + return; + } + + if (LastLetter == -1) { + // this was their first click + LastLetter = GetLetter(TouchEvent); + + // must start with the very first letter + if (LastLetter != 0) { + MessageBox::Show("That's not the right box! Remember, you need to alternate between letters and numbers, which means you're looking to find A to begin with, then 1, then B, etc.", GameTitle); + LastLetter = -1; + } else { + TheGame->PlaySound("card_flip"); + } + + return; + } + + // if we're still here, they clicked another letter already - is it the right one? + int CurrentLetter = GetLetter(TouchEvent); + + if (CurrentLetter == LastLetter + 1) { + TheGame->PlaySound("slide"); + ++CompleteLetters; + LastLetter = CurrentLetter; + } + + if (CompleteLetters == Letters->Count - 1) { + // this game is finished! + return; + } +} + +int BPMiniGame_Connex::GetLetter(BPPoint e) { + for (int i = 0; i < Objects->Count; ++i) { + BPMiniGame_Connex_Object* obj = (*Objects)[i]; + + if (TheGame->PointOverRect(e.X, e.Y, obj->X, obj->Y, 64, 64)) { + return Letters->IndexOf(obj->Text); + } + } + + return -1; +} diff --git a/connex.h b/connex.h new file mode 100644 index 0000000..b39c5b9 --- /dev/null +++ b/connex.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef __CONNEX_H__ +#define __CONNEX_H__ + +#include "Minigame.h" + +class BPMiniGame_Connex_Object { +public: + int X; + int Y; + const char* Text; + SpriteFont* sfcText; + + BPMiniGame_Connex_Object() { + Text = NULL; + sfcText = NULL; + } + + ~BPMiniGame_Connex_Object() { + SAFE_DELETE(sfcText); + } +}; + + +class BPMiniGame_Connex : public BPMiniGame { +public: + BPMiniGame_Connex(BPGame* game); + ~BPMiniGame_Connex(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseMove(); + void OnMouseDown(); + void OnMouseUp(); + int GetLetter(BPPoint e); + +protected: + Texture* sfcBackground; + Texture* sfcCard; + + BPList* Letters; + BPPList* Objects; + + int CompleteLetters; + int LastLetter; + int TimeStarted; +}; + +#endif diff --git a/cupsnballs.cpp b/cupsnballs.cpp new file mode 100644 index 0000000..ff07553 --- /dev/null +++ b/cupsnballs.cpp @@ -0,0 +1,289 @@ +// 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 "cupsnballs.h" +#include "Minigame.h" + +BPMiniGame_CupsNBalls::BPMiniGame_CupsNBalls(BPGame* game) : BPMiniGame(game) { + GameTitle = "Cups 'n' Balls"; + GameHelp = "Pull back the string on your catapult and fire balls into one of the green cups - but don't get them into the red cup or you'll lose points!"; + GameHelp2 = "This is one of the few games where your score isn't affected by how long you take, so pull back the string on the catapult and wait for the cups to move to where you want them to be. Then when you're ready, fire off a shot - or, better yet, fire off lots of shots!"; + + MiniGameType = ACTION; + + sfcBackground = TheGame->LoadBitmap("clouds", 320, 480); + sfcBall = TheGame->LoadBitmap("ball", 32, 32); + sfcPuff = TheGame->LoadBitmap("puff", 64, 64); + sfcSlingshot = TheGame->LoadBitmap("slingshot", 80, 90); + + sfcGreenCup = TheGame->LoadBitmap("green_cup", 86, 44); + sfcRedCup = TheGame->LoadBitmap("red_cup", 86, 44); + + BallHalfWidth = sfcBall->Width / 2; + BallHalfHeight = sfcBall->Height / 2; + + CupHalfWidth = sfcGreenCup->Width / 2; + CupHalfHeight = sfcGreenCup->Height / 2; + + SlingshotHalfWidth = sfcSlingshot->Width / 2; + + SlingshotX = 123; + SlingshotY = 296; + + // how low down the cup front picture is, in pixels; this gives us better collision detection + CupFrontOffset = 19; + Ammo = 20; + Score = 0; + + sfcScore = NULL; + sfcAmmo = NULL; + SetScore(); + SetAmmo(); + + CreateCup(0, 0); + CreateCup(90, 0); + CreateCup(180, 0); + CreateCup(270, 1); +} + +BPMiniGame_CupsNBalls::~BPMiniGame_CupsNBalls() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBall); + SAFE_DELETE(sfcPuff); + SAFE_DELETE(sfcSlingshot); + SAFE_DELETE(sfcGreenCup); + SAFE_DELETE(sfcRedCup); + + SAFE_DELETE(sfcScore); + SAFE_DELETE(sfcAmmo); + + Balls.Clear(); + Cups.Clear(); +} + +void BPMiniGame_CupsNBalls::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_CupsNBalls::GetWeight() { + return MinMax(round(Score * 31)); +} + +void BPMiniGame_CupsNBalls::Render() { + sfcBackground->Draw(0, 0); + + for (int i = 0; i < Cups.Count; ++i) { + BPMiniGame_CupsNBalls_Cup* cup = Cups[i]; + if (cup->Type == 0) { + sfcGreenCup->Draw(Round(cup->X), Round(cup->Y)); + } else { + sfcRedCup->Draw(Round(cup->X), Round(cup->Y)); + } + } + + for (int i = 0; i < Balls.Count; ++i) { + BPMiniGame_CupsNBalls_Ball* ball = Balls[i]; + + if (ball->HitTime == 0) { + if (ball->YSpeed <= 0) { + sfcBall->Draw(Round(ball->X), Round(ball->Y)); + } + } else { + sfcPuff->Draw(Round(ball->X - 10), Round(ball->Y - 5)); + } + } + + for (int i = 0; i < Balls.Count; ++i) { + BPMiniGame_CupsNBalls_Ball* ball = Balls[i]; + + if (ball->YSpeed >= 0) { + sfcBall->Draw(Round(ball->X), Round(ball->Y)); + } + } + + sfcSlingshot->Draw(SlingshotX, SlingshotY); + + if (IsStringPulled && MouseMoved) { + TheGame->DrawLine(SlingshotX + 5, SlingshotY, LastMousePos.X, LastMousePos.Y, TheGame->White, 3.0f); + TheGame->DrawLine(SlingshotX - 5 + sfcSlingshot->Width, SlingshotY, LastMousePos.X, LastMousePos.Y, TheGame->White, 3.0f); + + sfcBall->Draw(LastMousePos.X - BallHalfWidth, LastMousePos.Y - BallHalfHeight); + } else { + TheGame->DrawLine(SlingshotX + 5, SlingshotY, SlingshotX - 5 + sfcSlingshot->Width, SlingshotY, TheGame->White, 3.0f); + } + + TheGame->DrawString(sfcAmmo, (*TheGame->Black), 10, 380); + TheGame->DrawString(sfcScore, (*TheGame->Black), 182, 380); +} + +void BPMiniGame_CupsNBalls::Tick() { + if (Ammo == 0 && Balls.Count == 0) { + Success(); + } + + for (int i = Cups.Count - 1; i >= 0; --i) { + BPMiniGame_CupsNBalls_Cup* cup = Cups[i]; + + cup->SinVal += 20.0 * TheGame->ElapsedSeconds; + + cup->X = MiniGameHalfWidth + (sin(cup->SinVal * M_PI / 180) * 110) - CupHalfWidth; + cup->Y = MiniGameHalfHeight - 100 + (cos(cup->SinVal * M_PI / 180) * 50) - CupHalfHeight; + + for (int j = Balls.Count - 1; j >= 0; --j) { + BPMiniGame_CupsNBalls_Ball* ball = Balls[j]; + if (ball->HitTime == 0) { + // only balls (rather than ex-balls) can collide + if (ball->YSpeed <= 0) { + // only falling balls can collide + + if (TheGame->RectOverRect(Round(ball->X), Round(ball->Y), sfcBall->Width, sfcBall->Height, Round(cup->X) + 20, Round(cup->Y) + CupFrontOffset - 10, sfcGreenCup->Width - 40, 1)) { + ball->HitTime = TheGame->TickCount; + + if (cup->Type == 0) { + TheGame->PlaySound("balloon_pop"); + Score += 1; + } else { + TheGame->PlaySound("wrong"); + Score -= 1; + } + + SetScore(); + + continue; + } + } + } else { + if (ball->HitTime + 200 < TheGame->TickCount) { + Balls.RemoveAt(j); + } + } + } + } + + for (int i = Balls.Count - 1; i >= 0; --i) { + BPMiniGame_CupsNBalls_Ball* ball = Balls[i]; + + if (ball->HitTime == 0) { + // only move if still "alive" + ball->X -= ball->XSpeed * TheGame->ElapsedSeconds; + ball->Y -= ball->YSpeed * TheGame->ElapsedSeconds; + ball->YSpeed -= 1600.0f * TheGame->ElapsedSeconds; + + if (ball->Y > 490) Balls.RemoveAt(i); + } + } +} + +void BPMiniGame_CupsNBalls::OnMouseDown() { + MouseMoved = false; + LastMousePos = TouchEvent; + + if (Ammo > 0) { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, SlingshotX - 10, SlingshotY - 10, sfcSlingshot->Width + 20, sfcSlingshot->Height + 20)) { + IsStringPulled = true; + } + } +} + +void BPMiniGame_CupsNBalls::OnMouseMove() { + if (LastMousePos != TouchEvent) { + MouseMoved = true; + } + + LastMousePos = TouchEvent; +} + +void BPMiniGame_CupsNBalls::OnMouseUp() { + if (IsStringPulled && MouseMoved) { + IsStringPulled = false; + + // don't allow people to pull the string up then fire down into the cups + if (TouchEvent.Y < SlingshotY) return; + + FireBullet(); + } else { + IsStringPulled = false; + } +} + +void BPMiniGame_CupsNBalls::FireBullet() { + TheGame->PlaySound("swoosh_long"); + + // in Marathon Mode, ammo is infinite + if (!MarathonMode) { + --Ammo; + SetAmmo(); + } + + BPMiniGame_CupsNBalls_Ball* ball = new BPMiniGame_CupsNBalls_Ball(); + ball->X = TouchEvent.X - BallHalfWidth; + ball->Y = TouchEvent.Y - BallHalfHeight; + + double xdiff = TouchEvent.X - (SlingshotX + SlingshotHalfWidth); + double ydiff = TouchEvent.Y - SlingshotY; + double totaldiff = (fabs(xdiff) + fabs(ydiff)) / 4; + double angle = atan(ydiff / xdiff) / (M_PI / 180); + + if (xdiff < 0) angle += 180; + if (xdiff >= 0 && ydiff < 0) angle += 360; + + ball->XSpeed = (float)(cos(angle * M_PI / 180) * totaldiff) * 40.0f; + ball->YSpeed = (float)(sin(angle * M_PI / 180) * totaldiff) * 35.0f; + + Balls.Add(ball); +} + +void BPMiniGame_CupsNBalls::CreateCup(int sinval, int type) { + BPMiniGame_CupsNBalls_Cup* cup = new BPMiniGame_CupsNBalls_Cup(); + + cup->Type = type; + cup->SinVal = sinval; + + Cups.Add(cup); +} + +void BPMiniGame_CupsNBalls::SetMarathon() { + MarathonMode = true; + SAFE_DELETE(GameHelp); + GameHelp = "Pull back the string on your catapult and fire balls into one of the green cups - but don't get them into the red cup or you'll lose points!"; + Ammo = 999; +} + +void BPMiniGame_CupsNBalls::SetScore() { + if (Score > 0) { + ostringstream ScoreStr; + ScoreStr << "Score: " << TheGame->SeparateThousands(Score); + TheGame->AllocString(&sfcScore, ScoreStr.str().c_str(), NORMAL, 128, 50, RIGHT); + } else { + TheGame->AllocString(&sfcScore, "Score: 0", NORMAL, 128, 50, RIGHT); + Score = 0; // don't let it go below zero + } + + +} + +void BPMiniGame_CupsNBalls::SetAmmo() { + if (Ammo > 0) { + ostringstream AmmoStr; + AmmoStr << "Ammo: " << Ammo; + TheGame->AllocString(&sfcAmmo, AmmoStr.str().c_str(), NORMAL, 128, 50, LEFT); + } else { + TheGame->AllocString(&sfcAmmo, "Ammo: 0", NORMAL, 128, 50, LEFT); + } + + +} diff --git a/cupsnballs.h b/cupsnballs.h new file mode 100644 index 0000000..5baa8d9 --- /dev/null +++ b/cupsnballs.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef __CUPSNBALLS_H__ +#define __CUPSNBALLS_H__ + +#include "Minigame.h" + +class BPMiniGame_CupsNBalls_Cup { +public: + double X; + double Y; + int Type; // 0 for green, 1 for red + double SinVal; +}; + +class BPMiniGame_CupsNBalls_Ball { +public: + float X; + float Y; + float XSpeed; + float YSpeed; + bool GoingUp; + int HitTime; + + BPMiniGame_CupsNBalls_Ball() { + GoingUp = true; + HitTime = X = Y = 0; + XSpeed = YSpeed = 0; + } +}; + +class BPMiniGame_CupsNBalls : public BPMiniGame { +public: + bool MouseMoved; // set to true if mouse has moved after click and before release, to stop quick tapping to fire + bool IsStringPulled; + + Texture* sfcBackground; + Texture* sfcBall; + Texture* sfcPuff; + Texture* sfcSlingshot; + + Texture* sfcGreenCup; + Texture* sfcRedCup; + + BPPoint LastMousePos; + + int Score; + int Ammo; + + SpriteFont* sfcScore; + SpriteFont* sfcAmmo; + + int CupFrontOffset; + + short SlingshotX; + short SlingshotY; + + int BallHalfWidth; + int BallHalfHeight; + + int CupHalfWidth; + int CupHalfHeight; + + int SlingshotHalfWidth; + + BPPList Cups; + BPPList Balls; + + int TimeStarted; + + BPMiniGame_CupsNBalls(BPGame* game); + ~BPMiniGame_CupsNBalls(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void FireBullet(); + void CreateCup(int sinval, int type); + void SetMarathon(); + void SetScore(); + void SetAmmo(); +}; + +#endif diff --git a/debian/brainparty.docs b/debian/brainparty.docs new file mode 100644 index 0000000..39463cb --- /dev/null +++ b/debian/brainparty.docs @@ -0,0 +1,3 @@ +README +CREDITS +COPYING diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..874d00f --- /dev/null +++ b/debian/changelog @@ -0,0 +1,33 @@ +brainparty (0.5.91-2) fremantle; urgency=low + + * Merge OGG music support patch from mikkov + * Depend on brainparty-data >= 0.5.91 which includes the OGG music + * Remove "-g" (debugging) from compiler flags and add -O2 (optimize) + + -- Thomas Perl Wed, 21 Apr 2010 19:59:37 +0200 + +brainparty (0.5.91-1) fremantle; urgency=low + + * New upstream version "brainparty0.591" + + -- Thomas Perl Wed, 21 Apr 2010 02:08:36 +0200 + +brainparty (0.5-3) fremantle; urgency=low + + * Add call to SDL_GLES_Quit() at end of application + (http://talk.maemo.org/showpost.php?p=607035&postcount=71) + + -- Thomas Perl Mon, 12 Apr 2010 22:22:25 +0200 + +brainparty (0.5-2) fremantle; urgency=low + + * Added bugtracker field to make the Extras promoter happy + + -- Thomas Perl Sat, 3 Apr 2010 21:03:14 +0200 + +brainparty (0.5-1) fremantle; urgency=low + + * Initial release. + + -- Thomas Perl Sat, 3 Apr 2010 18:27:17 +0200 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..7d33aed --- /dev/null +++ b/debian/control @@ -0,0 +1,127 @@ +Source: brainparty +Section: user/games +Priority: extra +Maintainer: Thomas Perl +Build-Depends: debhelper (>= 5), libsdl-gles1.2-dev, libsdl-ttf2.0-dev, libsdl-mixeroggwav1.2-dev, libsdl-image1.2-dev, libsdl-gfx1.2-dev, libsdl1.2-dev, libgles1-sgx-img-dev +Standards-Version: 3.7.2 +XSB-Homepage: http://tuxradar.com/brainparty +XSBC-Bugtracker: mailto:thp@thpinfo.com + +Package: brainparty +Architecture: armel +Depends: ${shlibs:Depends}, ${misc:Depends}, brainparty-data (>= 0.5.91), libgles1-sgx-img, libvorbisidec1 +Description: A puzzle game to tease your brain + Brain Party is a fun, free puzzle game for all the + family that's made up of 36 minigames designed to + push your brain to its limits by testing memory, logic, + mathematics, reaction time and more! +XB-Maemo-Display-Name: Brain Party +XB-Maemo-Icon-26: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAKOWlDQ1BQaG90 + b3Nob3AgSUNDIHByb2ZpbGUAAHjanZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7 + k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZEsRAUVLAHJAgoMRhFVCxv + RtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs + AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnp + fIHomAARm7M5GSwRF4g4JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKj + mNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR3xKxRoowlSviN+LYVA4z + AwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd + li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0 + NDQzMv2qUP91829K3NtFehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI + 3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGXwzISF/QP/U+Hv6Gvvmck + Pu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF + hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygK + ESDR+8Vd/6NvvvgwIH554SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TP + EqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJVgMWSASpgA+yQB7YBApB + MdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB + 5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeu + QIPQXWgMmoZ+h97BCEyCqbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+F + O+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiEj6xHipAKpAFpRbqRPuQm + MorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I + 1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+ + TBvmHGYQM46Zw2Kx8lh9rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgo + HA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhGfDf+On4Cv0CQJmgT7Agh + hCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp + B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkji + uSReUlPSSXK1ZK5kheQJyeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU + 6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJCYVE2UxopFykTVAxVm+pF + TaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln + yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf + 4aLCzFLqUtulrKVFS48vvacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm + 7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0CvpvfRZVUVVT1Whar3qgOqC + mrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L + Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6i + Xo3edX1Y31Kfq79Pf9AAbWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtr + GEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0zllmN2S1zsrm7+QbzLvMX + y/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW + ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7 + A/ajDqoOTIcGh8eO6o5sxybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4e + rkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6eO7yHPFS8mJ5NXvNelt5 + r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw + YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX + 8LLw0QjjiHUR1yIVIrmRXVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUK + q1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNmWS6svaxnbEd2OXuaY8cp + 40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl + xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BH + uEU4lmmfWZP5Jiss60S2dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNa + V78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8V5vDN3cXKBVsLBjf4rGl + pVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za + Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJ + e4V7Ryt9K7uqNKp2Vr2vTqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5w + D9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqbPhziHRo9HHS4t9mqufmI + 4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/ + 0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69Azh + TMGZT2dzz86dSz83cz7h/HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9td + PnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavrXTesb3QPLh88M+QwdP6m + 681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS + fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnaf + vvF05dOJZ+nPFmYKf5X+tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aue + uYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/6H7o/ujz8cGn1E+f/gUD + mPP8kcBa2wAAAwBQTFRFAAAAAAIABQcMCw0KDg4TGBYZGBcfGx0rIiM1JyUp + JicuKChCLi8wLi9BLjQ2MDNAOzMoLTg+NjZJOzZDPDhUOjs+QUBFRj9HPkJE + QkBSR0MyQEBvR0NCQ0ZDRUNoQ0hQRUlLSEZlT0lDT0pEVEtQUE1MSk9ST05S + VU5hWVBFUVBgVVRaT1N4UVZZXVBzVFZTVVtrWV5hXVqBX11iY1xkXF9cYF1t + Y1x4XV91UmVvZmNWXGSCYmVqX2SVcWVmbWtpaWqDcWqNZ3B0d21wb29+dXN3 + e3Rsf3Z8fHeKent7hXeEhHx2boGMeX+Qg4KUkoKKhIeHhYS2hYajlIpskoqE + jY6MkY6jl5J+m46bh5SckJGwk5eVnpeQopWsnpyCn56Vnp6in6Cdl6OqraCO + mKO9rJ+snqK+oqWop6iyrqa+mq65sayqp6+vqa29obC3ubGqsLPLsba2uban + sriuwLedurvFwrvHwL68sMLOwL7YzsCb8LuOucbGwsfKwMjdwcnSu8vRtszY + x8jSv8rYy8fZycrHxsvO2se80sjOzs3E0dHCy9HUydPHxNPa2s/C0NHb0dPQ + 2tHN19Dg1NTL4NK/5NS79tCw3te36tO849fU2dni1Nvd0tvj39vG99THy97m + 3NvS49jf2NzZ7djH5trN/92i7d/G4N7669/S5+HM697e3uLf3eLl+N7B2ePt + 3eTZ5uTI8eLC6OLh6+Lb4+XV5eTb9eSv8uLQ+eHK/9/S5+Tp6ePy5Ofj4uvS + 3err/eLh/+PU/+XC8Obs/+e21u30/uTb/+bJ+ebb9+jO6Orn/ubP6On0/+i9 + 5uvt8enh/unE/OnK7erv9erc+OrW7ezj9+nm7+3e5e736e/k8uzr/+rZ/OzT + 6+/r/+67/u7B/u7O6fPu7fXj5vX58/H88fPv/PHj//He7vT3/fTM3/j9+fXS + 9vP39PXs/ve+9/bf+vTz//Xb7fva9vj1//fv/vzL6P7//vrr/fn+8P7++fz4 + 9/7z9/3///z6/v30+//u/f/8KNNXagAAAAF0Uk5TAEDm2GYAAAABYktHRACI + BR1IAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH2gQUFhAJfj+G1gAA + BPhJREFUSMfVlm1YU2UYx3cUMdQoX1AsFpOaM4iAdEpFCWokWYYkJC6TaSol + KdgLWOIUg9TjYrwk4TBIF6KbEO82hjCEyTnzOCaTtykgGwzaEEOFbcf5dA5S + qDnwq/8v51zn+v+e+76e577v51AoT4aee2ORn5/fnFmPaacv/a7kamtrq+KX + dfMewz59XX2bGkVlKIpKr7ZuGhOZl37414pKUgQhk7Zcfm8U87LIL1dm//79 + 4da6OgUhdT0qkylufMpOXvvKo+y+cbjFcqn0TJ2MTIdcnxQq+2NwcMAU9H// + Grxfq9EL6tGHdVSpaRoY/GHug/Y5iRaDqllz6szwuuhIkNp0TXvTZb14wf3+ + 10QGpb65uSBJSjik0nuATEq8EjpeoNNhVRr9shH/IvnfV8rkyuasBnV9fQs6 + HIFcnthgtEWANXYgDSr98v/y0V6XI02YUnkgBYZhnqBUPYyUCrJ4vMSkpFNV + zQiiQjTDMaZltGOYSoU1chKEGHb2bCZ3D79eKlVL4AQ+RnzIhGNyryjlKpVS + 6UT6ZweZETnB6zgZAAc6eBe3o4cbj8oECRguik0QGY0d1XsxMoJcH+5KAK47 + G4rFYgTL4AATiF3i5RWwhW/k8i7G41qWz3xmcJTO2JPJbVJhYrHySAgZIVJF + ABgcmgsAz8uLydwazeb3fMESgWAPJjMgPmKrsad6w1eIXFJUfuwDMidvTYGE + ADZgwBTgPt99cTSbGXo7JkIXT2cyPVjRwUz+7ept0cqyvPJrIdNIAFqpzBMJ + xXEiIPFwc2O8E8CgLWmEjyFhNHd3r0CfudSI29W7isVlwqbIyfe2aVwIVl5Y + EJ8BRAw6nc5wZtBe5ycJhKG0uaTcHUmgSChu32z770HYriovKD60G9fOp77o + 4sJwdng/MxXlbXOg0ag0uosj18iNFWddW2s7ctS2b50oOrZTDiLsqU5OTg4O + e7jn1Ty+z1RHRyr1WX9giks+Ife1ub+YoOfjivcfMBtZU+3tpzhFZSZcQgUJ + fPcphBYjICvptzxv6MFyhSaF5Rzl6fDY0EC2EItFcgQX4URsS2BghA4ID+bx + qdDD/QBNWF5QvB/DjTgQ7dLe7O/vN8EcHW405sYfNITPhB7RcpBrbS3M2bcv + hiNSFQozMKUY5sTs3XvgZNeHdtAjexqaVdtdU1H6c/IRXmHhRWFickpadu2Z + ylv+4yErU2DcS201iguVpTnb/Ves9v84LZ9ojgvdayZYnxvQvO7eob7ZAUEu + bYqWSrStO2jiaGPJZvr28xXnzsk66dDXXX/KpIr0V21GH2QQ9G4J0dRdP21U + KxTSko2ToLFG36Twv079mH/6dF12flpqV4rnWP4FiRb95+kDlzo7GwyGb8ss + 5s+eGdXv23fTHBQE9LgO0+vNwuXIdbB7xij+hdo+g2hGnIWYghITbgGePkB3 + d+dkq/6ZSeYB8OYLA/0kgEt4SPiEb8DdGyFWgU/UJ/sOTVxl6es3aZHYYBZL + ZOdmqMpum27FzzheV1fhbQPfGTQRQFTw6jCTp11aaWX3Cms7pJYqjj8129Q3 + BEh4Ei0IoXzUW9Oy2toRpPbeepviazGRgASQiqNMyVbccrN6v21aOp6yeYAA + gBYBOA6A3pny8o5lttZLCaI8feTunUEwBAxaLGAhUSxjVEfg+rAtbDYrmE08 + 1kcteIyLF7pfT8J/yD8/fdVPw2I/AwAAAABJRU5ErkJggg== + ==== + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..3b831fb --- /dev/null +++ b/debian/copyright @@ -0,0 +1,71 @@ +This package was debianized by Thomas Perl on +Sat, 3 Apr 2010 18:27:17 +0200. + +It was downloaded from . + +Source tarball: + + http://www.tuxradar.com/files/brainparty/brainparty0.591.tar.gz + +Icon by: infected69 on talk.maemo.org + +Upstream Authors: + + Programming + =========== + Paul Hudson + + Art + === + Paul Hudson + Scott Ewart + + Music + ===== + electricity.ogg - "Electricity" by Alexander Blu; CC-BY-SA 3.0. + morningwave.ogg - "RedQuadro - Morning Wave" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + highway.ogg - "Max Loginov - Highway" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + nevertoolate.ogg - "J-Sun Never Too Late" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + chekolake.ogg - "Al. Galizdra - Cheko Lake" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + starmarch.ogg - "Newalks - StarMarch" by Tunguska Electronic Music Society; CC-BY-ND 3.0. + + Music from Soundsnap + ==================== + ambient.ogg + brainrace.ogg + lively.ogg + professor.ogg + results.ogg + theme.ogg + + Soundsnap's licence allows all use, commercial and non-commercial, as long as you + do not "Resell or distribute the sounds 'as they are'. For example, you cannot download + and sell them as part of a CD library." + +Copyright: + + Copyright (C) 2010 Paul Hudson (http://www.tuxradar.com/brainparty) + +License: + + 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. + + +The Debian packaging is (C) 2010, Thomas Perl and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..ca882bb --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a1f2ce3 --- /dev/null +++ b/debian/rules @@ -0,0 +1,67 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + touch configure-stamp + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + $(MAKE) + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + -$(MAKE) clean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + install -D -m755 brainparty $(CURDIR)/debian/brainparty/opt/brainparty/brainparty + install -D -m644 brainparty.desktop $(CURDIR)/debian/brainparty/usr/share/applications/hildon/brainparty.desktop + install -D -m644 brainparty.png $(CURDIR)/debian/brainparty/usr/share/icons/hicolor/scalable/hildon/brainparty.png + +binary-indep: build install + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/diceoff.cpp b/diceoff.cpp new file mode 100644 index 0000000..1eca54c --- /dev/null +++ b/diceoff.cpp @@ -0,0 +1,328 @@ +// 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 "diceoff.h" +#include "Minigame.h" + +BPMiniGame_DiceOff::BPMiniGame_DiceOff(BPGame* game) : BPMiniGame(game) { + TheGame = game; + + State = WAITING; + OurTurn = true; + FirstAITurn = true; + + PlaySound = false; + + NeutralColor = Colour(0.7f, 0.7f, 0.7f, 1.0f); + PlayerColor = Colour(0.0f, 0.0f, 1.0f, 1.0f); + AIColor = Colour(1.0f, 0.0f, 0.0f, 1.0f); + + DicePics.Add(NULL); + DicePics.Add(TheGame->LoadBitmap("die_1", 50, 50)); + DicePics.Add(TheGame->LoadBitmap("die_2", 50, 50)); + DicePics.Add(TheGame->LoadBitmap("die_3", 50, 50)); + DicePics.Add(TheGame->LoadBitmap("die_4", 50, 50)); + + for (int i = 0; i < NumRows; ++i) { + for (int j = 0; j < NumCols; ++j) { + BPMiniGame_DiceOff_Die* die = new BPMiniGame_DiceOff_Die(); + die->Pos = BPPoint(10 + (j * DiceSize), 8 + (i * DiceSize)); + die->Value = 1; + die->Index = Dice.Count; + die->X = j; + die->Y = i; + die->Col = NeutralColor; + die->NumNeighbours = CountNeighbours(j, i); + Dice.Add(die); + } + } + + UpdateScore(); + + GameTitle = "Dice Off"; + GameHelp = "Press a dice to add one to its score. When it goes higher than the number of neighbours it has, it will overflow, taking over each of its neighbours and adding one to them. Can you take over all your opponent's dice?"; + GameHelp2 = "Dice in the corners have just two neighbours, so as soon as they reach three they will overflow and add one to each neighbour, taking control of them at the same time."; + + MiniGameType = ACTION; +} + +BPMiniGame_DiceOff::~BPMiniGame_DiceOff() { + DicePics.Clear(); + Dice.Clear(); +} + +void BPMiniGame_DiceOff::Start() { + +} + +int BPMiniGame_DiceOff::GetWeight() { + return 500; +} + +void BPMiniGame_DiceOff::Tick() { + switch (State) { + case SUCCESS: + if (LastStateChange + 1000 < TheGame->TickCount) { + Success(); + } + + break; + + case FAILURE: + if (LastStateChange + 1000 < TheGame->TickCount) { + Failure(); + } + + break; + + case WAITING: + // do nothing! + break; + + case THINKING: + // give a short delay, then make the AI act + if (LastStateChange + AIThinkSpeed < TheGame->TickCount) { + AITurn(); + } + + break; + + case CHANGING: + // wait before changing more pieces + if (LastStateChange + ChainSpeed < TheGame->TickCount) { + PlaySound = false; + + if (CheckChangeList.Count == 0 || ChangeStartTime + 4000 < TheGame->TickCount) { // force kill changes that take too long + // no more moves to make + if (OurTurn) { + OurTurn = false; + SetGameState(THINKING); + } else { + OurTurn = true; + SetGameState(WAITING); + } + + return; + } + + BPList ToChange; + + for (int i = 0; i < CheckChangeList.Count; ++i) { + ToChange.Add(CheckChangeList[i]); + } + + CheckChangeList.Clear(); + + for (int i = 0; i < ToChange.Count; ++i) { + BPMiniGame_DiceOff_Die* die = ToChange[i]; + + if (OurTurn) { + BumpDie(die, OWNER_PLAYER); + } else { + BumpDie(die, OWNER_AI); + } + } + + LastStateChange = TheGame->TickCount; + + if (PlaySound) { + // something interesting changed; we should play a sound! + TheGame->PlaySound("whack"); + } + + UpdateScore(); + } + } +} + +int BPMiniGame_DiceOff::CountNeighbours(int x, int y) { + int result = 0; + if (x > 0) ++result; // one to the left + if (x < NumCols - 1) ++result; // one to the right + if (y > 0) ++result; // one above + if (y < NumRows - 1) ++result; // one below + + return result; +} + +void BPMiniGame_DiceOff::Render() { + TheGame->Clear(TheGame->Black); + + for (int i = 0; i < Dice.Count; ++i) { + BPMiniGame_DiceOff_Die* die = Dice[i]; + + if (die->LastStateChange + ChangeSpeed > TheGame->TickCount) { + // this needs to be animated! + float diff = (TheGame->TickCount - die->LastStateChange) / (float)ChangeSpeed; + if (diff > 1.0f) diff = 1.0f; + + Colour drawcol = TheGame->ColourSmoothStep((*TheGame->White), die->Col, diff); + + DicePics[die->Value]->Draw(die->Pos.X + 25, die->Pos.Y + 25, 0.0f, 1.0f, drawcol); + } else { + // draw plain-old die + DicePics[die->Value]->Draw(die->Pos.X + 25, die->Pos.Y + 25, 0.0f, 1.0f, die->Col); + } + } +} + +void BPMiniGame_DiceOff::OnMouseDown() { + if (!OurTurn) return; + if (State != WAITING) return; + + for (int i = 0; i < Dice.Count; ++i) { + BPMiniGame_DiceOff_Die* die = Dice[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, die->Pos.X, die->Pos.Y, DiceSize, DiceSize)) { + if (die->Owner != OWNER_AI) { + CheckChangeList.Add(die); + SetGameState(CHANGING); + ChangeStartTime = TheGame->TickCount; + break; + } + } + } +} + +void BPMiniGame_DiceOff::OnMouseUp() { + +} + +void BPMiniGame_DiceOff::OnMouseMove() { + +} + +void BPMiniGame_DiceOff::BumpDie(BPMiniGame_DiceOff_Die* die, Ownership newowner) { + ++die->Value; + die->LastStateChange = TheGame->TickCount; + die->Owner = newowner; + + if (newowner == OWNER_PLAYER) { + die->Col = PlayerColor; + } else { + die->Col = AIColor; + } + + PlaySound = true; + + if (die->Value > die->NumNeighbours) { + // we need to split this! + die->Value = 1; + + if (die->X > 0) CheckChangeList.Add(Dice[die->Index - 1]); + if (die->X < NumCols - 1) CheckChangeList.Add(Dice[die->Index + 1]); + if (die->Y > 0) CheckChangeList.Add(Dice[die->Index - NumCols]); + if (die->Y < NumRows - 1) CheckChangeList.Add(Dice[die->Index + NumCols]); + } +} + +void BPMiniGame_DiceOff::AITurn() { + // this isn't a particularly smart AI, but it's good enough to provide a challenge to all but the best players + + OurTurn = false; + + BPList bestdice; + int bestscore = 0; + + for (int i = 0; i < Dice.Count; ++i) { + AIClosedList.Clear(); + + BPMiniGame_DiceOff_Die* die = Dice[i]; + if (die->Owner == OWNER_PLAYER) continue; // we can't move their pieces + AICheck(die); + + if (AIClosedList.Count > bestscore) { + // this is a better score than what we had before - clear all the options and use this one + bestscore = AIClosedList.Count; + bestdice.Clear(); + bestdice.Add(die); + } else if (AIClosedList.Count == bestscore) { + // this move is as good as our previous move + bestdice.Add(die); + } + } + + if (bestdice.Count > 0) { + BPMiniGame_DiceOff_Die* die = bestdice[TheGame->RandomRange(0, bestdice.Count - 1)]; + + if (FirstAITurn) { + // IF the player started in a corner + // AND if the AI chooses to start next to them + // THEN the player can win in one turn just by bumping their corner piece one more + // SO: stop the AI using an edge tile on their first turn + + while (die->NumNeighbours < 4) { + die = bestdice[TheGame->RandomRange(0, bestdice.Count - 1)]; + } + } + + CheckChangeList.Add(die); + SetGameState(CHANGING); + FirstAITurn = false; + ChangeStartTime = TheGame->TickCount; + } +} + +void BPMiniGame_DiceOff::AICheck(BPMiniGame_DiceOff_Die* die) { + if (AIClosedList.Contains(die)) return; + + AIClosedList.Add(die); + + if (die->Value + 1 > die->NumNeighbours) { + // this die would break if we bumped it! + if (die->X > 0) AICheck(Dice[die->Index - 1]); + if (die->X < NumCols - 1) AICheck(Dice[die->Index + 1]); + if (die->Y > 0) AICheck(Dice[die->Index - NumCols]); + if (die->Y < NumRows - 1) AICheck(Dice[die->Index + NumCols]); + } +} + +void BPMiniGame_DiceOff::SetGameState(MiniGameStates state) { + // if we've already won/lost, bail out + if (State == FAILURE || State == SUCCESS) return; + + State = state; + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_DiceOff::UpdateScore() { + int playerscore = 0; + int aiscore = 0; + + for (int i = 0; i < Dice.Count; ++i) { + BPMiniGame_DiceOff_Die* die = Dice[i]; + + if (die->Owner == OWNER_PLAYER) { + ++playerscore; + } else if (die->Owner == OWNER_AI) { + ++aiscore; + } + } + +// TheGame->AllocString(&sfcScore, [NSString stringWithFormat:"%d:%d", playerscore, aiscore], XLARGE, 320, 64, CENTRED, false); + + // if we're both low on points, this is the start of the game + if (playerscore <= 1 && aiscore <= 1) return; + + if (playerscore == 0 && State != FAILURE) { + SetGameState(FAILURE); + return; + } else if (aiscore == 0 && State != SUCCESS) { + SetGameState(SUCCESS); + return; + } +} diff --git a/diceoff.h b/diceoff.h new file mode 100644 index 0000000..04e2b2f --- /dev/null +++ b/diceoff.h @@ -0,0 +1,96 @@ +// 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. + +#ifndef __DICEOFF_H__ +#define __DICEOFF_H__ + +#include "Minigame.h" + +enum Ownership { OWNER_NONE, OWNER_PLAYER, OWNER_AI }; + +class BPMiniGame_DiceOff_Die { +public: + int Value; + int NumNeighbours; + + Ownership Owner; + BPPoint Pos; + + int Index; + int X; + int Y; + + Colour Col; + int LastStateChange; + + BPMiniGame_DiceOff_Die() { + Owner = OWNER_NONE; + LastStateChange = -5000; + } +}; + + + +class BPMiniGame_DiceOff : public BPMiniGame { + static const int NumRows = 8; + static const int NumCols = 6; + static const int DiceSize = 50; + static const int ChainSpeed = 150; + static const int ChangeSpeed = 300; + static const int AIThinkSpeed = 500; + + bool OurTurn; + bool FirstAITurn; // used to stop the AI from choosing edge squares in its first turn, as this can be fatal if they player started next to them! + + Colour NeutralColor; + Colour PlayerColor; + Colour AIColor; + + BPPList DicePics; + + BPPList Dice; + BPList AIClosedList; // this must be a BPList otherwise we delete dice in the Dice list! + BPList CheckChangeList; // this must be a BPList otherwise we delete dice in the Dice list! + + bool PlaySound; + + int ChangeStartTime; // allows us to kill changes if they enter a loop + int LastStateChange; + MiniGameStates State; + +public: + BPMiniGame_DiceOff(BPGame* game); + ~BPMiniGame_DiceOff(); + + void Start(); + int GetWeight(); + void Tick(); + void Render(); + + void OnMouseDown(); + void OnMouseUp(); + void OnMouseMove(); + + int CountNeighbours(int x, int y); + void BumpDie(BPMiniGame_DiceOff_Die* die, Ownership newowner); + void AITurn(); + void AICheck(BPMiniGame_DiceOff_Die* die); + void SetGameState(MiniGameStates state); + void UpdateScore(); +}; + +#endif diff --git a/flashcounting.cpp b/flashcounting.cpp new file mode 100644 index 0000000..84590a2 --- /dev/null +++ b/flashcounting.cpp @@ -0,0 +1,306 @@ +// 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 "flashcounting.h" +#include "Minigame.h" + +BPMiniGame_FlashCounting::BPMiniGame_FlashCounting(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("flashcounting", 320, 480); + CurrentLevel = 0; + + Balls = new BPPList(); + BallPics = new BPPList(); + BallIDs = new BPList(); + Directions = new BPList(); + + Answer = 0; + BallDirCount = 0; // used to assign random directions to balls + + LastStateChange = -1; + GameState = WAITING; + + TimeStarted = NumWrong = NumTries = 0; + + GameTitle = "Flash Counting"; + GameHelp = "I'm going to fill up your screen with more and more gems. Can you count them all quickly, then tell me which gem you can see most of?"; + GameHelp2 = "This is a game that relies on your ability to count without thinking too much. Once you've counted how many of each gem there are, look at the list of gems at the top and tap the one there is most of."; + + MiniGameType = LIVELY; + + BallPics->Add(TheGame->LoadBitmap("gem1", 64, 64)); + BallPics->Add(TheGame->LoadBitmap("gem2", 64, 64)); + BallPics->Add(TheGame->LoadBitmap("gem3", 64, 64)); + BallPics->Add(TheGame->LoadBitmap("gem4", 64, 64)); + BallPics->Add(TheGame->LoadBitmap("gem5", 64, 64)); + + for (int i = 0; i < BallPics->Count; ++i) { + BallIDs->Add(i); + } + + for (int i = 0; i < 360; i += 10) { + Directions->Add(i); + } + + LevelUp(); +} + +BPMiniGame_FlashCounting::~BPMiniGame_FlashCounting() { + SAFE_DELETE(sfcBackground); + + Balls->Clear(); + SAFE_DELETE(Balls); + + BallPics->Clear(); + SAFE_DELETE(BallPics); + + BallIDs->Clear(); + SAFE_DELETE(BallIDs); + + Directions->Clear(); + SAFE_DELETE(Directions); +} + +void BPMiniGame_FlashCounting::OnMouseDown() { + +} + +void BPMiniGame_FlashCounting::OnMouseMove() { + +} + +void BPMiniGame_FlashCounting::OnMouseUp() { + if (GameState == CORRECT || GameState == WRONG) return; + + if (TouchEvent.Y < 100) { + // a guess? + for (int i = 0; i < BallIDs->Count; ++i) { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, (i * 64), 28, 64, 64)) { + // clicked! + if (Answer == (*BallIDs)[i]) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + ++NumWrong; + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } + + break; + } + } + } else { + MessageBox::Show("Count up all the gems you see here. Once you've figured out which gem is the most common, choose it from the list at the bottom of the screen.", GameTitle); + } +} + +void BPMiniGame_FlashCounting::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_FlashCounting::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(600 - (NumWrong * 75) - floor(TimePassed * 3)); +} + +void BPMiniGame_FlashCounting::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Balls->Count; ++i) { + BPMiniGame_FlashCounting_Ball* ball = (*Balls)[i]; + TheGame->DrawImage((*BallPics)[ball->Type], round(ball->X), round(ball->Y)); + } + + for (int i = 0; i < BallIDs->Count; ++i) { + TheGame->DrawImage((*BallPics)[(*BallIDs)[i]], (i * 64), 28); + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_FlashCounting::Tick() { + if (NumTries >= 12 && LastStateChange + 400 < TheGame->TickCount) { + Success(); + return; + } + + switch (GameState) { + case CORRECT: + if (LastStateChange != -1 && LastStateChange + 400 < TheGame->TickCount) { + LevelUp(); + } + + break; + + case WRONG: + if (LastStateChange != -1 && LastStateChange + 400 < TheGame->TickCount) { + --CurrentLevel; + LevelUp(); + } + + break; + + case WAITING: + for (int i = 0; i < Balls->Count; ++i) { + BPMiniGame_FlashCounting_Ball* ball = (*Balls)[i]; + + double xmove = cos(ball->Direction * M_PI / 180) * TheGame->ElapsedSeconds * 100; + double ymove = sin(ball->Direction * M_PI / 180) * TheGame->ElapsedSeconds * 100; + + ball->X += xmove; + ball->Y += ymove; + + if (ball->X > MiniGameWidth - BallWidth) { + ball->X = MiniGameWidth - BallWidth; + ball->Direction = 180 - ball->Direction; + } + + if (ball->X < -1) { + ball->X = -1; + ball->Direction = 180 - ball->Direction; + } + + if (ball->Y < Top) { + ball->Y = Top; + ball->Direction = 360 - ball->Direction; + } + + if (ball->Y > Bottom - BallHeight) { + ball->Y = Bottom - BallHeight; + ball->Direction = 360 - ball->Direction; + } + + if (ball->Direction >= 360) ball->Direction = ball->Direction - 360; + if (ball->Direction < 0) ball->Direction = 360 - abs(ball->Direction); + } + + break; + } + +} + +void BPMiniGame_FlashCounting::LevelUp() { + ++NumTries; + + if (NumTries >= 12) { + return; + } + + LastStateChange = -1; + ++CurrentLevel; + BallDirCount = 0; + + Balls->Clear(); + BallIDs->Shuffle(); + + Answer = (*BallIDs)[0]; + + switch (CurrentLevel) { + case 1: + CreateBall(0); + CreateBall(0); + CreateBall(0); + CreateBall(1); + CreateBall(1); + CreateBall(2); + break; + + case 2: + for (int i = 0; i < 4; ++i) CreateBall(0); + for (int i = 0; i < 3; ++i) CreateBall(1); + break; + + case 3: + for (int i = 0; i < 4; ++i) CreateBall(0); + for (int i = 0; i < 2; ++i) CreateBall(1); + for (int i = 0; i < 2; ++i) CreateBall(2); + break; + + case 4: + for (int i = 0; i < 5; ++i) CreateBall(0); + for (int i = 0; i < 4; ++i) CreateBall(1); + break; + + case 5: + for (int i = 0; i < 4; ++i) CreateBall(0); + for (int i = 0; i < 3; ++i) CreateBall(1); + for (int i = 0; i < 3; ++i) CreateBall(2); + break; + + case 6: + for (int i = 0; i < 5; ++i) CreateBall(0); + for (int i = 0; i < 4; ++i) CreateBall(1); + for (int i = 0; i < 3; ++i) CreateBall(2); + break; + + case 7: + for (int i = 0; i < 5; ++i) CreateBall(0); + for (int i = 0; i < 4; ++i) CreateBall(1); + for (int i = 0; i < 4; ++i) CreateBall(2); + break; + + case 8: + for (int i = 0; i < 6; ++i) CreateBall(0); + for (int i = 0; i < 5; ++i) CreateBall(1); + for (int i = 0; i < 5; ++i) CreateBall(2); + break; + + case 9: + for (int i = 0; i < 6; ++i) CreateBall(0); + for (int i = 0; i < 5; ++i) CreateBall(1); + for (int i = 0; i < 5; ++i) CreateBall(2); + for (int i = 0; i < 2; ++i) CreateBall(3); + for (int i = 0; i < 2; ++i) CreateBall(4); + break; + + case 10: + for (int i = 0; i < 6; ++i) CreateBall(0); + for (int i = 0; i < 5; ++i) CreateBall(1); + for (int i = 0; i < 5; ++i) CreateBall(2); + for (int i = 0; i < 3; ++i) CreateBall(3); + for (int i = 0; i < 3; ++i) CreateBall(4); + break; + + default: + for (int i = 0; i < 6; ++i) CreateBall(0); + for (int i = 0; i < 5; ++i) CreateBall(1); + for (int i = 0; i < 5; ++i) CreateBall(2); + for (int i = 0; i < 5; ++i) CreateBall(3); + for (int i = 0; i < 5; ++i) CreateBall(4); + break; + } + + BallIDs->Shuffle(); + + GameState = WAITING; +} + +void BPMiniGame_FlashCounting::CreateBall(int type) { + BPMiniGame_FlashCounting_Ball* ball = new BPMiniGame_FlashCounting_Ball(); + ball->X = TheGame->RandomRange(0, 208); + ball->Y = TheGame->RandomRange(Top, Bottom - BallHeight); + ball->Direction = (*Directions)[BallDirCount]; + ball->Type = (*BallIDs)[type]; + + Balls->Add(ball); + ++BallDirCount; +} diff --git a/flashcounting.h b/flashcounting.h new file mode 100644 index 0000000..a3bcfe1 --- /dev/null +++ b/flashcounting.h @@ -0,0 +1,74 @@ +// 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. + +#ifndef __FLASHCOUNTING_H__ +#define __FLASHCOUNTING_H__ + +#include "Minigame.h" + +class BPMiniGame_FlashCounting_Ball { +public: + double X; + double Y; + int Direction; + int Type; + + BPMiniGame_FlashCounting_Ball() { + X = 0; + Y = 0; + } +}; + +class BPMiniGame_FlashCounting : public BPMiniGame { +public: + void CreateBall(int type); + void LevelUp(); + void Tick(); + void Render(); + int GetWeight(); + void Start(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + BPMiniGame_FlashCounting(BPGame* game); + ~BPMiniGame_FlashCounting(); +protected: + Texture* sfcBackground; + int CurrentLevel; + + BPPList* Balls; + BPPList* BallPics; + BPList* BallIDs; + BPList* Directions; + + static const int Top = 98; + static const int Bottom = 418; + static const int BallWidth = 64; + static const int BallHeight = 64; + + int Answer; + int BallDirCount; // used to assign random directions to balls + + int LastStateChange; + MiniGameStates GameState; + + int NumTries; + int TimeStarted; + int NumWrong; +}; + +#endif diff --git a/flashlight.cpp b/flashlight.cpp new file mode 100644 index 0000000..0e54e14 --- /dev/null +++ b/flashlight.cpp @@ -0,0 +1,233 @@ +// 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 "flashlight.h" +#include "Minigame.h" + +BPMiniGame_Flashlight::BPMiniGame_Flashlight(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("flashlight", 320, 416); + sfcLightOn = TheGame->LoadBitmap("light_on", 80, 80); + sfcLightOff = TheGame->LoadBitmap("light_off", 80, 80); + + CurrentLevel = NumTries = NumWrong = ChangeSpeed = LastStateChange = 0; + SuccessTime = CurrentlyFlashing = LastChange = -1; + GameState = PAUSING; + + TheGame->PlaySound("beep_hi", true); + + GameTitle = "Flashlight"; + GameHelp = "Watch carefully as the lights flash. When they are finished, can you tell which light hasn't flashed at all? I'll make it harder by adding more lights and making them flash faster, so do your best!"; + GameHelp2 = "Try to relax. If you clear your mind and unfocus your eyes just a little, your brain can do a much better job of keeping track of which lights have flashed."; + + MiniGameType = PUZZLE; + + Positions.Add(new BPPoint(20, 28)); + Positions.Add(new BPPoint(120, 28)); + Positions.Add(new BPPoint(220, 28)); + Positions.Add(new BPPoint(70, 98)); + Positions.Add(new BPPoint(170, 98)); + Positions.Add(new BPPoint(20, 168)); + Positions.Add(new BPPoint(120, 168)); + Positions.Add(new BPPoint(220, 168)); + Positions.Add(new BPPoint(70, 238)); + Positions.Add(new BPPoint(170, 238)); + Positions.Add(new BPPoint(20, 308)); + Positions.Add(new BPPoint(120, 308)); + Positions.Add(new BPPoint(220, 308)); +} + +BPMiniGame_Flashlight::~BPMiniGame_Flashlight() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcLightOn); + SAFE_DELETE(sfcLightOff); + + Positions.Clear(); + Lights.Clear(); +} + +void BPMiniGame_Flashlight::ShowLayout() { + Positions.Shuffle(); + + int numlights = 4; + + switch (CurrentLevel) { + case 0: + ChangeSpeed = 400; + numlights = 4; + break; + + case 1: + ChangeSpeed = 375; + numlights = 5; + break; + + case 2: + ChangeSpeed = 350; + numlights = 6; + break; + + case 3: + ChangeSpeed = 325; + numlights = 7; + break; + + case 4: + ChangeSpeed = 300; + numlights = 8; + break; + + case 5: + ChangeSpeed = 275; + numlights = 9; + break; + + case 6: + ChangeSpeed = 250; + numlights = 10; + break; + + case 7: + ChangeSpeed = 225; + numlights = 11; + break; + + case 8: + ChangeSpeed = 200; + numlights = 12; + break; + } + + Lights.Clear(); + + for (int i = 0; i < numlights; ++i) { + BPMiniGame_Flashlight_Light* light = new BPMiniGame_Flashlight_Light(); + light->X = Positions[i]->X; + light->Y = Positions[i]->Y; + + Lights.Add(light); + } + + // start currentlyflashing at 1 because the first light doesn't flash! + CurrentlyFlashing = 1; + LastChange = TheGame->TickCount + 500; // give a pause here so there's a period where all the lights are out + GameState = PAUSING; +} + +void BPMiniGame_Flashlight::Start() { + LastChange = TheGame->TickCount + 500; +} + +int BPMiniGame_Flashlight::GetWeight() { + return MinMax(500 - (NumWrong * 75)); +} + +void BPMiniGame_Flashlight::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Lights.Count; ++i) { + BPMiniGame_Flashlight_Light* light = Lights[i]; + + if (CurrentlyFlashing == i && GameState != PAUSING && LastChange < TheGame->TickCount) { + float diff = TheGame->TickCount - LastChange; + float cdiff = 1 - (diff / ChangeSpeed); + + Colour col = Colour(1.0f, 1.0f, 1.0f, cdiff); + TheGame->DrawImage(sfcLightOff, light->X + sfcLightOn->HalfWidth, light->Y + sfcLightOn->HalfHeight, 0.0f, 1.0f + (cdiff / 4), (*TheGame->White)); + TheGame->DrawImage(sfcLightOn, light->X + sfcLightOn->HalfWidth, light->Y + sfcLightOn->HalfHeight, 0.0f, 1.0f + (cdiff / 4), col); + } else { + TheGame->DrawImage(sfcLightOff, light->X + sfcLightOn->HalfWidth, light->Y + sfcLightOn->HalfHeight, 0.0f, 1.0f, (*TheGame->White)); + } + } + + switch (GameState) { + case CORRECT: + RenderCorrect(); + break; + + case WRONG: + RenderWrong(); + break; + } +} + +void BPMiniGame_Flashlight::Tick() { + if (SuccessTime != -1 && SuccessTime + 500 < TheGame->TickCount) { + Success(); + return; + } + + if (GameState == SHOWING || GameState == PAUSING) { + if (LastChange + ChangeSpeed < TheGame->TickCount) { + if (GameState == PAUSING) { + GameState = SHOWING; + LastChange = TheGame->TickCount; + TheGame->PlaySound("beep_hi"); + } else { + ++CurrentlyFlashing; + LastChange = TheGame->TickCount; + + if (CurrentlyFlashing == Lights.Count) { + GameState = WAITING; + } else { + TheGame->PlaySound("beep_hi"); + } + } + } + } + + if (LastStateChange != -1 && LastStateChange + 750 < TheGame->TickCount) { + // time to ask another question + LastStateChange = -1; + ShowLayout(); + } +} + +void BPMiniGame_Flashlight::OnMouseDown() { + +} + +void BPMiniGame_Flashlight::OnMouseMove() { + +} + +void BPMiniGame_Flashlight::OnMouseUp() { + if (GameState == CORRECT || GameState == WRONG) return; + + for (int i = 0; i < Lights.Count; ++i) { + BPMiniGame_Flashlight_Light* light = Lights[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, light->X, light->Y, 80, 80)) { + ++NumTries; + + if (Lights.IndexOf(light) == 0) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + ++CurrentLevel; + } else { + TheGame->PlaySound("wrong3"); + GameState = WRONG; + ++NumWrong; + } + + LastStateChange = TheGame->TickCount; + + if (NumTries >= 9 && SuccessTime == -1) SuccessTime = TheGame->TickCount; + + break; + } + } +} diff --git a/flashlight.h b/flashlight.h new file mode 100644 index 0000000..5ec96a4 --- /dev/null +++ b/flashlight.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef __FLASHLIGHT_H__ +#define __FLASHLIGHT_H__ + +#include "Minigame.h" + +class BPMiniGame_Flashlight_Light { +public: + int X; + int Y; +}; + + +class BPMiniGame_Flashlight : public BPMiniGame { +public: + BPMiniGame_Flashlight(BPGame* game); + ~BPMiniGame_Flashlight(); + void ShowLayout(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); +protected: + Texture* sfcBackground; + Texture* sfcLightOn; + Texture* sfcLightOff; + + BPPList Positions; + BPPList Lights; + + int CurrentLevel; + int NumTries; + int SuccessTime; + + int NumWrong; + + int CurrentlyFlashing; + int LastChange; + int ChangeSpeed; + + int LastStateChange; + MiniGameStates GameState; +}; + +#endif diff --git a/iqtest.cpp b/iqtest.cpp new file mode 100644 index 0000000..22c7adc --- /dev/null +++ b/iqtest.cpp @@ -0,0 +1,292 @@ +// 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 "iqtest.h" +#include "Minigame.h" + +BPMiniGame_IQTest::BPMiniGame_IQTest(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("iqtest", 320, 416); + + CurrentQuestion = NULL; + CurrentAnswer = false; + + sfcCurrentQuestion = sfcScore = NULL; + + Score = Tries = NumWrong = TimeStarted = 0; + SetScore(); + + GameState = WAITING; + LastStateChange = 0; + + SuccessTime = -1; + + FourLeggedAnimals = new BPList(); + TwoLeggedAnimals = new BPList(); + + GameTitle = "IQ Test"; + GameHelp = "Ah, mathematics. These simple questions might look easy, but you need to decide whether what I say is true or false. Take your time - you get a point for a correct answer, but you lose more when you get it wrong!"; + GameHelp2 = "To get the best score, try to group animals together: add up how many four-legged animals there are and multiply it by four, then add up how many two-legged animals there are and multiply it by two; now add those two numbers together to have the total number of legs."; + + MiniGameType = PUZZLE; + + FourLeggedAnimals->Add("dogs"); + FourLeggedAnimals->Add("cats"); + FourLeggedAnimals->Add("lions"); + FourLeggedAnimals->Add("tigers"); + FourLeggedAnimals->Add("bears"); + + TwoLeggedAnimals->Add("chickens"); + TwoLeggedAnimals->Add("ducks"); + TwoLeggedAnimals->Add("birds"); + TwoLeggedAnimals->Add("penguins"); + + AskQuestion(); +} + +BPMiniGame_IQTest::~BPMiniGame_IQTest() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(CurrentQuestion); + + FourLeggedAnimals->Clear(); + SAFE_DELETE(FourLeggedAnimals); + + TwoLeggedAnimals->Clear(); + SAFE_DELETE(TwoLeggedAnimals); + + SAFE_DELETE(sfcCurrentQuestion); + SAFE_DELETE(sfcScore); +} + +void BPMiniGame_IQTest::AskQuestion() { + if (SuccessTime != -1) return; + + int i, j, k, l, m, n, o, p; + ostringstream question; + int totallegs = 0; + + FourLeggedAnimals->Shuffle(); + TwoLeggedAnimals->Shuffle(); + + switch (Score) { + case 1: + // two four-legged animals + i = TheGame->RandomRange(2, 4); + j = TheGame->RandomRange(2, 4); + + totallegs = (i * 4) + (j * 4); + + question << i << " " << (*FourLeggedAnimals)[0] << " and " << j << " " << (*FourLeggedAnimals)[1]; + + break; + + case 2: + // two four-legged animals and a two-legged animal + i = TheGame->RandomRange(2, 4); + j = TheGame->RandomRange(2, 4); + k = TheGame->RandomRange(2, 4); + + totallegs = (i * 4) + (j * 4) + (k * 2); + + question << i << " " << (*FourLeggedAnimals)[0] << ", " << j << " " << (*FourLeggedAnimals)[1] << " and " << k << " " << (*TwoLeggedAnimals)[0]; + + break; + + case 3: + // two four-legged animals and two two-legged animals + i = TheGame->RandomRange(2, 4); + j = TheGame->RandomRange(2, 4); + k = TheGame->RandomRange(2, 4); + l = TheGame->RandomRange(2, 4); + + totallegs = (i * 4) + (j * 4) + (k * 2) + (l * 2); + + question << i << " " << (*FourLeggedAnimals)[0] << ", " << j << " " << (*FourLeggedAnimals)[1] << ", " << k << " " << (*TwoLeggedAnimals)[0] << " and " << l << " " << (*TwoLeggedAnimals)[1]; + break; + + case 4: + // three four-legged animals and three two-legged animals + i = TheGame->RandomRange(2, 4); + j = TheGame->RandomRange(2, 4); + k = TheGame->RandomRange(2, 4); + l = TheGame->RandomRange(2, 4); + m = TheGame->RandomRange(2, 4); + n = TheGame->RandomRange(2, 4); + + totallegs = (i * 4) + (j * 4) + (k * 4) + (l * 2) + (m * 2) + (n * 2); + + question << i << " " << (*FourLeggedAnimals)[0] << ", " << j << " " << (*FourLeggedAnimals)[1] << ", " << k << " " << (*FourLeggedAnimals)[2] << ", " << l << " " << (*TwoLeggedAnimals)[0] << ", " << m << " " << (*TwoLeggedAnimals)[1] << " and " << n << " " << (*TwoLeggedAnimals)[2]; + + break; + + case 5: + // four four-legged animals and four two-legged animals + i = TheGame->RandomRange(2, 4); + j = TheGame->RandomRange(2, 4); + k = TheGame->RandomRange(2, 4); + l = TheGame->RandomRange(2, 4); + m = TheGame->RandomRange(2, 4); + n = TheGame->RandomRange(2, 4); + o = TheGame->RandomRange(2, 4); + p = TheGame->RandomRange(2, 4); + + totallegs = (i * 4) + (j * 4) + (k * 4) + (l * 4) + (m * 2) + (n * 2) + (o * 2) + (p * 2); + + question << i << " " << (*FourLeggedAnimals)[0] << ", " << j << " " << (*FourLeggedAnimals)[1] << ", " << k << " " << (*FourLeggedAnimals)[2] << ", " << l << " " << (*FourLeggedAnimals)[3] << ", " << m << " " << (*TwoLeggedAnimals)[0] << ", " << n << " " << (*TwoLeggedAnimals)[1] << ", " << o << " " << (*TwoLeggedAnimals)[2] << " and " << p << " " << (*TwoLeggedAnimals)[3]; + break; + + default: + // one four-legged animal + i = TheGame->RandomRange(2, 4); + totallegs = i * 4; + + question << i << " " << (*FourLeggedAnimals)[0]; + + break; + } + + if (TheGame->RandomRange(0, 1) == 0) { + CurrentAnswer = true; + question << " have " << totallegs << " legs."; + } else { + CurrentAnswer = false; + question << " have " << totallegs + 4 << " legs."; + } + + TheGame->AllocString(&sfcCurrentQuestion, question.str().c_str(), LARGE, 273, 275, CENTRED); +} + +void BPMiniGame_IQTest::FalseClicked() { + if (GameState == CORRECT || GameState == WRONG) return; + + ++Tries; + + if (CurrentAnswer == false) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } +} + +void BPMiniGame_IQTest::TrueClicked() { + if (GameState == CORRECT || GameState == WRONG) return; + + ++Tries; + + if (CurrentAnswer == true) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } +} + +void BPMiniGame_IQTest::UpdateProgress() { + if (Score < 0) Score = 0; + + if (Tries >= 6 && SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + } + + SetScore(); +} + +void BPMiniGame_IQTest::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_IQTest::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(550 - (NumWrong * 150) - floor(TimePassed * 1.5f)); +} + +void BPMiniGame_IQTest::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + if (SuccessTime == -1) { + TheGame->DrawString(sfcCurrentQuestion, WHITE, 24, 35); + } + + TheGame->DrawString(sfcScore, WHITE, 48, 364); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_IQTest::Tick() { + if (SuccessTime != -1 && SuccessTime + 250 < TheGame->TickCount) { + Success(); + return; + } + + if (GameState != WAITING) { + if (LastStateChange + 500 < TheGame->TickCount) { + if (GameState == CORRECT) { + ++Score; + AskQuestion(); + } else { + ++NumWrong; + AskQuestion(); + } + + LastStateChange = TheGame->TickCount; + GameState = WAITING; + + UpdateProgress(); + } + } +} + +void BPMiniGame_IQTest::OnMouseUp() { + +} + +void BPMiniGame_IQTest::OnMouseMove() { + +} + +void BPMiniGame_IQTest::OnMouseDown() { + if (SuccessTime != -1) { + if (SuccessTime + 750 < TheGame->TickCount) { + Success(); + } + + return; + } + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 31, 312, 127, 49)) { + TrueClicked(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 164, 312, 127, 49)) { + FalseClicked(); + } +} + +void BPMiniGame_IQTest::SetScore() { + ostringstream score; + + score << "Score: " << Score << "/6"; + TheGame->AllocString(&sfcScore, score.str().c_str(), NORMAL, 229, 28, CENTRED); +} diff --git a/iqtest.h b/iqtest.h new file mode 100644 index 0000000..35d6a54 --- /dev/null +++ b/iqtest.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef __IQTEST_H__ +#define __IQTEST_H__ + +#include "Minigame.h" + +class BPMiniGame_IQTest : public BPMiniGame { +public: + BPMiniGame_IQTest(BPGame* game); + ~BPMiniGame_IQTest(); + void AskQuestion(); + void FalseClicked(); + void TrueClicked(); + void UpdateProgress(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void SetScore(); +protected: + Texture* sfcBackground; + + string* CurrentQuestion; + SpriteFont* sfcCurrentQuestion; + bool CurrentAnswer; + + int Score; + SpriteFont* sfcScore; + int Tries; + + int SuccessTime; + + BPList* FourLeggedAnimals; + BPList* TwoLeggedAnimals; + + int NumWrong; + + int TimeStarted; + + MiniGameStates GameState; + int LastStateChange; +}; + +#endif diff --git a/jewelflip.cpp b/jewelflip.cpp new file mode 100644 index 0000000..fe2a4e3 --- /dev/null +++ b/jewelflip.cpp @@ -0,0 +1,365 @@ +// 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 "jewelflip.h" +#include "Minigame.h" + +BPMiniGame_JewelFlip::BPMiniGame_JewelFlip(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("jewelflip", 320, 416); + sfcBottomBar = TheGame->LoadBitmap("jewelflip_bottombar", 320, 48); + sfcSelected = TheGame->LoadBitmap("selected48", 48, 48); + + SelectedGem = NULL; + sfcTotalScore = NULL; + + TheGame->AllocString(&sfcTotalScore, "Score: 0", LARGE, 320, 37, CENTRED, true); + + GameOverCounter = GameOverTimer = TotalScore = TimeStarted = 0; + GameOver = false; + + GameTitle = "Jewel Flip"; + GameHelp = "Tap adjacent gems to switch them, but only if one of the gems ends next to another of the same type. Try to group gems as best you can, then tap \"Ready\" once you're done - bigger groups get more points!"; + GameHelp2 = "To swap a gem, select it then select another gem above, below, left or right of it. You can swap gems only if one of them ends up next to another one of its own type. For example, if you want to swap a white gem and a red gem, then either the white gem must end up next to another white gem or the red gem must end up next to another red gem."; + + MiniGameType = PUZZLE; + + GemTypes.Add(TheGame->LoadBitmap("gem1_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem2_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem3_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem4_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem5_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem6_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem7_small", 48, 48)); + GemTypes.Add(TheGame->LoadBitmap("gem8_small", 48, 48)); + + BPList JewelList; + + // generate an equal distribution of gem types so that the scores aren't too uneven + for (int i = 0; i < 42; ++i) { + JewelList.Add(i % GemTypes.Count); + } + + JewelList.Shuffle(); + + int n = 0; + + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 7; ++j) { + BPMiniGame_JewelFlip_Jewel* gem = new BPMiniGame_JewelFlip_Jewel(); + gem->X = 11 + (i * 50); + gem->Y = 11 + (j * 50); + gem->GemType = JewelList[n]; + gem->DestX = gem->X; + gem->DestY = gem->Y; + + Gems.Add(gem); + + ++n; + } + } +} + +BPMiniGame_JewelFlip::~BPMiniGame_JewelFlip() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBottomBar); + SAFE_DELETE(sfcSelected); + + GemTypes.Clear(); + + Gems.Clear(); + + SAFE_DELETE(SelectedGem); + + SAFE_DELETE(sfcTotalScore); +} + +void BPMiniGame_JewelFlip::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_JewelFlip::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax((TotalScore + TotalScore + TotalScore) - round(TimePassed / 4.0f)); +} + +void BPMiniGame_JewelFlip::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Gems.Count; ++i) { + BPMiniGame_JewelFlip_Jewel* gem = Gems[i]; + + if (gem->Removed) continue; + + TheGame->DrawImage(GemTypes[gem->GemType], gem->X, gem->Y); + + if (SelectedGem == gem) { + TheGame->DrawImage(sfcSelected, gem->X, gem->Y); + } + } + + if (GameOver) { + TheGame->DrawImage(sfcBottomBar, 0, 368); + TheGame->DrawString(sfcTotalScore, WHITE, 0, 374); + } +} + +void BPMiniGame_JewelFlip::Tick() { + if (GameOver && GameOverTimer + 250 < TheGame->TickCount) { + GameOverTimer = TheGame->TickCount; + + if (GameOverCounter == Gems.Count) { + Success(); + return; + } + + while (Gems[GameOverCounter]->Removed) { + ++GameOverCounter; + + if (GameOverCounter == Gems.Count) { + Success(); + return; + } + } + + TotalScore += (int)pow(2.0f, RemoveGem(Gems[GameOverCounter])); + + ostringstream totalscore; + totalscore << "Score: " << TotalScore; + TheGame->AllocString(&sfcTotalScore, totalscore.str().c_str(), LARGE, 320, 37, CENTRED, true); + } else { + + for (int i = 0; i < Gems.Count; ++i) { + BPMiniGame_JewelFlip_Jewel* gem = Gems[i]; + if (gem->Speed != 0) { + // this gem needs to move + + if (gem->Y != gem->DestY) { + if (gem->Y < gem->DestY) { + ++gem->Speed; + } else { + --gem->Speed; + } + + gem->Y += gem->Speed; + + if (gem->Speed > 0 && gem->Y >= gem->DestY) { + gem->Y = gem->DestY; + gem->Speed = 0; + } else if (gem->Speed < 0 && gem->Y <= gem->DestY) { + gem->Y = gem->DestY; + gem->Speed = 0; + } + } else if (gem->X != gem->DestX) { + if (gem->X < gem->DestX) { + ++gem->Speed; + } else { + --gem->Speed; + } + + gem->X += gem->Speed; + + if (gem->Speed > 0 && gem->X >= gem->DestX) { + gem->X = gem->DestX; + gem->Speed = 0; + } else if (gem->Speed < 0 && gem->X <= gem->DestX) { + gem->X = gem->DestX; + gem->Speed = 0; + } + } + } + } + } +} + +bool BPMiniGame_JewelFlip::CanSwitch(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2) { + // if these two gems were swapped, would they match anything? + + if (GemMatches(gem1, gem2)) return true; + if (GemMatches(gem2, gem1)) return true; + + MessageBox::Show("You can't swap these two gems, because at least one of them must end up next to a gem of the same type.", GameTitle); + SelectedGem = NULL; + + return false; +} + +BPMiniGame_JewelFlip_Jewel* BPMiniGame_JewelFlip::AutoSubstituteGem(int position, BPMiniGame_JewelFlip_Jewel* match, BPMiniGame_JewelFlip_Jewel* replace) { + BPMiniGame_JewelFlip_Jewel* gem = Gems[position]; + if (gem == match) return replace; + return gem; +} + +bool BPMiniGame_JewelFlip::GemMatches(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2) { + int type = gem1->GemType; + int position = Gems.IndexOf(gem2); + + int Row; + int Col = DivRem(position, 7, Row); + + // try searching horizontally + if (Col > 0) { + // gem to the left + if (AutoSubstituteGem(position - 7, gem1, gem2)->GemType == type) return true; + } + + if (Col < 5) { + // one to the right + if (AutoSubstituteGem(position + 7, gem1, gem2)->GemType == type) return true; + } + + // try searching vertically + if (Row > 0) { + // gem above + if (AutoSubstituteGem(position - 1, gem1, gem2)->GemType == type) return true; + } + + if (Row < 6) { + // gem below + if (AutoSubstituteGem(position + 1, gem1, gem2)->GemType == type) return true; + } + + return false; +} + +void BPMiniGame_JewelFlip::OnMouseDown() { + +} + +void BPMiniGame_JewelFlip::OnMouseMove() { + +} + +void BPMiniGame_JewelFlip::OnMouseUp() { + if (GameOver == true) return; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 110, 370, 102, 47)) { + GameOver = true; + GameOverTimer = TheGame->TickCount; + return; + } + + for (int i = 0; i < Gems.Count; ++i) { + BPMiniGame_JewelFlip_Jewel* gem = Gems[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, gem->X, gem->DestY, 48, 48)) { + // we've clicked this gem! + if (gem->Removed) break; + + if (SelectedGem == gem) { + // if we've selected it already, deselect it + SelectedGem = NULL; + } else { + if (SelectedGem == NULL) { + SelectedGem = gem; + } else if (GemsAreAdjacent(SelectedGem, gem)) { + SwapGems(SelectedGem, gem); + } else { + MessageBox::Show("These two gems can't be swapped because they are not next to each other.", GameTitle); + SelectedGem = NULL; + } + } + + break; + } + } +} + +bool BPMiniGame_JewelFlip::GemsAreAdjacent(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2) { + return (abs(gem1->DestX - gem2->DestX) + abs(gem1->DestY - gem2->DestY)) == 50; +} + +void BPMiniGame_JewelFlip::SwapGems(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2) { + if (!CanSwitch(gem1, gem2)) return; + + TheGame->PlaySound("slide"); + + SelectedGem = NULL; + + gem1->DestX = gem2->X; + gem1->DestY = gem2->Y; + + gem2->DestX = gem1->X; + gem2->DestY = gem1->Y; + + if (gem1->DestX < gem1->X || gem1->DestY < gem1->Y) { + gem1->Speed = -1; + } else { + gem1->Speed = 1; + } + + if (gem2->DestX < gem2->X || gem2->DestY < gem2->Y) { + gem2->Speed = -1; + } else { + gem2->Speed = 1; + } + + // we need to swap positions in the Gems array, otherwise CanSwap() won't work + int Gem1Pos = Gems.IndexOf(gem1); + int Gem2Pos = Gems.IndexOf(gem2); + + // remove and insert the higher-positioned gem first + if (Gem1Pos > Gem2Pos) { + Gems.RemoveAt(Gem1Pos); + Gems.RemoveAt(Gem2Pos); + + Gems.Insert(Gem2Pos, gem1); + Gems.Insert(Gem1Pos, gem2); + } else { + Gems.RemoveAt(Gem2Pos); + Gems.RemoveAt(Gem1Pos); + + Gems.Insert(Gem1Pos, gem2); + Gems.Insert(Gem2Pos, gem1); + } +} + +int BPMiniGame_JewelFlip::RemoveGem(BPMiniGame_JewelFlip_Jewel* gem) { + // remove this gem, then remove any others that touch it + if (gem->Removed) return 0; // this has been removed already! + + gem->Removed = true; + if (SelectedGem == gem) SelectedGem = NULL; + + int position = Gems.IndexOf(gem); + + int Row; + int Col = DivRem(position, 7, Row); + + int num_removed = 1; // we've removed ourselves + + // remove the one above + if (Row > 0) { + if (Gems[position - 1]->GemType == gem->GemType) num_removed += RemoveGem(Gems[position - 1]); + } + + // remove the one below + if (Row < 6) { + if (Gems[position + 1]->GemType == gem->GemType) num_removed += RemoveGem(Gems[position + 1]); + } + + // remove the one to the left + if (Col > 0) { + if (Gems[position - 7]->GemType == gem->GemType) num_removed += RemoveGem(Gems[position - 7]); + } + + // remove the one to the right + if (Col < 5) { + if (Gems[position + 7]->GemType == gem->GemType) num_removed += RemoveGem(Gems[position + 7]); + } + + return num_removed; +} diff --git a/jewelflip.h b/jewelflip.h new file mode 100644 index 0000000..fca09c9 --- /dev/null +++ b/jewelflip.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef __JEWELFLIP_H__ +#define __JEWELFLIP_H__ + +#include "Minigame.h" + +class BPMiniGame_JewelFlip_Jewel { +public: + int GemType; + int X; + int Y; + int DestX; + int DestY; + int Speed; + + bool Removed; +}; + +class BPMiniGame_JewelFlip : public BPMiniGame { +public: + BPMiniGame_JewelFlip(BPGame* game); + ~BPMiniGame_JewelFlip(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + bool CanSwitch(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2); + BPMiniGame_JewelFlip_Jewel* AutoSubstituteGem(int position, BPMiniGame_JewelFlip_Jewel* match, BPMiniGame_JewelFlip_Jewel* replace); + bool GemMatches(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + bool GemsAreAdjacent(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2); + void SwapGems(BPMiniGame_JewelFlip_Jewel* gem1, BPMiniGame_JewelFlip_Jewel* gem2); + int RemoveGem(BPMiniGame_JewelFlip_Jewel* gem); +protected: + Texture* sfcBackground; + Texture* sfcBottomBar; + BPPList GemTypes; + BPPList Gems; + + Texture* sfcSelected; + + BPMiniGame_JewelFlip_Jewel* SelectedGem; + + int GameOverCounter; + int GameOverTimer; + bool GameOver; + int TotalScore; + SpriteFont* sfcTotalScore; + int TimeStarted; +}; + +#endif diff --git a/jeweljam.cpp b/jeweljam.cpp new file mode 100644 index 0000000..79f3051 --- /dev/null +++ b/jeweljam.cpp @@ -0,0 +1,510 @@ +// 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 "jeweljam.h" +#include "Minigame.h" + +BPMiniGame_JewelJam::BPMiniGame_JewelJam(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("jeweljam", 320, 416); + + SelectedRow = NULL; + Locked = false; // when true disallow movement + TimeStarted = 0; + SuccessTime = -1; + + sfcClock = sfcScoreStr = NULL; + + DisappearTime = 350; + + Score = 0; + SetScore(); + + GameTitle = "Jewel Jam"; + GameHelp = "Drag the rows of gems left and right, matching three or more of the same type scores points and more gems will fall. The more you match in one move, the more you score. But hurry - you have only two minutes!"; + GameHelp2 = "The key in this game is that you can only slide left and right, so you need to start looking for vertical columns of gems to match. Generally it's best to work near the bottom of the screen, because that way falling gems are more likely to have a chain reaction and earn you more points."; + + MiniGameType = PUZZLE; + + ColoursLo.Add(TheGame->LoadBitmap("gem1_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem2_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem3_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem4_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem5_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem6_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem7_40", 40, 40)); + ColoursLo.Add(TheGame->LoadBitmap("gem8_40", 40, 40)); + + ColoursHi.Add(TheGame->LoadBitmap("gem1_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem2_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem3_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem4_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem5_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem6_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem7_hi_40", 40, 40)); + ColoursHi.Add(TheGame->LoadBitmap("gem8_hi_40", 40, 40)); + + for (int j = 0; j < 9; ++j) { + vector* row = new vector(); + + for (int i = 0; i < 8; ++i) { + BPMiniGame_JewelJam_Box* box = new BPMiniGame_JewelJam_Box(); + box->X = i * BoxSize; + box->Y = -((9 - j) * BoxSize * i); + box->DestY = 77 + (j * BoxSize); + box->Colour = TheGame->RandomRange(0, ColoursLo.Count - 1); + row->push_back(box); + } + + Boxes.push_back(row); + } +} + +BPMiniGame_JewelJam::~BPMiniGame_JewelJam() { + SAFE_DELETE(sfcBackground); + + BPMiniGame_JewelJam_Box* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + SAFE_DELETE(box); + } + + row->clear(); + SAFE_DELETE(row); + } + + Boxes.clear(); + + ColoursLo.Clear(); + ColoursHi.Clear(); + + if (SelectedRow != NULL) { + // shouldn't have to do this, because it's just pointers to things that have already been deleted + //SelectedRow->clear(); + SAFE_DELETE(SelectedRow); + } + + SAFE_DELETE(sfcScoreStr); + SAFE_DELETE(sfcClock); +} + +void BPMiniGame_JewelJam::OnMouseMove() { + if (Locked) return; + if (SelectedRow == NULL) return; + + Locked = true; + + BPMiniGame_JewelJam_Box* box; + + if (LastPos != TheGame->EmptyPoint && LastPos != TouchEvent) { + if (TouchEvent.X < LastPos.X) { + // move left + + for (int i = SelectedRow->size() - 1; i >= 0; --i) { + box = (*SelectedRow)[i]; + + box->X += TouchEvent.X - LastPos.X; + + if (box->X <= -BoxSize) { + box->X = MiniGameWidth + box->X; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->push_back(box); + } + } + + } else { + // move right + + for (int i = 0; i < SelectedRow->size(); ++i) { + box = (*SelectedRow)[i]; + + box->X += TouchEvent.X - LastPos.X; + + if (box->X >= MiniGameWidth) { + box->X -= MiniGameWidth; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->insert(SelectedRow->begin() + 0, box); + } + } + } + } + + LastPos = TouchEvent; + + Locked = false; +} + +void BPMiniGame_JewelJam::OnMouseDown() { + if (Locked) return; + Locked = true; + + LastPos = TouchEvent; + + for (int i = 0; i < Boxes.size(); ++i) { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 57 + (i * BoxSize), 320, BoxSize)) { + // move this row + SelectedRow = Boxes[i]; + break; + } + } + + Locked = false; +} + +void BPMiniGame_JewelJam::OnMouseUp() { + if (Locked) return; + if (SelectedRow == NULL) return; + + BPMiniGame_JewelJam_Box* box; + + Locked = true; + + // we've let go of a row - snap it into place! + int xoffset = (*SelectedRow)[0]->X; + + int remainder = xoffset % BoxSize; + if (remainder != 0) { + int adjust = 0; + + if (remainder > HalfBoxSize || (remainder < 0 && remainder > -HalfBoxSize)) { + // snap to the right + if (remainder > 0) { + adjust = BoxSize - remainder; + } else { + adjust = abs(remainder); + } + + for (int i = 0; i < SelectedRow->size(); ++i) { + box = (*SelectedRow)[i]; + + box->X += adjust; + + if (box->X >= MiniGameWidth) { + box->X -= MiniGameWidth; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->insert(SelectedRow->begin() + 0, box); + } + } + } else { + // snap to the left + if (remainder < 0) { + adjust = abs(remainder) - BoxSize; + } else { + adjust = -remainder; + } + + for (int i = SelectedRow->size() - 1; i >= 0; --i) { + box = (*SelectedRow)[i]; + + box->X += adjust; + + if (box->X <= -BoxSize) { + box->X = MiniGameWidth + box->X; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->push_back(box); + } + } + + } + } + + SelectedRow = NULL; + + CheckForMatches(); + + // subtract one point for each move made + if (Score > 0) { + --Score; + SetScore(); + } +} + +void BPMiniGame_JewelJam::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_JewelJam::GetWeight() { + return MinMax(round(Score / 6)); +} + +void BPMiniGame_JewelJam::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + if (!MarathonMode) { + int TimePassed = TheGame->TickCount - TimeStarted; + TimePassed = 120000 - TimePassed; + + if (TimePassed <= 0) { + if (SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + Success(); + } + } + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), LARGE, 160, 50, RIGHT); + } + + TheGame->DrawString(sfcClock, WHITE, 150, 4); + } + + TheGame->DrawString(sfcScoreStr, WHITE, 6, 4); + + BPMiniGame_JewelJam_Box* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + + // HACK: There's an unknown bug in this minigame that occasionally puts boxes in the wrong X pos. + // To reproduce, just keep matching stuff while dragging around on the screen manically. + // This force-resets the X position for each box to the correct value. What a waste of CPU time! + if (SelectedRow != row) box->X = i * BoxSize; + + if (box->MatchTime == -1) { + TheGame->DrawImage(ColoursLo[box->Colour], box->X + HalfBoxSize, box->Y, 0.0f, 1.0f, (*TheGame->White)); + + if (box->X < 0) { + TheGame->DrawImage(ColoursLo[box->Colour], MiniGameWidth + box->X + HalfBoxSize, box->Y, 0.0f, 1.0f, (*TheGame->White)); + } else if (box->X > MiniGameWidth - BoxSize) { + TheGame->DrawImage(ColoursLo[box->Colour], HalfBoxSize + box->X - MiniGameWidth, box->Y, 0.0f, 1.0f, (*TheGame->White)); + } + } else { + float diff = TheGame->TickCount - box->MatchTime; + + if (diff <= DisappearTime) { + float step = diff / DisappearTime; // get a value between 0 and 1 + Colour col = Colour(1.0f, 1.0f, 1.0f, 1 - step); + + TheGame->DrawImage(ColoursHi[box->Colour], box->X + HalfBoxSize, box->Y, 0.0f, TheGame->SmoothStep(1.0f, 3.0f, step), col); + + if (box->X < 0) { + TheGame->DrawImage(ColoursHi[box->Colour], MiniGameWidth + box->X + HalfBoxSize, box->Y, 0.0f, TheGame->SmoothStep(1.0f, 3.0f, step), (*TheGame->White)); + } else if (box->X > MiniGameWidth - BoxSize) { + TheGame->DrawImage(ColoursHi[box->Colour], HalfBoxSize + box->X - MiniGameWidth, box->Y, 0.0f, TheGame->SmoothStep(1.0f, 3.0f, step), (*TheGame->White)); + } + } + } + } + } +} + +void BPMiniGame_JewelJam::Tick() { + vector* row; + BPMiniGame_JewelJam_Box* box; + BPMiniGame_JewelJam_Box* copybox; + + int MatchTimeout = TheGame->TickCount - 150; + + bool AllStill = true; + + for (int i = Boxes.size() - 1; i >= 0; --i) { + row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + box = (*row)[j]; + + if (box->MatchTime != -1) AllStill = false; + + if (box->Y != box->DestY) { + AllStill = false; + + box->YSpeed += 2.0f; + box->Y += box->YSpeed; + if (box->Y > box->DestY) { + box->Y = box->DestY; + box->YSpeed = 0; + } + } + + if (box->MatchTime != -1 && box->MatchTime < MatchTimeout) { + SAFE_DELETE(box); + + row->erase(row->begin() + j); + // move all boxes down above me + + for (int k = i - 1; k >= 0; --k) { + copybox = (*Boxes[k])[j]; + Boxes[k]->erase(Boxes[k]->begin() + j); + Boxes[k + 1]->insert(Boxes[k + 1]->begin() + j, copybox); + copybox->DestY = 77 + ((k + 1) * BoxSize); + } + + BPMiniGame_JewelJam_Box* newbox = new BPMiniGame_JewelJam_Box(); + newbox->X = j * BoxSize; + newbox->Y = -((9 - j) * BoxSize); + newbox->DestY = 77; + newbox->Colour = TheGame->RandomRange(0, ColoursLo.Count - 1); + Boxes[0]->insert(Boxes[0]->begin() + j, newbox); + } + } + } + + if (AllStill) { + if (Locked) { + CheckForMatches(); + } + } else { + Locked = true; + } +} + +void BPMiniGame_JewelJam::CheckForMatches() { + Locked = true; + + int ThisScore = 0; + + // if any three boxes are in a line, match them and any others touching them of the same colour + bool got_match = false; + + vector* row; + BPMiniGame_JewelJam_Box* box; + + for (int i = Boxes.size() - 1; i >= 0; --i) { + row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + // does this box match? + box = (*row)[j]; + + // check two boxes to the left + if (j > 1) { + if ((*row)[j - 1]->Colour == box->Colour && (*row)[j - 2]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + if (j > 0 && j < 7) { + // try matching one to the left and one to the right + if ((*row)[j - 1]->Colour == box->Colour && (*row)[j + 1]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + // check two boxes to the right + if (j < 6) { + if ((*row)[j + 1]->Colour == box->Colour && (*row)[j + 2]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + + // check two boxes above + if (i > 1) { + if ((*Boxes[i - 1])[j]->Colour == box->Colour && (*Boxes[i - 2])[j]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + // try matching one box above and one box below + if (i > 0 && i < 7) { + if ((*Boxes[i - 1])[j]->Colour == box->Colour && (*Boxes[i + 1])[j]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + // check two boxes below + if (i < 6) { + if ((*Boxes[i + 1])[j]->Colour == box->Colour && (*Boxes[i + 2])[j]->Colour == box->Colour) { + got_match = true; + ThisScore += MatchBox(box, i, j); + continue; + } + } + + } + } + + if (ThisScore != 0) { + Score += min(512, (int)round(pow(2.0f, ThisScore))); + SetScore(); + } + + Locked = got_match; + + if (got_match) { + TheGame->PlaySound("gem_select"); + } +} + +int BPMiniGame_JewelJam::MatchBox(BPMiniGame_JewelJam_Box* box, int row, int col) { + if (box->MatchTime == -1) { + box->MatchTime = TheGame->TickCount; + + int thisscore = 1; + + // now match all other identical boxes around it + if (row > 0) { + if ((*Boxes[row - 1])[col]->Colour == box->Colour && (*Boxes[row - 1])[col]->MatchTime == -1) { + thisscore += MatchBox((*Boxes[row - 1])[col], row - 1, col); + } + } + + if (row < 7) { + if ((*Boxes[row + 1])[col]->Colour == box->Colour && (*Boxes[row + 1])[col]->MatchTime == -1) { + thisscore += MatchBox((*Boxes[row + 1])[col], row + 1, col); + } + } + + if (col > 0) { + if ((*Boxes[row])[col - 1]->Colour == box->Colour && (*Boxes[row])[col - 1]->MatchTime == -1) { + thisscore += MatchBox((*Boxes[row])[col - 1], row, col - 1); + } + } + + if (col < 7) { + if ((*Boxes[row])[col + 1]->Colour == box->Colour && (*Boxes[row])[col + 1]->MatchTime == -1) { + thisscore += MatchBox((*Boxes[row])[col + 1], row, col + 1); + } + } + + return thisscore; + } else { + return 0; + } +} + +void BPMiniGame_JewelJam::SetMarathon() { + MarathonMode = true; + GameHelp = "Drag rows of jewels left and right, matching three or more to score points; the more you match, the more you score."; +} + +void BPMiniGame_JewelJam::SetScore() { + if (Score > 0) { + ostringstream score; + string scorestr = TheGame->SeparateThousands(Score); + score << "Score: " << scorestr; + TheGame->AllocString(&sfcScoreStr, score.str().c_str(), LARGE, 270, 50, LEFT); + } else { + TheGame->AllocString(&sfcScoreStr, "Score: 0", LARGE, 270, 50, LEFT); + } +} diff --git a/jeweljam.h b/jeweljam.h new file mode 100644 index 0000000..0bba291 --- /dev/null +++ b/jeweljam.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef __JEWELJAM_H__ +#define __JEWELJAM_H__ + +#include "Minigame.h" + +class BPMiniGame_JewelJam_Box { +public: + int X; + int Y; + int DestY; + int YSpeed; + int Colour; + int MatchTime; + + BPMiniGame_JewelJam_Box() { + X = Y = DestY = YSpeed = Colour = 0; + MatchTime = -1; + } +}; + +class BPMiniGame_JewelJam : public BPMiniGame { +public: + BPMiniGame_JewelJam(BPGame* game); + ~BPMiniGame_JewelJam(); + void OnMouseMove(); + void OnMouseDown(); + void OnMouseUp(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void CheckForMatches(); + int MatchBox(BPMiniGame_JewelJam_Box* box, int row, int col); + void SetMarathon(); + void SetScore(); + +protected: + Texture* sfcBackground; + + vector*> Boxes; + BPPList ColoursLo; + BPPList ColoursHi; + + SpriteFont* sfcClock; + + vector* SelectedRow; + + int DisappearTime; + BPPoint LastPos; + + static const int BoxSize = 40; + static const int HalfBoxSize = 20; + + bool Locked; // when true disallow movement + + int TimeStarted; + + int SuccessTime; + + int Score; + SpriteFont* sfcScoreStr; +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3e1c80e --- /dev/null +++ b/main.cpp @@ -0,0 +1,180 @@ +// 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 +#include "SDL.h" + +#include +#include + +#include "SDL_mixer.h" +#include "SDL_ttf.h" + +#include "BPGame.h" + +BPGame* Game; + +int main(int argc, char *argv[]) { + if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0 ) { + fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); + exit(1); + } + + // init all the SDL stuff + /*putenv((char*)"SDL_VIDEO_WINDOW_POS"); + putenv((char*)"SDL_VIDEO_CENTERED=1"); + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);*/ + + SDL_WM_SetCaption("Brain Party", "Brain Party"); + SDL_WM_SetIcon(SDL_LoadBMP("/opt/brainparty/Content/icon.bmp"), NULL); + + Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048); + TTF_Init(); + + SDL_GLES_Init(SDL_GLES_VERSION_1_1); + + SDL_Surface* screen = SDL_SetVideoMode(0, 0, 16, SDL_SWSURFACE | SDL_FULLSCREEN); + SDL_ShowCursor(0); + + SDL_GLES_Context *context; + + context = SDL_GLES_CreateContext(); + SDL_GLES_MakeCurrent(context); + + // clear the screen + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // load the splash screen texture + Texture* tex_splash = new Texture("hudzillagames", 320, 480); + + // set up all the OpenGL stuff + glViewport(0, 0, 800, 480); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glEnable(GL_LINE_SMOOTH); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + /** + * Rotate by 90 degrees, scale to fullscreen, keep + * aspect ratio and center on screen (see also the + * ConvertCoordinates function in BPGame.h). + **/ + glRotatef(90, 0, 0, 1); + glOrthof(0, 320, 506, -26, -100.0f, 100.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // now render the splash screen... + tex_splash->Draw(0, 0); + + // ...update the screen + SDL_GLES_SwapBuffers(); + + // and flush out any SDL events that are waiting - this makes the window activate + SDL_Event event; + while (SDL_PollEvent(&event)) { } + + // load all the game data + Game = new BPGame(); + Game->Init(320, 480); + + // finally sleep for a second so the splash screen is visible + SDL_Delay(1000); + + // free up the memory; the splash screen isn't used again + delete tex_splash; + + + float seconds_elapsed; + int previous_time = SDL_GetTicks(); + + bool mouse_down = false; + + while (true) { + int new_time = SDL_GetTicks(); + int ticks_elapsed = new_time - previous_time; + previous_time = new_time; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + mouse_down = true; + Game->TouchStart(event.button.x, event.button.y); + break; + case SDL_MOUSEBUTTONUP: + mouse_down = false; + Game->TouchStop(event.button.x, event.button.y); + break; + case SDL_MOUSEMOTION: + if (mouse_down) Game->TouchDrag(event.motion.x, event.motion.y); + break; + case SDL_KEYUP: + if (Game->ShowingClearScores) { + Game->ShowingMessageBox = false; + Game->ShowingClearScores = false; + + if (event.key.keysym.sym == (Uint16)'y') { + Game->ExecuteResetScores(); + } + } + + break; + case SDL_QUIT: + exit(0); + } + } + + if (Game->GameState == DO_QUIT) { + break; + } + + float target_time = 16; + + if (ticks_elapsed < target_time) { + SDL_Delay(target_time - ticks_elapsed); + } + + seconds_elapsed = ticks_elapsed / 1000.0f; + Game->Update(seconds_elapsed); + Game->Draw(); + SDL_GLES_SwapBuffers(); + } + + delete Game; + + SDL_GLES_DeleteContext(context); + SDL_GLES_Quit(); + + TTF_Quit(); + Mix_CloseAudio(); + + SDL_Quit(); + + return EXIT_SUCCESS; +} diff --git a/marbledrop.cpp b/marbledrop.cpp new file mode 100644 index 0000000..3b47a4d --- /dev/null +++ b/marbledrop.cpp @@ -0,0 +1,223 @@ +// 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 "marbledrop.h" +#include "Minigame.h" + +BPMiniGame_MarbleDrop::BPMiniGame_MarbleDrop(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("marbledrop", 320, 416); + + FinishTime = 0; + IsSuccess = false; + + TimeStarted = 0; + + GameTitle = "Marble Drop"; + GameHelp = "I dropped some marbles in the top box. Below that is an empty frame; by tapping the columns in that frame to place marbles, can you drop your marbles in the same order as me?"; + GameHelp2 = "The marbles that will be dropped are shown on the right of the screen. The key here is to look ahead: if there are two places a yellow marble could go, look at the marbles you'll have next to see whether one of them needs to go above a yellow marble in a certain place."; + + MiniGameType = PUZZLE; + + MarbleTypes.Add(TheGame->LoadBitmap("marble_red40", 40, 40)); + MarbleTypes.Add(TheGame->LoadBitmap("marble_green40", 40, 40)); + MarbleTypes.Add(TheGame->LoadBitmap("marble_blue40", 40, 40)); + MarbleTypes.Add(TheGame->LoadBitmap("marble_purple40", 40, 40)); + MarbleTypes.Add(TheGame->LoadBitmap("marble_yellow40", 40, 40)); + + GenerateBoard(); +} + +BPMiniGame_MarbleDrop::~BPMiniGame_MarbleDrop() { + SAFE_DELETE(sfcBackground); + MarbleTypes.Clear(); + MarbleOrder.Clear(); + OriginalFirstCol.Clear(); + OriginalSecondCol.Clear(); + OriginalThirdCol.Clear(); + OriginalFourthCol.Clear(); + PlayerFirstCol.Clear(); + PlayerSecondCol.Clear(); + PlayerThirdCol.Clear(); + PlayerFourthCol.Clear(); +} + +void BPMiniGame_MarbleDrop::OnMouseMove() { + +} + +void BPMiniGame_MarbleDrop::OnMouseDown() { + +} + +void BPMiniGame_MarbleDrop::OnMouseUp() { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 11, 212, 51, 197)) { + if (PlayerFirstCol.Count == 4) return; + MakeMove(PlayerFirstCol); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 71, 212, 51, 197)) { + if (PlayerSecondCol.Count == 4) return; + MakeMove(PlayerSecondCol); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 131, 212, 51, 197)) { + if (PlayerThirdCol.Count == 4) return; + MakeMove(PlayerThirdCol); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 191, 212, 51, 197)) { + if (PlayerFourthCol.Count == 4) return; + MakeMove(PlayerFourthCol); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 9, 7, 241, 197)) { + MessageBox::Show("This is the original box, where I placed my marbles. Tap anywhere in the bottom box to place marbles in the same places I put mine here.", GameTitle); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 259, 4, 58, 409)) { + MessageBox::Show("These are the marbles you need to place in the bottom box - place them by tapping any of the columns in the bottom box. The marble at the top of this list will be used next.", GameTitle); + } + + if (PlayerFirstCol.Count == 4 && PlayerSecondCol.Count == 4 && PlayerThirdCol.Count == 4 && PlayerFourthCol.Count == 4) { + // player has placed all their pieces; check their answer + IsSuccess = CheckAnswer(); + FinishTime = TheGame->TickCount; + } else { + // player hasn't placed all their pieces; check their answer anyway + if (!CheckAnswer()) { + MessageBox::Show("Oops - that's not right! You need to place the marbles in the exact same position as shown in the top. Try starting again...", "Try again..."); + GenerateBoard(); + } + } +} + + +bool BPMiniGame_MarbleDrop::CheckAnswer() { + for (int i = 0; i < OriginalFirstCol.Count; ++i) { + if (PlayerFirstCol.Count > i && OriginalFirstCol[i] != PlayerFirstCol[i]) return false; + } + + for (int i = 0; i < OriginalSecondCol.Count; ++i) { + if (PlayerSecondCol.Count > i && OriginalSecondCol[i] != PlayerSecondCol[i]) return false; + } + + for (int i = 0; i < OriginalThirdCol.Count; ++i) { + if (PlayerThirdCol.Count > i && OriginalThirdCol[i] != PlayerThirdCol[i]) return false; + } + + for (int i = 0; i < OriginalFourthCol.Count; ++i) { + if (PlayerFourthCol.Count > i && OriginalFourthCol[i] != PlayerFourthCol[i]) return false; + } + + // if we've made it this far, all the columns match! + return true; +} + +void BPMiniGame_MarbleDrop::MakeMove(BPList& playerlist) { + TheGame->PlaySound("down"); + + int nextmarble = MarbleOrder[0]; + playerlist.Add(nextmarble); + + MarbleOrder.RemoveAt(0); +} + +void BPMiniGame_MarbleDrop::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_MarbleDrop::GetWeight() { + if (IsSuccess) { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(660 - round(TimePassed * 8)); + } else { + return 0; + } +} + +void BPMiniGame_MarbleDrop::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < OriginalFirstCol.Count; ++i) TheGame->DrawImage(MarbleTypes[OriginalFirstCol[i]], 17, 154 - (i * 45)); + for (int i = 0; i < OriginalSecondCol.Count; ++i) TheGame->DrawImage(MarbleTypes[OriginalSecondCol[i]], 77, 154 - (i * 45)); + for (int i = 0; i < OriginalThirdCol.Count; ++i) TheGame->DrawImage(MarbleTypes[OriginalThirdCol[i]], 137, 154 - (i * 45)); + for (int i = 0; i < OriginalFourthCol.Count; ++i) TheGame->DrawImage(MarbleTypes[OriginalFourthCol[i]], 197, 154 - (i * 45)); + + for (int i = 0; i < PlayerFirstCol.Count; ++i) TheGame->DrawImage(MarbleTypes[PlayerFirstCol[i]], 17, 359 - (i * 45)); + for (int i = 0; i < PlayerSecondCol.Count; ++i) TheGame->DrawImage(MarbleTypes[PlayerSecondCol[i]], 77, 359 - (i * 45)); + for (int i = 0; i < PlayerThirdCol.Count; ++i) TheGame->DrawImage(MarbleTypes[PlayerThirdCol[i]], 137, 359 - (i * 45)); + for (int i = 0; i < PlayerFourthCol.Count; ++i) TheGame->DrawImage(MarbleTypes[PlayerFourthCol[i]], 197, 359 - (i * 45)); + + if (MarbleOrder.Count > 0) TheGame->DrawImage(MarbleTypes[MarbleOrder[0]], 265, 150); + if (MarbleOrder.Count > 1) TheGame->DrawImage(MarbleTypes[MarbleOrder[1]], 265, 194); + if (MarbleOrder.Count > 2) TheGame->DrawImage(MarbleTypes[MarbleOrder[2]], 265, 238); + if (MarbleOrder.Count > 3) TheGame->DrawImage(MarbleTypes[MarbleOrder[3]], 265, 282); + if (MarbleOrder.Count > 4) TheGame->DrawImage(MarbleTypes[MarbleOrder[4]], 265, 326); + if (MarbleOrder.Count > 5) TheGame->DrawImage(MarbleTypes[MarbleOrder[5]], 265, 370); +} + +void BPMiniGame_MarbleDrop::Tick() { + if (FinishTime != 0) { + if (FinishTime + 250 < TheGame->TickCount) { + if (IsSuccess) { + Success(); + } else { + Failure(); + } + } + } +} + +void BPMiniGame_MarbleDrop::GenerateBoard() { + MarbleOrder.Clear(); + OriginalFirstCol.Clear(); + OriginalSecondCol.Clear(); + OriginalThirdCol.Clear(); + OriginalFourthCol.Clear(); + PlayerFirstCol.Clear(); + PlayerSecondCol.Clear(); + PlayerThirdCol.Clear(); + PlayerFourthCol.Clear(); + + + // this randomises the list of possible positions; avoiding the possibility of placing more than four pieces in a given column + BPList PositionOrder; + + for (int i = 1; i < 5; ++i) { + PositionOrder.Add(i); + PositionOrder.Add(i); + PositionOrder.Add(i); + PositionOrder.Add(i); + } + + PositionOrder.Shuffle(); + + for (int i = 0; i < 16; ++i) { + // add a random marble type + int marbletype = TheGame->RandomRange(0, MarbleTypes.Count - 1); + + MarbleOrder.Add(marbletype); + + switch (PositionOrder[i]) { + case 1: + OriginalFirstCol.Add(marbletype); + break; + + case 2: + OriginalSecondCol.Add(marbletype); + break; + + case 3: + OriginalThirdCol.Add(marbletype); + break; + + case 4: + OriginalFourthCol.Add(marbletype); + break; + } + } +} diff --git a/marbledrop.h b/marbledrop.h new file mode 100644 index 0000000..a9ba72a --- /dev/null +++ b/marbledrop.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef __MARBLEDROP_H__ +#define __MARBLEDROP_H__ + +#include "Minigame.h" + +class BPMiniGame_MarbleDrop : public BPMiniGame { +public: + BPMiniGame_MarbleDrop(BPGame* game); + ~BPMiniGame_MarbleDrop(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + bool CheckAnswer(); + void MakeMove(BPList& playerlist); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void GenerateBoard(); + +protected: + Texture* sfcBackground; + + BPPList MarbleTypes; + + BPList MarbleOrder; + + BPList OriginalFirstCol; + BPList OriginalSecondCol; + BPList OriginalThirdCol; + BPList OriginalFourthCol; + + BPList PlayerFirstCol; + BPList PlayerSecondCol; + BPList PlayerThirdCol; + BPList PlayerFourthCol; + + int FinishTime; + bool IsSuccess; + + int TimeStarted; +}; + +#endif diff --git a/memoryblox.cpp b/memoryblox.cpp new file mode 100644 index 0000000..680773c --- /dev/null +++ b/memoryblox.cpp @@ -0,0 +1,249 @@ +// 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 "memoryblox.h" +#include "Minigame.h" + +BPMiniGame_MemoryBlox::BPMiniGame_MemoryBlox(BPGame* game) : BPMiniGame(game) { + IsShowing = true; // are all the tiles revealed? + TimeStarted = 0; + LastStateChange = -1; + + GameState = WAITING; + + GameTitle = "Memoryblox"; + GameHelp = "When you're ready, tap the screen to hide the colours of the blocks. Now find 6 blocks of the same colour to clear them, ie 6 red blocks or 6 blue blocks. How fast can you match them all and clear the whole board?"; + GameHelp2 = "To play, select at least six red OR blue squares. That is, the six squares you choose must be entirely red or entirely blue - if you select two red then choose a blue, you'll need to try again."; + + MiniGameType = PUZZLE; + + sfcBackground = TheGame->LoadBitmap("memoryblox", 320, 416); + sfcUnknown = TheGame->LoadBitmap("block_grey", 48, 48); + sfcRed = TheGame->LoadBitmap("block_red", 48, 48); + sfcBlue = TheGame->LoadBitmap("block_blue", 48, 48); + + BPList colours; + + for (int i = 0; i < 24; ++i) { + colours.Add(false); + colours.Add(true); + } + + colours.Shuffle(); + + for (int i = 0; i < 48; ++i) { + BPMiniGame_MemoryBlox_Tile* tile = new BPMiniGame_MemoryBlox_Tile(); + + switch (colours[i]) { + case true: + tile->IsRed = true; + break; + case false: + tile->IsRed = false; + break; + } + + TileSet.Add(tile); + } +} + +BPMiniGame_MemoryBlox::~BPMiniGame_MemoryBlox() { + TileSet.Clear(); + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcRed); + SAFE_DELETE(sfcBlue); + SAFE_DELETE(sfcUnknown); +} + +void BPMiniGame_MemoryBlox::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_MemoryBlox::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(670 - round(TimePassed * 5)); +} + +void BPMiniGame_MemoryBlox::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + int xpos; + int ypos; + + for (int i = 0; i < TileSet.Count; ++i) { + ypos = DivRem(i, 6, xpos); + + if (IsShowing) { + if (TileSet[i]->IsRed) { + TheGame->DrawImage(sfcRed, 16 + (xpos * 48), 16 + (ypos * 48)); + } else { + TheGame->DrawImage(sfcBlue, 16 + (xpos * 48), 16 + (ypos * 48)); + } + } else { + if (TileSet[i]->IsCleared) { + // do nothing; leave a nice black hole so they know it's cleared + } else { + if (TileSet[i]->IsSelected) { + if (TileSet[i]->IsRed) { + TheGame->DrawImage(sfcRed, 16 + (xpos * 48), 16 + (ypos * 48)); + } else { + TheGame->DrawImage(sfcBlue, 16 + (xpos * 48), 16 + (ypos * 48)); + } + } else { + TheGame->DrawImage(sfcUnknown, 16 + (xpos * 48), 16 + (ypos * 48)); + } + } + } + } + + if (GameState == WRONG) { + if (LastStateChange + 250 < TheGame->TickCount) { + // wrong! + } + + if (LastStateChange + 850 < TheGame->TickCount) { + GameState = WAITING; + LastStateChange = TheGame->TickCount; + + // reset the board, excluding cleared pieces + for (int i = 0; i < TileSet.Count; ++i) { + if (!TileSet[i]->IsCleared) { + TileSet[i]->IsSelected = false; + } + } + } + } else if (GameState == CORRECT) { + if (LastStateChange + 250 < TheGame->TickCount) { + for (int i = 0; i < TileSet.Count; ++i) { + if (TileSet[i]->IsSelected) { + TileSet[i]->IsSelected = false; + TileSet[i]->IsCleared = true; + } + } + + + int numleft = 0; + + for (int i = 0; i < TileSet.Count; ++i) { + if (!TileSet[i]->IsCleared) { + ++numleft; + } + } + + if (numleft == 0) { + GameState = SUCCESS; + LastStateChange = TheGame->TickCount; + } else { + GameState = WAITING; + LastStateChange = TheGame->TickCount; + } + } + } +} + +void BPMiniGame_MemoryBlox::Tick() { + if (GameState == SUCCESS && LastStateChange + 250 < TheGame->TickCount) { + Success(); + } +} + +void BPMiniGame_MemoryBlox::OnMouseDown() { + +} + +void BPMiniGame_MemoryBlox::OnMouseMove() { + +} + +void BPMiniGame_MemoryBlox::OnMouseUp() { + if (IsShowing) { + IsShowing = false; + TheGame->PlaySound("swoosh_long"); + return; + } + + if (GameState != WAITING) return; // don't let people make a move straight after a mistake or a correct answer + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 16, 16, 292, 390)) { + // we slightly overlap the boxes in this game, which means the far right boxes + // are slightly wider than the others. If we don't do anything special, clicking + // on the right edge of those boxes will be detected as clicking on the far left + // box, because it wraps over the size of the boxes. + // So, if the click was >= 305, we need to subtract 10 to skip this problem. + + int xpos = TouchEvent.X; + if (xpos > 305) xpos -= 10; + + int xoffset = (xpos - 17) / 48; + int yoffset = (TouchEvent.Y - 17) / 48; + + int totaloffset = (yoffset * 6) + xoffset; + + // this shouldn't be possible, but just in case! + if (totaloffset >= 48) return; + + // if the square has already been selected, ignore the click + if (TileSet[totaloffset]->IsSelected) return; + + TileSet[totaloffset]->IsSelected = true; + + if (CheckMove()) { + int numselected = 0; + + for (int i = 0; i < TileSet.Count; ++i) { + if (TileSet[i]->IsSelected && !TileSet[i]->IsCleared) { + ++numselected; + } + } + + if (numselected >= 6) { + LastStateChange = TheGame->TickCount; + GameState = CORRECT; + } + } else { + LastStateChange = TheGame->TickCount; + GameState = WRONG; + } + } +} + +bool BPMiniGame_MemoryBlox::CheckMove() { + bool firstsquare = true; + bool isred = false; + + for (int i = 0; i < TileSet.Count; ++i) { + if (TileSet[i]->IsSelected && !TileSet[i]->IsCleared) { + if (firstsquare) { + // set the initial selected colour, so we know what to compare against + isred = TileSet[i]->IsRed; + firstsquare = false; + TheGame->PlaySound("gem_select"); + } else { + if (TileSet[i]->IsRed != isred) { + // they selected tiles with different colours! + TheGame->PlaySound("wrong"); + return false; + } else { + TheGame->PlaySound("gem_select"); + } + } + } + } + + // current selection is OK; return good move + return true; +} diff --git a/memoryblox.h b/memoryblox.h new file mode 100644 index 0000000..c807863 --- /dev/null +++ b/memoryblox.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef __MEMORYBLOX_H__ +#define __MEMORYBLOX_H__ + +#include "Minigame.h" + +class BPMiniGame_MemoryBlox_Tile { +public: + bool IsRed; + bool IsSelected; + bool IsCleared; +}; + +class BPMiniGame_MemoryBlox : public BPMiniGame { +public: + BPMiniGame_MemoryBlox(BPGame* game); + ~BPMiniGame_MemoryBlox(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + bool CheckMove(); + void BadMove(); + +protected: + BPPList TileSet; + + Texture* sfcBackground; + Texture* sfcRed; + Texture* sfcBlue; + Texture* sfcUnknown; + + bool IsShowing; // are all the tiles revealed? + MiniGameStates GameState; + int LastStateChange; + + int TimeStarted; +}; + +#endif diff --git a/memorybox.cpp b/memorybox.cpp new file mode 100644 index 0000000..38bac19 --- /dev/null +++ b/memorybox.cpp @@ -0,0 +1,271 @@ +// 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 "memorybox.h" +#include "Minigame.h" + +BPMiniGame_MemoryBox::BPMiniGame_MemoryBox(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("memorybox", 320, 416); + sfcUnknownBox = TheGame->LoadBitmap("box_question", 80, 80); + + LastStateChange = OriginalLowestNumber = LowestNumber = TimeStarted = NumWrong = NumTries = 0; + Level = 4.0f; + ShowDelay = 1000; + + GameTitle = "Memory Box"; + GameHelp = "Look at the numbers on the screen, then, when they disappear, tap on the boxes in the order of their numbers, lowest to highest. It might start easy, but I'll be adding more numbers over time!"; + GameHelp2 = "This game requires you to look at numbers very quickly, and remember their positions. When they disappear, you need to tap them in the correct, ascending order. For example, if you see the numbers 5, 9, 1 and 3, you need to tap them in the order 1, 3, 5, 9."; + + MiniGameType = PUZZLE; + + Numbers[1] = TheGame->LoadBitmap("box_1", 80, 80); + Numbers[2] = TheGame->LoadBitmap("box_2", 80, 80); + Numbers[3] = TheGame->LoadBitmap("box_3", 80, 80); + Numbers[4] = TheGame->LoadBitmap("box_4", 80, 80); + Numbers[5] = TheGame->LoadBitmap("box_5", 80, 80); + Numbers[6] = TheGame->LoadBitmap("box_6", 80, 80); + Numbers[7] = TheGame->LoadBitmap("box_7", 80, 80); + Numbers[8] = TheGame->LoadBitmap("box_8", 80, 80); + Numbers[9] = TheGame->LoadBitmap("box_9", 80, 80); + Numbers[10] = TheGame->LoadBitmap("box_10", 80, 80); + Numbers[11] = TheGame->LoadBitmap("box_11", 80, 80); + Numbers[12] = TheGame->LoadBitmap("box_12", 80, 80); + Numbers[13] = TheGame->LoadBitmap("box_13", 80, 80); + Numbers[14] = TheGame->LoadBitmap("box_14", 80, 80); + Numbers[15] = TheGame->LoadBitmap("box_15", 80, 80); + Numbers[16] = TheGame->LoadBitmap("box_16", 80, 80); + + BPList boxplaces1; + boxplaces1.Add(BPPoint(70, 118)); + boxplaces1.Add(BPPoint(170, 118)); + boxplaces1.Add(BPPoint(70, 218)); + boxplaces1.Add(BPPoint(170, 218)); + Layouts[4] = boxplaces1; + + BPList boxplaces2; + boxplaces2.Add(BPPoint(20, 13)); + boxplaces2.Add(BPPoint(220, 13)); + boxplaces2.Add(BPPoint(120, 168)); + boxplaces2.Add(BPPoint(20, 323)); + boxplaces2.Add(BPPoint(220, 323)); + Layouts[5] = boxplaces2; + + BPList boxplaces3; + boxplaces3.Add(BPPoint(20, 113)); + boxplaces3.Add(BPPoint(120, 113)); + boxplaces3.Add(BPPoint(220, 113)); + boxplaces3.Add(BPPoint(20, 194)); + boxplaces3.Add(BPPoint(120, 194)); + boxplaces3.Add(BPPoint(220, 194)); + Layouts[6] = boxplaces3; + + BPList boxplaces4; + boxplaces4.Add(BPPoint(20, 13)); + boxplaces4.Add(BPPoint(220, 13)); + boxplaces4.Add(BPPoint(120, 113)); + boxplaces4.Add(BPPoint(120, 213)); + boxplaces4.Add(BPPoint(20, 313)); + boxplaces4.Add(BPPoint(120, 313)); + boxplaces4.Add(BPPoint(220, 313)); + Layouts[7] = boxplaces4; + + BPList boxplaces5; + boxplaces5.Add(BPPoint(120, 13)); + boxplaces5.Add(BPPoint(120, 113)); + boxplaces5.Add(BPPoint(120, 213)); + boxplaces5.Add(BPPoint(120, 313)); + boxplaces5.Add(BPPoint(20, 113)); + boxplaces5.Add(BPPoint(20, 213)); + boxplaces5.Add(BPPoint(220, 113)); + boxplaces5.Add(BPPoint(220, 213)); + Layouts[8] = boxplaces5; +} + +BPMiniGame_MemoryBox::~BPMiniGame_MemoryBox() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcUnknownBox); + + for(map::iterator it = Numbers.begin(); it != Numbers.end(); ++it) { + SAFE_DELETE(it->second); + } + + Numbers.clear(); + + Layouts.clear(); + ActiveBoxes.Clear(); +} + +void BPMiniGame_MemoryBox::Start() { + GenerateBoxes(); + + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_MemoryBox::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(550 - (NumWrong * 50) - round(TimePassed * 1.2)); +} + +void BPMiniGame_MemoryBox::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < ActiveBoxes.Count; ++i) { + BPMiniGame_MemoryBox_Box* box = ActiveBoxes[i]; + + if (GameState == SHOWING || box->Showing) { + TheGame->DrawImage(Numbers[box->Number], box->X, box->Y); + } else { + TheGame->DrawImage(sfcUnknownBox, box->X, box->Y); + } + } + + if (GameState == CORRECT) { + // note that we use a 150ms delay before showing the answer, so players can see the results of their actions + if (LastStateChange + 150 < TheGame->TickCount) { + RenderCorrect(); + } + } else if (GameState == WRONG) { + if (LastStateChange + 150 < TheGame->TickCount) { + RenderWrong(); + } + } +} + +void BPMiniGame_MemoryBox::Tick() { + if (GameState == SHOWING) { + if (LastStateChange + ShowDelay < TheGame->TickCount) { + SetGameState(GUESSING); + } + } else if (GameState == CORRECT) { + if (LastStateChange + 800 < TheGame->TickCount) { + if (Level == 9.0f || NumTries >= 12) { + Success(); + } else { + GenerateBoxes(); + } + } + } else if (GameState == WRONG) { + if (LastStateChange + 800 < TheGame->TickCount) { + if (NumTries >= 12) { + Success(); + } else { + for (int i = 0; i < ActiveBoxes.Count; ++i) { + ActiveBoxes[i]->Showing = false; + } + + ShowDelay += 155; + ++NumTries; + SetGameState(SHOWING); + LowestNumber = OriginalLowestNumber; + } + } + } + +} + +void BPMiniGame_MemoryBox::OnMouseDown() { + +} + +void BPMiniGame_MemoryBox::OnMouseMove() { + +} + +void BPMiniGame_MemoryBox::OnMouseUp() { + if (GameState == SHOWING) { + // they've tapped the screen - hurry up! + SetGameState(GUESSING); + } + + if (GameState != GUESSING) return; + + for (int i = 0; i < ActiveBoxes.Count; ++i) { + BPMiniGame_MemoryBox_Box* box = ActiveBoxes[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, box->X, box->Y, 80, 80)) { + // this box was clicked + if (box->Showing) continue; // don't let people tap on a showing box twice + + if (box->Number == LowestNumber) { + // this is the correct box! + box->Showing = true; + + // find the next lowest number + LowestNumber = 999; + + for (int i = 0; i < ActiveBoxes.Count; ++i) { + BPMiniGame_MemoryBox_Box* box2 = ActiveBoxes[i]; + + if (!box2->Showing) { + if (box2->Number < LowestNumber) LowestNumber = box2->Number; + } + } + + if (LowestNumber == 999) { + // no more boxes to reveal - player has won! + SetGameState(CORRECT); + TheGame->PlaySound("correct"); + Level += 0.5f; + } else { + // more boxes to reveal - play normal reveal sound + TheGame->PlaySound("card_flip"); + } + } else { + // oops - wrong box! + TheGame->PlaySound("wrong"); + box->Showing = true; + SetGameState(WRONG); + ++NumWrong; + } + } + } +} + +void BPMiniGame_MemoryBox::SetGameState(MiniGameStates gs) { + GameState = gs; + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_MemoryBox::GenerateBoxes() { + // show the boxes for longer each time; helps bad players and makes the harder levels easier + ShowDelay += 155; + ++NumTries; + + ActiveBoxes.Clear(); + + // the level count is stored as a float so we can add 0.5 each success and go up a level every two successes + int ilevel = floor(Level); + + Layouts[ilevel].Shuffle(); + + int num = TheGame->RandomRange(1, 4); + OriginalLowestNumber = num; + LowestNumber = num; + + for (int i = 0; i < ilevel; ++i) { + BPMiniGame_MemoryBox_Box* box = new BPMiniGame_MemoryBox_Box(); + box->Number = num; + num += TheGame->RandomRange(1, 2); // this is the number for the next box, to ensure we don't have the same number twice + + box->X = Layouts[ilevel][i].X; + box->Y = Layouts[ilevel][i].Y; + + ActiveBoxes.Add(box); + } + + SetGameState(SHOWING); +} diff --git a/memorybox.h b/memorybox.h new file mode 100644 index 0000000..674d05d --- /dev/null +++ b/memorybox.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef __MEMORYBOX_H__ +#define __MEMORYBOX_H__ + +#include "Minigame.h" + +class BPMiniGame_MemoryBox_Box { +public: + int Number; + int X; + int Y; + bool Showing; + + BPMiniGame_MemoryBox_Box() { + Number = X = Y = 0; + Showing = false; + } +}; + + +class BPMiniGame_MemoryBox : public BPMiniGame { +public: + BPMiniGame_MemoryBox(BPGame* game); + ~BPMiniGame_MemoryBox(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void SetGameState(MiniGameStates gs); + void GenerateBoxes(); + +protected: + Texture* sfcBackground; + + map Numbers; + Texture* sfcUnknownBox; + + map > Layouts; + BPPList ActiveBoxes; + + MiniGameStates GameState; + int LastStateChange; + float Level; + int OriginalLowestNumber; // the lowest number of all the boxes on the screen; this doesn't change for any given sequence of boxes, which is important because boxes are re-shown when the player makes a mistake + int LowestNumber; + int ShowDelay; + + int TimeStarted; + int NumWrong; + int NumTries; +}; + +#endif diff --git a/memorymaths.cpp b/memorymaths.cpp new file mode 100644 index 0000000..29fb0e6 --- /dev/null +++ b/memorymaths.cpp @@ -0,0 +1,408 @@ +// 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 "memorymaths.h" +#include "Minigame.h" + +BPMiniGame_MemoryMaths::BPMiniGame_MemoryMaths(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("memorymaths", 320, 416); + sfcRemember = TheGame->LoadBitmap("memorymaths_remember", 320, 416); + + TimeStarted = Result = NumWrong = NumTries = CurrentLevel = LastOp = 0; + LastStateChange = -1; + SuccessTime = -1; + + sfcCurrentAnswer = NULL; + CurrentAnswer = new string(); + + LastWrong = false; // when set to be true, re-show the "Remember" screen regardless of the score and don't increment the level counter + + GameTitle = "Memory Maths"; + GameHelp = "I'm going to assign a number to different food, and you need to remember how much each food item is worth, then answer some simple questions. Tap OK to submit your answer, or tap the answer box to clear it."; + GameHelp2 = "You'll be shown a list of foods, and each food will have a number assigned to it. When you're ready, tap the screen, and a question will be asked based around those food numbers, eg \"What is banana multiplied by banana?\" Remember: tap the answer box if you need to clear."; + + MiniGameType = PUZZLE; + + Food.Add(TheGame->LoadBitmap("card_1", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_2", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_3", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_4", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_5", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_6", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_7", 64, 64)); + Food.Add(TheGame->LoadBitmap("card_8", 64, 64)); + + sfcNumbers.Add(NULL); + + for (int i = 1; i < 10; ++i) { + Numbers.Add(i); + + SpriteFont* tex = NULL; + ostringstream number; + number << "= " << i; + TheGame->AllocString(&tex, number.str().c_str(), XLARGE, 64, 64, CENTRED, true); + sfcNumbers.Add(tex); + } + + Food.Shuffle(); + Numbers.Shuffle(); + + SpriteFont* tex1 = NULL; + TheGame->AllocString(&tex1, "+", XLARGE, 30, 40, CENTRED, true); + sfcOperators["+"] = tex1; + + + SpriteFont* tex2 = NULL; + TheGame->AllocString(&tex2, "-", XLARGE, 30, 40, CENTRED, true); + sfcOperators["-"] = tex2; + + + SpriteFont* tex3 = NULL; + TheGame->AllocString(&tex3, "x", XLARGE, 30, 40, CENTRED, true); + sfcOperators["X"] = tex3; + + LevelUp(); +} + +BPMiniGame_MemoryMaths::~BPMiniGame_MemoryMaths() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcRemember); + Food.Clear(); + Numbers.Clear(); + FoodNumbers.Clear(); + EquationFood.Clear(); + EquationOps.Clear(); + sfcNumbers.Clear(); + + for(map::iterator it = sfcOperators.begin(); it != sfcOperators.end(); ++it) { + SAFE_DELETE(it->second); + } + + SAFE_DELETE(CurrentAnswer); + SAFE_DELETE(sfcCurrentAnswer); +} + +void BPMiniGame_MemoryMaths::OnMouseDown() { + +} + +void BPMiniGame_MemoryMaths::OnMouseMove() { + +} + +void BPMiniGame_MemoryMaths::OnMouseUp() { + switch (GameState) { + case REMEMBER: + CurrentAnswer->clear(); + AnswerChanged(); + GameState = GUESSING; + break; + + case GUESSING: + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 351, 96, 50)) { + ButtonClicked("0"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 290, 96, 50)) { + ButtonClicked("1"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 290, 96, 50)) { + ButtonClicked("2"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 290, 96, 50)) { + ButtonClicked("3"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 230, 96, 50)) { + ButtonClicked("4"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 230, 96, 50)) { + ButtonClicked("5"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 230, 96, 50)) { + ButtonClicked("6"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 169, 96, 50)) { + ButtonClicked("7"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 169, 96, 50)) { + ButtonClicked("8"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 169, 96, 50)) { + ButtonClicked("9"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 351, 96, 50)) { + FlipSign(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 351, 96, 50)) { + if (CurrentAnswer->length() == 0) return; + if (CurrentAnswer->compare("-") == 0) return; + CheckAnswer(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 107, 300, 50)) { + CurrentAnswer->clear(); + AnswerChanged(); + TheGame->PlaySound("wrong"); + } + + break; + } +} + +void BPMiniGame_MemoryMaths::ButtonClicked(const char* btn) { + if (CurrentAnswer->length() > 8) return; + + CurrentAnswer->append(btn); + AnswerChanged(); + + TheGame->PlaySound("mouse_click"); +} + +void BPMiniGame_MemoryMaths::FlipSign() { + if (CurrentAnswer->length() == 0) { + CurrentAnswer->append("-"); + AnswerChanged(); + return; + } + + if (CurrentAnswer->substr(0, 1) == "-") { + CurrentAnswer->erase(0, 1); + } else { + CurrentAnswer->insert(0, "-"); + } + + AnswerChanged(); +} + +void BPMiniGame_MemoryMaths::CheckAnswer() { + + if (atoi(CurrentAnswer->c_str()) == Result) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + ++NumWrong; + } + + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_MemoryMaths::LevelUp() { + if (!LastWrong) ++CurrentLevel; + CurrentAnswer->clear(); + AnswerChanged(); + ++NumTries; + + if (NumTries >= 10) { + if (SuccessTime == -1) SuccessTime = TheGame->TickCount; + return; + } + + EquationFood.Clear(); + EquationOps.Clear(); + + if (LastWrong) { + // the player got the answer wrong; don't add any more food regardless of the level, + // but re-show the current food numbers instead to give them a reminder + GameState = REMEMBER; + LastOp = TheGame->RandomRange(0, 2); // start with a random operator + } else { + switch (CurrentLevel) { + case 1: + FoodNumbers.Add(0); + FoodNumbers.Add(1); + GameState = REMEMBER; + LastOp = TheGame->RandomRange(0, 2); // start with a random operator + + break; + + case 4: + FoodNumbers.Add(2); + GameState = REMEMBER; + LastOp = TheGame->RandomRange(0, 2); // start with a random operator + break; + + case 7: + FoodNumbers.Add(3); + GameState = REMEMBER; + LastOp = TheGame->RandomRange(0, 2); // start with a random operator + break; + + case 9: + FoodNumbers.Add(4); + GameState = REMEMBER; + LastOp = TheGame->RandomRange(0, 2); // start with a random operator + break; + + default: + GameState = GUESSING; + break; + } + } + + if (CurrentLevel < 6) { + AddFood(); + AddOp(); + AddFood(); + } else { + AddFood(); + AddOp(); + AddFood(); + AddOp(); + AddFood(); + } + + Solve(); + + LastStateChange = TheGame->TickCount; + LastWrong = false; // clear this flag +} + +void BPMiniGame_MemoryMaths::AddFood() { + EquationFood.Add(TheGame->RandomRange(0, FoodNumbers.Count - 1)); +} + +void BPMiniGame_MemoryMaths::AddOp() { + switch (LastOp) { + case 0: + EquationOps.Add("+"); + break; + + case 1: + EquationOps.Add("-"); + break; + + case 2: + if (EquationOps.Count > 0) { + // the multiply operator has a higher precedent than addition or subtraction + // yet most people will solve the equation left to right and thus get the wrong answer + // if multiply is used as the second operator. To solve this, never allow the + // multiply operator to be used if it isn't the first operator + EquationOps.Add("+"); + } else { + EquationOps.Add("X"); + } + break; + } + + ++LastOp; + if (LastOp > 2) LastOp = 0; +} + +void BPMiniGame_MemoryMaths::Solve() { + Result = 0; + + switch (EquationFood.Count) { + case 2: + if (EquationOps[0] == "+") { + Result = Numbers[EquationFood[0]] + Numbers[EquationFood[1]]; + } else if (EquationOps[0] == "-") { + Result = Numbers[EquationFood[0]] - Numbers[EquationFood[1]]; + } else { + Result = Numbers[EquationFood[0]] * Numbers[EquationFood[1]]; + } + + break; + case 3: + if (EquationOps[0] == "X") { + Result = Numbers[EquationFood[0]] * Numbers[EquationFood[1]]; + } else if (EquationOps[0] == "+") { + Result = Numbers[EquationFood[0]] + Numbers[EquationFood[1]]; + } else { + Result = Numbers[EquationFood[0]] - Numbers[EquationFood[1]]; + } + + if (EquationOps[1] == "+") { + Result += Numbers[EquationFood[2]]; + } else { + Result -= Numbers[EquationFood[2]]; + } + + break; + } +} + +void BPMiniGame_MemoryMaths::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_MemoryMaths::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax((550 - (NumWrong * 75)) - round(TimePassed)); +} + +void BPMiniGame_MemoryMaths::Render() { + switch (GameState) { + case REMEMBER: + TheGame->DrawImage(sfcRemember, 0, 0); + + for (int i = 0; i < FoodNumbers.Count; ++i) { + int foodnum = FoodNumbers[i]; + TheGame->DrawImage(Food[foodnum], 98, 54 + (70 * i)); + TheGame->DrawString(sfcNumbers[Numbers[foodnum]], WHITE, 168, 61 + (70 * i)); + + if (i == 5) { + break; + } + } + + break; + + default: + TheGame->DrawImage(sfcBackground, 0, 0); + + // this stops an equation being shown once we've won the game + if (SuccessTime == -1) { + switch (EquationFood.Count) { + case 2: + TheGame->DrawImage(Food[EquationFood[0]], 73, 16); + TheGame->DrawString(sfcOperators[EquationOps[0]], WHITE, 144, 25); + TheGame->DrawImage(Food[EquationFood[1]], 183, 16); + break; + + case 3: + TheGame->DrawImage(Food[EquationFood[0]], 18, 16); + TheGame->DrawString(sfcOperators[EquationOps[0]], WHITE, 90, 23); + TheGame->DrawImage(Food[EquationFood[1]], 128, 16); + TheGame->DrawString(sfcOperators[EquationOps[1]], WHITE, 200, 23); + TheGame->DrawImage(Food[EquationFood[2]], 238, 16); + break; + } + + TheGame->DrawString(sfcCurrentAnswer, WHITE, 18, 110); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } + } + + break; + } +} + +void BPMiniGame_MemoryMaths::Tick() { + if (SuccessTime != -1 && SuccessTime + 500 < TheGame->TickCount) { + Success(); + return; + } + + if (GameState == CORRECT) { + if (LastStateChange + 500 < TheGame->TickCount) { + LevelUp(); + } + } + + if (GameState == WRONG) { + if (LastStateChange + 500 < TheGame->TickCount) { + LastWrong = true; + LevelUp(); + } + } +} + +void BPMiniGame_MemoryMaths::AnswerChanged() { + TheGame->AllocString(&sfcCurrentAnswer, CurrentAnswer->c_str(), XLARGE, 284, 64, RIGHT, true); +} diff --git a/memorymaths.h b/memorymaths.h new file mode 100644 index 0000000..5f71a6e --- /dev/null +++ b/memorymaths.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef __MEMORYMATHS__ +#define __MEMORYMATHS__ + +#include "Minigame.h" + +class BPMiniGame_MemoryMaths : public BPMiniGame { +public: + BPMiniGame_MemoryMaths(BPGame* game); + ~BPMiniGame_MemoryMaths(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void ButtonClicked(const char* btn); + void FlipSign(); + void CheckAnswer(); + void LevelUp(); + void AddFood(); + void AddOp(); + void Solve(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void AnswerChanged(); +protected: + Texture* sfcBackground; + Texture* sfcRemember; + + int TimeStarted; + + MiniGameStates GameState; + + BPPList Food; + BPList Numbers; + BPList FoodNumbers; + BPPList sfcNumbers; + + BPList EquationFood; + BPList EquationOps; + int LastOp; // store the last operation used, so we always use a different one + + int Result; + int NumWrong; + int NumTries; + + int CurrentLevel; + int LastStateChange; + int SuccessTime; + + bool LastWrong; // when set to be true, re-show the "Remember" screen regardless of the score and don't increment the level counter + + string* CurrentAnswer; + SpriteFont* sfcCurrentAnswer; + + map sfcOperators; +}; + +#endif diff --git a/minesweep.cpp b/minesweep.cpp new file mode 100644 index 0000000..7aeea4b --- /dev/null +++ b/minesweep.cpp @@ -0,0 +1,222 @@ +// 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 "minesweep.h" +#include "Minigame.h" + +BPMiniGame_MineSweep::BPMiniGame_MineSweep(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("minesweep", 320, 416); + sfcTile = TheGame->LoadBitmap("minesweep_tile", 50, 50); + sfcBombTile = TheGame->LoadBitmap("minesweep_bombtile", 50, 50); + + TimeStarted = NumTaps = 0; + WinTime = -1; + + GameTitle = "Minesweep"; + GameHelp = "Tap squares to find mines; squares with mines nearby will show you how many mines are in adjacent squares. There are eight mines, and you need to uncover them in the fewest moves possible - good luck!"; + GameHelp2 = "When you see a number in a square, it means there are that many mines in the eight adjacent squares. You need to use that information to figure out where all 8 mines are. When you have found all the mines, you win - the faster you are, the higher you score!"; + + MiniGameType = LIVELY; + + GenerateMines(); +} + +BPMiniGame_MineSweep::~BPMiniGame_MineSweep() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcTile); + SAFE_DELETE(sfcBombTile); + Tiles.Clear(); +} + +void BPMiniGame_MineSweep::GenerateMines() { + Tiles.Clear(); + + BPMiniGame_MineSweep_Tile* tile; + + int index = 0; + + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 8; ++j) { + tile = new BPMiniGame_MineSweep_Tile(); + tile->Index = index++; + + tile->X = 11 + (i * 50); + tile->Y = 9 + (j * 50); + + tile->Row = j; + tile->Col = i; + + Tiles.Add(tile); + } + } + + // pick which squares are going to have mines + BPList NumList; + + for (int i = 0; i < Tiles.Count; ++i) NumList.Add(i); + NumList.Shuffle(); + + for (int i = 0; i < 8; ++i) { + if (NumList[i] == 40) NumList.SetValue(i, NumList[i + 10]); + + Tiles[NumList[i]]->HasMine = true; + Tiles[NumList[i]]->NumMines = "Eek!"; // this just avoids us showing mines by accident when clicking empty squares + } + + int num_mines; + + // now update all the other squares so they know how many mines are around them + for (int i = 0; i < Tiles.Count; ++i) { + tile = Tiles[i]; + + if (tile->HasMine) continue; + + num_mines = 0; + + if (tile->Row > 0 && tile->Col > 0 && Tiles[i - 9]->HasMine == true) ++num_mines; // up-left one + if (tile->Row > 0 && Tiles[i - 1]->HasMine == true) ++num_mines; // up one + if (tile->Row > 0 && tile->Col < 5 && Tiles[i + 7]->HasMine == true) ++num_mines; // up-right one + + if (tile->Col > 0 && Tiles[i - 8]->HasMine == true) ++num_mines; // left one + if (tile->Col < 5 && Tiles[i + 8]->HasMine == true) ++num_mines; // right one + + if (tile->Row < 7 && tile->Col > 0 && Tiles[i - 7]->HasMine == true) ++num_mines; // down-left one + if (tile->Row < 7 && Tiles[i + 1]->HasMine == true) ++num_mines; // down one + if (tile->Row < 7 && tile->Col < 5 && Tiles[i + 9]->HasMine == true) ++num_mines; // down-right one + + if (num_mines > 0) { + ostringstream mines; + mines << num_mines; + string str = mines.str(); + tile->NumMines = str; + TheGame->AllocString(&tile->sfcNumMines, tile->NumMines.c_str(), LARGE, 50, 35, CENTRED, true); + } else { + tile->NumMines = ""; + } + } + + WinTime = -1; +} + +void BPMiniGame_MineSweep::OnMouseDown() { + +} + +void BPMiniGame_MineSweep::OnMouseMove() { + +} + +void BPMiniGame_MineSweep::OnMouseUp() { + if (WinTime != -1) return; // we've lost; can't click any more squares + + for (int i = 0; i < Tiles.Count; ++i) { + BPMiniGame_MineSweep_Tile* tile = Tiles[i]; + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, tile->X, tile->Y, 50, 50)) { + if (tile->Showing) continue; + + ++NumTaps; + + if (tile->NumMines.size() == 0) { + // this square is empty - are there any other empty squares around? + RevealSquare(tile); + + TheGame->PlaySound("swoosh_long"); + } else { + tile->Showing = true; + + if (tile->HasMine) { + TheGame->PlaySound("explosion"); + } else { + TheGame->PlaySound("card_flip"); + } + } + + break; + } + } + + CheckWin(); +} + +void BPMiniGame_MineSweep::CheckWin() { + int num_found = 0; + + for (int i = 0; i < Tiles.Count; ++i) { + if (Tiles[i]->HasMine && Tiles[i]->Showing) { + ++num_found; + } + } + + if (num_found == 8) { + WinTime = TheGame->TickCount; + } +} + +void BPMiniGame_MineSweep::RevealSquare(BPMiniGame_MineSweep_Tile* tile) { + if (tile->Showing) return; + + tile->Showing = true; + + if (tile->NumMines.size() == 0) { + if (tile->Col > 0 && tile->Row > 0 && !Tiles[tile->Index - 9]->HasMine) RevealSquare(Tiles[tile->Index - 9]); // mine above left + if (tile->Row > 0 && !Tiles[tile->Index - 1]->HasMine) RevealSquare(Tiles[tile->Index - 1]); // mine above + if (tile->Col < 5 && tile->Row > 0 && !Tiles[tile->Index + 7]->HasMine) RevealSquare(Tiles[tile->Index + 7]); // mine above right + + if (tile->Col > 0 && !Tiles[tile->Index - 8]->HasMine) RevealSquare(Tiles[tile->Index - 8]); // mine to the left + if (tile->Col < 5 && !Tiles[tile->Index + 8]->HasMine) RevealSquare(Tiles[tile->Index + 8]); // mine to the right + + if (tile->Col > 0 && tile->Row < 7 && !Tiles[tile->Index - 7]->HasMine) RevealSquare(Tiles[tile->Index - 7]); // mine below left + if (tile->Row < 7 && !Tiles[tile->Index + 1]->HasMine) RevealSquare(Tiles[tile->Index + 1]); // mine below + if (tile->Col < 5 && tile->Row < 7 && !Tiles[tile->Index + 9]->HasMine) RevealSquare(Tiles[tile->Index + 9]); // mine below right + + } +} + +int BPMiniGame_MineSweep::GetWeight() { + return MinMax(950 - (NumTaps * 30)); +} + +void BPMiniGame_MineSweep::Start() { + TimeStarted = TheGame->TickCount; +} + +void BPMiniGame_MineSweep::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Tiles.Count; ++i) { + BPMiniGame_MineSweep_Tile* tile = Tiles[i]; + + if (tile->Showing) { + if (tile->HasMine) { + TheGame->DrawImage(sfcBombTile, tile->X, tile->Y); + } else { + if (tile->NumMines.size() != 0) { + TheGame->DrawString(tile->sfcNumMines, WHITE, tile->X, tile->Y + 7); + } + } + } else { + TheGame->DrawImage(sfcTile, tile->X, tile->Y); + } + } +} + +void BPMiniGame_MineSweep::Tick() { + if (WinTime != -1 && WinTime + 500 < TheGame->TickCount) { + Success(); + return; + } +} diff --git a/minesweep.h b/minesweep.h new file mode 100644 index 0000000..833618c --- /dev/null +++ b/minesweep.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef __MINESWEEP_H__ +#define __MINESWEEP_H__ + +#include "Minigame.h" + +class BPMiniGame_MineSweep_Tile { +public: + int Index; // our position in the tile array + int X; + int Y; + int Row; + int Col; + bool HasMine; + bool Showing; + string NumMines; + SpriteFont* sfcNumMines; + + BPMiniGame_MineSweep_Tile() { + Showing = HasMine = false; + sfcNumMines = NULL; + } + + ~BPMiniGame_MineSweep_Tile() { + SAFE_DELETE(sfcNumMines); + } +}; + + +class BPMiniGame_MineSweep : public BPMiniGame { +public: + BPMiniGame_MineSweep(BPGame* game); + ~BPMiniGame_MineSweep(); + void GenerateMines(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void CheckWin(); + void RevealSquare(BPMiniGame_MineSweep_Tile* tile); + int GetWeight(); + void Start(); + void Render(); + void Tick(); + +protected: + Texture* sfcBackground; + Texture* sfcTile; + Texture* sfcBombTile; + BPPList Tiles; + + int TimeStarted; + int WinTime; + int NumTaps; // used to track the score +}; + +#endif diff --git a/moonjump.cpp b/moonjump.cpp new file mode 100644 index 0000000..7300e8a --- /dev/null +++ b/moonjump.cpp @@ -0,0 +1,213 @@ +// 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 "moonjump.h" +#include "Minigame.h" + +BPMiniGame_MoonJump::BPMiniGame_MoonJump(BPGame* game) : BPMiniGame(game) { + sfcBackground1 = TheGame->LoadBitmap("moonjump1", 320, 512); + sfcBackground2 = TheGame->LoadBitmap("moonjump2", 320, 512); + sfcBackground3 = TheGame->LoadBitmap("moonjump3", 320, 512); + sfcBackground4 = TheGame->LoadBitmap("moonjump4", 320, 512); + sfcBackground5 = TheGame->LoadBitmap("moonjump5", 320, 512); + sfcBackground6 = TheGame->LoadBitmap("moonjump6", 320, 512); + + sfcSheepLeft = TheGame->LoadBitmap("sheep_left", 38, 34); + sfcSheepRight = TheGame->LoadBitmap("sheep_right", 38, 34); + + LastCreateTime = 0; + Y = -96; + SuccessTime = -1; + + SheepCreateDelay = 6000; + + GameTitle = "Moon Jump"; + GameHelp = "Tap on your sheep to have them jump in the air, but make sure you don't lose any! How high can you make them fly?"; + GameHelp2 = "Be careful: every few seconds a new sheep will join your group, and you need to make sure they all stay alive!"; + + MiniGameType = LIVELY; +} + +BPMiniGame_MoonJump::~BPMiniGame_MoonJump() { + SAFE_DELETE(sfcBackground1); + SAFE_DELETE(sfcBackground2); + SAFE_DELETE(sfcBackground3); + SAFE_DELETE(sfcBackground4); + SAFE_DELETE(sfcBackground5); + SAFE_DELETE(sfcBackground6); + + SAFE_DELETE(sfcSheepLeft); + SAFE_DELETE(sfcSheepRight); + + Sheep.Clear(); +} + +void BPMiniGame_MoonJump::Start() { + LastCreateTime = TheGame->TickCount + 3000; + CreateSheep(); +} + +int BPMiniGame_MoonJump::GetWeight() { + return MinMax(Y / 4.7f); // ensures that when they reach the top they get 500g; everything else is pro rata +} + +void BPMiniGame_MoonJump::Render() { + TheGame->DrawImage(sfcBackground6, 0, -2560 + round(Y)); + TheGame->DrawImage(sfcBackground5, 0, -2048 + round(Y)); + TheGame->DrawImage(sfcBackground4, 0, -1536 + round(Y)); + TheGame->DrawImage(sfcBackground3, 0, -1024 + round(Y)); + TheGame->DrawImage(sfcBackground2, 0, -512 + round(Y)); + TheGame->DrawImage(sfcBackground1, 0, round(Y)); + + for (int i = 0; i < Sheep.Count; ++i) { + BPMiniGame_MoonJump_Sheep* sheep = Sheep[i]; + + if (sheep->MovingRight) { + TheGame->DrawImage(sfcSheepRight, round(sheep->X), round(sheep->Y)); + } else { + TheGame->DrawImage(sfcSheepLeft, round(sheep->X), round(sheep->Y)); + } + } +} + +void BPMiniGame_MoonJump::Tick() { + if (SuccessTime != -1 && SuccessTime + 1000 < TheGame->TickCount) { + Success(); + return; + } + + if (Y < 2372) { + // only create balloons when we're in the atmosphere! + if (LastCreateTime + SheepCreateDelay < TheGame->TickCount) { + CreateSheep(); + } + } + + for (int i = 0; i < Sheep.Count; ++i) { + BPMiniGame_MoonJump_Sheep* sheep = Sheep[i]; + + sheep->X += sheep->XSpeed * TheGame->ElapsedSeconds; + sheep->Y += sheep->YSpeed * TheGame->ElapsedSeconds; + + sheep->YSpeed += 85.0f * TheGame->ElapsedSeconds; + if (sheep->YSpeed > 200) sheep->YSpeed = 200; + + sheep->XSpeed *= 0.999f; + + if (sheep->OnScreen) { + if (sheep->Y < 0) { + sheep->Y = 0; + sheep->YSpeed = 0; + } + } else { + if (sheep->Y >= 0) { + sheep->OnScreen = true; + } + } + + if (sheep->X < 0) { + sheep->X = 0; + sheep->MovingRight = true; + sheep->XSpeed = abs((int)round(sheep->XSpeed)); + } + + if (sheep->X + sfcSheepRight->Width > MiniGameWidth) { + sheep->X = MiniGameWidth - sfcSheepRight->Width; + sheep->MovingRight = false; + sheep->XSpeed = -sheep->XSpeed; + } + + if (sheep->Y > MiniGameHeight) { + Success(); + } + } + + + Y += 35.0f * TheGame->ElapsedSeconds; + + if (Y >= 2560.0f) { + if (SuccessTime == -1) SuccessTime = TheGame->TickCount; + Y = 2560.0f; + } +} + +void BPMiniGame_MoonJump::CreateSheep() { + BPMiniGame_MoonJump_Sheep* sheep = new BPMiniGame_MoonJump_Sheep(); + sheep->X = TheGame->RandomRange(50, 270); + sheep->Y = -sfcSheepLeft->Height; + sheep->XSpeed = TheGame->RandomRange(0, 10); + sheep->YSpeed = 0.0f; + Sheep.Add(sheep); + + LastCreateTime = TheGame->TickCount; + SheepCreateDelay += 600; +} + +void BPMiniGame_MoonJump::OnMouseDown() { + +} + +void BPMiniGame_MoonJump::OnMouseMove() { + +} + +void BPMiniGame_MoonJump::OnMouseUp() { + for (int i = 0; i < Sheep.Count; ++i) { + BPMiniGame_MoonJump_Sheep* sheep = Sheep[i]; + + if (sheep->YSpeed < 0) continue; // don't let him jump again if he's still jumping + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, round(sheep->X) - 15, round(sheep->Y) - 15, sfcSheepLeft->Width + 30, sfcSheepRight->Height + 30)) { + switch (TheGame->RandomRange(0, 10)) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + TheGame->PlaySound("baa"); + break; + + case 6: + case 7: + case 8: + case 9: + TheGame->PlaySound("baa2"); + break; + + case 10: + TheGame->PlaySound("baa3"); + break; + } + + int halfpoint = round(sheep->X) + 19; + int diff = TouchEvent.X - halfpoint; + + if (diff > 0) { + // we clicked to the right of the middle + sheep->XSpeed = -diff * 3; + sheep->MovingRight = false; + } else { + // we clicked to the left + sheep->XSpeed = -diff * 3; + sheep->MovingRight = true; + } + + sheep->YSpeed = -120.0f; + } + } +} diff --git a/moonjump.h b/moonjump.h new file mode 100644 index 0000000..0938b2a --- /dev/null +++ b/moonjump.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef __MOONJUMP_H__ +#define __MOONJUMP_H__ + +#include "Minigame.h" + +class BPMiniGame_MoonJump_Sheep { +public: + float X; + float Y; + float XSpeed; + float YSpeed; + bool MovingRight; + bool OnScreen; + + BPMiniGame_MoonJump_Sheep() { + X = Y = XSpeed = YSpeed = 0.0f; + MovingRight = true; + OnScreen = false; + } +}; + +class BPMiniGame_MoonJump : public BPMiniGame { +public: + ~BPMiniGame_MoonJump(); + BPMiniGame_MoonJump(BPGame* game); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void CreateSheep(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + +protected: + Texture* sfcBackground1; + Texture* sfcBackground2; + Texture* sfcBackground3; + Texture* sfcBackground4; + Texture* sfcBackground5; + Texture* sfcBackground6; + + Texture* sfcSheepLeft; + Texture* sfcSheepRight; + + BPPList Sheep; + + float Y; + int LastCreateTime; + int SheepCreateDelay; + + int SuccessTime; +}; + +#endif diff --git a/nextinline.cpp b/nextinline.cpp new file mode 100644 index 0000000..1a34050 --- /dev/null +++ b/nextinline.cpp @@ -0,0 +1,297 @@ +// 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 "nextinline.h" +#include "Minigame.h" + +BPMiniGame_NextInLine::BPMiniGame_NextInLine(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("nextinline", 320, 416); + + TimeStarted = Result = NumWrong = NumTries = 0; + LastStateChange = -1; + SuccessTime = -1; + + sfcCurrentQuestion = sfcCurrentAnswer = NULL; + + CurrentAnswer = new string(); + + GameTitle = "Next in Line"; + GameHelp = "If 2 becomes 4 and 4 becomes 8, then what does 8 become? 16, of course - but it gets harder! Tap OK to submit your answer, or tap the answer box to clear it. Good luck!"; + GameHelp2 = "Have the numbers been multiplied by themselves? Have they been multiplied by the previous number? Think hard - there's a rule that matches them all..."; + + for (int i = 0; i < 12; ++i) { + QuestionOrder.Add(i); + } + + QuestionOrder.Shuffle(); + + MiniGameType = PUZZLE; + + LevelUp(); +} + +BPMiniGame_NextInLine::~BPMiniGame_NextInLine() { + SAFE_DELETE(sfcBackground); + + SAFE_DELETE(CurrentAnswer); + SAFE_DELETE(sfcCurrentQuestion); + SAFE_DELETE(sfcCurrentAnswer); +} + +void BPMiniGame_NextInLine::OnMouseDown() { + +} + +void BPMiniGame_NextInLine::OnMouseMove() { + +} + +void BPMiniGame_NextInLine::OnMouseUp() { + switch (GameState) { + case GUESSING: + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 351, 96, 50)) { + ButtonClicked("0"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 290, 96, 50)) { + ButtonClicked("1"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 290, 96, 50)) { + ButtonClicked("2"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 290, 96, 50)) { + ButtonClicked("3"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 230, 96, 50)) { + ButtonClicked("4"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 230, 96, 50)) { + ButtonClicked("5"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 230, 96, 50)) { + ButtonClicked("6"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 169, 96, 50)) { + ButtonClicked("7"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 169, 96, 50)) { + ButtonClicked("8"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 169, 96, 50)) { + ButtonClicked("9"); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 113, 351, 96, 50)) { + FlipSign(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 351, 96, 50)) { + if (CurrentAnswer->length() == 0) return; + if (CurrentAnswer->compare("-") == 0) return; + CheckAnswer(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 10, 107, 300, 50)) { + CurrentAnswer->clear(); + AnswerChanged(); + TheGame->PlaySound("wrong"); + } + + break; + } +} + +void BPMiniGame_NextInLine::ButtonClicked(const char* btn) { + if (CurrentAnswer->length() > 8) return; + + CurrentAnswer->append(btn); + AnswerChanged(); + + TheGame->PlaySound("mouse_click"); +} + +void BPMiniGame_NextInLine::FlipSign() { + if (CurrentAnswer->length() == 0) { + CurrentAnswer->append("-"); + AnswerChanged(); + return; + } + + if (CurrentAnswer->substr(0, 1) == "-") { + CurrentAnswer->erase(0, 1); + } else { + CurrentAnswer->insert(0, "-"); + } + + AnswerChanged(); +} + +void BPMiniGame_NextInLine::CheckAnswer() { + if (atoi(CurrentAnswer->c_str()) == Result) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + ++NumWrong; + } + + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_NextInLine::LevelUp() { + CurrentAnswer->clear(); + AnswerChanged(); + ++NumTries; + GameState = GUESSING; + + if (NumTries >= 10) { + if (SuccessTime == -1) SuccessTime = TheGame->TickCount; + return; + } + + ostringstream question; + + int first, second, third, fourth; + + first = TheGame->RandomRange(2, 4); + second = TheGame->RandomRange(first + 1, first + 3); + third = TheGame->RandomRange(second + 1, second + 3); + fourth = TheGame->RandomRange(third + 1, third + 3); + + switch (QuestionOrder[NumTries]) { + case 0: + { + + question << "If " << first << " is " << first + first << ", " << second << " is " << second + second << ", and " << third << " is " << third + third << ", what is " << fourth << "?"; + Result = fourth + fourth; + break; + } + + case 1: + { + question << "If " << first << " is " << first * first << ", " << second << " is " << second * second << ", and " << third << " is " << third * third << ", what is " << fourth << "?"; + Result = fourth * fourth; + break; + } + case 2: + { + question << "If " << first << " is " << first * 3 << ", " << second << " is " << second * 3 << ", and " << third << " is " << third * 3 << ", what is " << fourth << "?"; + Result = fourth * 3; + break; + } + case 3: + { + question << "If " << first << " is " << first + first - 1 << ", " << second << " is " << second + second - 1 << ", and " << third << " is " << third + third - 1 << ", what is " << fourth << "?"; + Result = fourth + fourth - 1; + + break; + } + case 4: + { + question << "If " << first << " is " << first + first + 1 << ", " << second << " is " << second + second + 1 << ", and " << third << " is " << third + third + 1 << ", what is " << fourth << "?"; + Result = fourth + fourth + 1; + break; + } + case 5: + { + question << "If " << first << " is " << first * 4 << ", " << second << " is " << second * 4 << ", and " << third << " is " << third * 4 << ", what is " << fourth << "?"; + Result = fourth * 4; + + break; + } + case 6: + { + question << "If " << first * first << " is " << first << ", " << second * second << " is " << second << ", and " << third * third << " is " << third << ", what is " << fourth * fourth << "?"; + Result = fourth; + break; + } + case 7: + { + question << "If " << first << " is " << first * (first - 1) << ", " << second << " is " << second * (second - 1) << ", and " << third << " is " << third * (third - 1) << ", what is " << fourth << "?"; + Result = fourth * (fourth - 1); + + break; + } + case 8: + { + question << "If " << first << " is " << first << ", " << second << " is " << second + first << ", and " << third << " is " << third + second + first << ", what is " << fourth << "?"; + Result = fourth + third + second + first; + + break; + } + case 9: + { + question << "If " << first << " is " << first * fourth << ", " << second << " is " << second * fourth << ", and " << third << " is " << third * fourth << ", what is " << fourth << "?"; + Result = fourth * fourth; + + break; + } + case 10: + { + question << "If " << first << " is " << first + 20 << ", " << second << " is " << second + 40 << ", and " << third << " is " << third + 60 << ", what is " << fourth << "?"; + Result = fourth + 80; + + break; + } + case 11: + { + question << "If " << first << " is " << first - 10 << ", " << second << " is " << second - 10 << ", and " << third << " is " << third - 10 << ", what is " << fourth << "?"; + Result = fourth - 10; + + break; + } + } + + TheGame->AllocString(&sfcCurrentQuestion, question.str().c_str(), NORMAL, 300, 80, LEFT, true); + TheGame->AllocString(&sfcCurrentAnswer, CurrentAnswer->c_str(), XLARGE, 284, 64, RIGHT, true); + LastStateChange = TheGame->TickCount; + +} + +void BPMiniGame_NextInLine::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_NextInLine::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax((550 - (NumWrong * 75)) - round(TimePassed)); +} + +void BPMiniGame_NextInLine::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + // this stops an equation being shown once we've won the game + if (SuccessTime == -1) { + TheGame->DrawString(sfcCurrentQuestion, WHITE, 10, 16); + + TheGame->DrawString(sfcCurrentAnswer, WHITE, 18, 110); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } + } +} + +void BPMiniGame_NextInLine::Tick() { + if (SuccessTime != -1 && SuccessTime + 500 < TheGame->TickCount) { + Success(); + return; + } + + if (GameState == CORRECT) { + if (LastStateChange + 500 < TheGame->TickCount) { + LevelUp(); + } + } + + if (GameState == WRONG) { + if (LastStateChange + 500 < TheGame->TickCount) { + LevelUp(); + } + } +} + +void BPMiniGame_NextInLine::AnswerChanged() { + TheGame->AllocString(&sfcCurrentAnswer, CurrentAnswer->c_str(), XLARGE, 284, 64, RIGHT, true); +} diff --git a/nextinline.h b/nextinline.h new file mode 100644 index 0000000..72bee42 --- /dev/null +++ b/nextinline.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef __NEXTINLINE_H__ +#define __NEXTINLINE_H__ + +#include "Minigame.h" + +class BPMiniGame_NextInLine : public BPMiniGame { +public: + BPMiniGame_NextInLine(BPGame* game); + ~BPMiniGame_NextInLine(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void ButtonClicked(const char* btn); + void FlipSign(); + void CheckAnswer(); + void LevelUp(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void AnswerChanged(); +protected: + Texture* sfcBackground; + + MiniGameStates GameState; + int TimeStarted; + + int Result; + int NumWrong; + int NumTries; + + int LastStateChange; + int SuccessTime; + + string* CurrentAnswer; + SpriteFont* sfcCurrentQuestion; + SpriteFont* sfcCurrentAnswer; + + BPList QuestionOrder; +}; + +#endif diff --git a/numbersnake.cpp b/numbersnake.cpp new file mode 100644 index 0000000..5acb967 --- /dev/null +++ b/numbersnake.cpp @@ -0,0 +1,272 @@ +// 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 "numbersnake.h" +#include "Minigame.h" +#include +#include +#include +#include + +BPMiniGame_NumberSnake::BPMiniGame_NumberSnake(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("numbersnake", 320, 416); + sfcBlack = TheGame->LoadBitmap("numbersnake_black", 320, 128); + + CurrentLevel = 0; + + LastStateChange = LastQuestionChange = -1; + NumCorrect = 0; + + sfcQuestionParts = NULL; + Answer = 0; + + QuestionPos = 0; + QuestionSpeed = 1250; + + TimeStarted = 0; + + GameTitle = "Number Snake"; + GameHelp = "Watch carefully as simple mathematics questions flash on your screen, then see if you can figure out the answers!"; + GameHelp2 = "This is a game that tests your ability to perform simple sums very quickly. In fact, if you're not quick then you won't have finished the first sum by the time the second sum appears, so there really is no time to hang around!"; + + MiniGameType = PUZZLE; + + NumStrings.Add("zero"); + NumStrings.Add("one"); + NumStrings.Add("two"); + NumStrings.Add("three"); + NumStrings.Add("four"); + NumStrings.Add("five"); + NumStrings.Add("six"); + NumStrings.Add("seven"); + NumStrings.Add("eight"); + NumStrings.Add("nine"); +} + +BPMiniGame_NumberSnake::~BPMiniGame_NumberSnake() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBlack); + + NumStrings.Clear(); + + if (sfcQuestionParts != NULL) { + for (int i = 0; i < QuestionLength; ++i) { + SAFE_DELETE(sfcQuestionParts[i]); + } + + delete[] sfcQuestionParts; + } +} + +void BPMiniGame_NumberSnake::OnMouseDown() { + +} + +void BPMiniGame_NumberSnake::OnMouseMove() { + +} + +void BPMiniGame_NumberSnake::OnMouseUp() { + if (TouchEvent.Y < 100) { + // possible click on score + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 4, 52, 47)) SubmitAnswer(0); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 66, 4, 52, 47)) SubmitAnswer(1); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 134, 4, 52, 47)) SubmitAnswer(2); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 201, 4, 52, 47)) SubmitAnswer(3); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 272, 4, 52, 47)) SubmitAnswer(4); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 52, 52, 47)) SubmitAnswer(5); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 66, 52, 52, 47)) SubmitAnswer(6); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 134, 52, 52, 47)) SubmitAnswer(7); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 201, 52, 52, 47)) SubmitAnswer(8); + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 272, 52, 52, 47)) SubmitAnswer(9); + } +} + +void BPMiniGame_NumberSnake::Start() { + TimeStarted = TheGame->TickCount; + + LevelUp(); +} + +int BPMiniGame_NumberSnake::GetWeight() { + return MinMax(100 * NumCorrect); +} + +void BPMiniGame_NumberSnake::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + if (GameState == SHOWING) { + if (QuestionPos == QuestionLength - 1) { + float fadeamount = min(0.65f, Fader); + glColor4f(1.0f, 1.0f, 1.0f, fadeamount); + TheGame->DrawImage(sfcBlack, 0, 165); + } else { + glColor4f(1.0f, 1.0f, 1.0f, 0.65f); + TheGame->DrawImage(sfcBlack, 0, 165); + } + + glColor4f(1.0f, 1.0f, 1.0f, Fader); + TheGame->DrawString(sfcQuestionParts[QuestionPos], 0, 170); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_NumberSnake::Tick() { + if (GameState == SHOWING) { + if (LastQuestionChange + QuestionSpeed < TheGame->TickCount) { + ++QuestionPos; + LastQuestionChange = TheGame->TickCount; + + if (QuestionPos == QuestionLength) { + GameState = WAITING; + } + } + + float start = TheGame->TickCount + QuestionSpeed; + float end = LastQuestionChange + QuestionSpeed; + float alpha = (start - end) / QuestionSpeed; + Fader = TheGame->SmoothStep(1.0f, 0.0f, alpha); + } + + if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) { + if (!MarathonMode && CurrentLevel >= 5) { + Success(); + } else { + LevelUp(); + } + } +} + +void BPMiniGame_NumberSnake::SubmitAnswer(int answer) { + if (GameState != WAITING) return; + + if (answer == Answer) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + ++NumCorrect; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + } + + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_NumberSnake::LevelUp() { + ++CurrentLevel; + QuestionPos = 0; + LastQuestionChange = TheGame->TickCount; + QuestionSpeed -= 50; + + BPList Numbers; + + Answer = TheGame->RandomRange(1, 6); + + ostringstream PrintMe; + PrintMe << "What is " << NumStrings[Answer]; + + for (int i = 0; i < 4 + CurrentLevel; ++i) { + Numbers.Add(TheGame->RandomRange(1, 6)); + } + + int RandNum; + + for (int i = 0; i < Numbers.Count; ++i) { + int num = Numbers[i]; + + // we want to allow subtraction only if it doesn't dip below zero! + if (Answer - num > 0) { + // only allow multiplication if the total doesn't go above 25 + if (Answer * num < 26) { + RandNum = TheGame->RandomRange(0, 2); + } else { + RandNum = TheGame->RandomRangeExcept(0, 2, 1); + } + } else { + // only allow multiplication if the total doesn't go above 25 + if (Answer * num < 26) { + RandNum = TheGame->RandomRange(0, 1); + } else { + RandNum = 0; + } + } + + switch (RandNum) { + case 0: + // add + PrintMe << " plus " << NumStrings[num]; + Answer += num; + break; + + case 1: + PrintMe << " times " << NumStrings[num]; + Answer *= num; + break; + + case 2: + // subtract + PrintMe << " minus " << NumStrings[num]; + Answer -= num; + break; + } + } + + while (Answer >= 10) { + int newnum = TheGame->RandomRange(5, 9); + + PrintMe << " minus " << NumStrings[newnum]; + Answer -= newnum; + } + + PrintMe << " ?"; + + istringstream iss(PrintMe.str()); + vector parts; + copy(istream_iterator(iss), istream_iterator(), back_inserter >(parts)); + + if (sfcQuestionParts != NULL) { + for (int i = 0; i < QuestionLength; ++i) { + SAFE_DELETE(sfcQuestionParts[i]); + } + + delete[] sfcQuestionParts; + } + + QuestionLength = parts.size(); + + sfcQuestionParts = new SpriteFont*[QuestionLength]; + + int i = 0; + for (int i = 0; i < parts.size(); ++i) { + sfcQuestionParts[i] = NULL; + TheGame->AllocString(&sfcQuestionParts[i], parts[i].c_str(), EPIC, 320, 227, CENTRED); + } + + GameState = SHOWING; + LastStateChange = -1; +} + +void BPMiniGame_NumberSnake::SetMarathon() { + MarathonMode = true; + GameHelp = "Watch carefully as simple mathematics questions flash on your screen, then see if you can figure out the answers!"; +} diff --git a/numbersnake.h b/numbersnake.h new file mode 100644 index 0000000..283cfe8 --- /dev/null +++ b/numbersnake.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef __NUMBERSNAKE_H__ +#define __NUMBERSNAKE_H__ + +#include "Minigame.h" + +class BPMiniGame_NumberSnake : public BPMiniGame { +public: + BPMiniGame_NumberSnake(BPGame* game); + ~BPMiniGame_NumberSnake(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void SubmitAnswer(int answer); + void LevelUp(); + void SetMarathon(); +protected: + Texture* sfcBackground; + Texture* sfcBlack; + int CurrentLevel; + MiniGameStates GameState; + + BPList NumStrings; + + int LastStateChange; + int LastQuestionChange; + int NumCorrect; + float Fader; + + SpriteFont** sfcQuestionParts; + int QuestionLength; + int Answer; + + int QuestionPos; + int QuestionSpeed; + + static const int HalfHeight; + + int TimeStarted; +}; + +#endif diff --git a/oddoneout.cpp b/oddoneout.cpp new file mode 100644 index 0000000..3d823e9 --- /dev/null +++ b/oddoneout.cpp @@ -0,0 +1,223 @@ +// 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 "oddoneout.h" +#include "Minigame.h" + +BPMiniGame_OddOneOut::BPMiniGame_OddOneOut(BPGame* game) : BPMiniGame(game) { + Level = NumWrong = TimeStarted = NumTries = 0; + + GameTitle = "Odd One Out"; + GameHelp = "I'm going to show you lots of different items, but one of them only appears once. Can you spot which one?"; + GameHelp2 = "You'll be shown pictures of food, of which all the pictures except one appear more than once. For example, you might see five apples, seven bananas, two oranges and a hamburger. Because there's only one hamburger, that's the right answer."; + + MiniGameType = PUZZLE; + + GameState = WAITING; + LastStateChange = 0; + + sfcBackground = TheGame->LoadBitmap("oddoneout", 320, 416); + + CardTypes.Add(TheGame->LoadBitmap("card_1_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_2_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_3_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_4_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_5_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_6_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_7_48", 48, 48)); + CardTypes.Add(TheGame->LoadBitmap("card_8_48", 48, 48)); + + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 9; ++j) { + Coordinates.Add(new BPPoint(-2 + (i * 46), 0 + (j * 46))); + } + } +} + +BPMiniGame_OddOneOut::~BPMiniGame_OddOneOut() { + SAFE_DELETE(sfcBackground); + CardTypes.Clear(); + Coordinates.Clear(); + Items.Clear(); +} + +void BPMiniGame_OddOneOut::Start() { + LevelUp(); + + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_OddOneOut::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(500 - (NumWrong * 40) - round(TimePassed)); +} + +void BPMiniGame_OddOneOut::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_OddOneOut_Item* item = Items[i]; + TheGame->DrawImage(CardTypes[item->CardType], item->X, item->Y); + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_OddOneOut::Tick() { + if (GameState != WAITING) { + if (LastStateChange + 500 < TheGame->TickCount) { + if (GameState == CORRECT) { + LevelUp(); + } else { + LevelDown(); + } + + LastStateChange = TheGame->TickCount; + GameState = WAITING; + } + } +} + +void BPMiniGame_OddOneOut::OnMouseDown() { + +} + +void BPMiniGame_OddOneOut::OnMouseMove() { + +} + +void BPMiniGame_OddOneOut::OnMouseUp() { + if (GameState == CORRECT || GameState == WRONG) return; + + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_OddOneOut_Item* item = Items[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, item->X, item->Y, 48, 48)) { + // clicked this item! + if (item->CardType == 0) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } + + break; + } + } +} + +void BPMiniGame_OddOneOut::LevelUp() { + ++Level; + CreateLevel(); +} + +void BPMiniGame_OddOneOut::LevelDown() { + --Level; + if (Level < 1) Level = 1; + ++NumWrong; + CreateLevel(); +} + +void BPMiniGame_OddOneOut::CreateLevel() { + ++NumTries; + + if (NumTries > 12) Success(); + + switch (Level) { + case 0: + case 1: + GenerateLayout(5); + break; + + case 2: + GenerateLayout(9); + break; + + case 3: + GenerateLayout(15); + break; + + case 4: + GenerateLayout(21); + break; + + case 5: + GenerateLayout(27); + break; + + case 6: + GenerateLayout(37); + break; + + case 7: + GenerateLayout(49); + break; + + case 8: + GenerateLayout(63); + break; + + case 9: + Success(); + break; + } +} + +void BPMiniGame_OddOneOut::GenerateLayout(int num) { + Items.Clear(); + CardTypes.Shuffle(); + Coordinates.Shuffle(); + + // we need to add at least two of every object + int item_count = 1; + int num_used = 0; + + // make the odd one out first + BPPoint* odd = Coordinates[0]; + + BPMiniGame_OddOneOut_Item* odd_item = new BPMiniGame_OddOneOut_Item(); + odd_item->X = odd->X; + odd_item->Y = odd->Y; + odd_item->CardType = 0; + Items.Add(odd_item); + + for (int i = 1; i < num; ++i) { + BPMiniGame_OddOneOut_Item* item = new BPMiniGame_OddOneOut_Item(); + item->X = Coordinates[i]->X; + item->Y = Coordinates[i]->Y; + item->CardType = item_count; + ++num_used; + + if (num_used == 2) { + num_used = 0; + ++item_count; + } + + if (item_count >= CardTypes.Count) { + item_count = 1; + } + + Items.Add(item); + } +} diff --git a/oddoneout.h b/oddoneout.h new file mode 100644 index 0000000..da1eea2 --- /dev/null +++ b/oddoneout.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef __ODDONEOUT_H__ +#define __ODDONEOUT_H__ + +#include "Minigame.h" + +class BPMiniGame_OddOneOut_Item { +public: + int CardType; + int X; + int Y; +}; + + + +class BPMiniGame_OddOneOut : public BPMiniGame { +public: + BPMiniGame_OddOneOut(BPGame* game); + ~BPMiniGame_OddOneOut(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void LevelUp(); + void LevelDown(); + void CreateLevel(); + void GenerateLayout(int num); +protected: + Texture* sfcBackground; + BPPList CardTypes; + BPPList Coordinates; + BPPList Items; + + int Level; + int NumWrong; + int NumTries; + + int TimeStarted; + + MiniGameStates GameState; + int LastStateChange; +}; + +#endif diff --git a/patchmatch.cpp b/patchmatch.cpp new file mode 100644 index 0000000..d51ef3e --- /dev/null +++ b/patchmatch.cpp @@ -0,0 +1,312 @@ +// 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 "patchmatch.h" +#include "Minigame.h" + +BPMiniGame_PatchMatch::BPMiniGame_PatchMatch(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("patchmatch", 512, 512); + + Locked = false; // when true disallow movement + TimeStarted = 0; + SuccessTime = -1; + + sfcClock = sfcScoreStr = NULL; + SelectedBox = NULL; + + DisappearTime = 400; + + Score = 0; + SetScore(); + + GameTitle = "Patch Match"; + GameHelp = "Tap on any shape, then drag it onto a square with the same picture to score points. It sounds easy enough, but can you stay focused for two minutes?"; + GameHelp2 = "Not every box will have a match on the screen at any given time, so you need to look hard and find matches wherever you can."; + + MiniGameType = PUZZLE; + + for (int i = 1; i <= 59; ++i) { + ostringstream filename; + filename << "patch" << i; + PatchPictures.Add(TheGame->LoadBitmap(filename.str().c_str(), 48, 48)); + } + + for (int j = 0; j < 7; ++j) { + vector* row = new vector(); + + for (int i = 0; i < 6; ++i) { + BPMiniGame_PatchMatch_Box* box = new BPMiniGame_PatchMatch_Box(); + box->X = 36 + (i * 50); + box->Y = 36 + (j * 50); + box->DestY = 36 + (j * 50); + box->DrawX = box->X; + box->DrawY = box->Y; + box->Colour = TheGame->RandomRange(0, PatchPictures.Count - 1); + row->push_back(box); + } + + Boxes.push_back(row); + } +} + +BPMiniGame_PatchMatch::~BPMiniGame_PatchMatch() { + SAFE_DELETE(sfcBackground); + + BPMiniGame_PatchMatch_Box* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + SAFE_DELETE(box); + } + + row->clear(); + SAFE_DELETE(row); + } + + Boxes.clear(); + + PatchPictures.Clear(); + + SAFE_DELETE(sfcScoreStr); + SAFE_DELETE(sfcClock); +} + +void BPMiniGame_PatchMatch::OnMouseMove() { + if (Locked) return; + if (SelectedBox == NULL) return; + + Locked = true; + SelectedBox->DrawX = TouchEvent.X; + SelectedBox->DrawY = TouchEvent.Y; + + LastPos = TouchEvent; + + Locked = false; +} + +void BPMiniGame_PatchMatch::OnMouseDown() { + if (Locked) return; + Locked = true; + + LastPos = TouchEvent; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int j = 0; j < row->size(); ++j) { + BPMiniGame_PatchMatch_Box* box = (*row)[j]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, box->X - HalfBoxSize, box->Y - HalfBoxSize, BoxSize, BoxSize)) { + SelectedBox = (*row)[j]; + TheGame->PlaySound("card_flip"); + return; + } + } + } + + Locked = false; +} + +void BPMiniGame_PatchMatch::OnMouseUp() { + if (SelectedBox == NULL) return; + if (Locked) return; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int j = 0; j < row->size(); ++j) { + BPMiniGame_PatchMatch_Box* box = (*row)[j]; + + if (box == SelectedBox) continue; + + if (box->Colour != SelectedBox->Colour) continue; + + if (TheGame->RectOverRect(SelectedBox->DrawX, SelectedBox->DrawY, BoxSize, BoxSize, box->X, box->Y, BoxSize, BoxSize)) { + box->MatchTime = TheGame->TickCount; + SelectedBox->MatchTime = TheGame->TickCount; + + Score += 1; + SetScore(); + + TheGame->PlaySound("gem_select"); + + SelectedBox = NULL; + return; + } + } + } + + SelectedBox->DrawX = SelectedBox->X; + SelectedBox->DrawY = SelectedBox->Y; + SelectedBox = NULL; +} + +void BPMiniGame_PatchMatch::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_PatchMatch::GetWeight() { + return MinMax(round(Score * 17)); +} + +void BPMiniGame_PatchMatch::Render() { + TheGame->Clear(TheGame->Black); + + int time_passed = TheGame->TickCount - TimeStarted; + TheGame->DrawImage(sfcBackground, 160, 208, time_passed / 500.0f, 1.1f, (*TheGame->White)); + + Colour col = Colour(0.0f, 0.0f, 0.0f, 0.6f); + TheGame->FillRectangle(col, 0, 369, 320, 40); + + if (!MarathonMode) { + int TimePassed = TheGame->TickCount - TimeStarted; + TimePassed = 120000 - TimePassed; + + if (TimePassed <= 0) { + if (SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + Success(); + } + } + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), LARGE, 160, 50, RIGHT); + } + + TheGame->DrawString(sfcClock, WHITE, 145, 370); + } + + TheGame->DrawString(sfcScoreStr, WHITE, 14, 370); + + BPMiniGame_PatchMatch_Box* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + + if (box->MatchTime == -1) { + TheGame->DrawImage(PatchPictures[box->Colour], box->DrawX, box->DrawY, 0.0f, 1.0f, (*TheGame->White)); + } + } + } + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + + if (box->MatchTime != -1) { + float diff = TheGame->TickCount - box->MatchTime; + + if (diff <= DisappearTime) { + float step = diff / DisappearTime; // get a value between 0 and 1 + Colour col = Colour(1.0f, 1.0f, 1.0f, 1 - step); + + TheGame->DrawImage(PatchPictures[box->Colour], box->DrawX, box->DrawY, TheGame->Lerp(0, 360, step), TheGame->SmoothStep(1.0f, 6.0f, step), col); + } + } + } + } + + if (SelectedBox != NULL) { + TheGame->DrawImage(PatchPictures[SelectedBox->Colour], SelectedBox->DrawX, SelectedBox->DrawY, 0.0f, 1.0f, (*TheGame->White)); + } +} + +void BPMiniGame_PatchMatch::Tick() { + vector* row; + BPMiniGame_PatchMatch_Box* box; + BPMiniGame_PatchMatch_Box* copybox; + + int MatchTimeout = TheGame->TickCount - 400; + + bool AllStill = true; + + for (int i = Boxes.size() - 1; i >= 0; --i) { + row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + box = (*row)[j]; + + if (box->MatchTime != -1) AllStill = false; + + if (box->Y != box->DestY) { + AllStill = false; + + box->YSpeed += 2.0f; + box->Y += box->YSpeed; + if (box->Y > box->DestY) { + box->Y = box->DestY; + box->YSpeed = 0; + } + + box->DrawY = box->Y; + } + + if (box->MatchTime != -1 && box->MatchTime < MatchTimeout) { + SAFE_DELETE(box); + + row->erase(row->begin() + j); + // move all boxes down above me + + for (int k = i - 1; k >= 0; --k) { + copybox = (*Boxes[k])[j]; + Boxes[k]->erase(Boxes[k]->begin() + j); + Boxes[k + 1]->insert(Boxes[k + 1]->begin() + j, copybox); + copybox->DestY = 36 + ((k + 1) * BoxSize); + } + + BPMiniGame_PatchMatch_Box* newbox = new BPMiniGame_PatchMatch_Box(); + newbox->X = 36 + (j * BoxSize); + newbox->Y = -((9 - j) * BoxSize); + newbox->DrawX = newbox->X; + newbox->DrawY = newbox->Y; + newbox->DestY = 36; + newbox->Colour = TheGame->RandomRange(0, PatchPictures.Count - 1); + Boxes[0]->insert(Boxes[0]->begin() + j, newbox); + } + } + } + + if (AllStill) { + Locked = false; + } else { + Locked = true; + } +} + +void BPMiniGame_PatchMatch::SetMarathon() { + MarathonMode = true; + GameHelp = "Drag rows of jewels left and right, matching three or more to score points; the more you match, the more you score."; +} + +void BPMiniGame_PatchMatch::SetScore() { + if (Score > 0) { + ostringstream score; + string scorestr = TheGame->SeparateThousands(Score); + score << "Matches: " << scorestr; + TheGame->AllocString(&sfcScoreStr, score.str().c_str(), LARGE, 270, 50, LEFT); + } else { + TheGame->AllocString(&sfcScoreStr, "Matches: 0", LARGE, 270, 50, LEFT); + } +} diff --git a/patchmatch.h b/patchmatch.h new file mode 100644 index 0000000..ca4debe --- /dev/null +++ b/patchmatch.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef __PATCHMATCH_H__ +#define __PATCHMATCH_H__ + +#include "Minigame.h" + +class BPMiniGame_PatchMatch_Box { +public: + int X; + int Y; + int DestY; + int DrawX; + int DrawY; + int YSpeed; + int Colour; + int MatchTime; + + BPMiniGame_PatchMatch_Box() { + X = Y = DestY = DrawX = DrawY = YSpeed = Colour = 0; + MatchTime = -1; + } +}; + +class BPMiniGame_PatchMatch : public BPMiniGame { +public: + BPMiniGame_PatchMatch(BPGame* game); + ~BPMiniGame_PatchMatch(); + void OnMouseMove(); + void OnMouseDown(); + void OnMouseUp(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void SetMarathon(); + void SetScore(); + +protected: + Texture* sfcBackground; + + vector*> Boxes; + BPPList PatchPictures; + SpriteFont* sfcClock; + + int DisappearTime; + BPPoint LastPos; + + BPMiniGame_PatchMatch_Box* SelectedBox; + + static const int BoxSize = 50; + static const int HalfBoxSize = 25; + + bool Locked; // when true disallow movement + + int TimeStarted; + + int SuccessTime; + + int Score; + SpriteFont* sfcScoreStr; +}; + +#endif diff --git a/perfectpaths.cpp b/perfectpaths.cpp new file mode 100644 index 0000000..75e4546 --- /dev/null +++ b/perfectpaths.cpp @@ -0,0 +1,443 @@ +// 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 "perfectpaths.h" +#include "Minigame.h" + +BPMiniGame_PerfectPaths::BPMiniGame_PerfectPaths(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("perfectpaths", 320, 416); + + sfcPerfect = TheGame->LoadBitmap("perfect", 320, 92); + sfcOK = TheGame->LoadBitmap("ok", 320, 92); + + CurrentScore = BestScore = TotalDiff = TimeStarted = CurrentLevel = 0; + sfcScore = NULL; + SetScore(); + + GameTitle = "Perfect Paths"; + GameHelp = "Tap squares to make a path from the top white square to the bottom one, using the route with the lowest numbers along the way, then press Go when you're ready to advance. You can't move diagonally!"; + GameHelp2 = "Each square has a number on signifying how \"hard\" that square is to move over, and that number is used to calculate the total move difficulty. At the bottom of the screen you'll see the best possible score that you can get, along with your current score."; + + MiniGameType = LIVELY; + + GameState = WAITING; + + LoNumbers.Add(NULL); + LoNumbers.Add(TheGame->LoadBitmap("pp_1_lo", 44, 44)); + LoNumbers.Add(NULL); + LoNumbers.Add(TheGame->LoadBitmap("pp_3_lo", 44, 44)); + LoNumbers.Add(NULL); + LoNumbers.Add(TheGame->LoadBitmap("pp_5_lo", 44, 44)); + LoNumbers.Add(NULL); + LoNumbers.Add(TheGame->LoadBitmap("pp_7_lo", 44, 44)); + LoNumbers.Add(NULL); + LoNumbers.Add(TheGame->LoadBitmap("pp_9_lo", 44, 44)); + + HiNumbers.Add(NULL); + HiNumbers.Add(TheGame->LoadBitmap("pp_1_hi", 44, 44)); + HiNumbers.Add(NULL); + HiNumbers.Add(TheGame->LoadBitmap("pp_3_hi", 44, 44)); + HiNumbers.Add(NULL); + HiNumbers.Add(TheGame->LoadBitmap("pp_5_hi", 44, 44)); + HiNumbers.Add(NULL); + HiNumbers.Add(TheGame->LoadBitmap("pp_7_hi", 44, 44)); + HiNumbers.Add(NULL); + HiNumbers.Add(TheGame->LoadBitmap("pp_9_hi", 44, 44)); + + LevelUp(); +} + +BPMiniGame_PerfectPaths::~BPMiniGame_PerfectPaths() { + SAFE_DELETE(sfcBackground); + StartPositions.Clear(); + EndPositions.Clear(); + LoNumbers.Clear(); + HiNumbers.Clear(); +// SAFE_DELETE(StartPos); +// SAFE_DELETE(EndPos); + + SAFE_DELETE(sfcPerfect); + SAFE_DELETE(sfcOK); + + Squares.Clear(); + + SAFE_DELETE(sfcScore); +} + +void BPMiniGame_PerfectPaths::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_PerfectPaths::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(520 - (TotalDiff * 5) - round(TimePassed)); +} + +void BPMiniGame_PerfectPaths::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Squares.Count; ++i) { + BPMiniGame_PerfectPaths_Square* square = Squares[i]; + + if (square == EndPos || square == StartPos) { + TheGame->FillRectangle((*TheGame->White), square->XPos, square->YPos, 44, 44); + } else { + if (Moves.Contains(square)) { + if (square != StartPos) TheGame->DrawImage(HiNumbers[square->Difficulty], square->XPos, square->YPos); + } else { + TheGame->DrawImage(LoNumbers[square->Difficulty], square->XPos, square->YPos); + } + } + } + + TheGame->DrawString(sfcScore, WHITE, 6, 377); + + if (GameState == CORRECT) { + switch (LastDiff) { + case 0: + RenderPerfect(); + break; + + case 1: + case 2: + RenderCorrect(); + break; + + case 3: + case 4: + case 5: + RenderOK(); + break; + + default: + RenderWrong(); + break; + } + } +} + +void BPMiniGame_PerfectPaths::Tick() { + if (GameState == CORRECT && LastStateChange + 700 < TheGame->TickCount) { + LevelUp(); + } + + if (CurrentLevel >= 6) { + Success(); + } +} + +void BPMiniGame_PerfectPaths::OnMouseUp() { + if (GameState != WAITING) return; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 187, 368, 137, 47)) { + CheckResult(); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 0, 320, 364)) { + // making a move + for (int i = 0; i < Squares.Count; ++i) { + BPMiniGame_PerfectPaths_Square* square = Squares[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, square->XPos, square->YPos, 44, 44)) { + if (square == StartPos || square == EndPos) continue; + + if (Moves.Contains(square)) { + if (Moves[Moves.Count - 1] == square) { + // they can only remove a square if it was the last one to be added + Moves.Remove(square); + CalculateOurScore(); + } else { + MessageBox::Show("You can only remove the last position in your path.", "Oops!"); + } + } else { + BPMiniGame_PerfectPaths_Square* last = Moves[Moves.Count - 1]; + + if (CanMove(square, last)) { + Moves.Add(square); + CalculateOurScore(); + } else { + if (Moves.Count == 1) { + MessageBox::Show("You can only move to a square that's horizontally or vertically next to your previous move, starting from the top white square.", "Oops!"); + } else { + MessageBox::Show("You can only move to a square that's horizontally or vertically next to your previous move.", "Oops!"); + } + } + } + + break; + } + } + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 273, 72, 18)) { + MessageBox::Show("This shows the current number of moves it will take to get from the top to the bottom. The closer this is to the Best score, the more points you get.", GameTitle); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 80, 273, 72, 18)) { + MessageBox::Show("This shows the best possible number of moves between the top white square and the bottom white square. If you want to get the highest score, you must match this every time.", GameTitle); + } +} + +void BPMiniGame_PerfectPaths::OnMouseMove() { + +} + +void BPMiniGame_PerfectPaths::OnMouseDown() { + +} + +void BPMiniGame_PerfectPaths::CalculateOurScore() { + CurrentScore = 0; + + for (int i = 0; i < Moves.Count; ++i) { + BPMiniGame_PerfectPaths_Square* square = Moves[i]; + if (square == StartPos) continue; + CurrentScore += square->Difficulty; + } + + SetScore(); +} + +void BPMiniGame_PerfectPaths::CheckResult() { + BPMiniGame_PerfectPaths_Square* last = Moves[Moves.Count - 1]; + + if (CanMove(EndPos, last)) { + LastDiff = abs(BestScore - CurrentScore); + + TotalDiff += LastDiff; + + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + + switch (LastDiff) { + case 0: + case 1: + case 2: + case 3: + TheGame->PlaySound("correct"); + break; + + default: + TheGame->PlaySound("down"); + break; + } + } else { + MessageBox::Show("That doesn't work - your path needs to connect the white square at the top with the white square at the bottom!", "Try again"); + } +} + +void BPMiniGame_PerfectPaths::LevelUp() { + ++CurrentLevel; + CurrentScore = 0; + + if (CurrentLevel >= 6) { + return; + } + + Squares.Clear(); + Moves.Clear(); + MoveSquares.Clear(); + + int max_difficulty = 7; + + switch (CurrentLevel) { + case 1: + max_difficulty = 7; + break; + + case 2: + max_difficulty = 8; + break; + + case 3: + max_difficulty = 9; + break; + + case 4: + max_difficulty = 11; + break; + + case 5: + max_difficulty = 12; + break; + } + + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 8; ++j) { + BPMiniGame_PerfectPaths_Square* square = new BPMiniGame_PerfectPaths_Square(); + square->Difficulty = TheGame->RandomRange(0, max_difficulty); + + switch (square->Difficulty) { + case 0: + case 1: + case 2: + square->Difficulty = 1; + square->DifficultyStr = "1"; + square->Col = TheGame->Green; + break; + + case 3: + case 4: + case 5: + case 6: + square->Difficulty = 3; + square->DifficultyStr = "3"; + square->Col = TheGame->Orange; + break; + + case 7: + case 8: + case 9: + square->Difficulty = 5; + square->DifficultyStr = "5"; + square->Col = TheGame->Red; + break; + + case 10: + case 11: + square->Difficulty = 7; + square->DifficultyStr = "7"; + square->Col = TheGame->DarkRed; + break; + + case 12: + square->Difficulty = 9; + square->DifficultyStr = "9"; + square->Col = TheGame->DarkGrey; + break; + } + + square->X = i; + square->Y = j; + square->XPos = 6 + (i * 44); + square->YPos = 6 + (j * 44); + square->Pos = Squares.Count; + + Squares.Add(square); + } + } + + StartPositions.Clear(); + EndPositions.Clear(); + + if (TheGame->RandomRange(0, 1) == 0) { + StartPositions.Add(8); + StartPositions.Add(16); + StartPositions.Add(24); + EndPositions.Add(39); + EndPositions.Add(47); + EndPositions.Add(55); + } else { + StartPositions.Add(32); + StartPositions.Add(40); + StartPositions.Add(48); + EndPositions.Add(7); + EndPositions.Add(15); + EndPositions.Add(23); + } + + StartPositions.Shuffle(); + EndPositions.Shuffle(); + + StartPos = Squares[StartPositions[0]]; + EndPos = Squares[EndPositions[0]]; + + Moves.Add(StartPos); + + CalculateBestMove(); + + SetScore(); + + GameState = WAITING; + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_PerfectPaths::CalculateBestMove() { + MoveSquares.Add(StartPos); + StartPos->MoveCalc = 0; + + while (MoveSquares.Count != 0) { + BPMiniGame_PerfectPaths_Square* square = MoveSquares[0]; + MoveSquares.RemoveAt(0); + FloodFill(square); + } + + int probable_best = 999; + + // now we need to figure out what the best score was: what were the lowest numbers around the end position? + if (EndPos->X > 0) probable_best = Squares[EndPos->Pos - 8]->MoveCalc; + if (EndPos->X < 6 && Squares[EndPos->Pos + 8]->MoveCalc < probable_best) probable_best = Squares[EndPos->Pos + 8]->MoveCalc; + if (EndPos->Y > 0 && Squares[EndPos->Pos - 1]->MoveCalc < probable_best) probable_best = Squares[EndPos->Pos - 1]->MoveCalc; + if (EndPos->Y < 6 && Squares[EndPos->Pos + 1]->MoveCalc < probable_best) probable_best = Squares[EndPos->Pos + 1]->MoveCalc; + + BestScore = probable_best; +} + +void BPMiniGame_PerfectPaths::FloodFill(BPMiniGame_PerfectPaths_Square* square) { + int Plus1 = square->Pos + 1; + int Minus1 = square->Pos - 1; + int Plus10 = square->Pos + 8; + int Minus10 = square->Pos - 8; + + if (square->X > 0) { + // check square to the left + if (square->MoveCalc + Squares[Minus10]->Difficulty < Squares[Minus10]->MoveCalc) { + Squares[Minus10]->MoveCalc = square->MoveCalc + Squares[Minus10]->Difficulty; + MoveSquares.Add(Squares[Minus10]); + } + } + + if (square->X < 6) { + // check square to the right + if (square->MoveCalc + Squares[Plus10]->Difficulty < Squares[Plus10]->MoveCalc) { + Squares[Plus10]->MoveCalc = square->MoveCalc + Squares[Plus10]->Difficulty; + MoveSquares.Add(Squares[Plus10]); + } + } + + if (square->Y > 0) { + // check square above + if (square->MoveCalc + Squares[Minus1]->Difficulty < Squares[Minus1]->MoveCalc) { + Squares[Minus1]->MoveCalc = square->MoveCalc + Squares[Minus1]->Difficulty; + MoveSquares.Add(Squares[Minus1]); + } + } + + if (square->Y < 7) { + // check square below + if (square->MoveCalc + Squares[Plus1]->Difficulty < Squares[Plus1]->MoveCalc) { + Squares[Plus1]->MoveCalc = square->MoveCalc + Squares[Plus1]->Difficulty; + MoveSquares.Add(Squares[Plus1]); + } + } +} + +bool BPMiniGame_PerfectPaths::CanMove(BPMiniGame_PerfectPaths_Square* square1, BPMiniGame_PerfectPaths_Square* square2) { + // return true if this square is adjacent to the last move square + + return (abs(square1->X - square2->X) + abs(square1->Y - square2->Y)) == 1; +} + +void BPMiniGame_PerfectPaths::SetScore() { + ostringstream score; + + score << "Move: " << CurrentScore << " Best: " << BestScore; + + TheGame->AllocString(&sfcScore, score.str().c_str(), NORMAL, 239, 47, LEFT); +} + +void BPMiniGame_PerfectPaths::RenderPerfect() { + TheGame->DrawImage(sfcPerfect, 0, 172); +} + +void BPMiniGame_PerfectPaths::RenderOK() { + TheGame->DrawImage(sfcOK, 0, 172); +} diff --git a/perfectpaths.h b/perfectpaths.h new file mode 100644 index 0000000..aba4b6a --- /dev/null +++ b/perfectpaths.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef __PERFECTPATHS_H__ +#define __PERFECTPATHS_H__ + +#include "Minigame.h" + +class BPMiniGame_PerfectPaths_Square { +public: + int X; + int Y; + int XPos; // XPos and YPos are pixel co-ordinates to save calculations when drawing + int YPos; + Colour* Col; + int Difficulty; + const char* DifficultyStr; + + int MoveCalc; // used for flood fill best move calculation; set high so that any path is better + int Pos; // the position of the square in the Squares list; saves looking it up when doing flood fills + + BPMiniGame_PerfectPaths_Square() { + X = Y = XPos = YPos = Difficulty = Pos = 0; + MoveCalc = 9999; + DifficultyStr = NULL; + } + + ~BPMiniGame_PerfectPaths_Square() { + + } +}; + + + +class BPMiniGame_PerfectPaths : public BPMiniGame { +public: + BPMiniGame_PerfectPaths(BPGame* game); + ~BPMiniGame_PerfectPaths(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + void CalculateOurScore(); + void CheckResult(); + void LevelUp(); + void CalculateBestMove(); + void FloodFill(BPMiniGame_PerfectPaths_Square* square); + bool CanMove(BPMiniGame_PerfectPaths_Square* square1, BPMiniGame_PerfectPaths_Square* square2); + void SetScore(); + void RenderPerfect(); + void RenderOK(); +protected: + Texture* sfcBackground; + + Texture* sfcPerfect; + Texture* sfcOK; + + BPList StartPositions; + BPList EndPositions; + BPPList LoNumbers; + BPPList HiNumbers; + + BPMiniGame_PerfectPaths_Square* StartPos; + BPMiniGame_PerfectPaths_Square* EndPos; + + BPPList Squares; + BPList Moves; + BPList MoveSquares; // used for flood fill best move calculation + + int CurrentScore; + int BestScore; + + SpriteFont* sfcScore; + + int LastDiff; + int TotalDiff; + + int TimeStarted; + int CurrentLevel; + + MiniGameStates GameState; + int LastStateChange; +}; + +#endif diff --git a/routefinder.cpp b/routefinder.cpp new file mode 100644 index 0000000..5926db2 --- /dev/null +++ b/routefinder.cpp @@ -0,0 +1,318 @@ +// 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 "routefinder.h" +#include "Minigame.h" + +BPMiniGame_RouteFinder::BPMiniGame_RouteFinder(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("routefinder", 320, 416); + sfcSheep = TheGame->LoadBitmap("sheep_right", 38, 34); + sfcRemember = TheGame->LoadBitmap("remember", 320, 416); + + sfcMoveUp = TheGame->LoadBitmap("arrow_up", 48, 48); + sfcMoveDown = TheGame->LoadBitmap("arrow_down", 48, 48); + sfcMoveLeft = TheGame->LoadBitmap("arrow_left", 48, 48); + sfcMoveRight = TheGame->LoadBitmap("arrow_right", 48, 48); + + CurrentMove = -1; + + CurrentLevel = NumWrong = NumTries = SheepX = SheepY = LastStateChange = TimeStarted = 0; + + GameState = GUESSING; + + GameTitle = "Route Finder"; + GameHelp = "Remember the moves I'll show you, then tap once where the sheep will end up after it has made all those moves. Each arrow is one square of movement."; + GameHelp2 = "You can only tap the screen once: in the FINAL position of the sheep. You can reduce the memory work by cancelling out moves. For example, if the moves are \"right, right, down, up, left, right\", then the down and up can be said to cancel each other out because the sheep ends up unmoved after the two moves have taken place."; + + MiniGameType = PUZZLE; +} + +BPMiniGame_RouteFinder::~BPMiniGame_RouteFinder() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcSheep); + SAFE_DELETE(sfcRemember); + + SAFE_DELETE(sfcMoveUp); + SAFE_DELETE(sfcMoveDown); + SAFE_DELETE(sfcMoveLeft); + SAFE_DELETE(sfcMoveRight); + + Moves.Clear(); +} + +void BPMiniGame_RouteFinder::Start() { + LevelUp(); + + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_RouteFinder::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(560 - round(TimePassed * 1.5) - (NumWrong * 50)); +} + +void BPMiniGame_RouteFinder::Render() { + switch (GameState) { + case MOVES: + TheGame->DrawImage(sfcRemember, 0, 0); + + for (int i = 0; i < Moves.Count; ++i) { + switch (Moves[i]) { + case MT_UP: + TheGame->DrawImage(sfcMoveUp, 136, 53 + (i * 51)); + break; + case MT_DOWN: + TheGame->DrawImage(sfcMoveDown, 136, 53 + (i * 51)); + break; + case MT_LEFT: + TheGame->DrawImage(sfcMoveLeft, 136, 53 + (i * 51)); + break; + case MT_RIGHT: + TheGame->DrawImage(sfcMoveRight, 136, 53 + (i * 51)); + break; + } + } + + break; + + default: + TheGame->DrawImage(sfcBackground, 0, 0); + + TheGame->DrawImage(sfcSheep, SheepX, SheepY); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } + + break; + } +} + +void BPMiniGame_RouteFinder::LevelUp() { + GameState = MOVES; + + SheepX = 11 + (TheGame->RandomRange(0, 6) * 44); + SheepY = 15 + (TheGame->RandomRange(0, 8) * 44); + + CurrentMove = -1; + Moves.Clear(); + + ++CurrentLevel; + + switch (CurrentLevel) { + case 1: + AddMove(); + AddMove(); + break; + + case 2: + AddMove(); + AddMove(); + AddMove(); + break; + + case 3: + AddMove(); + AddMove(); + AddMove(); + AddMove(); + break; + + case 4: + AddMove(); + AddMove(); + AddMove(); + AddMove(); + AddMove(); + break; + + default: + AddMove(); + AddMove(); + AddMove(); + AddMove(); + AddMove(); + AddMove(); + break; + } +} + +void BPMiniGame_RouteFinder::Tick() { + if (GameState == MOVING) { + if (LastStateChange + 400 < TheGame->TickCount) { + // we've waited a little, go ahead and move + LastStateChange = TheGame->TickCount; + ++CurrentMove; + + if (CurrentMove == Moves.Count) { + // all moves done - were they correct? + CheckAnswer(); + } else { + TheGame->PlaySound("soft_boom"); + + switch (Moves[CurrentMove]) { + case MT_UP: + SheepY -= 44; + break; + + case MT_DOWN: + SheepY += 44; + break; + + case MT_LEFT: + SheepX -= 44; + break; + + case MT_RIGHT: + SheepX += 44; + break; + } + } + } + } + + if (GameState == CORRECT && LastStateChange + 500 < TheGame->TickCount) { + LevelUp(); + } + + if (GameState == WRONG && LastStateChange + 1000 < TheGame->TickCount) { + --CurrentLevel; + LevelUp(); + } + + if (GameState == SUCCESS && LastStateChange + 250 < TheGame->TickCount) { + Success(); + } + + if (GameState == FAILURE && LastStateChange + 250 < TheGame->TickCount) { + Failure(); + } +} + +void BPMiniGame_RouteFinder::CheckAnswer() { + if (TheGame->PointOverRect(GuessedPosition.X, GuessedPosition.Y, SheepX, SheepY, 64, 64)) { + if (NumTries < 7) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + } else { + GameState = SUCCESS; + } + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + ++NumWrong; + } + + ++NumTries; + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_RouteFinder::OnMouseUp() { + +} + +void BPMiniGame_RouteFinder::OnMouseMove() { + +} + +void BPMiniGame_RouteFinder::OnMouseDown() { + switch (GameState) { + case MOVES: + TheGame->PlaySound("card_flip2"); + GameState = GUESSING; + break; + + case GUESSING: + GuessedPosition = TouchEvent; + GameState = MOVING; + LastStateChange = TheGame->TickCount; + break; + } +} + +void BPMiniGame_RouteFinder::AddMove() { + int sheepx; + int sheepy; + + if (Moves.Count == 0) { + sheepx = SheepX; + sheepy = SheepY; + } else { + BPPoint* sheeppos = CalculateSheepPos(); + sheepx = sheeppos->X; + sheepy = sheeppos->Y; + } + + MoveTypes excluded1 = MT_NONE; + MoveTypes excluded2 = MT_NONE; + + if (sheepx <= 11) excluded1 = MT_LEFT; + if (sheepx >= 275) excluded1 = MT_RIGHT; + if (sheepy <= 15) excluded2 = MT_UP; + if (sheepy >= 367) excluded2 = MT_DOWN; + + + MoveTypes chosenmove = MT_NONE; + + do { + switch (TheGame->RandomRange(1, 4)) { + case 1: + chosenmove = MT_UP; + break; + case 2: + chosenmove = MT_DOWN; + break; + case 3: + chosenmove = MT_LEFT; + break; + case 4: + chosenmove = MT_RIGHT; + break; + } + } while (chosenmove == excluded1 || chosenmove == excluded2); + + Moves.Add(chosenmove); +} + +BPPoint* BPMiniGame_RouteFinder::CalculateSheepPos() { + BPPoint* ret = new BPPoint(SheepX, SheepY); + + for (int i = 0; i < Moves.Count; ++i) { + MoveTypes move = (MoveTypes)Moves[i]; + switch (move) { + case MT_UP: + ret->Y -= 44; + break; + + case MT_DOWN: + ret->Y += 44; + break; + + case MT_LEFT: + ret->X -= 44; + break; + + case MT_RIGHT: + ret->X += 44; + break; + } + } + + return ret; +} diff --git a/routefinder.h b/routefinder.h new file mode 100644 index 0000000..2538232 --- /dev/null +++ b/routefinder.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef __ROUTEFINDER_H__ +#define __ROUTEFINDER_H__ + +#include "Minigame.h" + +enum MoveTypes { MT_UP, MT_DOWN, MT_LEFT, MT_RIGHT, MT_NONE }; + +class BPMiniGame_RouteFinder : public BPMiniGame { +public: + BPMiniGame_RouteFinder(BPGame* game); + ~BPMiniGame_RouteFinder(); + void Start(); + int GetWeight(); + void Render(); + void LevelUp(); + void Tick(); + void CheckAnswer(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + void AddMove(); + BPPoint* CalculateSheepPos(); +protected: + Texture* sfcBackground; + Texture* sfcSheep; + Texture* sfcRemember; + + Texture* sfcMoveUp; + Texture* sfcMoveDown; + Texture* sfcMoveLeft; + Texture* sfcMoveRight; + + BPList Moves; + int CurrentMove; + + int CurrentLevel; + int NumWrong; + int NumTries; + int SheepX; + int SheepY; + + MiniGameStates GameState; + int LastStateChange; + BPPoint GuessedPosition; + + int TimeStarted; +}; + +#endif diff --git a/rps.cpp b/rps.cpp new file mode 100644 index 0000000..6e07f3b --- /dev/null +++ b/rps.cpp @@ -0,0 +1,193 @@ +// 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 "rps.h" +#include "Minigame.h" + +BPMiniGame_RPS::BPMiniGame_RPS(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("rockpaperscissors", 320, 416); + sfcLose = TheGame->LoadBitmap("rockpaperscissors_loses", 320, 64); + + sfcRock = TheGame->LoadBitmap("rock", 90, 90); + sfcPaper = TheGame->LoadBitmap("paper", 90, 90); + sfcScissors = TheGame->LoadBitmap("scissors", 90, 90); + + LastStateChange = -1; + GameState = GUESSING; + + SuccessTime = -1; + + CurrentPosition = NULL; + PositionNum = MustWin = Tries = NumWrong = TimeStarted = 0; + + GameTitle = "Rock vs Paper"; + GameHelp = "Paper beats rock, scissors beats paper and rock beats scissors - choose the right play each time. But here's the twist: sometimes you have to lose!"; + GameHelp2 = "You either have to WIN or you have to LOSE, so read the on-screen instructions carefully. When you have to win, your opponent will make a move (eg scissors) and you have to beat it. But when you have to lose, you need to choose whichever move would lose against your opponent's move."; + + MiniGameType = PUZZLE; + + Positions.Add(new BPMiniGame_RPS_Position("Rock", "Scissors", "Paper", sfcRock)); + Positions.Add(new BPMiniGame_RPS_Position("Paper", "Rock", "Scissors", sfcPaper)); + Positions.Add(new BPMiniGame_RPS_Position("Scissors", "Paper", "Rock", sfcScissors)); + + Play(); +} + +BPMiniGame_RPS::~BPMiniGame_RPS() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcLose); + SAFE_DELETE(sfcRock); + SAFE_DELETE(sfcPaper); + SAFE_DELETE(sfcScissors); + + Positions.Clear(); +} + +void BPMiniGame_RPS::Play() { + if (Tries >= 10) return; + + Positions.Shuffle(); + + if (TheGame->RandomRange(0, 7) % 2 == 0) { + MustWin = true; + } else { + MustWin = false; + } + + int newpos; + + if (PositionNum == -1) { + newpos = TheGame->RandomRange(0, 2); + } else { + newpos = TheGame->RandomRangeExcept(0, 2, PositionNum); + } + + PositionNum = newpos; + CurrentPosition = Positions[newpos]; + + GameState = GUESSING; +} + +void BPMiniGame_RPS::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_RPS::GetWeight() { + int TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(500 - (NumWrong * 60) - round(TimePassed * 1.55f)); +} + +void BPMiniGame_RPS::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + if (!MustWin) TheGame->DrawImage(sfcLose, 0, 229); + + TheGame->DrawImage(CurrentPosition->Sfc, 110, 89); + + TheGame->DrawImage(Positions[0]->Sfc, 15, 307); + TheGame->DrawImage(Positions[1]->Sfc, 115, 307); + TheGame->DrawImage(Positions[2]->Sfc, 215, 307); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_RPS::Tick() { + if (SuccessTime != -1 && SuccessTime + 250 < TheGame->TickCount) { + Success(); + } + + if (LastStateChange != -1 && LastStateChange + 750 < TheGame->TickCount) { + LastStateChange = -1; + Play(); + } +} + +void BPMiniGame_RPS::OnMouseDown() { + +} + +void BPMiniGame_RPS::OnMouseMove() { + +} + +void BPMiniGame_RPS::OnMouseUp() { + if (GameState == CORRECT || GameState == WRONG) return; + + BPMiniGame_RPS_Position* chosenposition = NULL; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 15, 307, 90, 90)) { + chosenposition = Positions[0]; + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 115, 307, 90, 90)) { + chosenposition = Positions[1]; + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 215, 307, 90, 90)) { + chosenposition = Positions[2]; + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 110, 89, 90, 90)) { + if (MustWin) { + MessageBox::Show("This shows the move an opponent has played. You have been asked to choose the move that beats your opponent, so you need to tap one of the three pictures below.", GameTitle); + } else { + MessageBox::Show("This shows the move an opponent has played. You have been asked to choose the move that loses to your opponent, so you need to tap one of the three pictures below.", GameTitle); + } + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 229, 320, 64)) { + if (MustWin) { + MessageBox::Show("You need to choose the right move to beat your opponent - which of the three below is the correct answer?", GameTitle); + } else { + MessageBox::Show("You need to choose the right move to lose to your opponent - which of the three below is the correct answer?", GameTitle); + } + } + + if (chosenposition == NULL) return; + + ++Tries; + + if (MustWin) { + if (CurrentPosition->BeatenBy == chosenposition->Position) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + ++NumWrong; + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } + } else { + if (CurrentPosition->Beats == chosenposition->Position) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + } else { + TheGame->PlaySound("wrong"); + ++NumWrong; + GameState = WRONG; + LastStateChange = TheGame->TickCount; + } + } + + UpdateScore(); +} + +void BPMiniGame_RPS::UpdateScore() { + if (Tries < 0) Tries = 0; + + if (Tries >= 10 && SuccessTime == -1) { + SuccessTime = TheGame->TickCount ; + } +} diff --git a/rps.h b/rps.h new file mode 100644 index 0000000..8a120fa --- /dev/null +++ b/rps.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef __RPS_H__ +#define __RPS_H__ + +#include "Minigame.h" + +class BPMiniGame_RPS_Position { +public: + const char* Position; + const char* Beats; + const char* BeatenBy; + Texture* Sfc; + + BPMiniGame_RPS_Position(const char* position, const char* beats, const char* beatenby, Texture* sfc) { + Position = position; + Beats = beats; + BeatenBy = beatenby; + Sfc = sfc; + } + + ~BPMiniGame_RPS_Position() { +// SAFE_DELETE(Position); +// SAFE_DELETE(Beats); +// SAFE_DELETE(BeatenBy); + } +}; + +class BPMiniGame_RPS : public BPMiniGame { +public: + BPMiniGame_RPS(BPGame* game); + ~BPMiniGame_RPS(); + void Play(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void UpdateScore(); +protected: + Texture* sfcBackground; + Texture* sfcLose; + + Texture* sfcRock; + Texture* sfcPaper; + Texture* sfcScissors; + + int LastStateChange; + MiniGameStates GameState; + + int SuccessTime; + + BPPList Positions; + + BPMiniGame_RPS_Position* CurrentPosition; + int PositionNum; + bool MustWin; + + int Tries; + int NumWrong; + + int TimeStarted; +}; + +#endif diff --git a/scrambled.cpp b/scrambled.cpp new file mode 100644 index 0000000..9ca25c3 --- /dev/null +++ b/scrambled.cpp @@ -0,0 +1,476 @@ +// 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 "scrambled.h" +#include "Minigame.h" + +BPMiniGame_Scrambled::BPMiniGame_Scrambled(BPGame* game) : BPMiniGame(game) { + AnswersYOffset = 9; + + CurrentLevel = Score = RandSeed = SelectedItem = TimeStarted = 0; + + LastStateChange = -1; + + sfcBackground = TheGame->LoadBitmap("scrambled", 320, 416); + + sfcAnswer1 = sfcAnswer2 = sfcAnswer3 = sfcAnswer4 = NULL; + TheQuestion = NULL; + + GameTitle = "Scrambled!"; + GameHelp = "I'm going to scramble words and numbers by adding, removing and switching letters. You need to figure out what I've done, then tap the correct answers to my questions."; + GameHelp2 = "Have letters been added, swapped or deleted? Has the whole word been reversed? Figure out what I changed to solve the problems!"; + + MiniGameType = PUZZLE; + + // note: each of these words have no duplicate letters, eg "DEERS" is not allowed + Words.Add(string("ACORN")); + Words.Add(string("AFTER")); + Words.Add(string("BEACH")); + Words.Add(string("BEAST")); + Words.Add(string("BLACK")); + Words.Add(string("BLADE")); + Words.Add(string("BLAST")); + Words.Add(string("BLAZE")); + Words.Add(string("BLOCK")); + Words.Add(string("BRAIN")); + Words.Add(string("BRAKE")); + Words.Add(string("BRICK")); + Words.Add(string("BROWN")); + Words.Add(string("CARDS")); + Words.Add(string("CHAMP")); + Words.Add(string("CHILD")); + Words.Add(string("CLAMP")); + Words.Add(string("CLEAR")); + Words.Add(string("CLEFT")); + Words.Add(string("CLOSE")); + Words.Add(string("CODER")); + Words.Add(string("COULD")); + Words.Add(string("CRASH")); + Words.Add(string("DAIRY")); + Words.Add(string("DIRTY")); + Words.Add(string("DOZEN")); + Words.Add(string("DRINK")); + Words.Add(string("ENJOY")); + Words.Add(string("FAITH")); + Words.Add(string("FIELD")); + Words.Add(string("FINAL")); + Words.Add(string("FLASH")); + Words.Add(string("FLASK")); + Words.Add(string("FLING")); + Words.Add(string("FLIRT")); + Words.Add(string("FRESH")); + Words.Add(string("FUDGE")); + Words.Add(string("GIANT")); + Words.Add(string("GLINT")); + Words.Add(string("GLITZ")); + Words.Add(string("GRAPH")); + Words.Add(string("GROWL")); + Words.Add(string("GUARD")); + Words.Add(string("GUIDE")); + Words.Add(string("HEART")); + Words.Add(string("HEAVY")); + Words.Add(string("HORSE")); + Words.Add(string("JUICY")); + Words.Add(string("LEAST")); + Words.Add(string("LEMON")); + Words.Add(string("MAGIC")); + Words.Add(string("MEANS")); + Words.Add(string("MICRO")); + Words.Add(string("MISTY")); + Words.Add(string("MODEL")); + Words.Add(string("MOUTH")); + Words.Add(string("MOVIE")); + Words.Add(string("OTHER")); + Words.Add(string("PAIRS")); + Words.Add(string("PARKS")); + Words.Add(string("PARTY")); + Words.Add(string("PHASE")); + Words.Add(string("PHONE")); + Words.Add(string("PLACE")); + Words.Add(string("PLANK")); + Words.Add(string("PLANT")); + Words.Add(string("POWER")); + Words.Add(string("PRICE")); + Words.Add(string("PRINT")); + Words.Add(string("QUICK")); + Words.Add(string("QUIET")); + Words.Add(string("RATED")); + Words.Add(string("RIGHT")); + Words.Add(string("RIVAL")); + Words.Add(string("ROYAL")); + Words.Add(string("RUSTY")); + Words.Add(string("SCALE")); + Words.Add(string("SCORE")); + Words.Add(string("SHAKE")); + Words.Add(string("SHAVE")); + Words.Add(string("SNORE")); + Words.Add(string("SPARK")); + Words.Add(string("SPEAR")); + Words.Add(string("SPEND")); + Words.Add(string("STAND")); + Words.Add(string("STALK")); + Words.Add(string("STORE")); + Words.Add(string("SUGAR")); + Words.Add(string("SWAMP")); + Words.Add(string("SWIFT")); + Words.Add(string("SWING")); + Words.Add(string("THING")); + Words.Add(string("TRAMP")); + Words.Add(string("UNDER")); + Words.Add(string("WATER")); + Words.Add(string("WHARF")); + Words.Add(string("WHILE")); + Words.Add(string("WHITE")); + Words.Add(string("WHOLE")); + Words.Add(string("WRITE")); + + Numbers.Add(string("0")); + Numbers.Add(string("1")); + Numbers.Add(string("2")); + Numbers.Add(string("3")); + Numbers.Add(string("4")); + Numbers.Add(string("5")); + Numbers.Add(string("6")); + Numbers.Add(string("7")); + Numbers.Add(string("8")); + Numbers.Add(string("9")); + + Letters.Add(string("A")); + Letters.Add(string("B")); + Letters.Add(string("C")); + Letters.Add(string("D")); + Letters.Add(string("E")); + Letters.Add(string("F")); + Letters.Add(string("G")); + Letters.Add(string("H")); + Letters.Add(string("J")); + Letters.Add(string("K")); + Letters.Add(string("L")); + Letters.Add(string("M")); + Letters.Add(string("N")); + Letters.Add(string("P")); + Letters.Add(string("Q")); + Letters.Add(string("R")); + Letters.Add(string("S")); + Letters.Add(string("T")); + Letters.Add(string("U")); + Letters.Add(string("V")); + Letters.Add(string("W")); + Letters.Add(string("X")); + Letters.Add(string("Y")); + Letters.Add(string("Z")); + + Positions.Add(0); + Positions.Add(1); + Positions.Add(2); + Positions.Add(3); + Positions.Add(4); + + for (int i = 0; i < 4; ++i) { + BPMiniGame_Scrambled_Answer* answer = new BPMiniGame_Scrambled_Answer(); + Answers.Add(answer); + } + + BPPoint point = BPPoint(29, 164); + AnswerPositions.Add(point); + + point = BPPoint(29, 222); + AnswerPositions.Add(point); + + point = BPPoint(29, 280); + AnswerPositions.Add(point); + + point = BPPoint(29, 337); + AnswerPositions.Add(point); + + LevelUp(); +} + +BPMiniGame_Scrambled::~BPMiniGame_Scrambled() { + SAFE_DELETE(sfcBackground); + Answers.Clear(); +} + +void BPMiniGame_Scrambled::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_Scrambled::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax((Score * 75) - round(TimePassed * 4)); +} + +void BPMiniGame_Scrambled::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + TheGame->DrawString(TheQuestion, BLACK, 29, 29); + + for (int i = 0; i < Answers.Count; ++i) { + BPMiniGame_Scrambled_Answer* answer = Answers[i]; + TheGame->DrawString(answer->Text, BLACK, answer->Pos.X, answer->Pos.Y + AnswersYOffset); + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +string BPMiniGame_Scrambled::FlipLetters(string Word) { + string retval = Word; + reverse(retval.begin(), retval.end()); + return retval; +} + +string BPMiniGame_Scrambled::RemoveLetters(string Word, int Pos) { + string retval = Word; + + retval = retval.erase(Pos, 1); + return retval; +} + +string BPMiniGame_Scrambled::SwitchLetters(string Word, int Pos) { + string retval = Word; + const char letter = retval[Pos]; + + if (Pos == retval.size() - 1) { + // this was the last letter; insert it at the front + retval[4] = retval[0]; + retval[0] = letter; + } else { + retval[Pos] = retval[Pos + 1]; + retval[Pos + 1] = letter; + } + + return retval; +} + +string BPMiniGame_Scrambled::AddLetters(string Word, string Letter, int Pos) { + string retval = Word; + retval.insert(Pos, Letter); + + return retval; +} + +string BPMiniGame_Scrambled::GetUniqueNumber(string Word1, string Word2) { + for (int i = 0; i < Numbers.Count; ++i) { + string Number = Numbers[i]; + if (Word1.find(Number) == string::npos && Word2.find(Number) == string::npos) return Number; + } + + return "D"; // no unique numbers are around; send back a letter +} + +string BPMiniGame_Scrambled::GetUniqueLetter(string Word1, string Word2) { + while (true) { + string Letter = Letters[TheGame->RandomRange(0, Letters.Count - 1)]; + if (Word1.find(Letter) == string::npos && Word2.find(Letter) == string::npos) return Letter; + } +} + +void BPMiniGame_Scrambled::Tick() { + if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) { + LevelUp(); + } +} + +void BPMiniGame_Scrambled::OnMouseDown() { + +} + +void BPMiniGame_Scrambled::OnMouseMove() { + +} + +void BPMiniGame_Scrambled::OnMouseUp() { + switch (GameState) { + case CORRECT: + LevelUp(); + break; + + case WRONG: + LevelUp(); + break; + + case WAITING: + for (int i = 0; i < Answers.Count; ++i) { + BPMiniGame_Scrambled_Answer* answer = Answers[i]; + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, answer->Pos.X, answer->Pos.Y, 264.0f, 47.0f)) { + if (answer->Correct) { + ++Score; + GameState = CORRECT; + LastStateChange = TheGame->TickCount; + TheGame->PlaySound("correct"); + } else { + --Score; + GameState = WRONG; + LastStateChange = TheGame->TickCount; + TheGame->PlaySound("wrong"); + } + + return; + } + } + + break; + } +} + +void BPMiniGame_Scrambled::LevelUp() { + LastStateChange = -1; + GameState = WAITING; + + if (!MarathonMode && CurrentLevel >= 10) { + Success(); + return; + } + + ++CurrentLevel; + + Numbers.Shuffle(); + Words.Shuffle(); + Positions.Shuffle(); + Answers.Shuffle(); + + bool NumbersOnly = false; + + switch (TheGame->RandomRange(0, 3)) { + case 0: + case 1: + // word to number comparison + ExampleWord = Words[0]; + TestWord.clear(); + + TestWord.append(Numbers[0]); + TestWord.append(Numbers[1]); + TestWord.append(Numbers[2]); + TestWord.append(Numbers[3]); + TestWord.append(Numbers[4]); + break; + + case 2: + // word to word comparison + ExampleWord = Words[0]; + TestWord = Words[1]; + break; + + case 3: + // number to number comparison + NumbersOnly = true; // so we add a number in AddLetters() rather than adding a letter + + ExampleWord.clear(); + + ExampleWord.append(Numbers[0]); + ExampleWord.append(Numbers[1]); + ExampleWord.append(Numbers[2]); + ExampleWord.append(Numbers[3]); + ExampleWord.append(Numbers[4]); + + Numbers.Shuffle(); + + TestWord.clear(); + + TestWord.append(Numbers[0]); + TestWord.append(Numbers[1]); + TestWord.append(Numbers[2]); + TestWord.append(Numbers[3]); + TestWord.append(Numbers[4]); + + break; + } + + switch (TheGame->RandomRange(0, 3)) { + case 0: + { + string LetterToAdd; + + if (NumbersOnly) { + LetterToAdd = GetUniqueNumber(ExampleWord, TestWord); + } else { + LetterToAdd = GetUniqueLetter(ExampleWord, TestWord); + } + + ExampleWordAnswer = AddLetters(ExampleWord, LetterToAdd, Positions[0]); + Answer1 = AddLetters(TestWord, LetterToAdd, Positions[0]); + Answer2 = AddLetters(TestWord, LetterToAdd, Positions[1]); + Answer3 = AddLetters(TestWord, LetterToAdd, Positions[2]); + Answer4 = AddLetters(TestWord, LetterToAdd, Positions[3]); + + break; + } + + case 1: + ExampleWordAnswer = FlipLetters(ExampleWord); + Answer1 = FlipLetters(TestWord); + + // we're going to be cruel, and flip these words then switch a letter so they look vaguely right at first glance + Answer2 = FlipLetters(SwitchLetters(TestWord, Positions[0])); + Answer3 = FlipLetters(SwitchLetters(TestWord, Positions[1])); + Answer4 = FlipLetters(SwitchLetters(TestWord, Positions[2])); + break; + + case 2: + ExampleWordAnswer = RemoveLetters(ExampleWord, Positions[0]); + Answer1 = RemoveLetters(TestWord, Positions[0]); + Answer2 = RemoveLetters(TestWord, Positions[1]); + Answer3 = RemoveLetters(TestWord, Positions[2]); + Answer4 = RemoveLetters(TestWord, Positions[3]); + break; + + case 3: + ExampleWordAnswer = SwitchLetters(ExampleWord, Positions[0]); + Answer1 = SwitchLetters(TestWord, Positions[0]); + + // to make this a bit harder, we're going to switch two letters in the others + Answer2 = SwitchLetters(TestWord, Positions[0]); + Answer2 = SwitchLetters(Answer2, Positions[1]); + + Answer3 = SwitchLetters(TestWord, Positions[0]); + Answer3 = SwitchLetters(Answer3, Positions[2]); + + Answer4 = SwitchLetters(TestWord, Positions[0]); + Answer4 = SwitchLetters(Answer4, Positions[3]); + break; + } + + Answers[0]->Correct = true; + Answers[1]->Correct = false; + Answers[2]->Correct = false; + Answers[3]->Correct = false; + + TheGame->AllocString(&Answers[0]->Text, Answer1.c_str(), NORMAL, 264.0f, 47.0f, CENTRED); + TheGame->AllocString(&Answers[1]->Text, Answer2.c_str(), NORMAL, 264.0f, 47.0f, CENTRED); + TheGame->AllocString(&Answers[2]->Text, Answer3.c_str(), NORMAL, 264.0f, 47.0f, CENTRED); + TheGame->AllocString(&Answers[3]->Text, Answer4.c_str(), NORMAL, 264.0f, 47.0f, CENTRED); + + Answers.Shuffle(); + + Answers[0]->Pos = AnswerPositions[0]; + Answers[1]->Pos = AnswerPositions[1]; + Answers[2]->Pos = AnswerPositions[2]; + Answers[3]->Pos = AnswerPositions[3]; + + ostringstream question; + question << "If " << ExampleWord.c_str() << " is " << ExampleWordAnswer.c_str() << " then " << TestWord.c_str() << " is..."; + TheGame->AllocString(&TheQuestion, question.str().c_str(), LARGE, 264.0f, 135.0f, CENTRED); +} + +void BPMiniGame_Scrambled::SetMarathon() { + MarathonMode = true; + GameHelp = "I'm going to scramble words and numbers by adding, removing and switching letters. Can you figure out what's changed?"; +} diff --git a/scrambled.h b/scrambled.h new file mode 100644 index 0000000..7acc657 --- /dev/null +++ b/scrambled.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef __SCRAMBLED_H__ +#define __SCRAMBLED_H__ + +#include "Minigame.h" + +class BPMiniGame_Scrambled_Answer { +public: + SpriteFont* Text; + bool Correct; + + BPPoint Pos; + + BPMiniGame_Scrambled_Answer() { + Text = NULL; + } + + ~BPMiniGame_Scrambled_Answer() { + SAFE_DELETE(Text); + } +}; + +class BPMiniGame_Scrambled : public BPMiniGame { + BPList Words; + BPList Numbers; // we use a list so each generated number is made up only of unique digits (eg 5335 is not allowed) + BPList Letters; + BPList Positions; // used to remove or switch random (but unique) individual letters/numbers + BPPList Answers; + BPList AnswerPositions; + + SpriteFont* TheQuestion; + + int AnswersYOffset; + + int CurrentLevel; + int Score; + + int RandSeed; + + string ExampleWord; + string ExampleWordAnswer; + string TestWord; + + string Answer1; + string Answer2; + string Answer3; + string Answer4; + + Texture* sfcAnswer1; + Texture* sfcAnswer2; + Texture* sfcAnswer3; + Texture* sfcAnswer4; + + MiniGameStates GameState; + int LastStateChange; + + Texture* sfcBackground; + + int SelectedItem; + + int TimeStarted; + +public: + BPMiniGame_Scrambled(BPGame* game); + ~BPMiniGame_Scrambled(); + void Start(); + int GetWeight(); + void Render(); + string FlipLetters(string Word); + string RemoveLetters(string Word, int Pos); + string SwitchLetters(string Word, int Pos); + string AddLetters(string Word, string Letter, int Pos); + string GetUniqueNumber(string Word1, string Word2); + string GetUniqueLetter(string Word1, string Word2); + void Tick(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void LevelUp(); + void SetMarathon(); +}; + +#endif diff --git a/setfinder.cpp b/setfinder.cpp new file mode 100644 index 0000000..4039f04 --- /dev/null +++ b/setfinder.cpp @@ -0,0 +1,490 @@ +// 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 "setfinder.h" +#include "Minigame.h" + +BPMiniGame_SetFinder::BPMiniGame_SetFinder(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("setfinder", 320, 416); + sfcSelectedHi = TheGame->LoadBitmap("selected48", 48, 48); + + sfcCircleBlueEmpty = TheGame->LoadBitmap("set_circle_blue_empty", 45, 45); + sfcCircleBlueFilled = TheGame->LoadBitmap("set_circle_blue_filled", 45, 45); + sfcCircleBlueStripy = TheGame->LoadBitmap("set_circle_blue_stripy", 45, 45); + sfcCircleGreenEmpty = TheGame->LoadBitmap("set_circle_green_empty", 45, 45); + sfcCircleGreenFilled = TheGame->LoadBitmap("set_circle_green_filled", 45, 45); + sfcCircleGreenStripy = TheGame->LoadBitmap("set_circle_green_stripy", 45, 45); + sfcCircleRedEmpty = TheGame->LoadBitmap("set_circle_red_empty", 45, 45); + sfcCircleRedFilled = TheGame->LoadBitmap("set_circle_red_filled", 45, 45); + sfcCircleRedStripy = TheGame->LoadBitmap("set_circle_red_stripy", 45, 45); + + sfcSquareBlueEmpty = TheGame->LoadBitmap("set_square_blue_empty", 45, 45); + sfcSquareBlueFilled = TheGame->LoadBitmap("set_square_blue_filled", 45, 45); + sfcSquareBlueStripy = TheGame->LoadBitmap("set_square_blue_stripy", 45, 45); + sfcSquareGreenEmpty = TheGame->LoadBitmap("set_square_green_empty", 45, 45); + sfcSquareGreenFilled = TheGame->LoadBitmap("set_square_green_filled", 45, 45); + sfcSquareGreenStripy = TheGame->LoadBitmap("set_square_green_stripy", 45, 45); + sfcSquareRedEmpty = TheGame->LoadBitmap("set_square_red_empty", 45, 45); + sfcSquareRedFilled = TheGame->LoadBitmap("set_square_red_filled", 45, 45); + sfcSquareRedStripy = TheGame->LoadBitmap("set_square_red_stripy", 45, 45); + + sfcTriangleBlueEmpty = TheGame->LoadBitmap("set_triangle_blue_empty", 45, 45); + sfcTriangleBlueFilled = TheGame->LoadBitmap("set_triangle_blue_filled", 45, 45); + sfcTriangleBlueStripy = TheGame->LoadBitmap("set_triangle_blue_stripy", 45, 45); + sfcTriangleGreenEmpty = TheGame->LoadBitmap("set_triangle_green_empty", 45, 45); + sfcTriangleGreenFilled = TheGame->LoadBitmap("set_triangle_green_filled", 45, 45); + sfcTriangleGreenStripy = TheGame->LoadBitmap("set_triangle_green_stripy", 45, 45); + sfcTriangleRedEmpty = TheGame->LoadBitmap("set_triangle_red_empty", 45, 45); + sfcTriangleRedFilled = TheGame->LoadBitmap("set_triangle_red_filled", 45, 45); + sfcTriangleRedStripy = TheGame->LoadBitmap("set_triangle_red_stripy", 45, 45); + + MatchCount = SuccessTime = TimeStarted = 0; + + sfcClock = NULL; + + GameTitle = "Set Finder"; + GameHelp = "You must find five sets of three items with different shapes, colours and patterns. For example, an empty green square, a filled blue circle and a stripy red triangle makes one set. Good luck!"; + GameHelp2 = "The key to this game is that each of the three items in your set must have completely different attributes. For example, having two circles and a square isn't allowed, and neither is having two red items and a blue item."; + + MiniGameType = PUZZLE; + + BPList Positions; + + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { + Positions.Add(BPPoint(12 + (i * 50), 63 + (j * 50))); + } + } + + Positions.Shuffle(); + + BPList TheFillTypes; + TheFillTypes.Add(EMPTY); + TheFillTypes.Add(STRIPED); + TheFillTypes.Add(FILLED); + + BPList TheShapeTypes; + TheShapeTypes.Add(CIRCLE); + TheShapeTypes.Add(SQUARE); + TheShapeTypes.Add(TRIANGLE); + + int loopcount = 0; + + for (int i = 0; i < TheFillTypes.Count; ++i) { + FillTypes filltype = TheFillTypes[i]; + + for (int j = 0; j < TheShapeTypes.Count; ++j) { + ShapeTypes shapetype = TheShapeTypes[j]; + + BPMiniGame_SetFinder_Item* item; + + item = new BPMiniGame_SetFinder_Item(); + item->Col = RED; + item->FillType = filltype; + item->ShapeType = shapetype; + item->X = Positions[loopcount].X; + item->Y = Positions[loopcount].Y; + CurrentItems.Add(item); + + ++loopcount; + + item = new BPMiniGame_SetFinder_Item(); + item->Col = GREEN; + item->FillType = filltype; + item->ShapeType = shapetype; + item->X = Positions[loopcount].X; + item->Y = Positions[loopcount].Y; + CurrentItems.Add(item); + + ++loopcount; + + item = new BPMiniGame_SetFinder_Item(); + item->Col = BLUE; + item->FillType = filltype; + item->ShapeType = shapetype; + item->X = Positions[loopcount].X; + item->Y = Positions[loopcount].Y; + CurrentItems.Add(item); + + ++loopcount; + } + } +} + +BPMiniGame_SetFinder::~BPMiniGame_SetFinder() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcSelectedHi); + + SAFE_DELETE(sfcCircleBlueEmpty); + SAFE_DELETE(sfcCircleBlueFilled); + SAFE_DELETE(sfcCircleBlueStripy); + SAFE_DELETE(sfcCircleGreenEmpty); + SAFE_DELETE(sfcCircleGreenFilled); + SAFE_DELETE(sfcCircleGreenStripy); + SAFE_DELETE(sfcCircleRedEmpty); + SAFE_DELETE(sfcCircleRedFilled); + SAFE_DELETE(sfcCircleRedStripy); + + SAFE_DELETE(sfcSquareBlueEmpty); + SAFE_DELETE(sfcSquareBlueFilled); + SAFE_DELETE(sfcSquareBlueStripy); + SAFE_DELETE(sfcSquareGreenEmpty); + SAFE_DELETE(sfcSquareGreenFilled); + SAFE_DELETE(sfcSquareGreenStripy); + SAFE_DELETE(sfcSquareRedEmpty); + SAFE_DELETE(sfcSquareRedFilled); + SAFE_DELETE(sfcSquareRedStripy); + + SAFE_DELETE(sfcTriangleBlueEmpty); + SAFE_DELETE(sfcTriangleBlueFilled); + SAFE_DELETE(sfcTriangleBlueStripy); + SAFE_DELETE(sfcTriangleGreenEmpty); + SAFE_DELETE(sfcTriangleGreenFilled); + SAFE_DELETE(sfcTriangleGreenStripy); + SAFE_DELETE(sfcTriangleRedEmpty); + SAFE_DELETE(sfcTriangleRedFilled); + SAFE_DELETE(sfcTriangleRedStripy); + + SAFE_DELETE(sfcClock); + + CurrentItems.Clear(); +} + +void BPMiniGame_SetFinder::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_SetFinder::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(800 - round(TimePassed * 10)); +} + +void BPMiniGame_SetFinder::Render() { + static float SelectedRotation = 0.0f; + static float SelectedScale = 0.0f; + SelectedRotation += TheGame->ElapsedSeconds * 50.0f; + SelectedScale += TheGame->ElapsedSeconds; + if (SelectedScale > 1.0f) SelectedScale = 0.0f; + + TheGame->DrawImage(sfcBackground, 0, 0); + + Colour drawcol = (*TheGame->White); + + for (int i = 0; i < CurrentItems.Count; ++i) { + BPMiniGame_SetFinder_Item* item = CurrentItems[i]; + + if (item->MatchTime == -1) { + drawcol = (*TheGame->White); + } else { + float diff = (TheGame->TickCount - item->MatchTime) / 150.0f; + if (diff > 1) diff = 1; + drawcol = Colour(1.0f, 1.0f, 1.0f, TheGame->Lerp(0.9f, 0, diff)); + } + + switch (item->ShapeType) { + case CIRCLE: + switch (item->Col) { + case BLUE: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcCircleBlueEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcCircleBlueFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcCircleBlueStripy, item->X, item->Y, drawcol); + break; + } + break; + + case GREEN: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcCircleGreenEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcCircleGreenFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcCircleGreenStripy, item->X, item->Y, drawcol); + break; + } + break; + + case RED: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcCircleRedEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcCircleRedFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcCircleRedStripy, item->X, item->Y, drawcol); + break; + } + + break; + } + + break; + case SQUARE: + switch (item->Col) { + case BLUE: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcSquareBlueEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcSquareBlueFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcSquareBlueStripy, item->X, item->Y, drawcol); + break; + } + break; + + case GREEN: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcSquareGreenEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcSquareGreenFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcSquareGreenStripy, item->X, item->Y, drawcol); + break; + } + break; + + case RED: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcSquareRedEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcSquareRedFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcSquareRedStripy, item->X, item->Y, drawcol); + break; + } + + break; + } + + break; + + case TRIANGLE: + switch (item->Col) { + case BLUE: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcTriangleBlueEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcTriangleBlueFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcTriangleBlueStripy, item->X, item->Y, drawcol); + break; + } + break; + + case GREEN: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcTriangleGreenEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcTriangleGreenFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcTriangleGreenStripy, item->X, item->Y, drawcol); + break; + } + break; + + case RED: + switch (item->FillType) { + case EMPTY: + TheGame->DrawImage(sfcTriangleRedEmpty, item->X, item->Y, drawcol); + break; + + case FILLED: + TheGame->DrawImage(sfcTriangleRedFilled, item->X, item->Y, drawcol); + break; + + case STRIPED: + TheGame->DrawImage(sfcTriangleRedStripy, item->X, item->Y, drawcol); + break; + } + + break; + } + + break; + } + } + + float scale = TheGame->SmoothStep2(1.0f, 1.2f, SelectedScale); + Colour col = Colour(1.0f, TheGame->SmoothStep2(0.0f, 1.0f, SelectedScale), 1.0f, 1.0f); + + for (int i = 0; i < CurrentItems.Count; ++i) { + BPMiniGame_SetFinder_Item* item = CurrentItems[i]; + + if (item->Selected) { + TheGame->DrawImage(sfcSelectedHi, item->X + sfcTriangleRedStripy->HalfWidth, item->Y + sfcTriangleRedStripy->HalfWidth, SelectedRotation, scale, col); + } + } + + + int TimePassed = TheGame->TickCount - TimeStarted; + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), XLARGE, 320, 49, CENTRED); + } + + TheGame->DrawString(sfcClock, WHITE, 0, 364); +} + +void BPMiniGame_SetFinder::Tick() { + if (SuccessTime != 0 && SuccessTime + 500 < TheGame->TickCount) { + Success(); + } + + CleanMatches(); +} + +void BPMiniGame_SetFinder::CleanMatches() { + for (int i = CurrentItems.Count - 1; i >= 0; --i) { + if (CurrentItems[i]->MatchTime != -1 && CurrentItems[i]->MatchTime + 150 < TheGame->TickCount) { + CurrentItems.RemoveAt(i); + } + } +} + +void BPMiniGame_SetFinder::OnMouseUp() { + +} + +void BPMiniGame_SetFinder::OnMouseMove() { + +} + +void BPMiniGame_SetFinder::OnMouseDown() { + int numselected = 0; + + for (int i = 0; i < CurrentItems.Count; ++i) { + BPMiniGame_SetFinder_Item* item = CurrentItems[i]; + + if (item->Selected && item->MatchTime == -1) ++numselected; + } + + for (int i = 0; i < CurrentItems.Count; ++i) { + BPMiniGame_SetFinder_Item* item = CurrentItems[i]; + + if (item->MatchTime != -1) continue; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, item->X, item->Y, 45, 45)) { + if (item->Selected) { + item->Selected = false; + TheGame->PlaySound("wrong"); + return; + } else if (numselected < 3) { + item->Selected = true; + + if (numselected < 2) { + TheGame->PlaySound("card_flip"); + } + } else { + // already have three; bail out + return; + } + + CheckForMatch(); + } + } +} + +void BPMiniGame_SetFinder::CheckForMatch() { + BPMiniGame_SetFinder_Item* selected1 = NULL; + BPMiniGame_SetFinder_Item* selected2 = NULL; + BPMiniGame_SetFinder_Item* selected3 = NULL; + + for (int i = 0; i < CurrentItems.Count; ++i) { + BPMiniGame_SetFinder_Item* item = CurrentItems[i]; + + if (item->Selected && item->MatchTime == -1) { + if (selected1 == NULL) { + selected1 = item; + } else if (selected2 == NULL) { + selected2 = item; + } else if (selected3 == NULL) { + selected3 = item; + break; + } + } + } + + if (selected3 != NULL) { + // three items matched - are they all different? + if (ItemsAreDifferent(selected1, selected2) && ItemsAreDifferent(selected1, selected3) && ItemsAreDifferent(selected2, selected3)) { + selected1->MatchTime = TheGame->TickCount; + selected2->MatchTime = TheGame->TickCount; + selected3->MatchTime = TheGame->TickCount; + + selected1->Selected = false; + selected2->Selected = false; + selected3->Selected = false; + + ++MatchCount; + + if (MatchCount >= 5 && SuccessTime == 0) { + SuccessTime = TheGame->TickCount; + } + + TheGame->PlaySound("card_flip2"); + } else { + TheGame->PlaySound("card_flip"); + } + } +} + +bool BPMiniGame_SetFinder::ItemsAreDifferent(BPMiniGame_SetFinder_Item* item1, BPMiniGame_SetFinder_Item* item2) { + if ((item1->Col != item2->Col) && (item1->ShapeType != item2->ShapeType) && (item1->FillType != item2->FillType)) return true; + + return false; +} diff --git a/setfinder.h b/setfinder.h new file mode 100644 index 0000000..2aa3fc7 --- /dev/null +++ b/setfinder.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef __SETFINDER_H__ +#define __SETFINDER_H__ + +#include "Minigame.h" + +enum FillTypes { EMPTY, STRIPED, FILLED }; +enum ShapeTypes { CIRCLE, SQUARE, TRIANGLE }; +enum ColourTypes { BLUE, GREEN, RED }; + +class BPMiniGame_SetFinder_Item { +public: + ColourTypes Col; + FillTypes FillType; + ShapeTypes ShapeType; + int X; + int Y; + bool Selected; + + int MatchTime; + + BPMiniGame_SetFinder_Item() { + MatchTime = -1; + Selected = false; + } +}; + +class BPMiniGame_SetFinder : public BPMiniGame { + Texture* sfcBackground; + Texture* sfcSelected; + Texture* sfcSelectedHi; + + BPPList CurrentItems; + + Texture* sfcCircleBlueEmpty; + Texture* sfcCircleBlueFilled; + Texture* sfcCircleBlueStripy; + Texture* sfcCircleGreenEmpty; + Texture* sfcCircleGreenFilled; + Texture* sfcCircleGreenStripy; + Texture* sfcCircleRedEmpty; + Texture* sfcCircleRedFilled; + Texture* sfcCircleRedStripy; + + Texture* sfcSquareBlueEmpty; + Texture* sfcSquareBlueFilled; + Texture* sfcSquareBlueStripy; + Texture* sfcSquareGreenEmpty; + Texture* sfcSquareGreenFilled; + Texture* sfcSquareGreenStripy; + Texture* sfcSquareRedEmpty; + Texture* sfcSquareRedFilled; + Texture* sfcSquareRedStripy; + + Texture* sfcTriangleBlueEmpty; + Texture* sfcTriangleBlueFilled; + Texture* sfcTriangleBlueStripy; + Texture* sfcTriangleGreenEmpty; + Texture* sfcTriangleGreenFilled; + Texture* sfcTriangleGreenStripy; + Texture* sfcTriangleRedEmpty; + Texture* sfcTriangleRedFilled; + Texture* sfcTriangleRedStripy; + + int MatchCount; + int SuccessTime; + + SpriteFont* sfcClock; + int TimeStarted; + +public: + BPMiniGame_SetFinder(BPGame* game); + ~BPMiniGame_SetFinder(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void CleanMatches(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + void CheckForMatch(); + bool ItemsAreDifferent(BPMiniGame_SetFinder_Item* item1, BPMiniGame_SetFinder_Item* item2); +}; + +#endif diff --git a/sharpshooter.cpp b/sharpshooter.cpp new file mode 100644 index 0000000..7ac06cb --- /dev/null +++ b/sharpshooter.cpp @@ -0,0 +1,240 @@ +// 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 "sharpshooter.h" +#include "Minigame.h" + +BPMiniGame_Sharpshooter::BPMiniGame_Sharpshooter(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("sharpshooter", 320, 416); + sfcTarget = TheGame->LoadBitmap("target", 64, 64); + sfcDontShoot = TheGame->LoadBitmap("marble_red", 64, 64); + + Positions.Add(new BPPoint(20, 28)); + Positions.Add(new BPPoint(120, 28)); + Positions.Add(new BPPoint(220, 28)); + Positions.Add(new BPPoint(70, 98)); + Positions.Add(new BPPoint(170, 98)); + Positions.Add(new BPPoint(20, 168)); + Positions.Add(new BPPoint(120, 168)); + Positions.Add(new BPPoint(220, 168)); + Positions.Add(new BPPoint(70, 238)); + Positions.Add(new BPPoint(170, 238)); + Positions.Add(new BPPoint(20, 308)); + Positions.Add(new BPPoint(120, 308)); + Positions.Add(new BPPoint(220, 308)); + + LastCreatedTime = NumTargets = NumHit = 0; + CreateDelay = 500; + TargetLife = 1700; + PauseTime = 0; + + SuccessTime = -1; + + GameTitle = "Sharpshooter"; + GameHelp = "Tap the screen to shoot as many targets as you can, but watch out - shooting bubbles will cost you points!"; + GameHelp2 = "When shooting the targets, don't worry about ammo: you have infinite shots, and firing more doesn't lower your score. For maximum points, don't miss any targets!"; + + MiniGameType = ACTION; +} + +BPMiniGame_Sharpshooter::~BPMiniGame_Sharpshooter() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcTarget); + SAFE_DELETE(sfcDontShoot); + + Targets.Clear(); + Positions.Clear(); +} + +void BPMiniGame_Sharpshooter::Start() { + LastCreatedTime = TheGame->TickCount + 2000; +} + +int BPMiniGame_Sharpshooter::GetWeight() { + int nummissed = 64 - NumHit; + return MinMax(500 - (nummissed * 6)); +} + +void BPMiniGame_Sharpshooter::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < Targets.Count; ++i) { + BPMiniGame_Sharpshooter_Target* target = Targets[i]; + + if (target->HitTime != -1 || target->CreatedTime + 150 > TheGame->TickCount) { + float diff; + + if (target->HitTime == -1) { + // target is appearing + diff = (((target->CreatedTime + 150) - TheGame->TickCount) / 150.0f); + } else { + // target is disappearing + diff = (TheGame->TickCount - target->HitTime) / 150.0f; + + } + + if (diff > 1) diff = 1; + Colour col = Colour(1.0f, 1.0f, 1.0f, TheGame->Lerp(1.0f, 0, diff)); + + if (target->HitTime != -1) { + diff = 1 - diff; + } + + if (target->TargetType == -1) { + TheGame->DrawImage(sfcTarget, target->X + sfcTarget->HalfWidth, target->Y + sfcTarget->HalfHeight, 0.0f, diff, col); + } else { + TheGame->DrawImage(sfcDontShoot, target->X + sfcTarget->HalfWidth, target->Y + sfcTarget->HalfHeight, 0.0f, diff, col); + } + } else { + if (target->TargetType == -1) { + TheGame->DrawImage(sfcTarget, target->X + sfcTarget->HalfWidth, target->Y + sfcTarget->HalfHeight, 0, 1.0f, (*TheGame->White)); + } else { + TheGame->DrawImage(sfcDontShoot, target->X + sfcTarget->HalfWidth, target->Y + sfcTarget->HalfHeight, 0, 1.0f, (*TheGame->White)); + } + } + } +} + +void BPMiniGame_Sharpshooter::Tick() { + if (SuccessTime != -1 && SuccessTime + 500 < TheGame->TickCount) { + Success(); + } + + if (LastCreatedTime + CreateDelay + PauseTime < TheGame->TickCount) { + switch (TheGame->RandomRange(0, 5)) { + case 0: + case 1: + case 2: + case 3: + CreateTarget(-1); + break; + case 4: + case 5: + CreateTarget(1); + break; + + case 6: + // take a breather + break; + } + } + + for (int i = Targets.Count - 1; i >= 0; --i) { + BPMiniGame_Sharpshooter_Target* target = Targets[i]; + + if (target->HitTime != -1 && target->HitTime + 150 < TheGame->TickCount) { + Targets.RemoveAt(i); + continue; + } + + if (target->CreatedTime != -1 && target->CreatedTime + TargetLife < TheGame->TickCount) { + if (target->HitTime == -1) { + target->HitTime = TheGame->TickCount; + } + } + } +} + +void BPMiniGame_Sharpshooter::OnMouseDown() { + for (int i = Targets.Count - 1; i >= 0; --i) { + BPMiniGame_Sharpshooter_Target* target = Targets[i]; + + if (target->HitTime != -1) continue; + + float distance = BPPoint::DistanceSquared(TouchEvent, BPPoint(target->X + sfcTarget->HalfWidth, target->Y + sfcTarget->HalfWidth)); + + if (distance < sfcTarget->HalfWidth * sfcTarget->HalfWidth) { + if (target->TargetType == -1) { + target->HitTime = TheGame->TickCount; + ++NumHit; + + TheGame->PlaySound("gun2"); + } else { + target->HitTime = TheGame->TickCount; + NumHit -= 5; + + TheGame->PlaySound("zap"); + } + + target->Hit = true; + + return; + } + } + + // if we're still here, it means we hit nothing - play the quieter of the two gun sounds + TheGame->PlaySound("gun1"); +} + +void BPMiniGame_Sharpshooter::OnMouseMove() { + +} + +void BPMiniGame_Sharpshooter::OnMouseUp() { + +} + +void BPMiniGame_Sharpshooter::CreateTarget(int type) { + if (NumTargets > 64) { + // create no more targets + if (Targets.Count == 0 && SuccessTime == -1) SuccessTime = TheGame->TickCount; + return; + } + + if (TheGame->RandomRange(0, 4) == 2) { + PauseTime = 2000; + } else { + PauseTime = 0; + } + + + Positions.Shuffle(); + + int i; + bool usethis; + + for (i = 0; i < Positions.Count; ++i) { + // find the first free position + usethis = true; + + for (int j = 0; j < Targets.Count; ++j) { + if (Targets[j]->X == Positions[i]->X && Targets[j]->Y == Positions[i]->Y) { + usethis = false; + break; + } + } + + if (usethis) break; + } + + if (!usethis) { + // no free spaces! + return; + } + + BPMiniGame_Sharpshooter_Target* target = new BPMiniGame_Sharpshooter_Target(); + target->CreatedTime = TheGame->TickCount; + target->X = Positions[i]->X; + target->Y = Positions[i]->Y; + target->TargetType = type; + + Targets.Add(target); + + LastCreatedTime = TheGame->TickCount; + CreateDelay -= 2; + ++NumTargets; +} diff --git a/sharpshooter.h b/sharpshooter.h new file mode 100644 index 0000000..ebf96c4 --- /dev/null +++ b/sharpshooter.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef __SHARPSHOOTER_H__ +#define __SHARPSHOOTER_H__ + +#include "Minigame.h" + +class BPMiniGame_Sharpshooter_Target { +public: + float X; + float Y; + int HitTime; + int CreatedTime; + int TargetType; // set to -1 if it's a plain old target + bool Hit; + + BPMiniGame_Sharpshooter_Target() { + HitTime = CreatedTime = -1; + X = Y = 0; + TargetType = -1; + Hit = false; + } +}; + + +class BPMiniGame_Sharpshooter : public BPMiniGame { + Texture* sfcBackground; + Texture* sfcTarget; + Texture* sfcDontShoot; + BPPList Targets; + BPPList Positions; + + int LastCreatedTime; + int PauseTime; + int CreateDelay; + int TargetLife; + + int NumTargets; + int SuccessTime; + int NumHit; + +public: + BPMiniGame_Sharpshooter(BPGame* game); + ~BPMiniGame_Sharpshooter(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void CreateTarget(int type); +}; + +#endif diff --git a/shortcircuitsudoku.cpp b/shortcircuitsudoku.cpp new file mode 100644 index 0000000..b3a404a --- /dev/null +++ b/shortcircuitsudoku.cpp @@ -0,0 +1,366 @@ +// 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 "shortcircuitsudoku.h" +#include "Minigame.h" + +BPMiniGame_ShortCircuitSudoku::BPMiniGame_ShortCircuitSudoku(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("shortcircuitsudoku", 320, 416); + + LastStateChange = NumTries = CurrentLevel = Score = TimeStarted = 0; + GameState = WAITING; + GuessSquare = NULL; + sfcQuestionMark = NULL; + + GameTitle = "Short Sudoku"; + GameHelp = "Can you figure out what number goes in the yellow squares in these Sudoku grids? If you get stuck, tap one of the question marks for a hint!"; + GameHelp2 = "If the square is the only missing number in its row or column, then the answer is easy - you just need to see which number is missing. Alternatively, if the box above has a 2 to the left and the box below has a 2 to the right, then the current box must have a 2 in the middle column."; + + MiniGameType = PUZZLE; + + TheGame->AllocString(&sfcQuestionMark, "?", NORMAL, 24, 24, CENTRED); +} + +BPMiniGame_ShortCircuitSudoku::~BPMiniGame_ShortCircuitSudoku() { + SAFE_DELETE(sfcBackground); + + Sudoku.Clear(); + + SAFE_DELETE(sfcQuestionMark); +} + +void BPMiniGame_ShortCircuitSudoku::Start() { + TimeStarted = TheGame->TickCount; + LevelUp(); +} + +int BPMiniGame_ShortCircuitSudoku::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(Score - round(TimePassed / 4)); +} + +void BPMiniGame_ShortCircuitSudoku::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + for (int i = 0; i < 9; ++i) { + for (int j = 0; j < 9; ++j) { + SudokuSquare* square = Sudoku[(i * 9) + j]; + + if (square == GuessSquare) { + TheGame->FillRectangle((*TheGame->Yellow), square->XPos, square->YPos, 32 , 32); + + if (GameState != WAITING) { + TheGame->DrawString(square->sfcStrValue, BLACK, square->XPos + 5, square->YPos + 2); + } + } else { + if (square->Showing) { + TheGame->DrawString(square->sfcStrValue, BLACK, square->XPos + 5, square->YPos + 2); + } else { + TheGame->DrawString(sfcQuestionMark, BLACK, square->XPos + 5, square->YPos + 2); + } + } + + + } + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_ShortCircuitSudoku::Tick() { + if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) { + LastStateChange = -1; + + if (NumTries >= 8 && !MarathonMode) { + Success(); + } else { + LevelUp(); + } + } +} + +void BPMiniGame_ShortCircuitSudoku::OnMouseDown() { + +} + +void BPMiniGame_ShortCircuitSudoku::OnMouseMove() { + +} + +void BPMiniGame_ShortCircuitSudoku::OnMouseUp() { + switch (GameState) { + case CORRECT: + case WRONG: + break; + + case WAITING: + if (TouchEvent.Y < 95) { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 32, 1, 52, 44)) { + SubmitAnswer(1); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 100, 1, 52, 44)) { + SubmitAnswer(2); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 167, 1, 52, 44)) { + SubmitAnswer(3); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 237, 1, 52, 44)) { + SubmitAnswer(4); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 49, 52, 44)) { + SubmitAnswer(5); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 66, 49, 52, 44)) { + SubmitAnswer(6); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 134, 49, 52, 44)) { + SubmitAnswer(7); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 202, 49, 52, 44)) { + SubmitAnswer(8); + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 268, 49, 52, 44)) { + SubmitAnswer(9); + } + } else { + // asking for a hint! + for (int i = 0; i < Sudoku.Count; ++i) { + SudokuSquare* square = Sudoku[i]; + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, square->XPos, square->YPos, 32, 32)) { + // clicked this square! + if (!square->Showing && square != GuessSquare) { + square->Showing = true; + Score -= 25; + } + } + } + } + + + break; + } +} + +void BPMiniGame_ShortCircuitSudoku::LevelUp() { + ++NumTries; + ++CurrentLevel; + LastStateChange = -1; + if (CurrentLevel > 15) CurrentLevel = 15; + + GenerateGrid(); + + BPList hidden; + + // remove some squares + for (int i = 0; i < 15 + CurrentLevel; ++i) { + int hideme = TheGame->RandomRange(0, Sudoku.Count - 1); + hidden.Add(hideme); + Sudoku[hideme]->Showing = false; + } + + hidden.Shuffle(); + + GuessSquare = Sudoku[hidden[0]]; + + GameState = WAITING; +} + +void BPMiniGame_ShortCircuitSudoku::GenerateGrid() { + Sudoku.Clear(); + SudokuSquare* Squares[81]; + + for (int i = 0; i < 81; ++i) { + Squares[i] = new SudokuSquare(); + } + + BPList* Available[81]; + int squarecounter = 0; + + for (int x = 0; x <= 81 - 1; x++) { + Available[x] = new BPList(); + for (int i = 1; i <= 9; i++) { + Available[x]->Add(i); + } + } + + + while (!(squarecounter == 81)) { + Beginning: + int i = TheGame->RandomRange(0, Available[squarecounter]->Count - 1); + if (!(Available[squarecounter]->Count == 0)) { // we may need to start again with this square? + int z = (*Available[squarecounter])[i]; + SudokuSquare* item = Item(squarecounter, z); + if (Conflicts(Squares, item) == false) { + // this number is good! + SAFE_DELETE(Squares[squarecounter]); + Squares[squarecounter] = item; + Available[squarecounter]->RemoveAt(i); + squarecounter += 1; + } else { + // number is no good - try again + SAFE_DELETE(item); + Available[squarecounter]->RemoveAt(i); + goto Beginning; // goto! eek! Jump back to the start + } + } else { + // reset the current square + for (int y = 1; y <= 9; y++) { + Available[squarecounter]->Add(y); + } + + // jump back to the start + SAFE_DELETE(Squares[squarecounter - 1]); + Squares[squarecounter - 1] = new SudokuSquare(); + squarecounter -= 1; + goto Beginning; + } + } + + // all done - allocate all the squares in the grid + for (int j = 0; j < 81; j++) { + ostringstream str; + str << Squares[j]->Value; + TheGame->AllocString(&Squares[j]->sfcStrValue, str.str().c_str(), NORMAL, 24, 24, CENTRED); + Sudoku.Add(Squares[j]); + } + + for (int i = 0; i < 81; ++i) { + SAFE_DELETE(Available[i]); + } +} + +bool BPMiniGame_ShortCircuitSudoku::Conflicts(SudokuSquare** CurrentValues, SudokuSquare* test) { + for (int i = 0; i < 81; ++i) { + SudokuSquare* s = CurrentValues[i]; + if (s->Across != 0 & s->Across == test->Across) { + if (s->Value == test->Value) { + return true; + } + } + } + + for (int i = 0; i < 81; ++i) { + SudokuSquare* s = CurrentValues[i]; + + if (s->Down != 0 & s->Down == test->Down) { + if (s->Value == test->Value) { + return true; + } + } + } + + for (int i = 0; i < 81; ++i) { + SudokuSquare* s = CurrentValues[i]; + + if (s->Region != 0 & s->Region == test->Region) { + if (s->Value == test->Value) { + return true; + } + } + } + return false; +} + + +SudokuSquare* BPMiniGame_ShortCircuitSudoku::Item(int n, int v) { + SudokuSquare* retval = new SudokuSquare(); + ++n; + + retval->Across = GetAcrossFromNumber(n); + retval->Down = GetDownFromNumber(n); + retval->Region = GetRegionFromNumber(n); + + retval->Value = v; + retval->Index = n - 1; + + retval->XPos = 6 + ((retval->Across - 1) * 34); + retval->YPos = 102 + ((retval->Down - 1) * 34); + + if (retval->Across > 6) { + retval->XPos += 4; + } else if (retval->Across > 3) { + retval->XPos += 2; + } + + if (retval->Down > 6) { + retval->YPos += 4; + } else if (retval->Down > 3) { + retval->YPos += 2; + } + + return retval; +} + +int BPMiniGame_ShortCircuitSudoku::GetAcrossFromNumber(int n) { + int k; + k = n % 9; + + if (k == 0) { + return 9; + } else { + return k; + } +} + +int BPMiniGame_ShortCircuitSudoku::GetDownFromNumber(int n) { + if (GetAcrossFromNumber(n) == 9) { + return n / 9; + } else { + return n / 9 + 1; + } +} + +int BPMiniGame_ShortCircuitSudoku::GetRegionFromNumber(int n) { + int a = GetAcrossFromNumber(n); + int d = GetDownFromNumber(n); + + if (1 <= a & a < 4 & 1 <= d & d < 4) { + return 1; + } else if (4 <= a & a < 7 & 1 <= d & d < 4) { + return 2; + } else if (7 <= a & a < 10 & 1 <= d & d < 4) { + return 3; + } else if (1 <= a & a < 4 & 4 <= d & d < 7) { + return 4; + } else if (4 <= a & a < 7 & 4 <= d & d < 7) { + return 5; + } else if (7 <= a & a < 10 & 4 <= d & d < 7) { + return 6; + } else if (1 <= a & a < 4 & 7 <= d & d < 10) { + return 7; + } else if (4 <= a & a < 7 & 7 <= d & d < 10) { + return 8; + } else if (7 <= a & a < 10 & 7 <= d & d < 10) { + return 9; + } +} + +void BPMiniGame_ShortCircuitSudoku::SubmitAnswer(int Answer) { + if (Answer == GuessSquare->Value) { + Score += 60; + TheGame->PlaySound("correct"); + GameState = CORRECT; + } else { + Score -= 100; + TheGame->PlaySound("wrong"); + GameState = WRONG; + --CurrentLevel; // LevelUp() auto-raises CurrentLevel, so we pre-cut it here + if (CurrentLevel < 0) CurrentLevel = 0; + } + + LastStateChange = TheGame->TickCount; +} + +void BPMiniGame_ShortCircuitSudoku::SetMarathon() { + MarathonMode = true; +} diff --git a/shortcircuitsudoku.h b/shortcircuitsudoku.h new file mode 100644 index 0000000..6a8c49b --- /dev/null +++ b/shortcircuitsudoku.h @@ -0,0 +1,89 @@ +// 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. + +#ifndef __SHORTCIRCUITSUDOKU_H__ +#define __SHORTCIRCUITSUDOKU_H__ + +#include "Minigame.h" + +class SudokuSquare { +public: + int Across; + int Down; + int Region; + int Value; + int Index; + + int XPos; + int YPos; + + SpriteFont* sfcStrValue; + + bool Showing; + + SudokuSquare() { + Across = Down = Region = Value = Index = XPos = YPos = 0; + sfcStrValue = NULL; + Showing = true; + } + + ~SudokuSquare() { + SAFE_DELETE(sfcStrValue); + } +}; + + +class BPMiniGame_ShortCircuitSudoku : public BPMiniGame { +public: + BPMiniGame_ShortCircuitSudoku(BPGame* game); + ~BPMiniGame_ShortCircuitSudoku(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void LevelUp(); + void GenerateGrid(); + bool Conflicts(SudokuSquare** CurrentValues, SudokuSquare* test); + SudokuSquare* Item(int n, int v); + int GetAcrossFromNumber(int n); + int GetDownFromNumber(int n); + int GetRegionFromNumber(int n); + void SubmitAnswer(int Answer); + void SetMarathon(); + +protected: + Texture* sfcBackground; + + SpriteFont* sfcQuestionMark; + + int LastStateChange; + MiniGameStates GameState; + int NumTries; + int CurrentLevel; + + SudokuSquare* GuessSquare; + + int Score; + + int TimeStarted; + BPPList Sudoku; +}; + +#endif diff --git a/shufflepuzzler.cpp b/shufflepuzzler.cpp new file mode 100644 index 0000000..d2119c2 --- /dev/null +++ b/shufflepuzzler.cpp @@ -0,0 +1,245 @@ +// 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 "shufflepuzzler.h" +#include "Minigame.h" + +BPMiniGame_ShufflePuzzler::BPMiniGame_ShufflePuzzler(BPGame* game) : BPMiniGame(game) { + ShowResult = Loaded = false; + + TimeStarted = TheGame->TickCount; + + SuccessTime = -1; + + GameTitle = "Shuffle Puzzler"; + GameHelp = "Re-arrange the pieces to form the original picture by tapping on a piece to move. To see how the finished picture should look, tap the 'Shuffle Puzzler' title."; + GameHelp2 = "We recommend solving the top three first, because that's quite easy to do. Once they are solved, don't move them again: treat it as a 3x2 problem."; + + MiniGameType = PUZZLE; + + sfcBackground = TheGame->LoadBitmap("shufflepuzzler", 320, 416); + sfcPicture = TheGame->LoadBitmap("frog", 300, 300); + + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler1", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler2", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler3", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler4", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler5", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler6", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler7", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler8", 100, 100)); + ImageBits.Add(TheGame->LoadBitmap("shufflepuzzler9", 100, 100)); + + sfcClock = NULL; + + int count = 0; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + BPMiniGame_ShufflePuzzler_Item* item = new BPMiniGame_ShufflePuzzler_Item(); + item->ProperPosition = count; + item->ActualPosition = count; + item->Pic = ImageBits[count]; + Items.Add(item); + ++count; + } + } +} + +BPMiniGame_ShufflePuzzler::~BPMiniGame_ShufflePuzzler() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcPicture); + + ImageBits.Clear(); + Items.Clear(); + + SAFE_DELETE(sfcClock); +} + +void BPMiniGame_ShufflePuzzler::OnMouseUp() { + +} + +void BPMiniGame_ShufflePuzzler::OnMouseMove() { + +} + +void BPMiniGame_ShufflePuzzler::OnMouseDown() { + if (TouchEvent.Y < 90) { + ShowResult = !ShowResult; + return; + } + + // still here? User has clicked outside of the game logo - if they are showing the results, hide them now + if (ShowResult) { + ShowResult = false; + return; + } + + int x = (TouchEvent.X - 10) / 100; + int y = (TouchEvent.Y - 106) / 100; + + int pos = (y * 3) + x; + + SwapPiece(pos); + + bool success = true; + + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_ShufflePuzzler_Item* item = Items[i]; + + if (item->ActualPosition != item->ProperPosition) { + success = false; + break; + } + } + + if (success && SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + } +} + +void BPMiniGame_ShufflePuzzler::SwapPiece(int pos) { + // SwapPiece() is called many times at the beginning; only call it when it's a real player move + + switch (pos) { + case 0: TrySwap(0, 1, 3, -1, -1); break; + case 1: TrySwap(1, 0, 2, 4, -1); break; + case 2: TrySwap(2, 1, 5, -1, -1); break; + case 3: TrySwap(3, 0, 4, 6, -1); break; + case 4: TrySwap(4, 1, 3, 5, 7); break; + case 5: TrySwap(5, 2, 4, 8, -1); break; + case 6: TrySwap(6, 3, 7, -1, -1); break; + case 7: TrySwap(7, 6, 4, 8, -1); break; + case 8: TrySwap(8, 5, 7, -1, -1); break; + } +} + +void BPMiniGame_ShufflePuzzler::TrySwap(int clicked, int pos1, int pos2, int pos3, int pos4) { + BPMiniGame_ShufflePuzzler_Item* item_clicked = FindImageWithPos(clicked); + BPMiniGame_ShufflePuzzler_Item* item1 = FindImageWithPos(pos1); + BPMiniGame_ShufflePuzzler_Item* item2 = FindImageWithPos(pos2); + BPMiniGame_ShufflePuzzler_Item* item3 = FindImageWithPos(pos3); + BPMiniGame_ShufflePuzzler_Item* item4 = FindImageWithPos(pos4); + + if (item1 != NULL && item1->ProperPosition == 8) { + if (Loaded) TheGame->PlaySound("slide"); + int tmp = item1->ActualPosition; + item1->ActualPosition = item_clicked->ActualPosition; + item_clicked->ActualPosition = tmp; + return; + } + + if (item2 != NULL && item2->ProperPosition == 8) { + if (Loaded) TheGame->PlaySound("slide"); + int tmp = item2->ActualPosition; + item2->ActualPosition = item_clicked->ActualPosition; + item_clicked->ActualPosition = tmp; + return; + } + + if (item3 != NULL && item3->ProperPosition == 8) { + if (Loaded) TheGame->PlaySound("slide"); + int tmp = item3->ActualPosition; + item3->ActualPosition = item_clicked->ActualPosition; + item_clicked->ActualPosition = tmp; + return; + } + + if (item4 != NULL && item4->ProperPosition == 8) { + if (Loaded) TheGame->PlaySound("slide"); + int tmp = item4->ActualPosition; + item4->ActualPosition = item_clicked->ActualPosition; + item_clicked->ActualPosition = tmp; + return; + } +} + + +BPMiniGame_ShufflePuzzler_Item* BPMiniGame_ShufflePuzzler::FindImageWithPos(int pos) { + for (int i = 0; i < Items.Count; ++i) { + if (Items[i]->ActualPosition == pos) { + return Items[i]; + } + } + + return NULL; +} + +void BPMiniGame_ShufflePuzzler::Start() { + Shuffle(); + TimeStarted = TheGame->TickCount; + Loaded = true; +} + +int BPMiniGame_ShufflePuzzler::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(500 - round(TimePassed * 1.25)); +} + +void BPMiniGame_ShufflePuzzler::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + if (ShowResult) { + TheGame->FillRectangle((*TheGame->Red), 8, 104, 304, 304); + TheGame->DrawImage(sfcPicture, 10, 106); + } else { + for (int i = 0; i <= 8; ++i) { + for (int j = 0; j < Items.Count; ++j) { + BPMiniGame_ShufflePuzzler_Item* item = Items[j]; + + if (item->ActualPosition == i) { + int x; + int y; + y = DivRem(i, 3, x); + TheGame->DrawImage(item->Pic, 10 + (x * 100), 106 + (y * 100)); + } + } + } + + if (SuccessTime == -1) { + TheGame->DrawLine(110, 106, 110, 406, TheGame->Black, 3); + TheGame->DrawLine(210, 106, 210, 406, TheGame->Black, 3); + TheGame->DrawLine(10, 206, 310, 206, TheGame->Black, 3); + TheGame->DrawLine(10, 306, 310, 306, TheGame->Black, 3); + } + } + + if (SuccessTime == -1) { + int TimePassed = TheGame->TickCount - TimeStarted; + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), XLARGE, 152, 49, RIGHT); + } + + TheGame->DrawString(sfcClock, WHITE, 149, 30); + } +} + +void BPMiniGame_ShufflePuzzler::Tick() { + // this delay is longer than normal to give them a moment to see the finished picture clearly + if (SuccessTime != -1 && SuccessTime + 750 < TheGame->TickCount) { + Success(); + } +} + +void BPMiniGame_ShufflePuzzler::Shuffle() { + // shuffle a la brute force + for (int i = 0; i < 1000; ++i) { + SwapPiece(TheGame->RandomRange(0, 8)); + } +} diff --git a/shufflepuzzler.h b/shufflepuzzler.h new file mode 100644 index 0000000..7d340e0 --- /dev/null +++ b/shufflepuzzler.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef __SHUFFLEPUZZLER_H__ +#define __SHUFFLEPUZZLER_H__ + +#include "Minigame.h" + +class BPMiniGame_ShufflePuzzler_Item { +public: + int ActualPosition; + int ProperPosition; + Texture* Pic; + + BPMiniGame_ShufflePuzzler_Item() { + ActualPosition = ProperPosition = 0; + Pic = NULL; + } +}; + + + +class BPMiniGame_ShufflePuzzler : public BPMiniGame { +public: + BPMiniGame_ShufflePuzzler(BPGame* game); + ~BPMiniGame_ShufflePuzzler(); + void OnMouseUp(); + void OnMouseMove(); + void OnMouseDown(); + void SwapPiece(int pos); + void TrySwap(int clicked, int pos1, int pos2, int pos3, int pos4); + BPMiniGame_ShufflePuzzler_Item* FindImageWithPos(int pos); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void Shuffle(); + +protected: + bool ShowResult; + + Texture* sfcBackground; + Texture* sfcPicture; + + SpriteFont* sfcClock; + + int TimeStarted; + + int SuccessTime; + bool Loaded; + + BPPList ImageBits; + BPPList Items; +}; + +#endif diff --git a/strangerdanger.cpp b/strangerdanger.cpp new file mode 100644 index 0000000..2b95398 --- /dev/null +++ b/strangerdanger.cpp @@ -0,0 +1,312 @@ + +// 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 "Minigame.h" +#include "strangerdanger.h" + +BPMiniGame_StrangerDanger::BPMiniGame_StrangerDanger(BPGame* game) : BPMiniGame(game) { + Level = NumWrong = TimeStarted = NumTries = 0; + + GameTitle = "Stranger Danger"; + GameHelp = "Remember the items on the screen, because when you tap the screen I'll hide them all and add one new item. Can you figure out which item is new?"; + GameHelp2 = "You need to remember all the different shapes you see, then tap on the one that wasn't there before. Remember: the colours change as well, so look closely!"; + + MiniGameType = PUZZLE; + + GameState = REMEMBER; + LastStateChange = 0; + + FlipBackground = false; + AnimPos = 0.0f; + + sfcBackground = TheGame->LoadBitmap("strangerdanger", 320, 480); + sfcRemember = TheGame->LoadBitmap("strangerdanger_remember", 320, 480); + sfcFlash = TheGame->LoadBitmap("lensflare", 512, 512); + sfcWhatsChanged = TheGame->LoadBitmap("whichisstranger", 512, 512); + + for (int i = 1; i <= 59; ++i) { + ostringstream filename; + filename << "patch" << i; + CardTypes.Add(TheGame->LoadBitmap(filename.str().c_str(), 48, 48)); + } + + for (int i = 0; i < 6; ++i) { + for (int j = 3; j < 8; ++j) { + Coordinates.Add(new BPPoint(10 + (i * 50), -30 + (j * 50))); + } + } +} + +BPMiniGame_StrangerDanger::~BPMiniGame_StrangerDanger() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcRemember); + SAFE_DELETE(sfcFlash); + SAFE_DELETE(sfcWhatsChanged); + + CardTypes.Clear(); + Coordinates.Clear(); + Items.Clear(); +} + +void BPMiniGame_StrangerDanger::Start() { + LevelUp(); + CreateLevel(); + + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_StrangerDanger::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(600 - (NumWrong * 75) - round(TimePassed) / 4); +} + +void BPMiniGame_StrangerDanger::Render() { + switch (GameState) { + case REMEMBER: + case GUESSING: + case CORRECT: + case WRONG: + RenderItems(); + break; + + case FADE_IN: + RenderItems(); + RenderGlow(); + break; + + case FADE_OUT: + RenderItems(); + RenderWhiteFade(); + break; + + case SHOWING: + TheGame->FillRectangle((*TheGame->White), 0, 0, 320, 480); + + Colour* col; + float scale; + + if (AnimPos < 0.333f) { + col = TheGame->ColorLerp(TheGame->TransparentWhite, TheGame->White, AnimPos * 3); + scale = TheGame->SmoothStep(4.0f, 0.6f, AnimPos * 3); + } else { + col = new Colour(1.0f, 1.0f, 1.0f, 1.0f); + scale = TheGame->SmoothStep(0.6f, 0.5f, AnimPos - 0.333f); + } + + sfcWhatsChanged->Draw(160, 208, 0.0f, scale, (*col)); + SAFE_DELETE(col); + + break; + } +} + +void BPMiniGame_StrangerDanger::Tick() { + if (AnimPos != 1.0f) { + switch (GameState) { + case SHOWING: + AnimPos += 0.5f * TheGame->ElapsedSeconds; + break; + + default: + AnimPos += 1.5f * TheGame->ElapsedSeconds; + } + + if (AnimPos >= 1.0f) { + AnimPos = 1.0f; + + // finished animating - now which state should we switch to? + switch (GameState) { + case FADE_IN: + FlipBackground = !FlipBackground; + + if (FlipBackground) { + SetGameState(SHOWING); + } else { + SetGameState(FADE_OUT); + CreateLevel(); + } + break; + + case SHOWING: + { + SetGameState(FADE_OUT); + + Coordinates.Shuffle(); + + // move all the items + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_StrangerDanger_Item* item = Items[i]; + item->X = Coordinates[i]->X; + item->Y = Coordinates[i]->Y; + } + + // now add a new one + BPMiniGame_StrangerDanger_Item* item = new BPMiniGame_StrangerDanger_Item(); + item->X = Coordinates[Coordinates.Count - 1]->X; + item->Y = Coordinates[Coordinates.Count - 1]->Y; + item->CardType = CardTypes.Count - 1; + Items.Add(item); + + break; + } + + case FADE_OUT: + if (FlipBackground) { + SetGameState(GUESSING); + } else { + SetGameState(REMEMBER); + } + break; + } + } + } + + if (GameState == CORRECT || GameState == WRONG) { + if (LastStateChange + 500 < TheGame->TickCount) { + if (GameState == CORRECT) { + LevelUp(); + } else { + LevelDown(); + } + + if (NumTries > 8) { + Success(); + } else { + SetGameState(FADE_IN); + } + } + } +} + +void BPMiniGame_StrangerDanger::OnMouseDown() { + +} + +void BPMiniGame_StrangerDanger::OnMouseMove() { + +} + +void BPMiniGame_StrangerDanger::OnMouseUp() { + if (GameState == REMEMBER) { + SetGameState(FADE_IN); + } else if (GameState == GUESSING) { + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_StrangerDanger_Item* item = Items[i]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, item->X, item->Y, 48, 48)) { + // clicked this item! + if (i == Items.Count - 1) { + TheGame->PlaySound("correct"); + SetGameState(CORRECT); + } else { + TheGame->PlaySound("wrong"); + SetGameState(WRONG); + } + + break; + } + } + } +} + +void BPMiniGame_StrangerDanger::LevelUp() { + ++Level; + ++NumTries; +} + +void BPMiniGame_StrangerDanger::LevelDown() { + --Level; + ++NumTries; + if (Level < 1) Level = 1; + ++NumWrong; +} + +void BPMiniGame_StrangerDanger::CreateLevel() { + if (NumTries > 8) { + Success(); + } + + Items.Clear(); + CardTypes.Shuffle(); + Coordinates.Shuffle(); + + int num_items = 1 + Level; + + for (int i = 0; i < num_items; ++i) { + BPMiniGame_StrangerDanger_Item* item = new BPMiniGame_StrangerDanger_Item(); + item->X = Coordinates[i]->X; + item->Y = Coordinates[i]->Y; + item->CardType = i; + Items.Add(item); + } +} + +void BPMiniGame_StrangerDanger::RenderItems() { + switch (GameState) { + case FADE_IN: + case REMEMBER: + if (FlipBackground) { + TheGame->DrawImage(sfcBackground, 0, 0); + } else { + TheGame->DrawImage(sfcRemember, 0, 0); + } + + break; + + + case FADE_OUT: + case GUESSING: + case CORRECT: + case WRONG: + if (FlipBackground) { + TheGame->DrawImage(sfcBackground, 0, 0); + } else { + TheGame->DrawImage(sfcRemember, 0, 0); + } + break; + } + + for (int i = 0; i < Items.Count; ++i) { + BPMiniGame_StrangerDanger_Item* item = Items[i]; + TheGame->DrawImage(CardTypes[item->CardType], item->X, item->Y); + } + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +void BPMiniGame_StrangerDanger::RenderGlow() { + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + sfcFlash->Draw(160, 208, 0.0f, AnimPos * 4, (*TheGame->White)); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void BPMiniGame_StrangerDanger::RenderWhiteFade() { + Colour col = Colour(1.0f, 1.0f, 1.0f, 1 - AnimPos); + TheGame->FillRectangle(col, 0, 0, 320, 480); +} + +void BPMiniGame_StrangerDanger::SetGameState(MiniGameStates state) { + GameState = state; + AnimPos = 0.0f; + LastStateChange = TheGame->TickCount; +} diff --git a/strangerdanger.h b/strangerdanger.h new file mode 100644 index 0000000..bb899f2 --- /dev/null +++ b/strangerdanger.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef __STRANGERDANGER_H__ +#define __STRANGERDANGER_H__ + +#include "Minigame.h" + +class BPMiniGame_StrangerDanger_Item { +public: + int CardType; + int X; + int Y; +}; + +class BPMiniGame_StrangerDanger : public BPMiniGame { +public: + BPMiniGame_StrangerDanger(BPGame* game); + ~BPMiniGame_StrangerDanger(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + void LevelUp(); + void LevelDown(); + void CreateLevel(); + void RenderItems(); + void RenderGlow(); + void RenderWhiteFade(); + void SetGameState(MiniGameStates state); + +protected: + Texture* sfcBackground; + Texture* sfcRemember; + Texture* sfcFlash; + Texture* sfcWhatsChanged; + BPPList CardTypes; + BPPList Coordinates; + BPPList Items; + + int Level; + int NumWrong; + int NumTries; + + int TimeStarted; + + bool FlipBackground; + float AnimPos; + + MiniGameStates GameState; + int LastStateChange; +}; + +#endif diff --git a/symboliclogic.cpp b/symboliclogic.cpp new file mode 100644 index 0000000..57da421 --- /dev/null +++ b/symboliclogic.cpp @@ -0,0 +1,351 @@ +// 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 "symboliclogic.h" +#include "Minigame.h" +#include +#include + +BPMiniGame_SymbolicLogic::BPMiniGame_SymbolicLogic(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("symboliclogic", 320, 416); + + LastStateChange = CurrentLevel = Score = NumTries = 0; + GameState = WAITING; + + sfcText = NULL; + + AnswerIsTrue = false; + + TimeStarted = 0; + + GameTitle = "Symbolic Logic"; + GameHelp = "I'm going to give you two statements that I'd like you to accept as truth. Based on those two facts, are my conclusions true or false?"; + GameHelp2 = "If you read the questions carefully, you should be OK, but remember: if the conclusion says \"A must be a B\" and you think \"A could be a B or it could not be a B; I don't know\", then clearly the conclusion is wrong!"; + + MiniGameType = PUZZLE; + + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can play the guitar", "can't play the guitar", "can play the guitar", "guitarists")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can play the piano", "can't play the piano", "can play the piano", "pianists")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can play the violin", "can't play the violin", "can play the violin", "violinists")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can speak Chinese", "can't speak Chinese", "can speak Chinese", "Chinese speakers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can speak French", "can't speak French", "can speak French", "French speakers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can speak Japanese", "can't speak Japanese", "can speak Japanese", "Japanese speakers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can speak Latin", "can't speak Latin", "can speak Latin", "Latin speakers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("can speak Spanish", "can't speak Spanish", "can speak Spanish", "Spanish speakers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("drives a car", "can't drive a car", "drive a car", "car drivers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("eats mashed potato", "doesn't eat mashed potato", "like mashed potato", "mashed potato eaters")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("enjoys cooking", "doesn't enjoy cooking", "enjoy cooking", "cooks")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("exercises regularly", "doesn't exercise regularly", "exercise regularly", "regular exercisers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("goes to church", "doesn't go to church", "go to church", "churchgoers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("has brown hair", "doesn't have brown hair", "have brown hair", "brown-haired people")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("has blue eyes", "doesn't have blue eyes", "have blue eyes", "blue-eyed people")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("has curly hair", "doesn't have curly hair", "have curly hair", "curly-haired people")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("hates computers", "doesn't hate computers", "hate computers", "computer haters")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("is very clever", "isn't very clever", "are very clever", "very clever people")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("is very popular", "isn't very popular", "are very popular", "very popular people")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes bowling", "doesn't like bowling", "like bowling", "bowlers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes broccoli", "doesn't like broccoli", "like broccoli", "broccoli eaters")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes eating carrots", "doesn't like eating carrots", "like eating carrots", "carrot eaters")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes eating dessert", "doesn't like eating dessert", "like eating dessert", "dessert eaters")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes puzzles", "doesn't like puzzles", "like puzzles", "people who like puzzles")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("likes to sing", "doesn't like to sing", "like to sing", "singers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("loves animals", "doesn't like animals", "like animals", "animal lovers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("loves Shakespeare", "doesn't love Shakespeare", "love Shakespeare", "Shakespeare lovers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a mansion", "doesn't own a mansion", "own a mansion", "mansion owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("plays golf", "doesn't play golf", "play golf", "golfers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("plays Sudoku", "doesn't play Sudoku", "play Sudoku", "Sudoku players")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("plays tennis", "doesn't play tennis", "play tennis", "tennis players")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a cat", "doesn't own a cat", "own a cat", "cat owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a cellphone", "doesn't own a cellphone", "own a cellphone", "cellphone owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a dog", "doesn't own a dog", "own a dog", "dog owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a goat", "doesn't own a goat", "own a goat", "goat owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a hamster", "doesn't own a hamster", "own a hamster", "hamster owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a parrot", "doesn't own a parrot", "own a parrot", "parrot owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("owns a rabbit", "doesn't own a rabbit", "own a rabbit", "rabbit owners")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("reads books", "doesn't read books", "read books", "people who read books")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("reads newspapers", "doesn't read newspapers", "read newspapers", "newspaper readers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("rides a bike", "doesn't ride a bike", "ride a bike", "bicyclists")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("walks to work", "doesn't walk to work", "walk to work", "people who walk to work")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("watches TV", "doesn't watch TV", "watch TV", "TV watchers")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("wears glasses", "doesn't wear glasses", "wear glasses", "people who wear glasses")); + LogicItems.Add(new BPMiniGame_SymbolicLogic_Item("wrestles crocodiles", "doesn't wrestle crocodiles", "wrestle crocodiles", "crocodile wrestlers")); +} + +BPMiniGame_SymbolicLogic::~BPMiniGame_SymbolicLogic() { + SAFE_DELETE(sfcBackground); + + LogicItems.Clear(); + Premises.Clear(); + + SAFE_DELETE(sfcText); +} + +void BPMiniGame_SymbolicLogic::Start() { + TimeStarted = TheGame->TickCount; + LevelUp(); +} + +int BPMiniGame_SymbolicLogic::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(Score - round(TimePassed)); +} + +void BPMiniGame_SymbolicLogic::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + TheGame->DrawString(sfcText, WHITE, 20, 19); + + if (GameState == CORRECT) { + RenderCorrect(); + } else if (GameState == WRONG) { + RenderWrong(); + } +} + +const char* BPMiniGame_SymbolicLogic::FlattenPremise(BPMiniGame_SymbolicLogic_Premise* premise) { + ostringstream result; + + switch (premise->Type) { + case ALL_JOB: + result << "All " << premise->Job << "s " << premise->Item->PluralDesc; + break; + + case NONE_JOB: + result << "No " << premise->Job << " " << premise->Item->SingularDesc; + break; + + case ALL_ITEM: + result << "All " << premise->Item->Noun << " are " << premise->Job << "s"; + break; + + case NONE_ITEM: + result << "No " << premise->Item->Noun << " are " << premise->Job << "s"; + break; + + case PERSON: + if (premise->Item == NULL) { + if (TheGame->StartsWithVowel(&premise->Job)) { + result << premise->Text << " is an " << premise->Job; + } else { + result << premise->Text << " is a " << premise->Job; + } + } else { + result << premise->Text << " " << premise->Item->SingularDesc; + } + + break; + } + + return result.str().c_str(); +} + +void BPMiniGame_SymbolicLogic::Tick() { + if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) { + if (NumTries >= 10 && !MarathonMode) { + Success(); + return; + } else { + LevelUp(); + } + } +} + +void BPMiniGame_SymbolicLogic::OnMouseDown() { + +} + +void BPMiniGame_SymbolicLogic::OnMouseMove() { + +} + +void BPMiniGame_SymbolicLogic::OnMouseUp() { + switch (GameState) { + case CORRECT: + case WRONG: + LevelUp(); + break; + + case WAITING: + // have we just answered the question? + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 11, 342, 143, 63)) { + // True clicked! + LastStateChange = TheGame->TickCount; + + if (AnswerIsTrue) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + Score += 50; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + Score -= 100; + } + } else if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 166, 342, 143, 63)) { + // False clicked! + LastStateChange = TheGame->TickCount; + + if (!AnswerIsTrue) { + TheGame->PlaySound("correct"); + GameState = CORRECT; + Score += 50; + } else { + TheGame->PlaySound("wrong"); + GameState = WRONG; + Score -= 50; + } + } + + break; + } +} + +void BPMiniGame_SymbolicLogic::LevelUp() { + if (NumTries >= 10 && !MarathonMode) { + return; + } + + ++CurrentLevel; + ++NumTries; + LastStateChange = -1; + GameState = WAITING; + + Premises.Clear(); + LogicItems.Shuffle(); + + BPMiniGame_SymbolicLogic_Premise* FirstPremise; + BPMiniGame_SymbolicLogic_Premise* SecondPremise; + + string Job; + string PersonName; + ostringstream NegativeConclusion; + ostringstream Conclusion; + + bool is_negative = false; + + if (TheGame->RandomRange(0, 1) == 1) { + NegativeConclusion << "not "; + is_negative = true; + } + + Job = GetRandomJob(); + PersonName = TheGame->GetName(); + + FirstPremise = new BPMiniGame_SymbolicLogic_Premise(); + FirstPremise->Type = GetPremiseType(); + FirstPremise->Job = Job; + FirstPremise->Item = LogicItems[0]; + Premises.Add(FirstPremise); + + SecondPremise = new BPMiniGame_SymbolicLogic_Premise(); + SecondPremise->Type = PERSON; + SecondPremise->Text = PersonName; + switch (TheGame->RandomRange(0, 1)) { + case 0: + SecondPremise->Item = LogicItems[0]; + Premises.Add(SecondPremise); + + if (TheGame->StartsWithVowel(&Job)) { + Conclusion << "Therefore, " << PersonName << " is " << NegativeConclusion.str() << "an " << Job; + } else { + Conclusion << "Therefore, " << PersonName << " is " << NegativeConclusion.str() << "a " << Job; + } + + break; + + case 1: + SecondPremise->Job = Job; + Premises.Add(SecondPremise); + + if (!is_negative) { + Conclusion << "Therefore, " << PersonName << " " << LogicItems[0]->SingularDesc; + } else { + Conclusion << "Therefore, " << PersonName << " " << LogicItems[0]->Negative; + } + + break; + } + + if (FirstPremise->Type == ALL_JOB) { + if (SecondPremise->Job.size() != 0) { + AnswerIsTrue = true; + if (is_negative) AnswerIsTrue = !AnswerIsTrue; + } else { + AnswerIsTrue = false; + } + } else if (FirstPremise->Type == ALL_ITEM) { + if (SecondPremise->Item != NULL) { + AnswerIsTrue = true; + if (is_negative) AnswerIsTrue = !AnswerIsTrue; + } else { + AnswerIsTrue = false; + } + } else if (FirstPremise->Type == NONE_JOB) { + if (SecondPremise->Job.size() != 0) { + AnswerIsTrue = false; + } else { + AnswerIsTrue = false; + } + + if (is_negative) AnswerIsTrue = !AnswerIsTrue; + } else { + if (SecondPremise->Item != NULL) { + AnswerIsTrue = false; + } else { + AnswerIsTrue = false; + } + + if (is_negative) AnswerIsTrue = !AnswerIsTrue; + } + + string PrintMe = string(); + + for (int i = 0; i < Premises.Count; ++i) { + BPMiniGame_SymbolicLogic_Premise* premise = Premises[i]; + string str = FlattenPremise(premise); + PrintMe.append(str); + PrintMe.append(". "); + } + + PrintMe.append(Conclusion.str()); + PrintMe.append(". "); + + TheGame->AllocString(&sfcText, PrintMe.c_str(), LARGE, 282, 306, LEFT); +} + +PremiseTypes BPMiniGame_SymbolicLogic::GetPremiseType() { + switch (TheGame->RandomRange(0, 3)) { + case 0: + return ALL_JOB; + case 1: + return NONE_JOB; + case 2: + return ALL_ITEM; + default: + return NONE_ITEM; + } +} + +string BPMiniGame_SymbolicLogic::GetRandomJob() { + string meh = TheGame->TestBrainJobs[TheGame->RandomRange(0, 51)]; + transform(meh.begin(), meh.end(), meh.begin(), (int(*)(int)) tolower); + return meh; +} + +void BPMiniGame_SymbolicLogic::SetMarathon() { + MarathonMode = true; +} diff --git a/symboliclogic.h b/symboliclogic.h new file mode 100644 index 0000000..33d4388 --- /dev/null +++ b/symboliclogic.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef __SYMBOLICLOGIC_H__ +#define __SYMBOLICLOGIC_H__ + +#include "Minigame.h" + +enum PremiseTypes { ALL_JOB, NONE_JOB, ALL_ITEM, NONE_ITEM, PERSON }; + +class BPMiniGame_SymbolicLogic_Item { +public: + string SingularDesc; + string Negative; + string PluralDesc; + string Noun; + + BPMiniGame_SymbolicLogic_Item(const char* singular, const char* negative, const char* plural, const char* noun) { + SingularDesc = string(singular); + Negative = string(negative); + PluralDesc = string(plural); + Noun = string(noun); + } + + ~BPMiniGame_SymbolicLogic_Item() { + + } +}; + +class BPMiniGame_SymbolicLogic_Premise { +public: + PremiseTypes Type; + BPMiniGame_SymbolicLogic_Item* Item; + BPMiniGame_SymbolicLogic_Item* Item2; // for example, "all cat owners own a dog" + string Job; + string Text; + + BPMiniGame_SymbolicLogic_Premise() { + Item = Item2 = NULL; + } + + ~BPMiniGame_SymbolicLogic_Premise() { +// if (Text != NULL) { +// SAFE_DELETE(Text); +// } + } +}; + +class BPMiniGame_SymbolicLogic : public BPMiniGame { +public: + BPMiniGame_SymbolicLogic(BPGame* game); + ~BPMiniGame_SymbolicLogic(); + void Start(); + int GetWeight(); + void Render(); + const char* FlattenPremise(BPMiniGame_SymbolicLogic_Premise* premise); + void Tick(); + + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void LevelUp(); + PremiseTypes GetPremiseType(); + string GetRandomJob(); + void SetMarathon(); + +protected: + Texture* sfcBackground; + + MiniGameStates GameState; + + int LastStateChange; + int CurrentLevel; + int NumTries; + int Score; + + BPPList LogicItems; + BPPList Premises; + + string* Conclusion; + bool AnswerIsTrue; + + SpriteFont* sfcText; + + int TimeStarted; +}; + +#endif diff --git a/underthehat.cpp b/underthehat.cpp new file mode 100644 index 0000000..e293c21 --- /dev/null +++ b/underthehat.cpp @@ -0,0 +1,284 @@ +// 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 "underthehat.h" +#include "Minigame.h" + +BPMiniGame_UnderTheHat::BPMiniGame_UnderTheHat(BPGame* game) : BPMiniGame(game) { + sfcBackground = TheGame->LoadBitmap("checkers1", 320, 416); + sfcBackground2 = TheGame->LoadBitmap("checkers2", 320, 416); + sfcHat = TheGame->LoadBitmap("propeller", 94, 71); + sfcBadHat = TheGame->LoadBitmap("propeller_bad", 94, 71); + sfcItem = TheGame->LoadBitmap("gem6_small", 64, 46); + + ChosenHat = NULL; + sfcCurrentLevel = NULL; + + CurrentLevel = Tries = MovesLeft = LastStateChange = TimeStarted = NumWrong = 0; + + SuccessTime = -1; + MoveSpeed = 7; + + BackgroundFade = 0.0f; + BackgroundUp = false; + + MoveAmount = 0.0f; + + State = SHOWING; + + GameTitle = "Under the Hat"; + GameHelp = "One hat has a gem underneath. When you're ready, tap the screen then watch the hats. When they stop, can you guess which hat covers the gem?"; + GameHelp2 = "Whether you get it right or wrong, the gem won't move to be under a different hat, so keep your eyes peeled!"; + + MiniGameType = PUZZLE; + + Points.Add(new BPPoint(7, 11)); + Points.Add(new BPPoint(112, 11)); + Points.Add(new BPPoint(218, 11)); + + Points.Add(new BPPoint(60, 91)); + Points.Add(new BPPoint(166, 91)); + + Points.Add(new BPPoint(7, 171)); + Points.Add(new BPPoint(112, 171)); + Points.Add(new BPPoint(218, 171)); + + Points.Add(new BPPoint(60, 251)); + Points.Add(new BPPoint(166, 251)); + Points.Shuffle(); + + for (int i = 0; i < 5; ++i) { + BPPoint* point = Points[i]; + + BPMiniGame_UnderTheHat_Hat* hat = new BPMiniGame_UnderTheHat_Hat(); + hat->X = point->X; + hat->Y = point->Y; + hat->DestX = point->X; + hat->DestY = point->Y; + + Hats.Add(hat); + } +} + +BPMiniGame_UnderTheHat::~BPMiniGame_UnderTheHat() { + SAFE_DELETE(sfcBackground); + SAFE_DELETE(sfcBackground2); + SAFE_DELETE(sfcHat); + SAFE_DELETE(sfcBadHat); + SAFE_DELETE(sfcItem); + + Hats.Clear(); + Points.Clear(); + + SAFE_DELETE(ChosenHat); + + SAFE_DELETE(sfcCurrentLevel); +} + +void BPMiniGame_UnderTheHat::Start() { + LevelUp(); + + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_UnderTheHat::GetWeight() { + return MinMax(500 - (NumWrong * 150)); +} + +void BPMiniGame_UnderTheHat::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + glColor4f(1.0f, 1.0f, 1.0f, TheGame->SmoothStep(0, 1.0f, BackgroundFade)); + TheGame->DrawImage(sfcBackground2, 0, 0); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + for (int i = 0; i < Hats.Count; ++i) { + BPMiniGame_UnderTheHat_Hat* hat = Hats[i]; + + if (State == WRONG && hat == ChosenHat) { + TheGame->DrawImage(sfcBadHat, hat->X, hat->Y); + } else { + TheGame->DrawImage(sfcHat, hat->X, hat->Y); + } + } + + if (State == SHOWING || State == CORRECT || State == WRONG) { + TheGame->DrawImage(sfcItem, Hats[0]->X + 21, Hats[0]->Y + 25); + } + + if (State == CORRECT) { + if (LastStateChange + 1000 > TheGame->TickCount) { + RenderCorrect(); + } + } else if (State == WRONG) { + if (LastStateChange + 1000 > TheGame->TickCount) { + RenderWrong(); + } + } + + TheGame->DrawString(sfcCurrentLevel, WHITE, 0, 360); +} + +void BPMiniGame_UnderTheHat::Tick() { + if (SuccessTime != -1 && SuccessTime + 250 < TheGame->TickCount) { + Success(); + return; + } + + if (BackgroundUp) { + BackgroundFade += 0.0025f; + if (BackgroundFade >= 1.0f) { + BackgroundUp = false; + } + } else { + BackgroundFade -= 0.0025f; + + if (BackgroundFade <= 0.0f) { + BackgroundUp = true; + } + } + + switch (State) { + case SHOWING: + break; + + case WAITING: + if (LastStateChange + 1000 < TheGame->TickCount) { + State = MOVING; + MoveAmount = 0.0f; + } + break; + + case CORRECT: + if (LastStateChange + 1750 < TheGame->TickCount) { + LevelUp(); + State = MOVING; + } + + break; + + case WRONG: + if (LastStateChange + 1750 < TheGame->TickCount) { + GenerateMoves(); + State = MOVING; + } + + break; + + case MOVING: + if (MoveAmount < 1.0f) { + MoveAmount += 0.1f * TheGame->ElapsedSeconds * MoveSpeed; + + for (int i = 0; i < Hats.Count; ++i) { + BPMiniGame_UnderTheHat_Hat* hat = Hats[i]; + hat->X = TheGame->SmoothStep(hat->StartX, hat->DestX, MoveAmount); + hat->Y = TheGame->SmoothStep(hat->StartY, hat->DestY, MoveAmount); + } + } else { + if (MovesLeft > 0) { + // finished a previous move, but we have more moves to do + MakeMove(); + } else { + // finished moving - time to guess! + State = GUESSING; + ChosenHat = NULL; + } + } + break; + } +} + +void BPMiniGame_UnderTheHat::OnMouseDown() { + +} + +void BPMiniGame_UnderTheHat::OnMouseMove() { + +} + +void BPMiniGame_UnderTheHat::OnMouseUp() { + switch (State) { + case SHOWING: + LastStateChange = TheGame->TickCount; + State = WAITING; + break; + + case WAITING: + State = MOVING; + break; + + case GUESSING: + ++Tries; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, Hats[0]->X, Hats[0]->Y, 75, 75)) { + TheGame->PlaySound("correct"); + State = CORRECT; + } else { + TheGame->PlaySound("wrong"); + State = WRONG; + ++NumWrong; + --CurrentLevel; + if (CurrentLevel < 1) CurrentLevel = 1; + } + + LastStateChange = TheGame->TickCount; + + if (Tries >= 10 && SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + return; + } + + break; + } +} + +void BPMiniGame_UnderTheHat::MakeMove() { + --MovesLeft; + + Points.Shuffle(); + + for (int i = 0; i < Hats.Count; ++i) { + BPMiniGame_UnderTheHat_Hat* hat = Hats[i]; + + hat->StartX = hat->X; + hat->StartY = hat->Y; + hat->DestX = Points[i]->X; + hat->DestY = Points[i]->Y; + } + + MoveAmount = 0.0f; +} + +void BPMiniGame_UnderTheHat::LevelUp() { + if (Tries >= 10 && SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + return; + } + + ++CurrentLevel; + GenerateMoves(); + MakeMove(); + + ostringstream level; + level << "Level: " << CurrentLevel << "/10"; + TheGame->AllocString(&sfcCurrentLevel, level.str().c_str(), LARGE, 320, 35, CENTRED, true); +} + +void BPMiniGame_UnderTheHat::GenerateMoves() { + MovesLeft = CurrentLevel + 4; + MoveSpeed = 9 + CurrentLevel; +} diff --git a/underthehat.h b/underthehat.h new file mode 100644 index 0000000..d2ee1e7 --- /dev/null +++ b/underthehat.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef __UNDERTHEHAT_H__ +#define __UNDERTHEHAT_H__ + +#include "Minigame.h" + +class BPMiniGame_UnderTheHat_Hat { +public: + int X; + int Y; + int StartX; + int StartY; + int DestX; + int DestY; +}; + + + + +class BPMiniGame_UnderTheHat : public BPMiniGame { +public: + BPMiniGame_UnderTheHat(BPGame* game); + ~BPMiniGame_UnderTheHat(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseDown(); + void OnMouseMove(); + void OnMouseUp(); + void MakeMove(); + void LevelUp(); + void GenerateMoves(); +protected: + Texture* sfcBackground; + Texture* sfcBackground2; + Texture* sfcHat; + Texture* sfcBadHat; + Texture* sfcItem; + + BPPList Hats; + BPPList Points; + + BPMiniGame_UnderTheHat_Hat* ChosenHat; + + int CurrentLevel; + SpriteFont* sfcCurrentLevel; + int Tries; + int MoveSpeed; + int MovesLeft; + int LastStateChange; + + float BackgroundFade; + bool BackgroundUp; + + int NumWrong; + int SuccessTime; + int TimeStarted; + + float MoveAmount; + + MiniGameStates State; +}; + +#endif diff --git a/untangler.cpp b/untangler.cpp new file mode 100644 index 0000000..4ff0199 --- /dev/null +++ b/untangler.cpp @@ -0,0 +1,284 @@ +// 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 "untangler.h" +#include "Minigame.h" + +BPMiniGame_Untangler::BPMiniGame_Untangler(BPGame* game) : BPMiniGame(game) { + SelectedPoint = NULL; + sfcCircleHandle = TheGame->LoadBitmap("circle_handle", 16, 16); + sfcBackground = TheGame->LoadBitmap("stars_glow", 320, 416); + + LastMoveTime = CurrentLevel = TimeStarted = 0; + SuccessTime = -1; + + GenerateStarfield(Stars); + + GameTitle = "Untangler"; + GameHelp = "Drag the line ends around so the lines don't cross. You need to ensure that none of the lines cross to advance!"; + GameHelp2 = "The white circles at the ends of each line can be moved. If a line is green, it means it doesn't cross any other line. If it is red, it's crossing at least one other line. You need to move the line ends until no lines cross any others."; + + MiniGameType = PUZZLE; + + LevelUp(); +} + +BPMiniGame_Untangler::~BPMiniGame_Untangler() { + TanglePoints.Clear(); + Stars.Clear(); + + SAFE_DELETE(SelectedPoint); + + SAFE_DELETE(sfcCircleHandle); + SAFE_DELETE(sfcBackground); +} + +void BPMiniGame_Untangler::Start() { + TimeStarted = TheGame->TickCount; +} + +int BPMiniGame_Untangler::GetWeight() { + float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f; + return MinMax(530 - floor(TimePassed * 1.1)); +} + +void BPMiniGame_Untangler::Render() { + TheGame->DrawImage(sfcBackground, 0, 0); + + DrawStarfield(Stars); + + bool p1_collides; + bool p2_collides; + + bool any_collisions = false; + + for (int i = 0; i < TanglePoints.Count; ++i) { + BPMiniGame_Untangler_Point* point = TanglePoints[i]; + + p1_collides = false; + p2_collides = false; + + // does this line collide with any other? + for (int j = 0; j < TanglePoints.Count; ++j) { + BPMiniGame_Untangler_Point* other_point = TanglePoints[j]; + //if (point == other_point) continue; + + // if our first point collides with the other first point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect1->Pos.X, other_point->Connect1->Pos.Y) == 1) { + p1_collides = true; + } else { + // if our first point collides with the other second point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect2->Pos.X, other_point->Connect2->Pos.Y) == 1) { + p1_collides = true; + } + } + + // if our second point collides with the other first point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect1->Pos.X, other_point->Connect1->Pos.Y) == 1) { + p2_collides = true; + } else if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect2->Pos.X, other_point->Connect2->Pos.Y) == 1) { + // if our second point collides with the other second point + p2_collides = true; + } + } + + if (p1_collides) { + any_collisions = true; + TheGame->DrawLine(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, TheGame->Red, 3.0f); + } else { + TheGame->DrawLine(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, TheGame->Green, 3.0f); + } + + if (p2_collides) { + any_collisions = true; + TheGame->DrawLine(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, TheGame->Red, 3.0f); + } else { + TheGame->DrawLine(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, TheGame->Green, 3.0f); + } + } + + // draw blocks separately to the lines so they always appear on top + for (int i = 0; i < TanglePoints.Count; ++i) { + BPMiniGame_Untangler_Point* point = TanglePoints[i]; + TheGame->DrawImage(sfcCircleHandle, point->Pos.X - 8, point->Pos.Y - 10); + } + + if (SelectedPoint == NULL && !any_collisions && LastMoveTime + 500 < TheGame->TickCount) { + // success! + TheGame->PlaySound("correct"); + LevelUp(); + } +} + +void BPMiniGame_Untangler::Tick() { + if (SuccessTime != -1 && SuccessTime + 250 < TheGame->TickCount) { + Success(); + return; + } + + UpdateStarfield(Stars); +} + +void BPMiniGame_Untangler::OnMouseUp() { + if (SelectedPoint != NULL) { + // don't let people drag the points off the screen! + if (SelectedPoint->Pos.X < Size) { + SelectedPoint->Pos.X = Size; + } else if (SelectedPoint->Pos.X > MiniGameWidth - Size) { + SelectedPoint->Pos.X = MiniGameWidth - Size; + } + + if (SelectedPoint->Pos.Y < Size) { + SelectedPoint->Pos.Y = Size; + } else if (SelectedPoint->Pos.Y > 416 - Size) { + SelectedPoint->Pos.Y = 416 - Size; + } + } + + SelectedPoint = NULL; + LastMoveTime = TheGame->TickCount; +} + +void BPMiniGame_Untangler::OnMouseDown() { + BPMiniGame_Untangler_Point* closest = NULL; + float distance = 999.0f; + + for (int i = 0; i < TanglePoints.Count; ++i) { + BPMiniGame_Untangler_Point* point = TanglePoints[i]; + + float thisdist = BPPoint::DistanceSquared(BPPoint(TouchEvent.X, TouchEvent.Y), BPPoint(point->Pos.X, point->Pos.Y)); + + if (thisdist < distance) { + closest = point; + distance = thisdist; + } + +// if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, point->Pos.X - (HalfSize + 8), point->Pos.Y - (HalfSize + 8), (Size + 16), (Size + 16))) { +// SelectedPoint = point; +// break; +// } + } + + if (distance < 2304.0f) { // 48 * 48 + SelectedPoint = closest; + } +} + +void BPMiniGame_Untangler::OnMouseMove() { + if (SelectedPoint != NULL) { + SelectedPoint->Pos = TouchEvent; + } +} + +float BPMiniGame_Untangler::abs2(float x) { + if (x < 0) { return -x; } + return x; +} + +int BPMiniGame_Untangler::LinesCross(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { + float d = (x1 - x0) * (y3 - y2) - (y1 - y0) * (x3 - x2); + if (abs2(d) < 0.001) { return -1; } + float AB = ((y0 - y2) * (x3 - x2) - (x0 - x2) * (y3 - y2)) / d; + if (AB > 0.0 && AB < 1.0) { + float CD = ((y0 - y2) * (x1 - x0) - (x0 - x2) * (y1 - y0)) / d; + if (CD > 0.0 && CD < 1.0) { + //linx = x0 + AB * (x1 - x0); + //liny = y0 + AB * (y1 - y0); + return 1; + } + } + return 0; +} + +void BPMiniGame_Untangler::LevelUp() { + if (!MarathonMode && CurrentLevel >= 8 && SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + return; + } + + if (SuccessTime != -1) { + // we've finished - bail out! + return; + } + + ++CurrentLevel; + + do { + TanglePoints.Clear(); + + // first, add all the lines + for (int i = 0; i < CurrentLevel + 5; ++i) { + BPMiniGame_Untangler_Point* line = new BPMiniGame_Untangler_Point(); + line->Pos.X = TheGame->RandomRange(5, 235); + line->Pos.Y = TheGame->RandomRange(5, 290); + TanglePoints.Add(line); + } + + for (int i = 0; i < TanglePoints.Count; ++i) { + if (i == 0) { + // this is the first item; set its Connect1 value to be the last item + TanglePoints[i]->Connect1 = TanglePoints[TanglePoints.Count - 1]; + } else { + TanglePoints[i]->Connect1 = TanglePoints[i - 1]; + } + + if (i == TanglePoints.Count - 1) { + // this is the last item; set its Connect2 value to be the first item + TanglePoints[i]->Connect2 = TanglePoints[0]; + } else { + TanglePoints[i]->Connect2 = TanglePoints[i + 1]; + } + } + } while (!AnyCollisions()); +} + +bool BPMiniGame_Untangler::AnyCollisions() { + for (int i = 0; i < TanglePoints.Count; ++i) { + BPMiniGame_Untangler_Point* point = TanglePoints[i]; + + // does this line collide with any other? + for (int j = 0; j < TanglePoints.Count; ++j) { + BPMiniGame_Untangler_Point* other_point = TanglePoints[j]; + //if (point == other_point) continue; + + // if our first point collides with the other first point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect1->Pos.X, other_point->Connect1->Pos.Y) == 1) { + return true; + } else { + // if our first point collides with the other second point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect1->Pos.X, point->Connect1->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect2->Pos.X, other_point->Connect2->Pos.Y) == 1) { + return true; + } + } + + // if our second point collides with the other first point + if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect1->Pos.X, other_point->Connect1->Pos.Y) == 1) { + return true; + } else if (LinesCross(point->Pos.X, point->Pos.Y, point->Connect2->Pos.X, point->Connect2->Pos.Y, other_point->Pos.X, other_point->Pos.Y, other_point->Connect2->Pos.X, other_point->Connect2->Pos.Y) == 1) { + // if our second point collides with the other second point + return true; + } + } + } + + // if we're stil here, there are no collisions + return false; +} + +void BPMiniGame_Untangler::SetMarathon() { + MarathonMode = true; +} diff --git a/untangler.h b/untangler.h new file mode 100644 index 0000000..54836c2 --- /dev/null +++ b/untangler.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef __UNTANGLER_H__ +#define __UNTANGLER_H__ + +#include "Minigame.h" + +class BPMiniGame_Untangler_Point { +public: + BPPoint Pos; + BPMiniGame_Untangler_Point* Connect1; + BPMiniGame_Untangler_Point* Connect2; +}; + + +class BPMiniGame_Untangler : public BPMiniGame { +public: + BPMiniGame_Untangler(BPGame* game); + ~BPMiniGame_Untangler(); + void Start(); + int GetWeight(); + void Render(); + void Tick(); + void OnMouseUp(); + void OnMouseDown(); + void OnMouseMove(); + float abs2(float x); + int LinesCross(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); + void LevelUp(); + void SetMarathon(); + bool AnyCollisions(); + +protected: + BPPList TanglePoints; + BPMiniGame_Untangler_Point* SelectedPoint; + Texture* sfcCircleHandle; + Texture* sfcBackground; + BPPList Stars; + + int LastMoveTime; + int CurrentLevel; + + static const int HalfSize = 8; + static const int Size = 16; + + int TimeStarted; + int SuccessTime; +}; + +#endif diff --git a/wordsmash.cpp b/wordsmash.cpp new file mode 100644 index 0000000..fa1efa9 --- /dev/null +++ b/wordsmash.cpp @@ -0,0 +1,598 @@ +// 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 "wordsmash.h" +#include "Minigame.h" + +BPMiniGame_WordSmash::BPMiniGame_WordSmash(BPGame* game) : BPMiniGame(game) { + TheGame = game; + + sfcBackgroundTop = TheGame->LoadBitmap("wordsmash_top", 320, 480); + sfcBackgroundBottom = TheGame->LoadBitmap("wordsmash_bottom", 320, 480); + + SelectedRow = NULL; + Locked = false; // when true disallow movement + TimeStarted = LastColour = 0; + SuccessTime = -1; + + GotMovement = JustMoved = false; + + DisappearTime = 250; + + sfcLetters["a"] = TheGame->LoadBitmap("a", 44, 44); + sfcLetters["b"] = TheGame->LoadBitmap("b", 44, 44); + sfcLetters["c"] = TheGame->LoadBitmap("c", 44, 44); + sfcLetters["d"] = TheGame->LoadBitmap("d", 44, 44); + sfcLetters["e"] = TheGame->LoadBitmap("e", 44, 44); + sfcLetters["f"] = TheGame->LoadBitmap("f", 44, 44); + sfcLetters["g"] = TheGame->LoadBitmap("g", 44, 44); + sfcLetters["h"] = TheGame->LoadBitmap("h", 44, 44); + sfcLetters["i"] = TheGame->LoadBitmap("i", 44, 44); + sfcLetters["j"] = TheGame->LoadBitmap("j", 44, 44); + sfcLetters["k"] = TheGame->LoadBitmap("k", 44, 44); + sfcLetters["l"] = TheGame->LoadBitmap("l", 44, 44); + sfcLetters["m"] = TheGame->LoadBitmap("m", 44, 44); + sfcLetters["n"] = TheGame->LoadBitmap("n", 44, 44); + sfcLetters["o"] = TheGame->LoadBitmap("o", 44, 44); + sfcLetters["p"] = TheGame->LoadBitmap("p", 44, 44); + sfcLetters["qu"] = TheGame->LoadBitmap("qu", 44, 44); + sfcLetters["r"] = TheGame->LoadBitmap("r", 44, 44); + sfcLetters["s"] = TheGame->LoadBitmap("s", 44, 44); + sfcLetters["t"] = TheGame->LoadBitmap("t", 44, 44); + sfcLetters["u"] = TheGame->LoadBitmap("u", 44, 44); + sfcLetters["v"] = TheGame->LoadBitmap("v", 44, 44); + sfcLetters["w"] = TheGame->LoadBitmap("w", 44, 44); + sfcLetters["x"] = TheGame->LoadBitmap("x", 44, 44); + sfcLetters["y"] = TheGame->LoadBitmap("y", 44, 44); + sfcLetters["z"] = TheGame->LoadBitmap("z", 44, 44); + + Words = new WordList("/opt/brainparty/Content/wordlist.txt"); + + for (int j = 0; j < RowCount; ++j) { + vector* row = new vector(); + + for (int i = 0; i < ColCount; ++i) { + BPMiniGame_WordSmash_Letter* box = new BPMiniGame_WordSmash_Letter(); + box->X = LeftIndent + (i * BoxSize); + box->Y = -BoxSize; + box->DestY = 57 + (j * BoxSize); + + box->Letter = GetLetter(); + row->push_back(box); + } + + Boxes.push_back(row); + } + + GameTitle = "Word Smash"; + GameHelp = "Drag the rows around, then tap squares to make words of four letters or more. Double-tap the last letter when you want to submit it; if you change your mind, just tap the first letter again to clear it."; + GameHelp2 = "You can spell words in any direction, even changing direction part way if you want to. For example, you could go down a letter, right a letter, down a letter, then right one more then tap the last letter again to submit it. You get more points for long words, so be creative!"; + + MiniGameType = PUZZLE; + + Score = 0; + sfcScoreStr = sfcClock = NULL; + SetScore(); +} + +BPMiniGame_WordSmash::~BPMiniGame_WordSmash() { + SAFE_DELETE(sfcBackgroundTop); + SAFE_DELETE(sfcBackgroundBottom); + + BPMiniGame_WordSmash_Letter* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int i = row->size() - 1; i >= 0; --i) { + box = (*row)[i]; + SAFE_DELETE(box); + } + + row->clear(); + SAFE_DELETE(row); + } + + Boxes.clear(); + + for(map::iterator it = sfcLetters.begin(); it != sfcLetters.end(); ++it) { + SAFE_DELETE(it->second); + } + + sfcLetters.clear(); + + SAFE_DELETE(Words); + + SAFE_DELETE(sfcScoreStr); + SAFE_DELETE(sfcClock); +} + +int BPMiniGame_WordSmash::GetWeight() { + return Score / 100; +} + +void BPMiniGame_WordSmash::OnMouseMove() { + if (Locked) return; + if (SelectedRow == NULL) return; + + if (!GotMovement) { + Moves.Clear(); + } + + GotMovement = true; + + BPMiniGame_WordSmash_Letter* box; + + if (LastPos != BPPoint::Zero && LastPos != TouchEvent) { + if (TouchEvent.X < LastPos.X) { + // move left + + for (int i = SelectedRow->size() - 1; i >= 0; --i) { + box = (*SelectedRow)[i]; + + box->X += TouchEvent.X - LastPos.X; + + if (box->X <= -BoxSize) { + box->X = GameWidth + box->X - LeftIndent - RightIndent; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->push_back(box); + } + } + + } else { + // move right + + for (int i = 0; i < SelectedRow->size(); ++i) { + box = (*SelectedRow)[i]; + + box->X += TouchEvent.X - LastPos.X; + + if (box->X >= GameWidth - RightIndent) { + box->X -= GameWidth - LeftIndent - RightIndent; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->insert(SelectedRow->begin() + 0, box); + } + } + } + } + + LastPos = TouchEvent; +} + +void BPMiniGame_WordSmash::OnMouseDown() { + if (Locked) return; + GotMovement = false; + + LastPos = TouchEvent; + + for (int i = 0; i < Boxes.size(); ++i) { + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 37 + (i * BoxSize) + HalfBoxSize, 320, BoxSize)) { + // move this row + SelectedRow = Boxes[i]; + break; + } + } +} + +void BPMiniGame_WordSmash::OnMouseUp() { + if (Locked) return; + if (SelectedRow == NULL) return; + + if (GotMovement) { + + BPMiniGame_WordSmash_Letter* box; + + // we've let go of a row - snap it into place! + int xoffset = (*SelectedRow)[0]->X - LeftIndent; + + int remainder = xoffset % BoxSize; + if (remainder != 0) { + int adjust = 0; + + if (remainder > HalfBoxSize || (remainder < 0 && remainder > -HalfBoxSize)) { + // snap to the right + if (remainder > 0) { + adjust = BoxSize - remainder; + } else { + adjust = abs(remainder); + } + + for (int i = 0; i < SelectedRow->size(); ++i) { + box = (*SelectedRow)[i]; + + box->X += adjust; + + if (box->X >= GameWidth - RightIndent) { + box->X -= GameWidth; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->insert(SelectedRow->begin() + 0, box); + } + } + } else { + // snap to the left + if (remainder < 0) { + adjust = abs(remainder) - BoxSize; + } else { + adjust = -remainder; + } + + for (int i = SelectedRow->size() - 1; i >= 0; --i) { + box = (*SelectedRow)[i]; + + box->X += adjust; + + if (box->X <= LeftIndent - BoxSize) { + box->X = GameWidth + box->X; + SelectedRow->erase(SelectedRow->begin() + i); + SelectedRow->push_back(box); + } + } + + } + } + + SelectedRow = NULL; + } else { + // find the box that was clicked on + BPList Possibles; + BPMiniGame_WordSmash_Letter* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + box = (*row)[j]; + + if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, box->X - HalfBoxSize, box->Y - HalfBoxSize, BoxSize * 2, BoxSize * 2)) { + Possibles.Add(box); + } + } + } + + box = NULL; + int maxdist = 999; + + for (int i = 0; i < Possibles.Count; ++i) { + int centrex = Possibles[i]->X + HalfBoxSize; + int centrey = Possibles[i]->Y + HalfBoxSize; + + int distance = abs((int)round(TouchEvent.X - centrex)) + abs((int)round(TouchEvent.Y - centrey)); + if (distance < maxdist) { + box = Possibles[i]; + maxdist = distance; + } + } + + // if we clicked on nothing, bail out + if (box == NULL) return; + + if (Moves.Contains(box)) { + if (Moves[Moves.Count - 1] == box) { + // they selected the last square + if (Moves.Count == 1) { + // if this is the first square (ie, the only square), remove it + Moves.Remove(box); + } else { + // otherwise, see if this matches a word! + string check; + + for (int i = 0; i < Moves.Count; ++i) { + check.append(Moves[i]->Letter); + } + + if (IsWord(check)) { + for (int i = 0; i < Moves.Count; ++i) { + Moves[i]->MatchTime = TheGame->TickCount; + } + + int ScoreAdd = (Moves.Count * Moves.Count) * 100; + ModifyScore(ScoreAdd); + TheGame->PlaySound("correct"); + ++LastColour; + + if (LastColour > 3) LastColour = 0; + + Moves.Clear(); + } + + } + + } else { + // they selected a square other than the last one - remove all moves after this one + int pos = Moves.IndexOf(box); + + for (int i = Moves.Count - 1; i > pos; --i) { + Moves.RemoveAt(i); + } + + TheGame->PlaySound("wrong3"); + } + } else { + if (Moves.Count > 0) { + BPMiniGame_WordSmash_Letter* last = Moves[Moves.Count - 1]; + + if (CanMove(box, last)) { + Moves.Add(box); + TheGame->PlaySound("gem_select"); + } else { + // illegal move! + // [MessageBox Show:"You can only move to a square that's horizontally or vertically next to your previous move.":"Oops!"]; + } + } else { + Moves.Add(box); + TheGame->PlaySound("gem_select"); + } + } + } +} + +bool BPMiniGame_WordSmash::CanMove(BPMiniGame_WordSmash_Letter* square1, BPMiniGame_WordSmash_Letter* square2) { + // return true if this square is adjacent to the last move square + + return (abs(square1->X - square2->X) + abs(square1->Y - square2->Y)) == BoxSize; +} + +void BPMiniGame_WordSmash::Start() { + TimeStarted = TheGame->TickCount; +} + + +void BPMiniGame_WordSmash::Render() { + sfcBackgroundBottom->Draw(0, -20); + + int TimePassed = TheGame->TickCount - TimeStarted; + TimePassed = 300000 - TimePassed; + + if (TimePassed <= 0) { + if (SuccessTime == -1) { + SuccessTime = TheGame->TickCount; + Success(); + } + } + + if (sfcClock == NULL || RedrawClock()) { + TheGame->AllocString(&sfcClock, TheGame->TicksToTime(TimePassed)->c_str(), LARGE, 80, 47, LEFT); + } + + TheGame->DrawString(sfcClock, WHITE, 235, 10); + + TheGame->DrawString(sfcScoreStr, WHITE, 15, 10); + + BPMiniGame_WordSmash_Letter* box; + + for (int i = 0; i < Boxes.size(); ++i) { + vector* row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + box = (*row)[j]; + + // HACK: There's an unknown bug in this minigame that occasionally puts boxes in the wrong X pos. + // To reproduce, just keep matching stuff while dragging around on the screen manically. + // This force-resets the X position for each box to the correct value. What a waste of CPU time! + if (SelectedRow != row) box->X = LeftIndent + (j * BoxSize); + + if (box->MatchTime == -1) { + if (Moves.Contains(box)) { + switch (LastColour) { + case 0: + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->LightRed)); + break; + + case 1: + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->Yellow)); + break; + + case 2: + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->Green)); + break; + + case 3: + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->Cyan)); + break; + } + } else { + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->White)); + + if (box->X < LeftIndent) { + sfcLetters[box->Letter]->Draw(GameWidth + box->X + HalfBoxSize - LeftIndent - RightIndent, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->White)); + } else if (box->X > GameWidth - BoxSize - RightIndent) { + sfcLetters[box->Letter]->Draw(HalfBoxSize + LeftIndent + RightIndent + box->X - GameWidth, box->Y + HalfBoxSize, 0.0f, 1.0f, (*TheGame->White)); + } + } + } else { + float diff = TheGame->TickCount - box->MatchTime; + + if (diff <= DisappearTime) { + float step = diff / DisappearTime; // get a value between 0 and 1 + Colour col = Colour(1.0f, 1.0f, 1.0f, 1 - step); + + sfcLetters[box->Letter]->Draw(box->X + HalfBoxSize, box->Y + HalfBoxSize, 0.0f, TheGame->SmoothStep(1.0f, 3.0f, step), col); + } + } + } + } + + // draw the background last to mask the hidden letters + sfcBackgroundTop->Draw(0, -20); +} + +void BPMiniGame_WordSmash::Tick() { + vector* row; + BPMiniGame_WordSmash_Letter* box; + BPMiniGame_WordSmash_Letter* copybox; + + int MatchTimeout = TheGame->TickCount - DisappearTime; + + bool AllStill = true; + + for (int i = Boxes.size() - 1; i >= 0; --i) { + row = Boxes[i]; + + for (int j = row->size() - 1; j >= 0; --j) { + box = (*row)[j]; + + if (box->MatchTime != -1) AllStill = false; + + if (box->Y != box->DestY) { + AllStill = false; + + box->YSpeed += 2.0f; + box->Y += box->YSpeed; + if (box->Y > box->DestY) { + box->Y = box->DestY; + box->YSpeed = 0; + } + } + + if (box->MatchTime != -1 && box->MatchTime < MatchTimeout) { + row->erase(row->begin() + j); + // move all boxes down above me + + for (int k = i - 1; k >= 0; --k) { + copybox = (*Boxes[k])[j]; + Boxes[k]->erase(Boxes[k]->begin() + j); + Boxes[k + 1]->insert(Boxes[k + 1]->begin() + j, copybox); + copybox->DestY = 57 + ((k + 1) * BoxSize); + } + + BPMiniGame_WordSmash_Letter* newbox = new BPMiniGame_WordSmash_Letter(); + newbox->X = LeftIndent + j * BoxSize; + newbox->Y = -BoxSize; + newbox->DestY = 57; + newbox->Letter = GetLetter(); + Boxes[0]->insert(Boxes[0]->begin() + j, newbox); + } + } + } + + if (AllStill) { + Locked = false; + JustMoved = false; + } else { + Locked = true; + } +} + +bool BPMiniGame_WordSmash::IsWord(string &word) { + return Words->Contains(word); +} + + +string BPMiniGame_WordSmash::GetLetter() { + int freq_a = 8167; + int freq_b = freq_a + 1492; + int freq_c = freq_b + 2782; + int freq_d = freq_c + 4253; + int freq_e = freq_d + 12702; + int freq_f = freq_e + 2228; + int freq_g = freq_f + 2015; + int freq_h = freq_g + 6094; + int freq_i = freq_h + 6966; + int freq_j = freq_i + 153; + int freq_k = freq_j + 772; + int freq_l = freq_k + 4025; + int freq_m = freq_l + 2406; + int freq_n = freq_m + 6749; + int freq_o = freq_n + 7507; + int freq_p = freq_o + 1929; + int freq_q = freq_p + 95; + int freq_r = freq_q + 5987; + int freq_s = freq_r + 6327; + int freq_t = freq_s + 9056; + int freq_u = freq_t + 2758; + int freq_v = freq_u + 978; + int freq_w = freq_v + 2360; + int freq_x = freq_w + 150; + int freq_y = freq_x + 1974; + // int freq_z = freq_y + 74; + + int random = TheGame->RandomRange(0, 100000); + + if (random < freq_a) { + return string("a"); + } else if (random < freq_b) { + return string("b"); + } else if (random < freq_c) { + return string("c"); + } else if (random < freq_d) { + return string("d"); + } else if (random < freq_e) { + return string("e"); + } else if (random < freq_f) { + return string("f"); + } else if (random < freq_g) { + return string("g"); + } else if (random < freq_h) { + return string("h"); + } else if (random < freq_i) { + return string("i"); + } else if (random < freq_j) { + return string("j"); + } else if (random < freq_k) { + return string("k"); + } else if (random < freq_l) { + return string("l"); + } else if (random < freq_m) { + return string("m"); + } else if (random < freq_n) { + return string("n"); + } else if (random < freq_o) { + return string("o"); + } else if (random < freq_p) { + return string("p"); + } else if (random < freq_q) { + return string("qu"); + } else if (random < freq_r) { + return string("r"); + } else if (random < freq_s) { + return string("s"); + } else if (random < freq_t) { + return string("t"); + } else if (random < freq_u) { + return string("u"); + } else if (random < freq_v) { + return string("v"); + } else if (random < freq_w) { + return string("w"); + } else if (random < freq_x) { + return string("x"); + } else if (random < freq_y) { + return string("y"); + } else { + return string("z"); + } + + return string(""); +} + +void BPMiniGame_WordSmash::ModifyScore(int adjust) { + Score += adjust; + + if (Score < 0) Score = 0; + + SetScore(); +} + +void BPMiniGame_WordSmash::SetScore() { + if (Score > 0) { + ostringstream score; + score << "Score: " << TheGame->SeparateThousands(Score); + TheGame->AllocString(&sfcScoreStr, score.str().c_str(), LARGE, 250, 50, LEFT); + } else { + TheGame->AllocString(&sfcScoreStr, "Score: 0", LARGE, 250, 50, LEFT); + } +} diff --git a/wordsmash.h b/wordsmash.h new file mode 100644 index 0000000..a8683cf --- /dev/null +++ b/wordsmash.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef __WORDSMASH_H__ +#define __WORDSMASH_H__ + +#include "Minigame.h" +#include "WordList.h" + +class BPMiniGame_WordSmash_Letter { +public: + int X; + int Y; + int DestY; + int YSpeed; + int MatchTime; + string Letter; + + BPMiniGame_WordSmash_Letter() { + Letter = string("A"); + X = Y = DestY = YSpeed = 0; + MatchTime = -1; + } +}; + + +class BPMiniGame_WordSmash : public BPMiniGame { +public: + BPMiniGame_WordSmash(BPGame* game); + ~BPMiniGame_WordSmash(); + void OnMouseMove(); + void OnMouseDown(); + void OnMouseUp(); + void Start(); + void Render(); + void Tick(); + int GetWeight(); + void CheckForMatches(); + bool IsWord(string &word); + string GetLetter(); + bool CanMove(BPMiniGame_WordSmash_Letter* square1, BPMiniGame_WordSmash_Letter* square2); + void ModifyScore(int adjust); + void SetScore(); + +protected: + static const int GameWidth = 320; + static const int GameHeight = 480; + + static const int RowCount = 8; + static const int ColCount = 7; + + static const int MinWordLength = 4; + + static const int LeftIndent = 6; + static const int RightIndent = 6; + + WordList* Words; + + int LastColour; + + Texture* sfcBackgroundTop; + Texture* sfcBackgroundBottom; + + vector*> Boxes; + BPList Moves; // this needs to be a BPList to avoid double-freeing boxes in Boxes + map sfcLetters; + + vector* SelectedRow; + + bool GotMovement; // used to track whether a drag took place + bool JustMoved; // used to track whether to check for words when dragging has finished + + int DisappearTime; + BPPoint LastPos; + + int Score; + SpriteFont* sfcScoreStr; + + SpriteFont* sfcClock; + + static const int BoxSize = 44; + static const int HalfBoxSize = 22; + + bool Locked; // when true disallow movement + + int TimeStarted; + + int SuccessTime; +}; + +#endif