1 // Brain Party
2 // Copyright (C) 2010 Paul Hudson (http://www.tuxradar.com/brainparty)
3
4 // Brain Party is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 #include "numbersnake.h"
19 #include "Minigame.h"
20 #include <sstream>
21 #include <iostream>
22 #include <algorithm>
23 #include <iterator>
24
BPMiniGame_NumberSnake(BPGame * game)25 BPMiniGame_NumberSnake::BPMiniGame_NumberSnake(BPGame* game) : BPMiniGame(game) {
26 sfcBackground = TheGame->LoadBitmap("numbersnake", 320, 416);
27 sfcBlack = TheGame->LoadBitmap("numbersnake_black", 320, 128);
28
29 CurrentLevel = 0;
30
31 LastStateChange = LastQuestionChange = -1;
32 NumCorrect = 0;
33
34 sfcQuestionParts = NULL;
35 Answer = 0;
36
37 QuestionPos = 0;
38 QuestionSpeed = 1250;
39
40 TimeStarted = 0;
41
42 GameTitle = "Number Snake";
43 GameHelp = "Watch carefully as simple mathematics questions flash on your screen, then see if you can figure out the answers!";
44 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!";
45
46 MiniGameType = PUZZLE;
47
48 NumStrings.Add("zero");
49 NumStrings.Add("one");
50 NumStrings.Add("two");
51 NumStrings.Add("three");
52 NumStrings.Add("four");
53 NumStrings.Add("five");
54 NumStrings.Add("six");
55 NumStrings.Add("seven");
56 NumStrings.Add("eight");
57 NumStrings.Add("nine");
58 }
59
~BPMiniGame_NumberSnake()60 BPMiniGame_NumberSnake::~BPMiniGame_NumberSnake() {
61 SAFE_DELETE(sfcBackground);
62 SAFE_DELETE(sfcBlack);
63
64 NumStrings.Clear();
65
66 if (sfcQuestionParts != NULL) {
67 for (int i = 0; i < QuestionLength; ++i) {
68 SAFE_DELETE(sfcQuestionParts[i]);
69 }
70
71 delete[] sfcQuestionParts;
72 }
73 }
74
OnMouseDown()75 void BPMiniGame_NumberSnake::OnMouseDown() {
76
77 }
78
OnMouseMove()79 void BPMiniGame_NumberSnake::OnMouseMove() {
80
81 }
82
OnMouseUp()83 void BPMiniGame_NumberSnake::OnMouseUp() {
84 if (TouchEvent.Y < 100) {
85 // possible click on score
86 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 4, 52, 47)) SubmitAnswer(0);
87 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 66, 4, 52, 47)) SubmitAnswer(1);
88 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 134, 4, 52, 47)) SubmitAnswer(2);
89 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 201, 4, 52, 47)) SubmitAnswer(3);
90 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 272, 4, 52, 47)) SubmitAnswer(4);
91 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 0, 52, 52, 47)) SubmitAnswer(5);
92 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 66, 52, 52, 47)) SubmitAnswer(6);
93 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 134, 52, 52, 47)) SubmitAnswer(7);
94 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 201, 52, 52, 47)) SubmitAnswer(8);
95 if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, 272, 52, 52, 47)) SubmitAnswer(9);
96 }
97 }
98
Start()99 void BPMiniGame_NumberSnake::Start() {
100 TimeStarted = TheGame->TickCount;
101
102 LevelUp();
103 }
104
GetWeight()105 int BPMiniGame_NumberSnake::GetWeight() {
106 return MinMax(100 * NumCorrect);
107 }
108
Render()109 void BPMiniGame_NumberSnake::Render() {
110 TheGame->DrawImage(sfcBackground, 0, 0);
111
112 if (GameState == SHOWING) {
113 if (QuestionPos == QuestionLength - 1) {
114 float fadeamount = min(0.65f, Fader);
115 glColor4f(1.0f, 1.0f, 1.0f, fadeamount);
116 TheGame->DrawImage(sfcBlack, 0, 165);
117 } else {
118 glColor4f(1.0f, 1.0f, 1.0f, 0.65f);
119 TheGame->DrawImage(sfcBlack, 0, 165);
120 }
121
122 glColor4f(1.0f, 1.0f, 1.0f, Fader);
123 TheGame->DrawString(sfcQuestionParts[QuestionPos], 0, 170);
124 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
125 }
126
127 if (GameState == CORRECT) {
128 RenderCorrect();
129 } else if (GameState == WRONG) {
130 RenderWrong();
131 }
132 }
133
Tick()134 void BPMiniGame_NumberSnake::Tick() {
135 if (GameState == SHOWING) {
136 if (LastQuestionChange + QuestionSpeed < TheGame->TickCount) {
137 ++QuestionPos;
138 LastQuestionChange = TheGame->TickCount;
139
140 if (QuestionPos == QuestionLength) {
141 GameState = WAITING;
142 }
143 }
144
145 float start = TheGame->TickCount + QuestionSpeed;
146 float end = LastQuestionChange + QuestionSpeed;
147 float alpha = (start - end) / QuestionSpeed;
148 Fader = TheGame->SmoothStep(1.0f, 0.0f, alpha);
149 }
150
151 if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) {
152 if (!MarathonMode && CurrentLevel >= 5) {
153 Success();
154 } else {
155 LevelUp();
156 }
157 }
158 }
159
SubmitAnswer(int answer)160 void BPMiniGame_NumberSnake::SubmitAnswer(int answer) {
161 if (GameState != WAITING) return;
162
163 if (answer == Answer) {
164 TheGame->PlaySound("correct");
165 GameState = CORRECT;
166 ++NumCorrect;
167 } else {
168 TheGame->PlaySound("wrong");
169 GameState = WRONG;
170 }
171
172 LastStateChange = TheGame->TickCount;
173 }
174
LevelUp()175 void BPMiniGame_NumberSnake::LevelUp() {
176 ++CurrentLevel;
177 QuestionPos = 0;
178 LastQuestionChange = TheGame->TickCount;
179 QuestionSpeed -= 50;
180
181 BPList<int> Numbers;
182
183 Answer = TheGame->RandomRange(1, 6);
184
185 ostringstream PrintMe;
186 PrintMe << "What is " << NumStrings[Answer];
187
188 for (int i = 0; i < 4 + CurrentLevel; ++i) {
189 Numbers.Add(TheGame->RandomRange(1, 6));
190 }
191
192 int RandNum;
193
194 for (int i = 0; i < Numbers.Count; ++i) {
195 int num = Numbers[i];
196
197 // we want to allow subtraction only if it doesn't dip below zero!
198 if (Answer - num > 0) {
199 // only allow multiplication if the total doesn't go above 25
200 if (Answer * num < 26) {
201 RandNum = TheGame->RandomRange(0, 2);
202 } else {
203 RandNum = TheGame->RandomRangeExcept(0, 2, 1);
204 }
205 } else {
206 // only allow multiplication if the total doesn't go above 25
207 if (Answer * num < 26) {
208 RandNum = TheGame->RandomRange(0, 1);
209 } else {
210 RandNum = 0;
211 }
212 }
213
214 switch (RandNum) {
215 case 0:
216 // add
217 PrintMe << " plus " << NumStrings[num];
218 Answer += num;
219 break;
220
221 case 1:
222 PrintMe << " times " << NumStrings[num];
223 Answer *= num;
224 break;
225
226 case 2:
227 // subtract
228 PrintMe << " minus " << NumStrings[num];
229 Answer -= num;
230 break;
231 }
232 }
233
234 while (Answer >= 10) {
235 int newnum = TheGame->RandomRange(5, 9);
236
237 PrintMe << " minus " << NumStrings[newnum];
238 Answer -= newnum;
239 }
240
241 PrintMe << " ?";
242
243 istringstream iss(PrintMe.str());
244 vector<string> parts;
245 copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter<vector<string> >(parts));
246
247 if (sfcQuestionParts != NULL) {
248 for (int i = 0; i < QuestionLength; ++i) {
249 SAFE_DELETE(sfcQuestionParts[i]);
250 }
251
252 delete[] sfcQuestionParts;
253 }
254
255 QuestionLength = parts.size();
256
257 sfcQuestionParts = new SpriteFont*[QuestionLength];
258
259 int i = 0;
260 for (int i = 0; i < parts.size(); ++i) {
261 sfcQuestionParts[i] = NULL;
262 TheGame->AllocString(&sfcQuestionParts[i], parts[i].c_str(), EPIC, 320, 227, CENTRED);
263 }
264
265 GameState = SHOWING;
266 LastStateChange = -1;
267 }
268
SetMarathon()269 void BPMiniGame_NumberSnake::SetMarathon() {
270 MarathonMode = true;
271 GameHelp = "Watch carefully as simple mathematics questions flash on your screen, then see if you can figure out the answers!";
272 }
273