change key shortcut
[exterlulz-kokogems.git] / src / Game.m
bloba8684df65c153b1ac8924665f0afe7ee640dd1e8
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;
31 @synthesize _bonusMultiplier;
33 - (id)init
35   self = [super init];
36   if (self != nil) {
37     gemsFaded = 0;
38     for (int i = 0; i < 8; i++) {
39       for (int j = 0; j < 8; j++) {
40         board[i][j] = [[Gem alloc] init];
41       }
42     }
43     
44     // MW
45     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
46   }
47   
48   return self;
51 - (id)initWithImagesFrom:(NSArray *)imageArray
53   self = [super init];
54   if (self != nil) {
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];
60         board[i][j] = gem;
61         [gem setPositionOnBoard:i:j];
62         [gem setPositionOnScreen:i*48:j*48];
63         [gem shake];
64       }
65     }
66     
67     // MW...
68     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
69     
70     _score = 0;
71     gemsFaded = 0;
72     _bonusMultiplier = 1;
73   }
74   
75   return self;
78 - (id)initWithSpritesFrom:(NSArray *)spriteArray
80   self = [super init];
81   if (self != nil) {
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];
90         [board[i][j] shake];
91       }
92     }
93     
94     // MW...
95     scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
96     
97     _score = 0;
98     gemsFaded = 0;
99     _bonusMultiplier = 1;
100   }
101   
102   return self;
105 - (void)dealloc
107   for (int i = 0; i < 8; i++) {
108     for (int j = 0; j < 8; j++) {
109       [board[i][j] release];
110     }
111   }
112   
113   // MW...
114   [scoreBubbles release];
115   
116   [super dealloc];
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]];
125     }
126   }
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]]];
135     }
136   }
139 - (int)randomGemTypeAt:(int)x :(int)y
141     int c = (x+y) % 2;
142     int r = rand() % 7;
143     if (c)
144         return (r & 6); // even
145     if (r == 6)
146         return 1;       // catch returning 7
147     return (r | 1);     // odd
150 - (Gem *)gemAt:(int)x :(int)y {
151   return board[x][y];
154 // MW...
155 - (NSMutableArray *)scoreBubbles {
156     return scoreBubbles;
159 - (void)setMuted:(BOOL)value
161   muted = value;
162   if (muted) {
163     for (int i = 0; i < 8; i++) {
164       for (int j = 0; j < 8; j++) {
165         [board[i][j] setSoundsTink:NULL Sploink:NULL];
166       }
167     }
168   }
169   else {
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"]];
173       }
174     }
175   }
176 }    
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;
188 - (void)unswap {
189   [self swap:sx1:sy1 and:sx2:sy2];
192 - (BOOL) testForThreeAt:(int) x :(int) y
194     int tx,ty,cx,cy;
195     int bonus, linebonus, scorePerGem;
196     float scorebubble_x = -1.0;
197     float scorebubble_y = -1.0;
198     BOOL result = NO;
199     int gemtype = [board[x][y] gemType];
200     tx = x; ty = y; cx = x; cy = y;
201     bonus = 0;
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;
205     if ((cx-tx) >= 2)
206     {
207         // horizontal line
208         int i,j;
209         linebonus= 0;
210         scorePerGem = (cx-tx)*5;
211         for (i = tx; i <= cx; i++)
212         {
213             linebonus+= scorePerGem;
214             [board[i][y] fade];
215             for (j=7; j>y; j--) {
216                 if (board[i][j]._state != GEMSTATE_FADING) {
217                     [board[i][j] shiver];       //      MW prepare to fall
218                 }
219             }
220         }
221         // to center scorebubble ...
222         scorebubble_x = tx + (cx-tx)/2.0;
223         scorebubble_y = y;
224         
225         bonus += linebonus;
226         result = YES;
227     }
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;
230     if ((cy-ty) >= 2)
231     {
232         // vertical line
233         int i,j;
234         linebonus= 0;
235         scorePerGem = (cy-ty)*5;
236         for (i = ty; i <= cy; i++)
237         {
238             linebonus += scorePerGem;
239             [board[x][i] fade];
240         }
241         for (j=7; j>cy; j--) {
242             if (board[x][j]._state != GEMSTATE_FADING) {
243                 [board[x][j] shiver];           //      MW prepare to fall
244             }
245         }
246         // to center scorebubble ...
247         if (scorebubble_x < 0)  // only if one hasn't been placed already ! (for T and L shapes)
248         {
249             scorebubble_x = x;
250             scorebubble_y = ty + (cy-ty)/2.0;
251         }
252         else                    // select the original gem position
253         {
254             scorebubble_x = x;
255             scorebubble_y = y;
256         }
257         //
258         bonus += linebonus;
259         result = YES;
260     }
262   // CASCADE BONUS
263   if (cascade>=1) {
264         bonus *= cascade;
265   }
266   
267   // MW's scorebubble
268     if (bonus>0)
269         [scoreBubbles addObject:[ScoreBubble scoreWithValue:(bonus * _bonusMultiplier)
270                                                          at:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
271                                                    duration:40]];
273   _score += bonus * _bonusMultiplier;
274     return result;
277 - (BOOL) finalTestForThreeAt:(int) x :(int) y
279     int tx,ty,cx,cy;
280     BOOL result = NO;
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;
285         
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;
288     if ((cx-tx) >= 2)
289     {
290         // horizontal line
291         int i;
292         for (i = tx; i <= cx; i++)
293             [board[i][y] fade];
294         result = YES;
295     }
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;
298     if ((cy-ty) >= 2)
299     {
300         // vertical line
301         int i;
302         for (i = ty; i <= cy; i++)
303             [board[x][i] fade];
304         result = YES;
305     }
306     return result;
309 - (BOOL) checkForThreeAt:(int) x :(int) y
311     int tx,ty,cx,cy;
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;
316     if ((cx-tx) >= 2)
317         return YES;
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;
320     if ((cy-ty) >= 2)
321         return YES;
322     return NO;
325 - (BOOL)checkBoardForThrees
327   BOOL result = NO;
328   
329   // CASCADE BONUS increase
330   cascade++;
331   
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];
336       }
337     }
338   }
339   
340   // CASCADE BONUS check for reset
341   if (!result) {
342     cascade = 1;
343   }
344   
345   return result;
348 - (void)showAllBoardMoves
350     // test every possible move
351     int i,j;
353     // horizontal moves
354     for (j = 0; j < 8; j++)
355         for (i = 0; i < 7; i++)
356         {
357             [self swap:i:j and:i+1:j];
358             [self finalTestForThreeAt:i:j];
359             [self finalTestForThreeAt:i+1:j];
360             [self unswap];
361         }
363     // vertical moves
364     for (i = 0; i < 8; i++)
365         for (j = 0; j < 7; j++)
366         {
367             [self swap:i:j and:i:j+1];
368             [self finalTestForThreeAt:i:j];
369             [self finalTestForThreeAt:i:j+1];
370             [self unswap];
371         }
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++)
376         {
377             if (board[i][j]._state == GEMSTATE_FADING)
378             {
379                 [board[i][j] erupt];
380                 // !!!: remove: [board[i][j] setAnimationCounter:1];
381               board[i][j]._animationCounter = 1;
382             }
383             else
384                 [board[i][j] erupt];
385         }
387     
390 - (BOOL)boardHasMoves
392   // test every possible move
393   BOOL  result = NO;
394   
395   // horizontal moves
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];
400       [self unswap];
401       if (result) {
402         hintx = i;
403         hinty = j;
404         return result;
405       }
406     }
407   }
408   
409   // vertical moves
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];
414       [self unswap];
415       if (result) {
416         hintx = i;
417         hinty = j;
418         return result;
419       }
420     }
421   }
422   
423   return NO;
426 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
428     int i,j,fades, y;
429     for (i = 0; i < 8; i++)
430     {
431         Gem     *column[8];
432         fades = 0;
433         y = 0;
434         // let non-faded gems fall into place
435         for (j = 0; j < 8; j++)
436         {
437             if (board[i][j]._state != GEMSTATE_FADING)
438             {
439                 column[y] = board[i][j];
440                 if ([board[i][j] positionOnScreen].y > y*48)
441                     [board[i][j] fall];
442                 y++;
443             }
444             else
445                 fades++;
446         }
447         // transfer faded gems to top of column
448         for (j = 0; j < 8; j++)
449         {
450             if (board[i][j]._state == GEMSTATE_FADING)
451             {
452                 // randomly reassign
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];
459                 [board[i][j] fall];
460                 y++;
461                 gemsFaded++;
462                 fades--;
463             }
464         }
465         // OK, shuffling all done - reorganise column
466         for (j = 0; j < 8; j++)
467         {
468             board[i][j] = column[j];
469             [board[i][j] setPositionOnBoard:i:j];
470         }
471     }
474 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
476     int i,j,fades, y;
477     for (i = 0; i < 8; i++)
478     {
479         Gem     *column[8];
480         fades = 0;
481         y = 0;
482         // let non-faded gems fall into place
483         for (j = 0; j < 8; j++)
484         {
485             if (board[i][j]._state != GEMSTATE_FADING)
486             {
487                 column[y] = board[i][j];
488                 if ([board[i][j] positionOnScreen].y > y*48)
489                     [board[i][j] fall];
490                 y++;
491             }
492             else
493                 fades++;
494         }
495         // transfer faded gems to top of column
496         for (j = 0; j < 8; j++)
497         {
498             if (board[i][j]._state == GEMSTATE_FADING)
499             {
500                 // randomly reassign
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];
507                 [board[i][j] fall];
508                 y++;
509                 gemsFaded++;
510                 fades--;
511             }
512         }
513         // OK, shuffling all done - reorganise column
514         for (j = 0; j < 8; j++)
515         {
516             board[i][j] = column[j];
517             [board[i][j] setPositionOnBoard:i:j];
518         }
519     }
522 - (void)shake
524   for (int i = 0; i < 8; i++) {
525     for (int j = 0; j < 8; j++) {
526       [board[i][j] shake];
527     }
528   }
531 - (void)erupt
533   if (!muted) {
534     [[NSSound soundNamed:@"yes"] play];
535   }
536   
537   for (int i = 0; i < 8; i++) {
538     for (int j = 0; j < 8; j++) {
539       [board[i][j] erupt];
540     }
541   }
544 - (void) explodeGameOver
546     //int i,j;
547     if (!muted) [[NSSound soundNamed:@"explosion"] play];
548     /*--
549     for (i = 0; i < 8; i++)
550         for (j = 0; j < 8; j++)
551             [board[i][j] erupt];
552     --*/
553     [self showAllBoardMoves];   // does a delayed eruption
556 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
558     int i,j;
559     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
560     for (i = 0; i < 8; i++)
561         for (j = 0; j < 8; j++)
562         {
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];
569             [board[i][j] fall];
570         }
571             _score = 0;
572     gemsFaded = 0;
573     _bonusMultiplier = 1;
576 - (void)wholeNewGameWithSpritesFrom:(NSArray *)spriteArray
578   srand([[NSDate date] timeIntervalSince1970]); // seed by time
579   
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];
586       [gem setGemType:r];
587       [gem setSprite:[spriteArray objectAtIndex:r]];
588       [gem setPositionOnBoard:i:j];
589       [gem setPositionOnScreen:i*48:(15-j)*48];
590       [gem fall];
591     }
592   }
593   
594   _score = 0;
595   gemsFaded = 0;
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;
607     gemsFaded = 0;
608     return result;
611 - (void)increaseBonusMultiplier {
612   _bonusMultiplier++;
615 @end