cleaning
[exterlulz-kokomonds.git] / src / Game.m
blobc4a17b62cce923e06d7540ba85790c7ea981f6d7
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   self = [super init];
41   if (self != nil) {
42     gemsFaded = 0;
43     for (int i = 0; i < 8; i++) {
44       for (int j = 0; j < 8; j++) {
45         board[i][j] = [[Gem alloc] init];
46       }
47     }
48     
49     // MW
50     scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
51   }
52   
53   return self;
56 - (id) initWithImagesFrom:(NSArray *) imageArray
58   int i,j;
59   self = [super init];
60   srand([[NSDate date] timeIntervalSince1970]); // seed by time
61   for (i = 0; i < 8; i++)
62     for (j = 0; j < 8; j++)
63     {
64       // TODO: replace with gem type?
65       int r = [self randomGemTypeAt:i:j];
66       
67       Gem *gem = [[Gem gemWithNumber:r andImage:[imageArray objectAtIndex:r]] retain];
68       gem._positionOnBoard  = NSMakePoint(i, j);
69       gem._positionOnScreen = NSMakePoint(i * 48, j * 48);
70       [gem shake];
71       board[i][j] = gem;
72       /* TODO: remove, replaced with previous code
73       [board[i][j] setPositionOnBoard:i:j];
74       [board[i][j] setPositionOnScreen:i*48:j*48];
75       [board[i][j] shake];
76        */
77     }
78   // MW...
79   scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
80   //
81   score = 0;
82   gemsFaded = 0;
83   bonusMultiplier = 1;
84   return self;
87 - (id) initWithSpritesFrom:(NSArray *) spriteArray
89   int i,j;
90   self = [super init];
91   srand([[NSDate date] timeIntervalSince1970]); // seed by time
92   for (i = 0; i < 8; i++)
93     for (j = 0; j < 8; j++)
94     {
95       //int r = (rand() % 3)*2+((i+j)%2);
96       // TODO: replace with gem type?
97       int r = [self randomGemTypeAt:i:j];
98       Gem *gem = [[Gem gemWithNumber:r andSprite:[spriteArray objectAtIndex:r]] retain];
99       gem._positionOnBoard = NSMakePoint(i, j);
100       gem._positionOnScreen = NSMakePoint(i * 48, j * 48);
101       [gem shake];
102       board[i][j] = gem;
103       /* TODO: remove, replaced with the previous code
104       [board[i][j] setPositionOnBoard:i:j];
105       [board[i][j] setPositionOnScreen:i*48:j*48];
106       [board[i][j] shake];
107        */
108     }
109   // MW...
110   scoreBubbles= [[NSMutableArray arrayWithCapacity:12] retain];
111   //
112   score = 0;
113   gemsFaded = 0;
114   bonusMultiplier = 1;
115   return self;
118 - (void) dealloc
120   int i,j;
121   for (i = 0; i < 8; i++)
122     for (j = 0; j < 8; j++)
123       [board[i][j] release];
124   // MW...
125   [scoreBubbles release];
126   //
127   [super dealloc];
130 - (void) setImagesFrom:(NSArray *) imageArray
132   int i,j;
133   for (i = 0; i < 8; i++)
134     for (j = 0; j < 8; j++)
135       [board[i][j] setImage:[imageArray objectAtIndex:[board[i][j] gemType]]];
138 - (void)setSpritesFrom:(NSArray *)spriteArray
140   Gem *gem;
141   for (int i = 0; i < 8; i++) {
142     for (int j = 0; j < 8; j++) {
143       gem = board[i][j];
144       [gem setSprite:[spriteArray objectAtIndex:[gem gemType]]];
145     }
146   }
149 - (int) randomGemTypeAt:(int)x :(int)y
151   int c = (x+y) % 2;
152   int r = rand() % 7;
153   
154   if (c) {
155     return (r & 6);     // even
156   }
157   
158   if (r == 6) {
159     return 1;   // catch returning 7
160   }
161   
162   return (r | 1);       // odd
165 - (Gem *) gemAt:(int)x :(int)y {
166   return board[x][y];
170 // MW...
172 - (NSMutableArray *)scoreBubbles
174   return scoreBubbles;
177 ////
179 - (void) setMuted:(BOOL)value
181   int i,j;
182   muted = value;
183   if (muted)
184     for (i = 0; i < 8; i++)
185       for (j = 0; j < 8; j++)
186         [board[i][j] setSoundsTink:NULL Sploink:NULL];
187   else
188     for (i = 0; i < 8; i++)
189       for (j = 0; j < 8; j++)
190         [board[i][j] setSoundsTink:[NSSound soundNamed:@"tink"] Sploink:[NSSound soundNamed:@"sploink"]];
191 }    
193 - (void)swap:(int)xa :(int)ya and:(int)xb :(int)yb
195   Gem   *swap = board[xa][ya];
196   board[xa][ya] = board[xb][yb];
197   board[xa][ya]._positionOnBoard = NSMakePoint(xa, ya);
198   board[xb][yb] = swap;
199   board[xb][yb]._positionOnBoard = NSMakePoint(xb, yb);
200   sxa = xa; sxb = xb; sya = ya; syb = yb;
203 - (void)unswap {
204   [self swap:sxa:sya and:sxb:syb];
207 - (BOOL)testForThreeAt:(int) x :(int) y
209   int   tx,ty,cx,cy;
210   int bonus, linebonus, scorePerGem;
211   float scorebubble_x = -1.0;
212   float scorebubble_y = -1.0;
213   BOOL result = NO;
214   int   gemtype = [board[x][y] gemType];
215   tx = x; ty = y; cx = x; cy = y;
216   bonus = 0;
217   if (board[x][y].state == GEMSTATE_FADING) {
218     result = YES;
219   }
220   while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
221   while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
222   if ((cx-tx) >= 2)
223   {
224     // horizontal line
225     int i,j;
226     linebonus= 0;
227     scorePerGem = (cx-tx)*5;
228     for (i = tx; i <= cx; i++)
229     {
230       linebonus+= scorePerGem;
231       [board[i][y] fade];
232       for (j=7; j>y; j--) {
233         if (board[i][j].state != GEMSTATE_FADING) {
234           [board[i][j] shiver]; //      MW prepare to fall
235         }
236       }
237     }
238     // to center scorebubble ...
239     scorebubble_x = tx + (cx-tx)/2.0;
240     scorebubble_y = y;
241     //
242     bonus += linebonus;
243     result = YES;
244   }
245   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
246   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
247   if ((cy-ty) >= 2)
248   {
249     // vertical line
250     int i,j;
251     linebonus= 0;
252     scorePerGem = (cy-ty)*5;
253     for (i = ty; i <= cy; i++)
254     {
255       linebonus += scorePerGem;
256       [board[x][i] fade];
257     }
258     for (j=7; j>cy; j--) {
259       if (board[x][j].state != GEMSTATE_FADING) {
260         [board[x][j] shiver];           //      MW prepare to fall
261       }
262     }
263     // to center scorebubble ...
264     if (scorebubble_x < 0)      // only if one hasn't been placed already ! (for T and L shapes)
265     {
266       scorebubble_x = x;
267       scorebubble_y = ty + (cy-ty)/2.0;
268     }
269     else                        // select the original gem position
270     {
271       scorebubble_x = x;
272       scorebubble_y = y;
273     }
274     //
275     bonus += linebonus;
276     result = YES;
277   }
278   
279   // CASCADE BONUS
280   if (cascade >= 1) {
281     bonus *= cascade;
282   }
284   // MW's scorebubble
285   if (bonus>0) {
286     [scoreBubbles addObject:[ScoreBubble scoreWithValue:bonus*bonusMultiplier
287                                                      at:NSMakePoint(scorebubble_x * 48 + 24, scorebubble_y * 48 + 24)
288                                                duration:40]];
289   }
290   
291   score += bonus * bonusMultiplier;
292   return result;
295 - (BOOL)finalTestForThreeAt:(int)x :(int)y
297   int   tx,ty,cx,cy;
298   BOOL result = NO;
299   int   gemtype = [board[x][y] gemType];
300   tx = x; ty = y; cx = x; cy = y;
301   
302   if (board[x][y].state == GEMSTATE_FADING)     return YES;
303   
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   {
308     // horizontal line
309     int i;
310     for (i = tx; i <= cx; i++)
311       [board[i][y] fade];
312     result = YES;
313   }
314   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
315   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
316   if ((cy-ty) >= 2)
317   {
318     // vertical line
319     int i;
320     for (i = ty; i <= cy; i++)
321       [board[x][i] fade];
322     result = YES;
323   }
324   return result;
327 - (BOOL) checkForThreeAt:(int) x :(int) y
329   int   tx,ty,cx,cy;
330   int   gemtype = [board[x][y] gemType];
331   tx = x; ty = y; cx = x; cy = y;
332   while ((tx > 0)&&([board[tx-1][y] gemType]==gemtype)) tx = tx-1;
333   while ((cx < 7)&&([board[cx+1][y] gemType]==gemtype)) cx = cx+1;
334   if ((cx-tx) >= 2)
335     return YES;
336   while ((ty > 0)&&([board[x][ty-1] gemType]==gemtype)) ty = ty-1;
337   while ((cy < 7)&&([board[x][cy+1] gemType]==gemtype)) cy = cy+1;
338   if ((cy-ty) >= 2)
339     return YES;
340   return NO;
343 - (BOOL) checkBoardForThrees
345   int i,j;
346   BOOL result = NO;
347   // CASCADE BONUS increase
348   cascade++;
349   //
350   for (i = 0; i < 8; i++)
351     for (j = 0; j < 8; j++)
352       if (board[i][j].state != GEMSTATE_FADING)
353         result = result | [self testForThreeAt:i:j];
354   // CASCADE BONUS check for reset
355   if (!result) cascade = 1;
356   return result;
360 - (void) showAllBoardMoves
362   // test every possible move
363   int i,j;
364   
365   // horizontal moves
366   for (j = 0; j < 8; j++)
367     for (i = 0; i < 7; i++)
368     {
369       [self swap:i:j and:i+1:j];
370       [self finalTestForThreeAt:i:j];
371       [self finalTestForThreeAt:i+1:j];
372       [self unswap];
373     }
374   
375   // vertical moves
376   for (i = 0; i < 8; i++)
377     for (j = 0; j < 7; j++)
378     {
379       [self swap:i:j and:i:j+1];
380       [self finalTestForThreeAt:i:j];
381       [self finalTestForThreeAt:i:j+1];
382       [self unswap];
383     }
384   
385   // over the entire board, set the animationtime for the marked gems higher
386   for (i = 0; i < 8; i++)
387     for (j = 0; j < 8; j++)
388     {
389       if (board[i][j].state == GEMSTATE_FADING)
390       {
391         [board[i][j] erupt];
392         [board[i][j] setAnimationCounter:1];
393       }
394       else
395         [board[i][j] erupt];
396     }
397   
398   
401 - (BOOL) boardHasMoves
403   // test every possible move
404   int i,j;
405   BOOL  result = NO;
406   // horizontal moves
407   for (j = 0; j < 8; j++)
408     for (i = 0; i < 7; i++)
409     {
410       [self swap:i:j and:i+1:j];
411       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i+1:j];
412       [self unswap];
413       if (result)
414       {
415         hintx = i;
416         hinty = j;
417         return result;
418       }
419     }
420   
421   // vertical moves
422   for (i = 0; i < 8; i++)
423     for (j = 0; j < 7; j++)
424     {
425       [self swap:i:j and:i:j+1];
426       result = [self checkForThreeAt:i:j] | [self checkForThreeAt:i:j+1];
427       [self unswap];
428       if (result)
429       {
430         hintx = i;
431         hinty = j;
432         return result;
433       }
434     }
435   return NO;
438 - (void) removeFadedGemsAndReorganiseWithImagesFrom:(NSArray *) imageArray
440   int i,j,fades, y;
441   for (i = 0; i < 8; i++)
442   {
443     Gem *column[8];
444     fades = 0;
445     y = 0;
446     // let non-faded gems fall into place
447     for (j = 0; j < 8; j++)
448     {
449       if (board[i][j].state != GEMSTATE_FADING)
450       {
451         column[y] = board[i][j];
452         if (board[i][j]._positionOnScreen.y > y*48)
453           [board[i][j] fall];
454         y++;
455       }
456       else
457         fades++;
458     }
459     // transfer faded gems to top of column
460     for (j = 0; j < 8; j++)
461     {
462       if (board[i][j].state == GEMSTATE_FADING)
463       {
464         // randomly reassign
465         int r = (rand() % 7);
466         [board[i][j]    setGemType:r];
467         [board[i][j]    setImage:[imageArray objectAtIndex:r]];
468         
469         column[y] = board[i][j];
470         board[i][j]._positionOnScreen = NSMakePoint(i * 48, (7 + fades) * 48);
471         [board[i][j] fall];
472         y++;
473         gemsFaded++;
474         fades--;
475       }
476     }
477     // OK, shuffling all done - reorganise column
478     for (j = 0; j < 8; j++)
479     {
480       board[i][j] = column[j];
481       board[i][j]._positionOnBoard = NSMakePoint(i, j);
482     }
483   }
486 - (void) removeFadedGemsAndReorganiseWithSpritesFrom:(NSArray *) spriteArray
488   int j,fades, y;
489   
490   for (int i = 0; i < 8; i++) {
491     Gem *column[8];
492     fades = 0;
493     y = 0;
494     
495     // let non-faded gems fall into place
496     for (j = 0; j < 8; j++) {
497       if (board[i][j].state != GEMSTATE_FADING) {
498         column[y] = board[i][j];
499         if (board[i][j]._positionOnScreen.y > y * 48)
500           [board[i][j] fall];
501         y++;
502       }
503       else {
504         fades++;
505       }
506     }
507     
508     // transfer faded gems to top of column
509     for (j = 0; j < 8; j++)
510     {
511       if (board[i][j].state == GEMSTATE_FADING)
512       {
513         // randomly reassign
514         int r = (rand() % 7);
515         [board[i][j]    setGemType:r];
516         [board[i][j]    setSprite:[spriteArray objectAtIndex:r]];
517         
518         column[y] = board[i][j];
519         board[i][j]._positionOnScreen = NSMakePoint(i * 48, (7 + fades) * 48);
520         [board[i][j] fall];
521         y++;
522         gemsFaded++;
523         fades--;
524       }
525     }
526     // OK, shuffling all done - reorganise column
527     for (j = 0; j < 8; j++)
528     {
529       board[i][j] = column[j];
530       board[i][j]._positionOnBoard = NSMakePoint(i, j);
531     }
532   }
535 - (void) shake
537   int i,j;
538   for (i = 0; i < 8; i++)
539     for (j = 0; j < 8; j++)
540       [board[i][j] shake];
543 - (void) erupt
545   int i,j;
546   if (!muted)   [[NSSound soundNamed:@"yes"] play];
547   for (i = 0; i < 8; i++)
548     for (j = 0; j < 8; j++)
549       [board[i][j] erupt];
552 - (void) explodeGameOver
554   //int i,j;
555   if (!muted)   [[NSSound soundNamed:@"explosion"] play];
556   /*--
557    for (i = 0; i < 8; i++)
558    for (j = 0; j < 8; j++)
559    [board[i][j] erupt];
560    --*/
561   [self showAllBoardMoves];     // does a delayed eruption
564 - (void) wholeNewGameWithImagesFrom:(NSArray *) imageArray
566   int i,j;
567   srand([[NSDate date] timeIntervalSince1970]); // seed by time
568   for (i = 0; i < 8; i++)
569     for (j = 0; j < 8; j++)
570     {
571       //int r = (rand() % 3)*2+((i+j)%2);
572       int r = [self randomGemTypeAt:i:j];
573       [board[i][j] setGemType:r];
574       [board[i][j] setImage:[imageArray objectAtIndex:r]];
575       board[i][j]._positionOnBoard = NSMakePoint(i, j);
576       board[i][j]._positionOnScreen = NSMakePoint(i * 48, (15 - j) * 48);
577       [board[i][j] fall];
578     }
579   score = 0;
580   gemsFaded = 0;
581   bonusMultiplier = 1;
584 - (void) wholeNewGameWithSpritesFrom:(NSArray *) spriteArray
586   int i,j;
587   srand([[NSDate date] timeIntervalSince1970]); // seed by time
588   for (i = 0; i < 8; i++)
589     for (j = 0; j < 8; j++)
590     {
591       //int r = (rand() % 3)*2+((i+j)%2);
592       int r = [self randomGemTypeAt:i:j];
593       [board[i][j] setGemType:r];
594       [board[i][j] setSprite:[spriteArray objectAtIndex:r]];
595       board[i][j]._positionOnBoard = NSMakePoint(i, j);
596       board[i][j]._positionOnScreen = NSMakePoint(i * 48, (15 - j) * 48);
597       [board[i][j] fall];
598     }
599   score = 0;
600   gemsFaded = 0;
601   bonusMultiplier = 1;
604 - (NSPoint)     hintPoint {
605   return NSMakePoint(hintx * 48,hinty * 48);
608 - (float)collectGemsFaded
610   float result = (float)gemsFaded;
611   gemsFaded = 0;
612   return result;
615 - (int)score {
616   return score;
619 - (int)bonusMultiplier {
620   return bonusMultiplier;
623 - (void)increaseBonusMultiplier {
624   bonusMultiplier++;
627 @end