change default gems and title
[exterlulz-kokogems.git] / src / Game.m
blobdde807aa26225efaf08d7de99b10ac55e17c01aa
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...
27 #import "ScoreBubble.h"
30 @implementation Game
32 - (id) init
34     int i,j;
35     self = [super init];
36     gemsFaded = 0;
37     for (i = 0; i < 8; i++)
38         for (j = 0; j < 8; j++)
39             board[i][j] = [[Gem alloc] init];
40     // MW
41     scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
42     //
43     return self;
46 - (id) initWithImagesFrom:(NSArray *) imageArray
48     int i,j;
49     self = [super init];
50     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
51     for (i = 0; i < 8; i++)
52         for (j = 0; j < 8; j++)
53         {
54             int r = [self randomGemTypeAt:i:j];
55             board[i][j] = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
56             [board[i][j] setPositionOnBoard:i:j];
57             [board[i][j] setPositionOnScreen:i*48:j*48];
58             [board[i][j] shake];
59         }
60             // MW...
61             scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
62     //
63     score = 0;
64     gemsFaded = 0;
65     bonusMultiplier = 1;
66     return self;
69 - (id) initWithSpritesFrom:(NSArray *) spriteArray
71     int i,j;
72     self = [super init];
73     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
74     for (i = 0; i < 8; i++)
75         for (j = 0; j < 8; j++)
76         {
77             //int r = (rand() % 3)*2+((i+j)%2);
78             int r = [self randomGemTypeAt:i:j];
79             board[i][j] = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
80             [board[i][j] setPositionOnBoard:i:j];
81             [board[i][j] setPositionOnScreen:i*48:j*48];
82             [board[i][j] shake];
83         }
84             // MW...
85             scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
86     //
87     score = 0;
88     gemsFaded = 0;
89     bonusMultiplier = 1;
90     return self;
93 - (void) dealloc
95     int i,j;
96     for (i = 0; i < 8; i++)
97         for (j = 0; j < 8; j++)
98             [board[i][j] release];
99     // MW...
100     [scoreBubbles release];
101     //
102     [super dealloc];
105 - (void) setImagesFrom:(NSArray *) imageArray
107     int i,j;
108     for (i = 0; i < 8; i++)
109         for (j = 0; j < 8; j++)
110             [board[i][j] setImage:[imageArray objectAtIndex:[board[i][j] gemType]]];
113 - (void) setSpritesFrom:(NSArray *) spriteArray
115     int i,j;
116     for (i = 0; i < 8; i++)
117         for (j = 0; j < 8; j++)
118             [board[i][j] setSprite:[spriteArray objectAtIndex:[board[i][j] gemType]]];
121 - (int) randomGemTypeAt:(int)x :(int)y
123     int c = (x+y) % 2;
124     int r = rand() % 7;
125     if (c)
126         return (r & 6); // even
127     if (r == 6)
128         return 1;       // catch returning 7
129     return (r | 1);     // odd
132 - (Gem *) gemAt:(int)x :(int)y
134     return board[x][y];
138 // MW...
140 - (NSMutableArray *)scoreBubbles
142     return scoreBubbles;
145 ////
147 - (void) setMuted:(BOOL)value
149     int i,j;
150     muted = value;
151     if (muted)
152         for (i = 0; i < 8; i++)
153             for (j = 0; j < 8; j++)
154                 [board[i][j] setSoundsTink:NULL Sploink:NULL];
155     else
156         for (i = 0; i < 8; i++)
157             for (j = 0; j < 8; j++)
158                 [board[i][j] setSoundsTink:[NSSound soundNamed:@"tink"] Sploink:[NSSound soundNamed:@"sploink"]];
159 }    
161 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
163     Gem *swap = board[x1][y1];
164     board[x1][y1] = board[x2][y2];
165     [board[x1][y1] setPositionOnBoard:x1:y1];
166     board[x2][y2] = swap;
167     [board[x2][y2] setPositionOnBoard:x2:y2];
168     sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
171 - (void) unswap
173     [self swap:sx1:sy1 and:sx2:sy2];
176 - (BOOL) testForThreeAt:(int) x :(int) y
178     int tx,ty,cx,cy;
179     int bonus, linebonus, scorePerGem;
180     float scorebubble_x = -1.0;
181     float scorebubble_y = -1.0;
182     BOOL result = NO;
183     int gemtype = [board[x][y] gemType];
184     tx = x; ty = y; cx = x; cy = y;
185     bonus = 0;
186     if ([board[x][y] state] == GEMSTATE_FADING)         result = YES;
187     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
188     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
189     if ((cx-tx) >= 2)
190     {
191         // horizontal line
192         int i,j;
193         linebonus= 0;
194         scorePerGem = (cx-tx)*5;
195         for (i = tx; i <= cx; i++)
196         {
197             linebonus+= scorePerGem;
198             [board[i][y] fade];
199             for (j=7; j>y; j--) {
200                 if ([board[i][j] state]!= GEMSTATE_FADING) {
201                     [board[i][j] shiver];       //      MW prepare to fall
202                 }
203             }
204         }
205         // to center scorebubble ...
206         scorebubble_x = tx + (cx-tx)/2.0;
207         scorebubble_y = y;
208         //
209         bonus += linebonus;
210         result = YES;
211     }
212     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
213     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
214     if ((cy-ty) >= 2)
215     {
216         // vertical line
217         int i,j;
218         linebonus= 0;
219         scorePerGem = (cy-ty)*5;
220         for (i = ty; i <= cy; i++)
221         {
222             linebonus += scorePerGem;
223             [board[x][i] fade];
224         }
225         for (j=7; j>cy; j--) {
226             if ([board[x][j] state]!= GEMSTATE_FADING) {
227                 [board[x][j] shiver];           //      MW prepare to fall
228             }
229         }
230         // to center scorebubble ...
231         if (scorebubble_x < 0)  // only if one hasn't been placed already ! (for T and L shapes)
232         {
233             scorebubble_x = x;
234             scorebubble_y = ty + (cy-ty)/2.0;
235         }
236         else                    // select the original gem position
237         {
238             scorebubble_x = x;
239             scorebubble_y = y;
240         }
241         //
242         bonus += linebonus;
243         result = YES;
244     }
245     // CASCADE BONUS
246     if (cascade>=1)
247         bonus *= cascade;
248     //
249     // MW's scorebubble
250     //
251     if (bonus>0)
252         [scoreBubbles addObject:[ScoreBubble scoreWithValue:bonus*bonusMultiplier
253                                                          At:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
254                                                    Duration:40]];
255     //
256     score += bonus * bonusMultiplier;
257     return result;
260 - (BOOL) finalTestForThreeAt:(int) x :(int) y
262     int tx,ty,cx,cy;
263     BOOL result = NO;
264     int gemtype = [board[x][y] gemType];
265     tx = x; ty = y; cx = x; cy = y;
267     if ([board[x][y] state] == GEMSTATE_FADING) return YES;
268         
269     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
270     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
271     if ((cx-tx) >= 2)
272     {
273         // horizontal line
274         int i;
275         for (i = tx; i <= cx; i++)
276             [board[i][y] fade];
277         result = YES;
278     }
279     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
280     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
281     if ((cy-ty) >= 2)
282     {
283         // vertical line
284         int i;
285         for (i = ty; i <= cy; i++)
286             [board[x][i] fade];
287         result = YES;
288     }
289     return result;
292 - (BOOL) checkForThreeAt:(int) x :(int) y
294     int tx,ty,cx,cy;
295     int gemtype = [board[x][y] gemType];
296     tx = x; ty = y; cx = x; cy = y;
297     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
298     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
299     if ((cx-tx) >= 2)
300         return YES;
301     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
302     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
303     if ((cy-ty) >= 2)
304         return YES;
305     return NO;
308 - (BOOL) checkBoardForThrees
310     int i,j;
311     BOOL result = NO;
312     // CASCADE BONUS increase
313     cascade++;
314     //
315     for (i = 0; i < 8; i++)
316         for (j = 0; j < 8; j++)
317         if ([board[i][j] state]!=GEMSTATE_FADING)
318             result = result | [self testForThreeAt:i:j];
319     // CASCADE BONUS check for reset
320     if (!result) cascade = 1;
321     return result;
325 - (void) showAllBoardMoves
327     // test every possible move
328     int i,j;
330     // horizontal moves
331     for (j = 0; j < 8; j++)
332         for (i = 0; i < 7; i++)
333         {
334             [self swap:i:j and:i+1:j];
335             [self finalTestForThreeAt:i:j];
336             [self finalTestForThreeAt:i+1:j];
337             [self unswap];
338         }
340     // vertical moves
341     for (i = 0; i < 8; i++)
342         for (j = 0; j < 7; j++)
343         {
344             [self swap:i:j and:i:j+1];
345             [self finalTestForThreeAt:i:j];
346             [self finalTestForThreeAt:i:j+1];
347             [self unswap];
348         }
350     // over the entire board, set the animationtime for the marked gems higher
351     for (i = 0; i < 8; i++)
352         for (j = 0; j < 8; j++)
353         {
354             if ([board[i][j] state] == GEMSTATE_FADING)
355             {
356                 [board[i][j] erupt];
357                 [board[i][j] setAnimationCounter:1];
358             }
359             else
360                 [board[i][j] erupt];
361         }
363     
366 - (BOOL) boardHasMoves
368     // test every possible move
369     int i,j;
370     BOOL        result = NO;
371     // horizontal moves
372     for (j = 0; j < 8; j++)
373         for (i = 0; i < 7; i++)
374         {
375             [self swap:i:j and:i+1:j];
376             result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
377             [self unswap];
378             if (result)
379             {
380                 hintx = i;
381                 hinty = j;
382                 return result;
383             }
384         }
386             // vertical moves
387             for (i = 0; i < 8; i++)
388                 for (j = 0; j < 7; j++)
389                 {
390                     [self swap:i:j and:i:j+1];
391                     result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
392                     [self unswap];
393                     if (result)
394                     {
395                         hintx = i;
396                         hinty = j;
397                         return result;
398                     }
399                 }
400                     return NO;
403 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
405     int i,j,fades, y;
406     for (i = 0; i < 8; i++)
407     {
408         Gem     *column[8];
409         fades = 0;
410         y = 0;
411         // let non-faded gems fall into place
412         for (j = 0; j < 8; j++)
413         {
414             if ([board[i][j] state] != GEMSTATE_FADING)
415             {
416                 column[y] = board[i][j];
417                 if ([board[i][j] positionOnScreen].y > y*48)
418                     [board[i][j] fall];
419                 y++;
420             }
421             else
422                 fades++;
423         }
424         // transfer faded gems to top of column
425         for (j = 0; j < 8; j++)
426         {
427             if ([board[i][j] state] == GEMSTATE_FADING)
428             {
429                 // randomly reassign
430                 int r = (rand() % 7);
431                 [board[i][j]    setGemType:r];
432                 [board[i][j]    setImage:[imageArray objectAtIndex:r]];
434                 column[y] = board[i][j];
435                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
436                 [board[i][j] fall];
437                 y++;
438                 gemsFaded++;
439                 fades--;
440             }
441         }
442         // OK, shuffling all done - reorganise column
443         for (j = 0; j < 8; j++)
444         {
445             board[i][j] = column[j];
446             [board[i][j] setPositionOnBoard:i:j];
447         }
448     }
451 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
453     int i,j,fades, y;
454     for (i = 0; i < 8; i++)
455     {
456         Gem     *column[8];
457         fades = 0;
458         y = 0;
459         // let non-faded gems fall into place
460         for (j = 0; j < 8; j++)
461         {
462             if ([board[i][j] state] != GEMSTATE_FADING)
463             {
464                 column[y] = board[i][j];
465                 if ([board[i][j] positionOnScreen].y > y*48)
466                     [board[i][j] fall];
467                 y++;
468             }
469             else
470                 fades++;
471         }
472         // transfer faded gems to top of column
473         for (j = 0; j < 8; j++)
474         {
475             if ([board[i][j] state] == GEMSTATE_FADING)
476             {
477                 // randomly reassign
478                 int r = (rand() % 7);
479                 [board[i][j]    setGemType:r];
480                 [board[i][j]    setSprite:[spriteArray objectAtIndex:r]];
482                 column[y] = board[i][j];
483                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
484                 [board[i][j] fall];
485                 y++;
486                 gemsFaded++;
487                 fades--;
488             }
489         }
490         // OK, shuffling all done - reorganise column
491         for (j = 0; j < 8; j++)
492         {
493             board[i][j] = column[j];
494             [board[i][j] setPositionOnBoard:i:j];
495         }
496     }
499 - (void) shake
501     int i,j;
502     for (i = 0; i < 8; i++)
503         for (j = 0; j < 8; j++)
504             [board[i][j] shake];
507 - (void) erupt
509     int i,j;
510     if (!muted) [[NSSound soundNamed:@"yes"] play];
511     for (i = 0; i < 8; i++)
512         for (j = 0; j < 8; j++)
513             [board[i][j] erupt];
516 - (void) explodeGameOver
518     //int i,j;
519     if (!muted) [[NSSound soundNamed:@"explosion"] play];
520     /*--
521     for (i = 0; i < 8; i++)
522         for (j = 0; j < 8; j++)
523             [board[i][j] erupt];
524     --*/
525     [self showAllBoardMoves];   // does a delayed eruption
528 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
530     int i,j;
531     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
532     for (i = 0; i < 8; i++)
533         for (j = 0; j < 8; j++)
534         {
535             //int r = (rand() % 3)*2+((i+j)%2);
536             int r = [self randomGemTypeAt:i:j];
537             [board[i][j] setGemType:r];
538             [board[i][j] setImage:[imageArray objectAtIndex:r]];
539             [board[i][j] setPositionOnBoard:i:j];
540             [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
541             [board[i][j] fall];
542         }
543             score = 0;
544     gemsFaded = 0;
545     bonusMultiplier = 1;
548 - (void) wholeNewGameWithSpritesFrom:(NSArray *) spriteArray
550     int i,j;
551     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
552     for (i = 0; i < 8; i++)
553         for (j = 0; j < 8; j++)
554         {
555             //int r = (rand() % 3)*2+((i+j)%2);
556             int r = [self randomGemTypeAt:i:j];
557             [board[i][j] setGemType:r];
558             [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
559             [board[i][j] setPositionOnBoard:i:j];
560             [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
561             [board[i][j] fall];
562         }
563             score = 0;
564     gemsFaded = 0;
565     bonusMultiplier = 1;
568 - (NSPoint)     hintPoint
570     return NSMakePoint(hintx*48,hinty*48);
573 - (float) collectGemsFaded
575     float result = (float)gemsFaded;
576     gemsFaded = 0;
577     return result;
580 - (int) score
582     return score;
585 - (int) bonusMultiplier
587     return bonusMultiplier;
590 - (void) increaseBonusMultiplier
592     bonusMultiplier++;
595 @end