1 /* ----====----====----====----====----====----====----====----====----====----
4 JewelToy is a simple game played against the clock.
5 Copyright (C) 2001 Giles Williams
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 ----====----====----====----====----====----====----====----====----====---- */
26 #import "ScoreBubble.h"
31 @synthesize _bonusMultiplier;
38 for (int i = 0; i < 8; i++) {
39 for (int j = 0; j < 8; j++) {
40 board[i][j] = [[Gem alloc] init];
45 scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
51 - (id)initWithImagesFrom:(NSArray *)imageArray
55 srand([[NSDate date] timeIntervalSince1970]); // seed by time
56 for (int i = 0; i < 8; i++) {
57 for (int j = 0; j < 8; j++) {
58 int r = [self randomGemTypeAt:i:j];
59 Gem *gem = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
61 [gem setPositionOnBoard:i:j];
62 [gem setPositionOnScreen:i*48:j*48];
68 scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
78 - (id)initWithSpritesFrom:(NSArray *)spriteArray
82 srand([[NSDate date] timeIntervalSince1970]); // seed by time
83 for (int i = 0; i < 8; i++) {
84 for (int j = 0; j < 8; j++) {
85 //int r = (rand() % 3)*2+((i+j)%2);
86 int r = [self randomGemTypeAt:i:j];
87 board[i][j] = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
88 [board[i][j] setPositionOnBoard:i:j];
89 [board[i][j] setPositionOnScreen:i*48:j*48];
95 scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
107 for (int i = 0; i < 8; i++) {
108 for (int j = 0; j < 8; j++) {
109 [board[i][j] release];
114 [scoreBubbles release];
119 - (void)setImagesFrom:(NSArray *)imageArray
121 for (int i = 0; i < 8; i++) {
122 for (int j = 0; j < 8; j++) {
123 Gem *gem = board[i][j];
124 gem._image = [imageArray objectAtIndex:[gem gemType]];
129 - (void)setSpritesFrom:(NSArray *)spriteArray
131 for (int i = 0; i < 8; i++) {
132 for (int j = 0; j < 8; j++) {
133 Gem *gem = board[i][j];
134 [gem setSprite:[spriteArray objectAtIndex:[gem gemType]]];
139 - (int)randomGemTypeAt:(int)x :(int)y
144 return (r & 6); // even
146 return 1; // catch returning 7
147 return (r | 1); // odd
150 - (Gem *)gemAt:(int)x :(int)y {
155 - (NSMutableArray *)scoreBubbles {
159 - (void)setMuted:(BOOL)value
163 for (int i = 0; i < 8; i++) {
164 for (int j = 0; j < 8; j++) {
165 [board[i][j] setSoundsTink:NULL Sploink:NULL];
170 for (int i = 0; i < 8; i++) {
171 for (int j = 0; j < 8; j++) {
172 [board[i][j] setSoundsTink:[NSSound soundNamed:@"tink"] Sploink:[NSSound soundNamed:@"sploink"]];
178 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
180 Gem *swap = board[x1][y1];
181 board[x1][y1] = board[x2][y2];
182 [board[x1][y1] setPositionOnBoard:x1:y1];
183 board[x2][y2] = swap;
184 [board[x2][y2] setPositionOnBoard:x2:y2];
185 sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
189 [self swap:sx1:sy1 and:sx2:sy2];
192 - (BOOL) testForThreeAt:(int) x :(int) y
195 int bonus, linebonus, scorePerGem;
196 float scorebubble_x = -1.0;
197 float scorebubble_y = -1.0;
199 int gemtype = [board[x][y] gemType];
200 tx = x; ty = y; cx = x; cy = y;
202 if (board[x][y]._state == GEMSTATE_FADING) result = YES;
203 while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
204 while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
210 scorePerGem = (cx-tx)*5;
211 for (i = tx; i <= cx; i++)
213 linebonus+= scorePerGem;
215 for (j=7; j>y; j--) {
216 if (board[i][j]._state != GEMSTATE_FADING) {
217 [board[i][j] shiver]; // MW prepare to fall
221 // to center scorebubble ...
222 scorebubble_x = tx + (cx-tx)/2.0;
228 while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
229 while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
235 scorePerGem = (cy-ty)*5;
236 for (i = ty; i <= cy; i++)
238 linebonus += scorePerGem;
241 for (j=7; j>cy; j--) {
242 if (board[x][j]._state != GEMSTATE_FADING) {
243 [board[x][j] shiver]; // MW prepare to fall
246 // to center scorebubble ...
247 if (scorebubble_x < 0) // only if one hasn't been placed already ! (for T and L shapes)
250 scorebubble_y = ty + (cy-ty)/2.0;
252 else // select the original gem position
269 [scoreBubbles addObject:[ScoreBubble scoreWithValue:(bonus * _bonusMultiplier)
270 at:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
273 _score += bonus * _bonusMultiplier;
277 - (BOOL) finalTestForThreeAt:(int) x :(int) y
281 int gemtype = [board[x][y] gemType];
282 tx = x; ty = y; cx = x; cy = y;
284 if (board[x][y]._state == GEMSTATE_FADING) return YES;
286 while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
287 while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
292 for (i = tx; i <= cx; i++)
296 while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
297 while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
302 for (i = ty; i <= cy; i++)
309 - (BOOL) checkForThreeAt:(int) x :(int) y
312 int gemtype = [board[x][y] gemType];
313 tx = x; ty = y; cx = x; cy = y;
314 while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
315 while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
318 while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
319 while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
325 - (BOOL)checkBoardForThrees
329 // CASCADE BONUS increase
332 for (int i = 0; i < 8; i++) {
333 for (int j = 0; j < 8; j++) {
334 if (board[i][j]._state != GEMSTATE_FADING) {
335 result = result | [self testForThreeAt:i:j];
340 // CASCADE BONUS check for reset
348 - (void)showAllBoardMoves
350 // test every possible move
354 for (j = 0; j < 8; j++)
355 for (i = 0; i < 7; i++)
357 [self swap:i:j and:i+1:j];
358 [self finalTestForThreeAt:i:j];
359 [self finalTestForThreeAt:i+1:j];
364 for (i = 0; i < 8; i++)
365 for (j = 0; j < 7; j++)
367 [self swap:i:j and:i:j+1];
368 [self finalTestForThreeAt:i:j];
369 [self finalTestForThreeAt:i:j+1];
373 // over the entire board, set the animationtime for the marked gems higher
374 for (i = 0; i < 8; i++)
375 for (j = 0; j < 8; j++)
377 if (board[i][j]._state == GEMSTATE_FADING)
380 // !!!: remove: [board[i][j] setAnimationCounter:1];
381 board[i][j]._animationCounter = 1;
390 - (BOOL)boardHasMoves
392 // test every possible move
396 for (int j = 0; j < 8; j++) {
397 for (int i = 0; i < 7; i++) {
398 [self swap:i:j and:i+1:j];
399 result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
410 for (int i = 0; i < 8; i++) {
411 for (int j = 0; j < 7; j++) {
412 [self swap:i:j and:i:j+1];
413 result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
426 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
429 for (i = 0; i < 8; i++)
434 // let non-faded gems fall into place
435 for (j = 0; j < 8; j++)
437 if (board[i][j]._state != GEMSTATE_FADING)
439 column[y] = board[i][j];
440 if ([board[i][j] positionOnScreen].y > y*48)
447 // transfer faded gems to top of column
448 for (j = 0; j < 8; j++)
450 if (board[i][j]._state == GEMSTATE_FADING)
453 int r = (rand() % 7);
454 [board[i][j] setGemType:r];
455 board[i][j]._image = [imageArray objectAtIndex:r];
457 column[y] = board[i][j];
458 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
465 // OK, shuffling all done - reorganise column
466 for (j = 0; j < 8; j++)
468 board[i][j] = column[j];
469 [board[i][j] setPositionOnBoard:i:j];
474 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
477 for (i = 0; i < 8; i++)
482 // let non-faded gems fall into place
483 for (j = 0; j < 8; j++)
485 if (board[i][j]._state != GEMSTATE_FADING)
487 column[y] = board[i][j];
488 if ([board[i][j] positionOnScreen].y > y*48)
495 // transfer faded gems to top of column
496 for (j = 0; j < 8; j++)
498 if (board[i][j]._state == GEMSTATE_FADING)
501 int r = (rand() % 7);
502 [board[i][j] setGemType:r];
503 [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
505 column[y] = board[i][j];
506 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
513 // OK, shuffling all done - reorganise column
514 for (j = 0; j < 8; j++)
516 board[i][j] = column[j];
517 [board[i][j] setPositionOnBoard:i:j];
524 for (int i = 0; i < 8; i++) {
525 for (int j = 0; j < 8; j++) {
534 [[NSSound soundNamed:@"yes"] play];
537 for (int i = 0; i < 8; i++) {
538 for (int j = 0; j < 8; j++) {
544 - (void) explodeGameOver
547 if (!muted) [[NSSound soundNamed:@"explosion"] play];
549 for (i = 0; i < 8; i++)
550 for (j = 0; j < 8; j++)
553 [self showAllBoardMoves]; // does a delayed eruption
556 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
559 srand([[NSDate date] timeIntervalSince1970]); // seed by time
560 for (i = 0; i < 8; i++)
561 for (j = 0; j < 8; j++)
563 //int r = (rand() % 3)*2+((i+j)%2);
564 int r = [self randomGemTypeAt:i:j];
565 [board[i][j] setGemType:r];
566 board[i][j]._image = [imageArray objectAtIndex:r];
567 [board[i][j] setPositionOnBoard:i:j];
568 [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
573 _bonusMultiplier = 1;
576 - (void)wholeNewGameWithSpritesFrom:(NSArray *)spriteArray
578 srand([[NSDate date] timeIntervalSince1970]); // seed by time
580 for (int i = 0; i < 8; i++) {
581 for (int j = 0; j < 8; j++) {
582 //int r = (rand() % 3)*2+((i+j)%2);
583 int r = [self randomGemTypeAt:i:j];
585 Gem *gem = board[i][j];
587 [gem setSprite:[spriteArray objectAtIndex:r]];
588 [gem setPositionOnBoard:i:j];
589 [gem setPositionOnScreen:i*48:(15-j)*48];
596 _bonusMultiplier = 1;
599 // TODO: set as dynamic readonly property
600 - (NSPoint)hintPoint {
601 return NSMakePoint(hintx * 48, hinty * 48);
604 - (float)collectGemsFaded
606 float result = (float)gemsFaded;
611 - (void)increaseBonusMultiplier {