add Gem.sound properties
[exterlulz-kokogems.git] / src / Game.m
blobf939dd745946b6f4711e7e6e813c337b039f9f56
1 /* ----====----====----====----====----====----====----====----====----====----
2 Game.m (jeweltoy)
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 ----====----====----====----====----====----====----====----====----====---- */
22 #import "Game.h"
23 #import "Gem.h"
25 // MW...
26 #import "ScoreBubble.h"
28 @implementation Game
30 @synthesize score = _score;
31 @synthesize bonusMultiplier = _bonusMultiplier;
33 @dynamic hintPoint;
35 - (id)init
37   self = [super init];
38   if (self != nil) {
39     _fadedGems = 0;
40     for (int i = 0; i < 8; i++) {
41       for (int j = 0; j < 8; j++) {
42         board[i][j] = [[Gem alloc] init];
43       }
44     }
45     
46     // MW
47     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
48   }
49   
50   return self;
53 - (id)initWithImagesFrom:(NSArray *)imageArray
55   self = [super init];
56   if (self != nil) {
57     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
58     for (int i = 0; i < 8; i++) {
59       for (int j = 0; j < 8; j++) {
60         int r = [self randomGemTypeAt:i:j];
61         Gem *gem = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
62         board[i][j] = gem;
63         [gem setPositionOnBoard:i:j];
64         [gem setPositionOnScreen:i*48:j*48];
65         [gem shake];
66       }
67     }
68     
69     // MW...
70     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
71     
72     _score = 0;
73     _fadedGems = 0;
74     _bonusMultiplier = 1;
75   }
76   
77   return self;
80 - (id)initWithSpritesFrom:(NSArray *)spriteArray
82   self = [super init];
83   if (self != nil) {
84     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
85     for (int i = 0; i < 8; i++) {
86       for (int j = 0; j < 8; j++) {
87         //int r = (rand() % 3)*2+((i+j)%2);
88         int r = [self randomGemTypeAt:i:j];
89         board[i][j] = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
90         [board[i][j] setPositionOnBoard:i:j];
91         [board[i][j] setPositionOnScreen:i*48:j*48];
92         [board[i][j] shake];
93       }
94     }
95     
96     // MW...
97     scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
98     
99     _score = 0;
100     _fadedGems = 0;
101     _bonusMultiplier = 1;
102   }
103   
104   return self;
107 - (void)dealloc
109   for (int i = 0; i < 8; i++) {
110     for (int j = 0; j < 8; j++) {
111       [board[i][j] release];
112     }
113   }
114   
115   // MW...
116   [scoreBubbles release];
117   
118   [super dealloc];
121 - (void)setImagesFrom:(NSArray *)imageArray
123   for (int i = 0; i < 8; i++) {
124     for (int j = 0; j < 8; j++) {
125       Gem *gem = board[i][j];
126       gem.image = [imageArray objectAtIndex:[gem gemType]];
127     }
128   }
131 - (void)setSpritesFrom:(NSArray *)spriteArray
133   for (int i = 0; i < 8; i++) {
134     for (int j = 0; j < 8; j++) {
135       Gem *gem = board[i][j];
136       [gem setSprite:[spriteArray objectAtIndex:[gem gemType]]];
137     }
138   }
141 - (int)randomGemTypeAt:(int)x :(int)y
143     int c = (x+y) % 2;
144     int r = rand() % 7;
145     if (c)
146         return (r & 6); // even
147     if (r == 6)
148         return 1;       // catch returning 7
149     return (r | 1);     // odd
152 - (Gem *)gemAt:(int)x :(int)y {
153   return board[x][y];
156 // MW...
157 - (NSMutableArray *)scoreBubbles {
158     return scoreBubbles;
161 - (void)setMuted:(BOOL)value
163   muted = value;
164   if (muted) {
165     for (int i = 0; i < 8; i++) {
166       for (int j = 0; j < 8; j++) {
167         Gem *gem = board[i][j];
168         
169         gem.tinkSound     = NULL;
170         gem.sploinkSound  = NULL;
171       }
172     }
173   }
174   else {
175     for (int i = 0; i < 8; i++) {
176       for (int j = 0; j < 8; j++) {
177         Gem *gem = board[i][j];
178         
179         // TODO: retain?
180         gem.tinkSound     = [NSSound soundNamed:@"tink"];
181         gem.sploinkSound  = [NSSound soundNamed:@"sploink"];
182       }
183     }
184   }
187 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
189     Gem *swap = board[x1][y1];
190     board[x1][y1] = board[x2][y2];
191     [board[x1][y1] setPositionOnBoard:x1:y1];
192     board[x2][y2] = swap;
193     [board[x2][y2] setPositionOnBoard:x2:y2];
194     sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
197 - (void)unswap {
198   [self swap:sx1:sy1 and:sx2:sy2];
201 - (BOOL) testForThreeAt:(int) x :(int) y
203     int tx,ty,cx,cy;
204     int bonus, linebonus, scorePerGem;
205     float scorebubble_x = -1.0;
206     float scorebubble_y = -1.0;
207     BOOL result = NO;
208     int gemtype = [board[x][y] gemType];
209     tx = x; ty = y; cx = x; cy = y;
210     bonus = 0;
211     if (board[x][y].state == GEMSTATE_FADING)           result = YES;
212     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
213     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
214     if ((cx-tx) >= 2)
215     {
216         // horizontal line
217         int i,j;
218         linebonus= 0;
219         scorePerGem = (cx-tx)*5;
220         for (i = tx; i <= cx; i++)
221         {
222             linebonus+= scorePerGem;
223             [board[i][y] fade];
224             for (j=7; j>y; j--) {
225                 if (board[i][j].state != GEMSTATE_FADING) {
226                     [board[i][j] shiver];       //      MW prepare to fall
227                 }
228             }
229         }
230         // to center scorebubble ...
231         scorebubble_x = tx + (cx-tx)/2.0;
232         scorebubble_y = y;
233         
234         bonus += linebonus;
235         result = YES;
236     }
237     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
238     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
239     if ((cy-ty) >= 2)
240     {
241         // vertical line
242         int i,j;
243         linebonus= 0;
244         scorePerGem = (cy-ty)*5;
245         for (i = ty; i <= cy; i++)
246         {
247             linebonus += scorePerGem;
248             [board[x][i] fade];
249         }
250         for (j=7; j>cy; j--) {
251             if (board[x][j].state != GEMSTATE_FADING) {
252                 [board[x][j] shiver];           //      MW prepare to fall
253             }
254         }
255         // to center scorebubble ...
256         if (scorebubble_x < 0)  // only if one hasn't been placed already ! (for T and L shapes)
257         {
258             scorebubble_x = x;
259             scorebubble_y = ty + (cy-ty)/2.0;
260         }
261         else                    // select the original gem position
262         {
263             scorebubble_x = x;
264             scorebubble_y = y;
265         }
266         //
267         bonus += linebonus;
268         result = YES;
269     }
271   // CASCADE BONUS
272   if (cascade>=1) {
273         bonus *= cascade;
274   }
275   
276   // MW's scorebubble
277     if (bonus>0)
278         [scoreBubbles addObject:[ScoreBubble scoreWithValue:(bonus * _bonusMultiplier)
279                                                          at:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
280                                                    duration:40]];
282   _score += bonus * _bonusMultiplier;
283     return result;
286 - (BOOL) finalTestForThreeAt:(int) x :(int) y
288     int tx,ty,cx,cy;
289     BOOL result = NO;
290     int gemtype = [board[x][y] gemType];
291     tx = x; ty = y; cx = x; cy = y;
293     if (board[x][y].state == GEMSTATE_FADING)   return YES;
294         
295     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
296     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
297     if ((cx-tx) >= 2)
298     {
299         // horizontal line
300         int i;
301         for (i = tx; i <= cx; i++)
302             [board[i][y] fade];
303         result = YES;
304     }
305     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
306     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
307     if ((cy-ty) >= 2)
308     {
309         // vertical line
310         int i;
311         for (i = ty; i <= cy; i++)
312             [board[x][i] fade];
313         result = YES;
314     }
315     return result;
318 - (BOOL) checkForThreeAt:(int) x :(int) y
320     int tx,ty,cx,cy;
321     int gemtype = [board[x][y] gemType];
322     tx = x; ty = y; cx = x; cy = y;
323     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
324     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
325     if ((cx-tx) >= 2)
326         return YES;
327     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
328     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
329     if ((cy-ty) >= 2)
330         return YES;
331     return NO;
334 - (BOOL)checkBoardForThrees
336   BOOL result = NO;
337   
338   // CASCADE BONUS increase
339   cascade++;
340   
341   for (int i = 0; i < 8; i++) {
342     for (int j = 0; j < 8; j++) {
343       if (board[i][j].state != GEMSTATE_FADING) {
344         result = result | [self testForThreeAt:i:j];
345       }
346     }
347   }
348   
349   // CASCADE BONUS check for reset
350   if (!result) {
351     cascade = 1;
352   }
353   
354   return result;
357 - (void)showAllBoardMoves
359     // test every possible move
360     int i,j;
362     // horizontal moves
363     for (j = 0; j < 8; j++)
364         for (i = 0; i < 7; i++)
365         {
366             [self swap:i:j and:i+1:j];
367             [self finalTestForThreeAt:i:j];
368             [self finalTestForThreeAt:i+1:j];
369             [self unswap];
370         }
372     // vertical moves
373     for (i = 0; i < 8; i++)
374         for (j = 0; j < 7; j++)
375         {
376             [self swap:i:j and:i:j+1];
377             [self finalTestForThreeAt:i:j];
378             [self finalTestForThreeAt:i:j+1];
379             [self unswap];
380         }
382     // over the entire board, set the animationtime for the marked gems higher
383     for (i = 0; i < 8; i++)
384         for (j = 0; j < 8; j++)
385         {
386             if (board[i][j].state == GEMSTATE_FADING)
387             {
388                 [board[i][j] erupt];
389                 // !!!: remove: [board[i][j] setAnimationCounter:1];
390               board[i][j].animationCounter = 1;
391             }
392             else
393                 [board[i][j] erupt];
394         }
396     
399 - (BOOL)boardHasMoves
401   // test every possible move
402   BOOL  result = NO;
403   
404   // horizontal moves
405   for (int j = 0; j < 8; j++) {
406     for (int i = 0; i < 7; i++) {
407       [self swap:i:j and:i+1:j];
408       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
409       [self unswap];
410       if (result) {
411         hintx = i;
412         hinty = j;
413         return result;
414       }
415     }
416   }
417   
418   // vertical moves
419   for (int i = 0; i < 8; i++) {
420     for (int j = 0; j < 7; j++) {
421       [self swap:i:j and:i:j+1];
422       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
423       [self unswap];
424       if (result) {
425         hintx = i;
426         hinty = j;
427         return result;
428       }
429     }
430   }
431   
432   return NO;
435 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
437     int i,j,fades, y;
438     for (i = 0; i < 8; i++)
439     {
440         Gem     *column[8];
441         fades = 0;
442         y = 0;
443         // let non-faded gems fall into place
444         for (j = 0; j < 8; j++)
445         {
446             if (board[i][j].state != GEMSTATE_FADING)
447             {
448                 column[y] = board[i][j];
449                 if ([board[i][j] positionOnScreen].y > y*48)
450                     [board[i][j] fall];
451                 y++;
452             }
453             else
454                 fades++;
455         }
456         // transfer faded gems to top of column
457         for (j = 0; j < 8; j++)
458         {
459             if (board[i][j].state == GEMSTATE_FADING)
460             {
461                 // randomly reassign
462                 int r = (rand() % 7);
463                 [board[i][j]    setGemType:r];
464               board[i][j].image = [imageArray objectAtIndex:r];
466                 column[y] = board[i][j];
467                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
468                 [board[i][j] fall];
469                 y++;
470                 _fadedGems++;
471                 fades--;
472             }
473         }
474         // OK, shuffling all done - reorganise column
475         for (j = 0; j < 8; j++)
476         {
477             board[i][j] = column[j];
478             [board[i][j] setPositionOnBoard:i:j];
479         }
480     }
483 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
485     int i,j,fades, y;
486     for (i = 0; i < 8; i++)
487     {
488         Gem     *column[8];
489         fades = 0;
490         y = 0;
491         // let non-faded gems fall into place
492         for (j = 0; j < 8; j++)
493         {
494             if (board[i][j].state != GEMSTATE_FADING)
495             {
496                 column[y] = board[i][j];
497                 if ([board[i][j] positionOnScreen].y > y*48)
498                     [board[i][j] fall];
499                 y++;
500             }
501             else
502                 fades++;
503         }
504         // transfer faded gems to top of column
505         for (j = 0; j < 8; j++)
506         {
507             if (board[i][j].state == GEMSTATE_FADING)
508             {
509                 // randomly reassign
510                 int r = (rand() % 7);
511                 [board[i][j]    setGemType:r];
512                 [board[i][j]    setSprite:[spriteArray objectAtIndex:r]];
514                 column[y] = board[i][j];
515                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
516                 [board[i][j] fall];
517                 y++;
518                 _fadedGems++;
519                 fades--;
520             }
521         }
522         // OK, shuffling all done - reorganise column
523         for (j = 0; j < 8; j++)
524         {
525             board[i][j] = column[j];
526             [board[i][j] setPositionOnBoard:i:j];
527         }
528     }
531 - (void)shake
533   for (int i = 0; i < 8; i++) {
534     for (int j = 0; j < 8; j++) {
535       [board[i][j] shake];
536     }
537   }
540 - (void)erupt
542   if (!muted) {
543     [[NSSound soundNamed:@"yes"] play];
544   }
545   
546   for (int i = 0; i < 8; i++) {
547     for (int j = 0; j < 8; j++) {
548       [board[i][j] erupt];
549     }
550   }
553 - (void) explodeGameOver
555     //int i,j;
556     if (!muted) [[NSSound soundNamed:@"explosion"] play];
557     /*--
558     for (i = 0; i < 8; i++)
559         for (j = 0; j < 8; j++)
560             [board[i][j] erupt];
561     --*/
562     [self showAllBoardMoves];   // does a delayed eruption
565 - (void)wholeNewGameWithImagesFrom:(NSArray *)imageArray
567   srand([[NSDate date] timeIntervalSince1970]); // seed by time
568   
569   for (int i = 0; i < 8; i++) {
570     for (int j = 0; j < 8; j++) {
571       //int r = (rand() % 3)*2+((i+j)%2);
572       int r = [self randomGemTypeAt:i:j];
573       
574       [board[i][j] setGemType:r];
575       board[i][j].image = [imageArray objectAtIndex:r];
576       [board[i][j] setPositionOnBoard:i:j];
577       [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
578       [board[i][j] fall];
579     }
580   }
581   
582   _score = 0;
583   _fadedGems = 0;
584   _bonusMultiplier = 1;
587 - (void)wholeNewGameWithSpritesFrom:(NSArray *)spriteArray
589   srand([[NSDate date] timeIntervalSince1970]); // seed by time
590   
591   for (int i = 0; i < 8; i++) {
592     for (int j = 0; j < 8; j++) {
593       //int r = (rand() % 3)*2+((i+j)%2);
594       int r = [self randomGemTypeAt:i:j];
596       Gem *gem = board[i][j];
597       [gem setGemType:r];
598       [gem setSprite:[spriteArray objectAtIndex:r]];
599       [gem setPositionOnBoard:i:j];
600       [gem setPositionOnScreen:i*48:(15-j)*48];
601       [gem fall];
602     }
603   }
604   
605   _score = 0;
606   _fadedGems = 0;
607   _bonusMultiplier = 1;
610 - (NSPoint)hintPoint {
611   return NSMakePoint(hintx * 48, hinty * 48);
614 - (float)collectFadedGems
616     float result = (float)_fadedGems;
617     _fadedGems = 0;
618     return result;
621 - (void)increaseBonusMultiplier {
622   _bonusMultiplier++;
625 @end