first commit
[exterlulz-kokogems.git] / src / Game.m
blobf38ed9aca378875041741bfab7eab2c060363f8b
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 }    
162 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
164     Gem *swap = board[x1][y1];
165     board[x1][y1] = board[x2][y2];
166     [board[x1][y1] setPositionOnBoard:x1:y1];
167     board[x2][y2] = swap;
168     [board[x2][y2] setPositionOnBoard:x2:y2];
169     sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
172 - (void) unswap
174     [self swap:sx1:sy1 and:sx2:sy2];
177 - (BOOL) testForThreeAt:(int) x :(int) y
179     int tx,ty,cx,cy;
180     int bonus, linebonus, scorePerGem;
181     float scorebubble_x = -1.0;
182     float scorebubble_y = -1.0;
183     BOOL result = NO;
184     int gemtype = [board[x][y] gemType];
185     tx = x; ty = y; cx = x; cy = y;
186     bonus = 0;
187     if ([board[x][y] state] == GEMSTATE_FADING)         result = YES;
188     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
189     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
190     if ((cx-tx) >= 2)
191     {
192         // horizontal line
193         int i,j;
194         linebonus= 0;
195         scorePerGem = (cx-tx)*5;
196         for (i = tx; i <= cx; i++)
197         {
198             linebonus+= scorePerGem;
199             [board[i][y] fade];
200             for (j=7; j>y; j--) {
201                 if ([board[i][j] state]!= GEMSTATE_FADING) {
202                     [board[i][j] shiver];       //      MW prepare to fall
203                 }
204             }
205         }
206         // to center scorebubble ...
207         scorebubble_x = tx + (cx-tx)/2.0;
208         scorebubble_y = y;
209         //
210         bonus += linebonus;
211         result = YES;
212     }
213     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
214     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
215     if ((cy-ty) >= 2)
216     {
217         // vertical line
218         int i,j;
219         linebonus= 0;
220         scorePerGem = (cy-ty)*5;
221         for (i = ty; i <= cy; i++)
222         {
223             linebonus += scorePerGem;
224             [board[x][i] fade];
225         }
226         for (j=7; j>cy; j--) {
227             if ([board[x][j] state]!= GEMSTATE_FADING) {
228                 [board[x][j] shiver];           //      MW prepare to fall
229             }
230         }
231         // to center scorebubble ...
232         if (scorebubble_x < 0)  // only if one hasn't been placed already ! (for T and L shapes)
233         {
234             scorebubble_x = x;
235             scorebubble_y = ty + (cy-ty)/2.0;
236         }
237         else                    // select the original gem position
238         {
239             scorebubble_x = x;
240             scorebubble_y = y;
241         }
242         //
243         bonus += linebonus;
244         result = YES;
245     }
246     // CASCADE BONUS
247     if (cascade>=1)
248         bonus *= cascade;
249     //
250     // MW's scorebubble
251     //
252     if (bonus>0)
253         [scoreBubbles addObject:[ScoreBubble scoreWithValue:bonus*bonusMultiplier
254                                                          At:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
255                                                    Duration:40]];
256     //
257     score += bonus * bonusMultiplier;
258     return result;
261 - (BOOL) finalTestForThreeAt:(int) x :(int) y
263     int tx,ty,cx,cy;
264     BOOL result = NO;
265     int gemtype = [board[x][y] gemType];
266     tx = x; ty = y; cx = x; cy = y;
268     if ([board[x][y] state] == GEMSTATE_FADING) return YES;
269         
270     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
271     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
272     if ((cx-tx) >= 2)
273     {
274         // horizontal line
275         int i;
276         for (i = tx; i <= cx; i++)
277             [board[i][y] fade];
278         result = YES;
279     }
280     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
281     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
282     if ((cy-ty) >= 2)
283     {
284         // vertical line
285         int i;
286         for (i = ty; i <= cy; i++)
287             [board[x][i] fade];
288         result = YES;
289     }
290     return result;
293 - (BOOL) checkForThreeAt:(int) x :(int) y
295     int tx,ty,cx,cy;
296     int gemtype = [board[x][y] gemType];
297     tx = x; ty = y; cx = x; cy = y;
298     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
299     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
300     if ((cx-tx) >= 2)
301         return YES;
302     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
303     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
304     if ((cy-ty) >= 2)
305         return YES;
306     return NO;
309 - (BOOL) checkBoardForThrees
311     int i,j;
312     BOOL result = NO;
313     // CASCADE BONUS increase
314     cascade++;
315     //
316     for (i = 0; i < 8; i++)
317         for (j = 0; j < 8; j++)
318         if ([board[i][j] state]!=GEMSTATE_FADING)
319             result = result | [self testForThreeAt:i:j];
320     // CASCADE BONUS check for reset
321     if (!result) cascade = 1;
322     return result;
326 - (void) showAllBoardMoves
328     // test every possible move
329     int i,j;
331     // horizontal moves
332     for (j = 0; j < 8; j++)
333         for (i = 0; i < 7; i++)
334         {
335             [self swap:i:j and:i+1:j];
336             [self finalTestForThreeAt:i:j];
337             [self finalTestForThreeAt:i+1:j];
338             [self unswap];
339         }
341     // vertical moves
342     for (i = 0; i < 8; i++)
343         for (j = 0; j < 7; j++)
344         {
345             [self swap:i:j and:i:j+1];
346             [self finalTestForThreeAt:i:j];
347             [self finalTestForThreeAt:i:j+1];
348             [self unswap];
349         }
351     // over the entire board, set the animationtime for the marked gems higher
352     for (i = 0; i < 8; i++)
353         for (j = 0; j < 8; j++)
354         {
355             if ([board[i][j] state] == GEMSTATE_FADING)
356             {
357                 [board[i][j] erupt];
358                 [board[i][j] setAnimationCounter:1];
359             }
360             else
361                 [board[i][j] erupt];
362         }
364     
367 - (BOOL) boardHasMoves
369     // test every possible move
370     int i,j;
371     BOOL        result = NO;
372     // horizontal moves
373     for (j = 0; j < 8; j++)
374         for (i = 0; i < 7; i++)
375         {
376             [self swap:i:j and:i+1:j];
377             result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
378             [self unswap];
379             if (result)
380             {
381                 hintx = i;
382                 hinty = j;
383                 return result;
384             }
385         }
387             // vertical moves
388             for (i = 0; i < 8; i++)
389                 for (j = 0; j < 7; j++)
390                 {
391                     [self swap:i:j and:i:j+1];
392                     result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
393                     [self unswap];
394                     if (result)
395                     {
396                         hintx = i;
397                         hinty = j;
398                         return result;
399                     }
400                 }
401                     return NO;
404 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
406     int i,j,fades, y;
407     for (i = 0; i < 8; i++)
408     {
409         Gem     *column[8];
410         fades = 0;
411         y = 0;
412         // let non-faded gems fall into place
413         for (j = 0; j < 8; j++)
414         {
415             if ([board[i][j] state] != GEMSTATE_FADING)
416             {
417                 column[y] = board[i][j];
418                 if ([board[i][j] positionOnScreen].y > y*48)
419                     [board[i][j] fall];
420                 y++;
421             }
422             else
423                 fades++;
424         }
425         // transfer faded gems to top of column
426         for (j = 0; j < 8; j++)
427         {
428             if ([board[i][j] state] == GEMSTATE_FADING)
429             {
430                 // randomly reassign
431                 int r = (rand() % 7);
432                 [board[i][j]    setGemType:r];
433                 [board[i][j]    setImage:[imageArray objectAtIndex:r]];
435                 column[y] = board[i][j];
436                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
437                 [board[i][j] fall];
438                 y++;
439                 gemsFaded++;
440                 fades--;
441             }
442         }
443         // OK, shuffling all done - reorganise column
444         for (j = 0; j < 8; j++)
445         {
446             board[i][j] = column[j];
447             [board[i][j] setPositionOnBoard:i:j];
448         }
449     }
452 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
454     int i,j,fades, y;
455     for (i = 0; i < 8; i++)
456     {
457         Gem     *column[8];
458         fades = 0;
459         y = 0;
460         // let non-faded gems fall into place
461         for (j = 0; j < 8; j++)
462         {
463             if ([board[i][j] state] != GEMSTATE_FADING)
464             {
465                 column[y] = board[i][j];
466                 if ([board[i][j] positionOnScreen].y > y*48)
467                     [board[i][j] fall];
468                 y++;
469             }
470             else
471                 fades++;
472         }
473         // transfer faded gems to top of column
474         for (j = 0; j < 8; j++)
475         {
476             if ([board[i][j] state] == GEMSTATE_FADING)
477             {
478                 // randomly reassign
479                 int r = (rand() % 7);
480                 [board[i][j]    setGemType:r];
481                 [board[i][j]    setSprite:[spriteArray objectAtIndex:r]];
483                 column[y] = board[i][j];
484                 [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
485                 [board[i][j] fall];
486                 y++;
487                 gemsFaded++;
488                 fades--;
489             }
490         }
491         // OK, shuffling all done - reorganise column
492         for (j = 0; j < 8; j++)
493         {
494             board[i][j] = column[j];
495             [board[i][j] setPositionOnBoard:i:j];
496         }
497     }
500 - (void) shake
502     int i,j;
503     for (i = 0; i < 8; i++)
504         for (j = 0; j < 8; j++)
505             [board[i][j] shake];
508 - (void) erupt
510     int i,j;
511     if (!muted) [[NSSound soundNamed:@"yes"] play];
512     for (i = 0; i < 8; i++)
513         for (j = 0; j < 8; j++)
514             [board[i][j] erupt];
517 - (void) explodeGameOver
519     //int i,j;
520     if (!muted) [[NSSound soundNamed:@"explosion"] play];
521     /*--
522     for (i = 0; i < 8; i++)
523         for (j = 0; j < 8; j++)
524             [board[i][j] erupt];
525     --*/
526     [self showAllBoardMoves];   // does a delayed eruption
529 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
531     int i,j;
532     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
533     for (i = 0; i < 8; i++)
534         for (j = 0; j < 8; j++)
535         {
536             //int r = (rand() % 3)*2+((i+j)%2);
537             int r = [self randomGemTypeAt:i:j];
538             [board[i][j] setGemType:r];
539             [board[i][j] setImage:[imageArray objectAtIndex:r]];
540             [board[i][j] setPositionOnBoard:i:j];
541             [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
542             [board[i][j] fall];
543         }
544             score = 0;
545     gemsFaded = 0;
546     bonusMultiplier = 1;
549 - (void) wholeNewGameWithSpritesFrom:(NSArray *) spriteArray
551     int i,j;
552     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
553     for (i = 0; i < 8; i++)
554         for (j = 0; j < 8; j++)
555         {
556             //int r = (rand() % 3)*2+((i+j)%2);
557             int r = [self randomGemTypeAt:i:j];
558             [board[i][j] setGemType:r];
559             [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
560             [board[i][j] setPositionOnBoard:i:j];
561             [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
562             [board[i][j] fall];
563         }
564             score = 0;
565     gemsFaded = 0;
566     bonusMultiplier = 1;
569 - (NSPoint)     hintPoint
571     return NSMakePoint(hintx*48,hinty*48);
574 - (float) collectGemsFaded
576     float result = (float)gemsFaded;
577     gemsFaded = 0;
578     return result;
581 - (int) score
583     return score;
586 - (int) bonusMultiplier
588     return bonusMultiplier;
591 - (void) increaseBonusMultiplier
593     bonusMultiplier++;
596 @end