change Game._bonusMultiplier property
[exterlulz-kokogems.git] / src / Game.m
blobd5901d91669c3f8ddd0d47b5e35a2bbd79abf39b
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;
32 - (id)init
34   self = [super init];
35   if (self != nil) {
36     gemsFaded = 0;
37     for (int i = 0; i < 8; i++) {
38       for (int j = 0; j < 8; j++) {
39         board[i][j] = [[Gem alloc] init];
40       }
41     }
42     
43     // MW
44     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
45   }
46   
47   return self;
50 - (id)initWithImagesFrom:(NSArray *)imageArray
52   self = [super init];
53   if (self != nil) {
54     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
55     for (int i = 0; i < 8; i++) {
56       for (int j = 0; j < 8; j++) {
57         int r = [self randomGemTypeAt:i:j];
58         Gem *gem = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
59         board[i][j] = gem;
60         [gem setPositionOnBoard:i:j];
61         [gem setPositionOnScreen:i*48:j*48];
62         [gem shake];
63       }
64     }
65     
66     // MW...
67     scoreBubbles = [[NSMutableArray arrayWithCapacity:12] retain];
68     
69     _score = 0;
70     gemsFaded = 0;
71     _bonusMultiplier = 1;
72   }
73   
74   return self;
77 - (id)initWithSpritesFrom:(NSArray *)spriteArray
79   self = [super init];
80   if (self != nil) {
81     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
82     for (int i = 0; i < 8; i++) {
83       for (int j = 0; j < 8; j++) {
84         //int r = (rand() % 3)*2+((i+j)%2);
85         int r = [self randomGemTypeAt:i:j];
86         board[i][j] = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
87         [board[i][j] setPositionOnBoard:i:j];
88         [board[i][j] setPositionOnScreen:i*48:j*48];
89         [board[i][j] shake];
90       }
91     }
92     
93     // MW...
94     scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
95     
96     _score = 0;
97     gemsFaded = 0;
98     _bonusMultiplier = 1;
99   }
100   
101   return self;
104 - (void)dealloc
106   for (int i = 0; i < 8; i++) {
107     for (int j = 0; j < 8; j++) {
108       [board[i][j] release];
109     }
110   }
111   
112   // MW...
113   [scoreBubbles release];
114   
115   [super dealloc];
118 - (void)setImagesFrom:(NSArray *)imageArray
120   for (int i = 0; i < 8; i++) {
121     for (int j = 0; j < 8; j++) {
122       Gem *gem = board[i][j];
123       [gem setImage:[imageArray objectAtIndex:[gem gemType]]];
124     }
125   }
128 - (void)setSpritesFrom:(NSArray *)spriteArray
130   for (int i = 0; i < 8; i++) {
131     for (int j = 0; j < 8; j++) {
132       Gem *gem = board[i][j];
133       [gem setSprite:[spriteArray objectAtIndex:[gem gemType]]];
134     }
135   }
138 - (int)randomGemTypeAt:(int)x :(int)y
140     int c = (x+y) % 2;
141     int r = rand() % 7;
142     if (c)
143         return (r & 6); // even
144     if (r == 6)
145         return 1;       // catch returning 7
146     return (r | 1);     // odd
149 - (Gem *)gemAt:(int)x :(int)y {
150   return board[x][y];
153 // MW...
154 - (NSMutableArray *)scoreBubbles {
155     return scoreBubbles;
158 - (void)setMuted:(BOOL)value
160   muted = value;
161   if (muted) {
162     for (int i = 0; i < 8; i++) {
163       for (int j = 0; j < 8; j++) {
164         [board[i][j] setSoundsTink:NULL Sploink:NULL];
165       }
166     }
167   }
168   else {
169     for (int i = 0; i < 8; i++) {
170       for (int j = 0; j < 8; j++) {
171         [board[i][j] setSoundsTink:[NSSound soundNamed:@"tink"] Sploink:[NSSound soundNamed:@"sploink"]];
172       }
173     }
174   }
175 }    
177 - (void) swap:(int) x1 :(int) y1 and:(int) x2:(int) y2
179     Gem *swap = board[x1][y1];
180     board[x1][y1] = board[x2][y2];
181     [board[x1][y1] setPositionOnBoard:x1:y1];
182     board[x2][y2] = swap;
183     [board[x2][y2] setPositionOnBoard:x2:y2];
184     sx1 = x1; sx2 = x2; sy1 = y1; sy2 = y2;
187 - (void)unswap {
188   [self swap:sx1:sy1 and:sx2:sy2];
191 - (BOOL) testForThreeAt:(int) x :(int) y
193     int tx,ty,cx,cy;
194     int bonus, linebonus, scorePerGem;
195     float scorebubble_x = -1.0;
196     float scorebubble_y = -1.0;
197     BOOL result = NO;
198     int gemtype = [board[x][y] gemType];
199     tx = x; ty = y; cx = x; cy = y;
200     bonus = 0;
201     if (board[x][y]._state == GEMSTATE_FADING)          result = YES;
202     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
203     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
204     if ((cx-tx) >= 2)
205     {
206         // horizontal line
207         int i,j;
208         linebonus= 0;
209         scorePerGem = (cx-tx)*5;
210         for (i = tx; i <= cx; i++)
211         {
212             linebonus+= scorePerGem;
213             [board[i][y] fade];
214             for (j=7; j>y; j--) {
215                 if (board[i][j]._state != GEMSTATE_FADING) {
216                     [board[i][j] shiver];       //      MW prepare to fall
217                 }
218             }
219         }
220         // to center scorebubble ...
221         scorebubble_x = tx + (cx-tx)/2.0;
222         scorebubble_y = y;
223         //
224         bonus += linebonus;
225         result = YES;
226     }
227     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
228     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
229     if ((cy-ty) >= 2)
230     {
231         // vertical line
232         int i,j;
233         linebonus= 0;
234         scorePerGem = (cy-ty)*5;
235         for (i = ty; i <= cy; i++)
236         {
237             linebonus += scorePerGem;
238             [board[x][i] fade];
239         }
240         for (j=7; j>cy; j--) {
241             if (board[x][j]._state != GEMSTATE_FADING) {
242                 [board[x][j] shiver];           //      MW prepare to fall
243             }
244         }
245         // to center scorebubble ...
246         if (scorebubble_x < 0)  // only if one hasn't been placed already ! (for T and L shapes)
247         {
248             scorebubble_x = x;
249             scorebubble_y = ty + (cy-ty)/2.0;
250         }
251         else                    // select the original gem position
252         {
253             scorebubble_x = x;
254             scorebubble_y = y;
255         }
256         //
257         bonus += linebonus;
258         result = YES;
259     }
261   // CASCADE BONUS
262   if (cascade>=1) {
263         bonus *= cascade;
264   }
265   
266   // MW's scorebubble
267     if (bonus>0)
268         [scoreBubbles addObject:[ScoreBubble scoreWithValue:(bonus * _bonusMultiplier)
269                                                          at:NSMakePoint(scorebubble_x*48+24, scorebubble_y*48+24)
270                                                    duration:40]];
272   _score += bonus * _bonusMultiplier;
273     return result;
276 - (BOOL) finalTestForThreeAt:(int) x :(int) y
278     int tx,ty,cx,cy;
279     BOOL result = NO;
280     int gemtype = [board[x][y] gemType];
281     tx = x; ty = y; cx = x; cy = y;
283     if (board[x][y]._state == GEMSTATE_FADING)  return YES;
284         
285     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
286     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
287     if ((cx-tx) >= 2)
288     {
289         // horizontal line
290         int i;
291         for (i = tx; i <= cx; i++)
292             [board[i][y] fade];
293         result = YES;
294     }
295     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
296     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
297     if ((cy-ty) >= 2)
298     {
299         // vertical line
300         int i;
301         for (i = ty; i <= cy; i++)
302             [board[x][i] fade];
303         result = YES;
304     }
305     return result;
308 - (BOOL) checkForThreeAt:(int) x :(int) y
310     int tx,ty,cx,cy;
311     int gemtype = [board[x][y] gemType];
312     tx = x; ty = y; cx = x; cy = y;
313     while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype))       tx = tx-1;
314     while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype))       cx = cx+1;
315     if ((cx-tx) >= 2)
316         return YES;
317     while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype))       ty = ty-1;
318     while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype))       cy = cy+1;
319     if ((cy-ty) >= 2)
320         return YES;
321     return NO;
324 - (BOOL)checkBoardForThrees
326   BOOL result = NO;
327   
328   // CASCADE BONUS increase
329   cascade++;
330   
331   for (int i = 0; i < 8; i++) {
332     for (int j = 0; j < 8; j++) {
333       if (board[i][j]._state != GEMSTATE_FADING) {
334         result = result | [self testForThreeAt:i:j];
335       }
336     }
337   }
338   
339   // CASCADE BONUS check for reset
340   if (!result) {
341     cascade = 1;
342   }
343   
344   return result;
347 - (void) showAllBoardMoves
349     // test every possible move
350     int i,j;
352     // horizontal moves
353     for (j = 0; j < 8; j++)
354         for (i = 0; i < 7; i++)
355         {
356             [self swap:i:j and:i+1:j];
357             [self finalTestForThreeAt:i:j];
358             [self finalTestForThreeAt:i+1:j];
359             [self unswap];
360         }
362     // vertical moves
363     for (i = 0; i < 8; i++)
364         for (j = 0; j < 7; j++)
365         {
366             [self swap:i:j and:i:j+1];
367             [self finalTestForThreeAt:i:j];
368             [self finalTestForThreeAt:i:j+1];
369             [self unswap];
370         }
372     // over the entire board, set the animationtime for the marked gems higher
373     for (i = 0; i < 8; i++)
374         for (j = 0; j < 8; j++)
375         {
376             if (board[i][j]._state == GEMSTATE_FADING)
377             {
378                 [board[i][j] erupt];
379                 // !!!: remove: [board[i][j] setAnimationCounter:1];
380               board[i][j]._animationCounter = 1;
381             }
382             else
383                 [board[i][j] erupt];
384         }
386     
389 - (BOOL) boardHasMoves
391     // test every possible move
392     int i,j;
393     BOOL        result = NO;
394     // horizontal moves
395     for (j = 0; j < 8; j++)
396         for (i = 0; i < 7; i++)
397         {
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             {
403                 hintx = i;
404                 hinty = j;
405                 return result;
406             }
407         }
409             // vertical moves
410             for (i = 0; i < 8; i++)
411                 for (j = 0; j < 7; j++)
412                 {
413                     [self swap:i:j and:i:j+1];
414                     result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
415                     [self unswap];
416                     if (result)
417                     {
418                         hintx = i;
419                         hinty = j;
420                         return result;
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]    setImage:[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] setImage:[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     int i,j;
579     srand([[NSDate date] timeIntervalSince1970]);       // seed by time
580     for (i = 0; i < 8; i++)
581         for (j = 0; j < 8; j++)
582         {
583             //int r = (rand() % 3)*2+((i+j)%2);
584             int r = [self randomGemTypeAt:i:j];
585             [board[i][j] setGemType:r];
586             [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
587             [board[i][j] setPositionOnBoard:i:j];
588             [board[i][j] setPositionOnScreen:i*48:(15-j)*48];
589             [board[i][j] fall];
590         }
591             _score = 0;
592     gemsFaded = 0;
593     _bonusMultiplier = 1;
596 // TODO: set as dynamic readonly property
597 - (NSPoint)hintPoint {
598   return NSMakePoint(hintx * 48, hinty * 48);
601 - (float)collectGemsFaded
603     float result = (float)gemsFaded;
604     gemsFaded = 0;
605     return result;
608 - (void) increaseBonusMultiplier
610     _bonusMultiplier++;
613 @end