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 "cupsnballs.h"
19 #include "Minigame.h"
20 
BPMiniGame_CupsNBalls(BPGame * game)21 BPMiniGame_CupsNBalls::BPMiniGame_CupsNBalls(BPGame* game) : BPMiniGame(game) {
22 	GameTitle = "Cups 'n' Balls";
23 	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!";
24 	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!";
25 
26 	MiniGameType = ACTION;
27 
28 	sfcBackground = TheGame->LoadBitmap("clouds", 320, 480);
29 	sfcBall = TheGame->LoadBitmap("ball", 32, 32);
30 	sfcPuff = TheGame->LoadBitmap("puff", 64, 64);
31 	sfcSlingshot = TheGame->LoadBitmap("slingshot", 80, 90);
32 
33 	sfcGreenCup = TheGame->LoadBitmap("green_cup", 86, 44);
34 	sfcRedCup = TheGame->LoadBitmap("red_cup", 86, 44);
35 
36 	BallHalfWidth = sfcBall->Width / 2;
37 	BallHalfHeight = sfcBall->Height / 2;
38 
39 	CupHalfWidth = sfcGreenCup->Width / 2;
40 	CupHalfHeight = sfcGreenCup->Height / 2;
41 
42 	SlingshotHalfWidth = sfcSlingshot->Width / 2;
43 
44 	SlingshotX = 123;
45 	SlingshotY = 296;
46 
47 	// how low down the cup front picture is, in pixels; this gives us better collision detection
48 	CupFrontOffset = 19;
49 	Ammo = 20;
50 	Score = 0;
51 
52 	sfcScore = NULL;
53 	sfcAmmo = NULL;
54 	SetScore();
55 	SetAmmo();
56 
57 	CreateCup(0, 0);
58 	CreateCup(90, 0);
59 	CreateCup(180, 0);
60 	CreateCup(270, 1);
61 }
62 
~BPMiniGame_CupsNBalls()63 BPMiniGame_CupsNBalls::~BPMiniGame_CupsNBalls() {
64 	SAFE_DELETE(sfcBackground);
65 	SAFE_DELETE(sfcBall);
66 	SAFE_DELETE(sfcPuff);
67 	SAFE_DELETE(sfcSlingshot);
68 	SAFE_DELETE(sfcGreenCup);
69 	SAFE_DELETE(sfcRedCup);
70 
71 	SAFE_DELETE(sfcScore);
72 	SAFE_DELETE(sfcAmmo);
73 
74 	Balls.Clear();
75 	Cups.Clear();
76 }
77 
Start()78 void BPMiniGame_CupsNBalls::Start() {
79 	TimeStarted = TheGame->TickCount;
80 }
81 
GetWeight()82 int BPMiniGame_CupsNBalls::GetWeight() {
83 	return MinMax(round(Score * 31));
84 }
85 
Render()86 void BPMiniGame_CupsNBalls::Render() {
87 	sfcBackground->Draw(0, 0);
88 
89 	for (int i = 0; i < Cups.Count; ++i) {
90 		BPMiniGame_CupsNBalls_Cup* cup = Cups[i];
91 		if (cup->Type == 0) {
92 			sfcGreenCup->Draw(Round(cup->X), Round(cup->Y));
93 		} else {
94 			sfcRedCup->Draw(Round(cup->X), Round(cup->Y));
95 		}
96 	}
97 
98 	for (int i = 0; i < Balls.Count; ++i) {
99 		BPMiniGame_CupsNBalls_Ball* ball = Balls[i];
100 
101 		if (ball->HitTime == 0) {
102 			if (ball->YSpeed <= 0) {
103 				sfcBall->Draw(Round(ball->X), Round(ball->Y));
104 			}
105 		} else {
106 			sfcPuff->Draw(Round(ball->X - 10), Round(ball->Y - 5));
107 		}
108 	}
109 
110 	for (int i = 0; i < Balls.Count; ++i) {
111 		BPMiniGame_CupsNBalls_Ball* ball = Balls[i];
112 
113 		if (ball->YSpeed >= 0) {
114 			sfcBall->Draw(Round(ball->X), Round(ball->Y));
115 		}
116 	}
117 
118 	sfcSlingshot->Draw(SlingshotX, SlingshotY);
119 
120 	if (IsStringPulled && MouseMoved) {
121 		TheGame->DrawLine(SlingshotX + 5, SlingshotY, LastMousePos.X, LastMousePos.Y, TheGame->White, 3.0f);
122 		TheGame->DrawLine(SlingshotX - 5 + sfcSlingshot->Width, SlingshotY, LastMousePos.X, LastMousePos.Y, TheGame->White, 3.0f);
123 
124 		sfcBall->Draw(LastMousePos.X - BallHalfWidth, LastMousePos.Y - BallHalfHeight);
125 	} else {
126 		TheGame->DrawLine(SlingshotX + 5, SlingshotY, SlingshotX - 5 + sfcSlingshot->Width, SlingshotY, TheGame->White, 3.0f);
127 	}
128 
129 	TheGame->DrawString(sfcAmmo, (*TheGame->Black), 10, 380);
130 	TheGame->DrawString(sfcScore, (*TheGame->Black), 182, 380);
131 }
132 
Tick()133 void BPMiniGame_CupsNBalls::Tick() {
134 	if (Ammo == 0 && Balls.Count == 0) {
135 		Success();
136 	}
137 
138 	for (int i = Cups.Count - 1; i >= 0; --i) {
139 		BPMiniGame_CupsNBalls_Cup* cup = Cups[i];
140 
141 		cup->SinVal += 20.0 * TheGame->ElapsedSeconds;
142 
143 		cup->X = MiniGameHalfWidth + (sin(cup->SinVal * M_PI / 180) * 110) - CupHalfWidth;
144 		cup->Y = MiniGameHalfHeight - 100 + (cos(cup->SinVal * M_PI / 180) * 50) - CupHalfHeight;
145 
146 		for (int j = Balls.Count - 1; j >= 0; --j) {
147 			BPMiniGame_CupsNBalls_Ball* ball = Balls[j];
148 			if (ball->HitTime == 0) {
149 				// only balls (rather than ex-balls) can collide
150 				if (ball->YSpeed <= 0) {
151 					// only falling balls can collide
152 
153 					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)) {
154 						ball->HitTime = TheGame->TickCount;
155 
156 						if (cup->Type == 0) {
157 							TheGame->PlaySound("balloon_pop");
158 							Score += 1;
159 						} else {
160 							TheGame->PlaySound("wrong");
161 							Score -= 1;
162 						}
163 
164 						SetScore();
165 
166 						continue;
167 					}
168 				}
169 			} else {
170 				if (ball->HitTime + 200 < TheGame->TickCount) {
171 					Balls.RemoveAt(j);
172 				}
173 			}
174 		}
175 	}
176 
177 	for (int i = Balls.Count - 1; i >= 0; --i) {
178 		BPMiniGame_CupsNBalls_Ball* ball = Balls[i];
179 
180 		if (ball->HitTime == 0) {
181 			// only move if still "alive"
182 			ball->X -= ball->XSpeed * TheGame->ElapsedSeconds;
183 			ball->Y -= ball->YSpeed * TheGame->ElapsedSeconds;
184 			ball->YSpeed -= 1600.0f * TheGame->ElapsedSeconds;
185 
186 			if (ball->Y > 490) Balls.RemoveAt(i);
187 		}
188 	}
189 }
190 
OnMouseDown()191 void BPMiniGame_CupsNBalls::OnMouseDown() {
192 	MouseMoved = false;
193 	LastMousePos = TouchEvent;
194 
195 	if (Ammo > 0) {
196 		if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, SlingshotX - 10, SlingshotY - 10, sfcSlingshot->Width + 20, sfcSlingshot->Height + 20)) {
197 			IsStringPulled = true;
198 		}
199 	}
200 }
201 
OnMouseMove()202 void BPMiniGame_CupsNBalls::OnMouseMove() {
203 	if (LastMousePos != TouchEvent) {
204 		MouseMoved = true;
205 	}
206 
207 	LastMousePos = TouchEvent;
208 }
209 
OnMouseUp()210 void BPMiniGame_CupsNBalls::OnMouseUp() {
211 	if (IsStringPulled && MouseMoved) {
212 		IsStringPulled = false;
213 
214 		// don't allow people to pull the string up then fire down into the cups
215 		if (TouchEvent.Y < SlingshotY) return;
216 
217 		FireBullet();
218 	} else {
219 		IsStringPulled = false;
220 	}
221 }
222 
FireBullet()223 void BPMiniGame_CupsNBalls::FireBullet() {
224 	TheGame->PlaySound("swoosh_long");
225 
226 	// in Marathon Mode, ammo is infinite
227 	if (!MarathonMode) {
228 		--Ammo;
229 		SetAmmo();
230 	}
231 
232 	BPMiniGame_CupsNBalls_Ball* ball = new BPMiniGame_CupsNBalls_Ball();
233 	ball->X = TouchEvent.X - BallHalfWidth;
234 	ball->Y = TouchEvent.Y - BallHalfHeight;
235 
236 	double xdiff = TouchEvent.X - (SlingshotX + SlingshotHalfWidth);
237 	double ydiff = TouchEvent.Y - SlingshotY;
238 	double totaldiff = (fabs(xdiff) + fabs(ydiff)) / 4;
239 	double angle = atan(ydiff / xdiff) / (M_PI / 180);
240 
241 	if (xdiff < 0) angle += 180;
242 	if (xdiff >= 0 && ydiff < 0) angle += 360;
243 
244 	ball->XSpeed = (float)(cos(angle * M_PI / 180) * totaldiff) * 40.0f;
245 	ball->YSpeed = (float)(sin(angle * M_PI / 180) * totaldiff) * 35.0f;
246 
247 	Balls.Add(ball);
248 }
249 
CreateCup(int sinval,int type)250 void BPMiniGame_CupsNBalls::CreateCup(int sinval, int type) {
251 	BPMiniGame_CupsNBalls_Cup* cup = new BPMiniGame_CupsNBalls_Cup();
252 
253 	cup->Type = type;
254 	cup->SinVal = sinval;
255 
256 	Cups.Add(cup);
257 }
258 
SetMarathon()259 void BPMiniGame_CupsNBalls::SetMarathon() {
260 	MarathonMode = true;
261 	SAFE_DELETE(GameHelp);
262 	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!";
263 	Ammo = 999;
264 }
265 
SetScore()266 void BPMiniGame_CupsNBalls::SetScore() {
267 	if (Score > 0) {
268 		ostringstream ScoreStr;
269 		ScoreStr << "Score: " << TheGame->SeparateThousands(Score);
270 		TheGame->AllocString(&sfcScore, ScoreStr.str().c_str(), NORMAL, 128, 50, RIGHT);
271 	} else {
272 		TheGame->AllocString(&sfcScore, "Score: 0", NORMAL, 128, 50, RIGHT);
273 		Score = 0; // don't let it go below zero
274 	}
275 
276 
277 }
278 
SetAmmo()279 void BPMiniGame_CupsNBalls::SetAmmo() {
280 	if (Ammo > 0) {
281 		ostringstream AmmoStr;
282 		AmmoStr << "Ammo: " << Ammo;
283 		TheGame->AllocString(&sfcAmmo, AmmoStr.str().c_str(), NORMAL, 128, 50, LEFT);
284 	} else {
285 		TheGame->AllocString(&sfcAmmo, "Ammo: 0", NORMAL, 128, 50, LEFT);
286 	}
287 
288 
289 }
290