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