first commit
[exterlulz-kokomonds.git] / src / Game.m
blobaa457d7698113ebe5bda6c7d7ba8628fd1f9f3d5
1 /* ----====----====----====----====----====----====----====----====----====----
2  Game.m (jeweltoy)
3  
4  JewelToy is a simple game played against the clock.
5  Copyright (C) 2001  Giles Williams
6  
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 /* kokomonds is a fork of JewelToy.
23  * repository: http://github.com/exterlulz/kokomonds
24  */
26 // TODO: clean
28 #import "Game.h"
29 #import "Gem.h"
31 // MW...
33 #import "ScoreBubble.h"
36 @implementation Game
38 - (id) init
40   int i,j;
41   self = [super init];
42   gemsFaded = 0;
43   for (i = 0; i < 8; i++)
44     for (j = 0; j < 8; j++)
45       board[i][j] = [[Gem alloc] init];
46   // MW
47   scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
48   //
49   return self;
52 - (id) initWithImagesFrom:(NSArray *) imageArray
54   int i,j;
55   self = [super init];
56   srand([[NSDate date] timeIntervalSince1970]); // seed by time
57   for (i = 0; i < 8; i++)
58     for (j = 0; j < 8; j++)
59     {
60       int r = [self randomGemTypeAt:i:j];
61       board[i][j] = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
62       [board[i][j] setPositionOnBoard:i:j];
63       [board[i][j] setPositionOnScreen:i*48:j*48];
64       [board[i][j] shake];
65     }
66   // MW...
67   scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
68   //
69   score = 0;
70   gemsFaded = 0;
71   bonusMultiplier = 1;
72   return self;
75 - (id) initWithSpritesFrom:(NSArray *) spriteArray
77   int i,j;
78   self = [super init];
79   srand([[NSDate date] timeIntervalSince1970]); // seed by time
80   for (i = 0; i < 8; i++)
81     for (j = 0; j < 8; j++)
82     {
83       //int r = (rand() % 3)*2+((i+j)%2);
84       int r = [self randomGemTypeAt:i:j];
85       board[i][j] = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
86       [board[i][j] setPositionOnBoard:i:j];
87       [board[i][j] setPositionOnScreen:i*48:j*48];
88       [board[i][j] shake];
89     }
90   // MW...
91   scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
92   //
93   score = 0;
94   gemsFaded = 0;
95   bonusMultiplier = 1;
96   return self;
99 - (void) dealloc
101   int i,j;
102   for (i = 0; i < 8; i++)
103     for (j = 0; j < 8; j++)
104       [board[i][j] release];
105   // MW...
106   [scoreBubbles release];
107   //
108   [super dealloc];
111 - (void) setImagesFrom:(NSArray *) imageArray
113   int i,j;
114   for (i = 0; i < 8; i++)
115     for (j = 0; j < 8; j++)
116       [board[i][j] setImage:[imageArray objectAtIndex:[board[i][j] gemType]]];
119 - (void) setSpritesFrom:(NSArray *) spriteArray
121   int i,j;
122   for (i = 0; i < 8; i++)
123     for (j = 0; j < 8; j++)
124       [board[i][j] setSprite:[spriteArray objectAtIndex:[board[i][j] gemType]]];
127 - (int) randomGemTypeAt:(int)x :(int)y
129   int c = (x+y) % 2;
130   int r = rand() % 7;
131   if (c)
132     return (r & 6);     // even
133   if (r == 6)
134     return 1;   // catch returning 7
135   return (r | 1);       // odd
138 - (Gem *) gemAt:(int)x :(int)y
140   return board[x][y];
144 // MW...
146 - (NSMutableArray *)scoreBubbles
148   return scoreBubbles;
151 ////
153 - (void) setMuted:(BOOL)value
155   int i,j;
156   muted = value;
157   if (muted)
158     for (i = 0; i < 8; i++)
159       for (j = 0; j < 8; j++)
160         [board[i][j] setSoundsTink:NULL Sploink:NULL];
161   else
162     for (i = 0; i < 8; i++)
163       for (j = 0; j < 8; j++)
164         [board[i][j] setSoundsTink:[NSSound soundNamed:@"tink"] Sploink:[NSSound soundNamed:@"sploink"]];
165 }    
168 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
170   Gem   *swap = board[x1][y1];
171   board[x1][y1] = board[x2][y2];
172   [board[x1][y1] setPositionOnBoard:x1:y1];
173   board[x2][y2] = swap;
174   [board[x2][y2] setPositionOnBoard:x2:y2];
175   sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
178 - (void) unswap
180   [self swap:sx1:sy1 and:sx2:sy2];
183 - (BOOL) testForThreeAt:(int) x :(int) y
185   int   tx,ty,cx,cy;
186   int bonus, linebonus, scorePerGem;
187   float scorebubble_x = -1.0;
188   float scorebubble_y = -1.0;
189   BOOL result = NO;
190   int   gemtype = [board[x][y] gemType];
191   tx = x; ty = y; cx = x; cy = y;
192   bonus = 0;
193   if ([board[x][y] state] == GEMSTATE_FADING)           result = YES;
194   while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
195   while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
196   if ((cx-tx) >= 2)
197   {
198     // horizontal line
199     int i,j;
200     linebonus= 0;
201     scorePerGem = (cx-tx)*5;
202     for (i = tx; i <= cx; i++)
203     {
204       linebonus+= scorePerGem;
205       [board[i][y] fade];
206       for (j=7; j>y; j--) {
207         if ([board[i][j] state]!= GEMSTATE_FADING) {
208           [board[i][j] shiver]; //      MW prepare to fall
209         }
210       }
211     }
212     // to center scorebubble ...
213     scorebubble_x = tx + (cx-tx)/2.0;
214     scorebubble_y = y;
215     //
216     bonus += linebonus;
217     result = YES;
218   }
219   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
220   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
221   if ((cy-ty) >= 2)
222   {
223     // vertical line
224     int i,j;
225     linebonus= 0;
226     scorePerGem = (cy-ty)*5;
227     for (i = ty; i <= cy; i++)
228     {
229       linebonus += scorePerGem;
230       [board[x][i] fade];
231     }
232     for (j=7; j>cy; j--) {
233       if ([board[x][j] state]!= GEMSTATE_FADING) {
234         [board[x][j] shiver];           //      MW prepare to fall
235       }
236     }
237     // to center scorebubble ...
238     if (scorebubble_x < 0)      // only if one hasn't been placed already ! (for T and L shapes)
239     {
240       scorebubble_x = x;
241       scorebubble_y = ty + (cy-ty)/2.0;
242     }
243     else                        // select the original gem position
244     {
245       scorebubble_x = x;
246       scorebubble_y = y;
247     }
248     //
249     bonus += linebonus;
250     result = YES;
251   }
252   // CASCADE BONUS
253   if (cascade>=1)
254     bonus *= cascade;
255   //
256   // MW's scorebubble
257   //
258   if (bonus>0)
259     [scoreBubbles addObject:[ScoreBubble scoreWithValue:bonus*bonusMultiplier
260                                                      At:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
261                                                Duration:40]];
262   //
263   score += bonus * bonusMultiplier;
264   return result;
267 - (BOOL) finalTestForThreeAt:(int) x :(int) y
269   int   tx,ty,cx,cy;
270   BOOL result = NO;
271   int   gemtype = [board[x][y] gemType];
272   tx = x; ty = y; cx = x; cy = y;
273   
274   if ([board[x][y] state] == GEMSTATE_FADING)   return YES;
275   
276   while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
277   while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
278   if ((cx-tx) >= 2)
279   {
280     // horizontal line
281     int i;
282     for (i = tx; i <= cx; i++)
283       [board[i][y] fade];
284     result = YES;
285   }
286   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
287   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
288   if ((cy-ty) >= 2)
289   {
290     // vertical line
291     int i;
292     for (i = ty; i <= cy; i++)
293       [board[x][i] fade];
294     result = YES;
295   }
296   return result;
299 - (BOOL) checkForThreeAt:(int) x :(int) y
301   int   tx,ty,cx,cy;
302   int   gemtype = [board[x][y] gemType];
303   tx = x; ty = y; cx = x; cy = y;
304   while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
305   while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
306   if ((cx-tx) >= 2)
307     return YES;
308   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
309   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
310   if ((cy-ty) >= 2)
311     return YES;
312   return NO;
315 - (BOOL) checkBoardForThrees
317   int i,j;
318   BOOL result = NO;
319   // CASCADE BONUS increase
320   cascade++;
321   //
322   for (i = 0; i < 8; i++)
323     for (j = 0; j < 8; j++)
324       if ([board[i][j] state]!=GEMSTATE_FADING)
325         result = result | [self testForThreeAt:i:j];
326   // CASCADE BONUS check for reset
327   if (!result) cascade = 1;
328   return result;
332 - (void) showAllBoardMoves
334   // test every possible move
335   int i,j;
336   
337   // horizontal moves
338   for (j = 0; j < 8; j++)
339     for (i = 0; i < 7; i++)
340     {
341       [self swap:i:j and:i+1:j];
342       [self finalTestForThreeAt:i:j];
343       [self finalTestForThreeAt:i+1:j];
344       [self unswap];
345     }
346   
347   // vertical moves
348   for (i = 0; i < 8; i++)
349     for (j = 0; j < 7; j++)
350     {
351       [self swap:i:j and:i:j+1];
352       [self finalTestForThreeAt:i:j];
353       [self finalTestForThreeAt:i:j+1];
354       [self unswap];
355     }
356   
357   // over the entire board, set the animationtime for the marked gems higher
358   for (i = 0; i < 8; i++)
359     for (j = 0; j < 8; j++)
360     {
361       if ([board[i][j] state] == GEMSTATE_FADING)
362       {
363         [board[i][j] erupt];
364         [board[i][j] setAnimationCounter:1];
365       }
366       else
367         [board[i][j] erupt];
368     }
369   
370   
373 - (BOOL) boardHasMoves
375   // test every possible move
376   int i,j;
377   BOOL  result = NO;
378   // horizontal moves
379   for (j = 0; j < 8; j++)
380     for (i = 0; i < 7; i++)
381     {
382       [self swap:i:j and:i+1:j];
383       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
384       [self unswap];
385       if (result)
386       {
387         hintx = i;
388         hinty = j;
389         return result;
390       }
391     }
392   
393   // vertical moves
394   for (i = 0; i < 8; i++)
395     for (j = 0; j < 7; j++)
396     {
397       [self swap:i:j and:i:j+1];
398       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
399       [self unswap];
400       if (result)
401       {
402         hintx = i;
403         hinty = j;
404         return result;
405       }
406     }
407   return NO;
410 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
412   int i,j,fades, y;
413   for (i = 0; i < 8; i++)
414   {
415     Gem *column[8];
416     fades = 0;
417     y = 0;
418     // let non-faded gems fall into place
419     for (j = 0; j < 8; j++)
420     {
421       if ([board[i][j] state] != GEMSTATE_FADING)
422       {
423         column[y] = board[i][j];
424         if ([board[i][j] positionOnScreen].y > y*48)
425           [board[i][j] fall];
426         y++;
427       }
428       else
429         fades++;
430     }
431     // transfer faded gems to top of column
432     for (j = 0; j < 8; j++)
433     {
434       if ([board[i][j] state] == GEMSTATE_FADING)
435       {
436         // randomly reassign
437         int r = (rand() % 7);
438         [board[i][j]    setGemType:r];
439         [board[i][j]    setImage:[imageArray objectAtIndex:r]];
440         
441         column[y] = board[i][j];
442         [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
443         [board[i][j] fall];
444         y++;
445         gemsFaded++;
446         fades--;
447       }
448     }
449     // OK, shuffling all done - reorganise column
450     for (j = 0; j < 8; j++)
451     {
452       board[i][j] = column[j];
453       [board[i][j] setPositionOnBoard:i:j];
454     }
455   }
458 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
460   int i,j,fades, y;
461   for (i = 0; i < 8; i++)
462   {
463     Gem *column[8];
464     fades = 0;
465     y = 0;
466     // let non-faded gems fall into place
467     for (j = 0; j < 8; j++)
468     {
469       if ([board[i][j] state] != GEMSTATE_FADING)
470       {
471         column[y] = board[i][j];
472         if ([board[i][j] positionOnScreen].y > y*48)
473           [board[i][j] fall];
474         y++;
475       }
476       else
477         fades++;
478     }
479     // transfer faded gems to top of column
480     for (j = 0; j < 8; j++)
481     {
482       if ([board[i][j] state] == GEMSTATE_FADING)
483       {
484         // randomly reassign
485         int r = (rand() % 7);
486         [board[i][j]    setGemType:r];
487         [board[i][j]    setSprite:[spriteArray objectAtIndex:r]];
488         
489         column[y] = board[i][j];
490         [board[i][j] setPositionOnScreen:i*48:(7+fades)*48];
491         [board[i][j] fall];
492         y++;
493         gemsFaded++;
494         fades--;
495       }
496     }
497     // OK, shuffling all done - reorganise column
498     for (j = 0; j < 8; j++)
499     {
500       board[i][j] = column[j];
501       [board[i][j] setPositionOnBoard:i:j];
502     }
503   }
506 - (void) shake
508   int i,j;
509   for (i = 0; i < 8; i++)
510     for (j = 0; j < 8; j++)
511       [board[i][j] shake];
514 - (void) erupt
516   int i,j;
517   if (!muted)   [[NSSound soundNamed:@"yes"] play];
518   for (i = 0; i < 8; i++)
519     for (j = 0; j < 8; j++)
520       [board[i][j] erupt];
523 - (void) explodeGameOver
525   //int i,j;
526   if (!muted)   [[NSSound soundNamed:@"explosion"] play];
527   /*--
528    for (i = 0; i < 8; i++)
529    for (j = 0; j < 8; j++)
530    [board[i][j] erupt];
531    --*/
532   [self showAllBoardMoves];     // does a delayed eruption
535 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
537   int i,j;
538   srand([[NSDate date] timeIntervalSince1970]); // seed by time
539   for (i = 0; i < 8; i++)
540     for (j = 0; j < 8; j++)
541     {
542       //int r = (rand() % 3)*2+((i+j)%2);
543       int r = [self randomGemTypeAt:i:j];
544       [board[i][j] setGemType:r];
545       [board[i][j] setImage:[imageArray objectAtIndex:r]];
546       [board[i][j] setPositionOnBoard:i:j];
547       [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
548       [board[i][j] fall];
549     }
550   score = 0;
551   gemsFaded = 0;
552   bonusMultiplier = 1;
555 - (void) wholeNewGameWithSpritesFrom:(NSArray *) spriteArray
557   int i,j;
558   srand([[NSDate date] timeIntervalSince1970]); // seed by time
559   for (i = 0; i < 8; i++)
560     for (j = 0; j < 8; j++)
561     {
562       //int r = (rand() % 3)*2+((i+j)%2);
563       int r = [self randomGemTypeAt:i:j];
564       [board[i][j] setGemType:r];
565       [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
566       [board[i][j] setPositionOnBoard:i:j];
567       [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
568       [board[i][j] fall];
569     }
570   score = 0;
571   gemsFaded = 0;
572   bonusMultiplier = 1;
575 - (NSPoint)     hintPoint
577   return NSMakePoint(hintx*48,hinty*48);
580 - (float) collectGemsFaded
582   float result = (float)gemsFaded;
583   gemsFaded = 0;
584   return result;
587 - (int) score
589   return score;
592 - (int) bonusMultiplier
594   return bonusMultiplier;
597 - (void) increaseBonusMultiplier
599   bonusMultiplier++;
602 @end