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