add pause sound
[exterlulz-kokogems.git] / src / GameView.m
blob3824a37d3f0e7f1f3a9d121b6f3a0ea2d2c5ecf7
1 /* ----====----====----====----====----====----====----====----====----====----
2 GameView.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 "GameView.h"
24 #import "GameController.h"
25 #import "Game.h"
26 #import "Gem.h"
28 // MW...
29 #import "ScoreBubble.h"
31 #import "OpenGLSprite.h"
33 #include <math.h>
35 @implementation GameView
37 @synthesize animating = _animating;
38 @synthesize muted     = _muted;
39 @synthesize game      = _game;
41 // ???: @synthesize gemImageArray = _gemImageArray;
43 @synthesize gemSpriteArray = _gemSpriteArray;
45 - (id)initWithFrame:(NSRect)frame
47   //NSData      *tiffData;
48   
49   NSOpenGLPixelFormatAttribute attrs[] = {
50     NSOpenGLPFADepthSize, 1,
51     NSOpenGLPFAAccelerated,
52     0};
53   NSOpenGLPixelFormat *pixFmt;
54   
55   pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
56   
57   self = [super initWithFrame:frame pixelFormat:pixFmt];
58   if (self != nil) {
59     m_glContextInitialized = NO;
60     
61     _animating = NO;
62     showHighScores = NO;
63     showHint = YES;
64     ticksSinceLastMove = 0;
65     
66     docTypeDictionary = [NSDictionary dictionary];
67   }
68   
69   return self;
72 - (void) dealloc
74     if (backgroundColor)
75         [backgroundColor release];
76     if (_gemImageArray)
77         [_gemImageArray release];
78     if (_gemSpriteArray)
79         // ???: bug in the original code: [gemImageArray release];
80       [_gemSpriteArray release];
81     if (backgroundImagePathArray)
82         [backgroundImagePathArray release];
83     [super dealloc];
86 - (void) graphicSetUp
88     int         i;
89     NSData      *tiffData;
91     backgroundColor = [[NSColor purpleColor] retain];
93     [self loadImageArray];
95         tiffData = [[NSUserDefaults standardUserDefaults]       dataForKey:@"backgroundTiffData"];
96     if (tiffData)
97         backgroundImage = [[NSImage alloc] initWithData:tiffData];
98     else
99         backgroundImage = [NSImage imageNamed:@"background"];
101     crosshairImage = [NSImage imageNamed:@"cross"];
102     movehintImage = [NSImage imageNamed:@"movehint"];
104     //////
105     //
106     // Make the Open GL Sprites!
107     //
108     backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
109                                              cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
110                                                       size:NSMakeSize(384.0,384.0)];
112     crosshairSprite = [[OpenGLSprite alloc] initWithImage:crosshairImage
113                                             cropRectangle:NSMakeRect(0.0, 0.0, [crosshairImage size].width, [crosshairImage size].height)
114                                                      size:NSMakeSize(48.0,48.0)];
115     movehintSprite = [[OpenGLSprite alloc] initWithImage:movehintImage
116                                            cropRectangle:NSMakeRect(0.0, 0.0, [movehintImage size].width, [movehintImage size].height)
117                                                     size:NSMakeSize(48.0,48.0)];
118     if (_gemSpriteArray)
119         [_gemSpriteArray release];
120     _gemSpriteArray = [[NSMutableArray arrayWithCapacity:0] retain];
121     for (i = 0; i < 7; i++)
122     {
123         NSImage *image = [_gemImageArray objectAtIndex:i];
124         OpenGLSprite *sprite = [[OpenGLSprite alloc] initWithImage:image
125                                                      cropRectangle:NSMakeRect(0.0, 0.0, [image size].width, [image size].height)
126                                                               size:NSMakeSize(48.0,48.0)];
127         
128         [_gemSpriteArray addObject:sprite];
129         [sprite release];
130     }
132     if (!legendImage)
133     {
134         legendImage = [[NSImage alloc] initWithSize:NSMakeSize(384,384)];
135         [legendImage lockFocus];
136         [[NSColor clearColor] set];
137         NSRectFill(NSMakeRect(0,0,384,384));
138         [legendImage unlockFocus];
139         legendSprite = [[OpenGLSprite alloc] initWithImage:legendImage
140                                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)
141                                                     size:[legendImage size]];
142         
143         [self setLegend:[NSImage imageNamed:@"title"]];
144     }
145     //
146     //
147     //////
149     // if custom backgrounds are to be used initialise the array of paths to images
150     //
151     if ([[NSUserDefaults standardUserDefaults]  boolForKey:@"useCustomBackgrounds"])
152     {
153         NSString *customBackgroundFolderPath = [[[NSUserDefaults standardUserDefaults] stringForKey:@"customBackgroundFolderPath"] stringByResolvingSymlinksInPath];
155         //NSLog(@"customBackgroundFolderPath = ",customBackgroundFolderPath);
156         
157         if (customBackgroundFolderPath)
158         {
159             // borrowed code here
160             NSDirectoryEnumerator *picturesFolderEnum;
161             NSString *relativeFilePath,*fullPath;
162             // grab all picture formats NSImage knows about - we'll assume that if we can read them,
163             // we can set them to be the desktop picture
164             NSArray *imageFormats=[NSImage imageFileTypes];
166             if (backgroundImagePathArray)
167                 [backgroundImagePathArray autorelease];
168             backgroundImagePathArray = [[NSMutableArray arrayWithCapacity:0] retain];
169             // build the array
171             // borrowed code here
172             // now we need to go scan the folder chosen, enumerating through to find all picture files
173             picturesFolderEnum=[[NSFileManager defaultManager] enumeratorAtPath:customBackgroundFolderPath];
175             relativeFilePath=[picturesFolderEnum nextObject];
176             while (relativeFilePath)
177             {
178                 fullPath=[NSString stringWithFormat:@"%@/%@",customBackgroundFolderPath,relativeFilePath];
180                 // If the file's extension or type matches a format that NSImage understands,
181                 // then we're good to go, and we add a new menu item, using the display name
182                 // (which may have a hidden extension) for the menu item's title and passing
183                 // the full path to the picture to store with the menu item
184                 if ([imageFormats containsObject:[relativeFilePath pathExtension]] ||
185                     [imageFormats containsObject:NSHFSTypeOfFile(fullPath)])
186                 {
187                     [backgroundImagePathArray addObject:fullPath];
188                 }
189                 relativeFilePath=[picturesFolderEnum nextObject];
190             }
192             //NSLog(@"[backgroundImagePathArray count]= %d",[backgroundImagePathArray count]);
193             
194             //
195         }
197     }
199     [self newBackground];
200     
201     
204 - (void) loadImageArray
206     BOOL        useAlternateGraphics, useImportedGraphics;
207     useAlternateGraphics = [[NSUserDefaults standardUserDefaults]
208                                         boolForKey:@"useAlternateGraphics"];
209     useImportedGraphics = [[NSUserDefaults standardUserDefaults]
210                                         boolForKey:@"useImportedGraphics"];
211     if (_gemImageArray)
212         [_gemImageArray release];
213     _gemImageArray = [[NSMutableArray arrayWithCapacity:0] retain];
214     if (!useAlternateGraphics)
215     {
216         //NSLog(@"Loading regular graphics");
217         [_gemImageArray addObject:[NSImage imageNamed:@"1gem"]];
218         [_gemImageArray addObject:[NSImage imageNamed:@"2gem"]];
219         [_gemImageArray addObject:[NSImage imageNamed:@"3gem"]];
220         [_gemImageArray addObject:[NSImage imageNamed:@"4gem"]];
221         [_gemImageArray addObject:[NSImage imageNamed:@"5gem"]];
222         [_gemImageArray addObject:[NSImage imageNamed:@"6gem"]];
223         [_gemImageArray addObject:[NSImage imageNamed:@"7gem"]];
224     }
225     else
226     {
227         //NSData        *tiffData = [[[NSUserDefaults standardUserDefaults]
228         //                                dataForKey:@"tiffData"] retain];
229         if (!useImportedGraphics)
230         {
231             //NSLog(@"Loading alternate graphics");
232             [_gemImageArray addObject:[NSImage imageNamed:@"1gemA"]];
233             [_gemImageArray addObject:[NSImage imageNamed:@"2gemA"]];
234             [_gemImageArray addObject:[NSImage imageNamed:@"3gemA"]];
235             [_gemImageArray addObject:[NSImage imageNamed:@"4gemA"]];
236             [_gemImageArray addObject:[NSImage imageNamed:@"5gemA"]];
237             [_gemImageArray addObject:[NSImage imageNamed:@"6gemA"]];
238             [_gemImageArray addObject:[NSImage imageNamed:@"7gemA"]];
239         }
240         else
241         {
242             int i = 0;
243             //NSLog(@"Loading custom graphics");
244             for (i = 0; i < 7; i++)
245             {
246                 NSString *key = [NSString stringWithFormat:@"tiffGemImage%d", i];
247                 NSData  *tiffData = [[NSUserDefaults standardUserDefaults]
248                                         dataForKey:key];
249                 NSImage *gemImage = [[NSImage alloc] initWithData:tiffData];
250                 [_gemImageArray addObject:gemImage];
251                 [gemImage release];
252             }
253             
254         }
255     }
258 - (void) setShowHint:(BOOL)value
260     showHint = value;
263 - (void) setPaused:(BOOL)value
265     paused = value;
266     if (paused)
267     {
268         animationStatus = _animating;
269         _animating = NO;
270     }
271     else
272         _animating = animationStatus;
275 // ANIMATE called by the Timer
276 - (void) animate
278     if (!paused)
279     {
280         //
281         // MIKE WESSLER'S Scorebubbles
282         //
283       
284         //
285         // needsUpdate added so setNeedsDisplay gets called once at most
286         //
287         BOOL needsUpdate = FALSE;
288         
289         // animate bubbles, if any
290         for (NSUInteger b = 0; b < [[_game scoreBubbles] count]; b++) {
291             ScoreBubble *sb = [[_game scoreBubbles] objectAtIndex:b];
292             int more = [sb animate];
293             needsUpdate = TRUE;
294             if (!more) {
295                 [[_game scoreBubbles] removeObjectAtIndex:b];
296                 b--;
297             }
298         }
299         
300         if (_animating)
301         {
302             //NSLog(@"GameView.animate");
303             if (_game != nil)
304             {
305                 int i,j,c;      // animate each gem in the grid
306                 c = 0;          // animation accumulator
307                 for (i = 0; i < 8; i++)
308                     for (j = 0; j < 8; j++)
309                         if ([_game gemAt:i:j])  c += [[_game gemAt:i:j] animate];
310                 if (c == 0)
311                     [gameController animationEnded];
312             }
313             needsUpdate = TRUE;
314         }
315         else
316         {
317             ticksSinceLastMove++;
318             if (ticksSinceLastMove > 500)
319             {
320                 needsUpdate = TRUE;
321             }
322         }
323         if (needsUpdate)
324             [self setNeedsDisplay:YES];
325     }
328 - (void) newBackground
330     if (([gameController useCustomBackgrounds])&&(backgroundImagePathArray)&&([backgroundImagePathArray count] > 0))
331     {
332         NSString *imagePath = [backgroundImagePathArray objectAtIndex:0];
333         [backgroundImagePathArray addObject:imagePath];
334         [backgroundImagePathArray removeObjectAtIndex:0];
335         //NSLog(@"Taking image from path: %@",imagePath);
336         
337         backgroundImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
338         
339         //NSLog(@"Image size is %f x %f",[backgroundImage size].width, [backgroundImage size].height);
341         [backgroundSprite substituteTextureFromImage:backgroundImage];
342         //backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
343         //                                         cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
344         //                                                 size:NSMakeSize(384.0,384.0)];
345     }
346     else
347     {
348         NSData *tiffData = [[NSUserDefaults standardUserDefaults]       dataForKey:@"backgroundTiffData"];
349         if (tiffData)
350             backgroundImage = [[NSImage alloc] initWithData:tiffData];
351         else
352             backgroundImage = [NSImage imageNamed:@"background"];
353         backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
354                                                  cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
355                                                           size:NSMakeSize(384.0,384.0)];
356     }    
359 - (void) setLegend:(id)value
361     // NEED TO DRAW LEGEND INTO LEGENDIMAGE THEN REPLACE THE TEXTURE IN LEGENDSPRITE
363     if (!value)         // is null
364     {
365         //NSLog(@"Legend cleared");
366         legend = value;
367         [self setNeedsDisplay:YES];
368         return;
369     }
371     //
372     //
373     [legendImage lockFocus];
374     [[NSColor clearColor] set];
375     NSRectFill(NSMakeRect(0,0,384,384));
376     if ([value isKindOfClass:[NSAttributedString class]])
377     {
378         NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
379         [(NSAttributedString *)value drawAtPoint:legendPoint];
380     }
381     if ([value isKindOfClass:[NSImage class]])
382     {
383         NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
384         [(NSImage *)value compositeToPoint:legendPoint operation:NSCompositeSourceOver];
385     }
386     [legendImage unlockFocus];
387     [legendSprite replaceTextureFromImage:legendImage
388                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
390     legend = legendSprite;
391     ticksSinceLastMove = 0;
392     showHighScores = NO;
393     _animating = NO;
395     [self setNeedsDisplay:YES];
396     
397     //
398     //
401 - (void) setHTMLLegend:(NSString *)value
403     NSData              *htmlData = [NSData dataWithBytes:[value UTF8String] length:[value length]];
404     [self setLegend:[[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL]];
407 - (void) setHiScoreLegend:(NSAttributedString *)value
409     hiScoreLegend = value;
412 - (void) setHTMLHiScoreLegend:(NSString *)value
414     NSData *htmlData = [NSData dataWithBytes:[value UTF8String] length:[value length]];
415     [self setHiScoreLegend:[[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL]];
418 - (void) setLastMoveDate
420     ticksSinceLastMove = 0;
423 - (void) showHighScores:(NSArray *)scores andNames:(NSArray *)names
425     hiScoreNumbers = scores;
426     hiScoreNames = names;
427     showHighScores = YES;
428     _animating = NO;
429     scoreScroll = 0;
430     [self setNeedsDisplay:YES];
433 - (void)drawRect:(NSRect)rect
435   #pragma unused (rect)
436   
437     int i,j;
439     // Open GL
440     float       size = 384.0/2.0;       // screenSize/2;
441     float       clearDepth = 1.0;
443     // try to fix image loading problem
444     if (!legendImage)
445         [self graphicSetUp];
447     //////////
449     if (!m_glContextInitialized)
450     {
451         glShadeModel(GL_FLAT);
453         glMatrixMode(GL_PROJECTION);
454         glLoadIdentity();       // reset matrix
456         glFrustum(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);     // set projection matrix
457         glMatrixMode(GL_MODELVIEW);
459         //glEnable(GL_DEPTH_TEST);              // depth buffer
461         glEnable(GL_BLEND);                     // alpha blending
462         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);              // alpha blending
463         m_glContextInitialized = YES;
464     }
466     glClearColor(0.3, 0.3, 0.3, 0.0);
467     glClearDepth(clearDepth);
468     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
470     glLoadIdentity();   // allows me to resize the window but keep position OK
472     glTranslatef(-1.0,-1.0,0.0);
473     glScalef(1/size,1/size,1.0);// scale to screen size and width
475     if (backgroundSprite)
476     {
477         [backgroundSprite blitToX:0.0
478                                 Y:0.0
479                                 Z:0.0];
480     }
481     if ((_game != nil) && (!paused))
482     {
483         for (i = 0; i < 8; i++)
484             for (j = 0; j < 8; j++)
485                 [[_game gemAt:i :j] drawSprite];
486         //
487         // MW - Scorebubbles
488         //
489         for (i=0; i<[[_game scoreBubbles] count]; i++) {
490             ScoreBubble *sb= [[_game scoreBubbles] objectAtIndex:i];
491             [sb drawSprite];
492         }
493         //
494         ////
495     }
497     if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
498     {
499       NSPoint crossHairPosition = gameController.crossHair1Position;
500       
501         [crosshairSprite blitToX:crossHairPosition.x
502                                Y:crossHairPosition.y
503                                Z:-0.5];
504     }
505     if (showHighScores)
506     {
507         [self showScores];      // draws the HighScores in the current focused view (Quartz)
508         //return;               // now legendSprite contains the drawing...
509     }
510     if (!legend)
511     {
512         if ((ticksSinceLastMove > 500)&&(showHint))
513         {
514           NSPoint hintPoint = _game.hintPoint;
515             [movehintSprite blitToX:hintPoint.x
516                                    Y:hintPoint.y
517                                    Z:-0.4
518                               Alpha:(sin((ticksSinceLastMove-497.0)/4.0)+1.0)/2.0];
519         }
520     }
521     else
522     {
523         if (ticksSinceLastMove > 500)
524             [self setLegend:[NSImage imageNamed:@"title"]];     // show Logo
525         if ([legend isKindOfClass:[OpenGLSprite class]])
526         {
527             //NSLog(@"Blitting legend");
528             [legend blitToX:0.0 Y:0.0 Z:-0.75];
529         }
530     }
531     glFlush();
532     
535 - (void) showScores
537     int i;
538     NSPoint legendPoint;
539     NSRect panelRect;
540     NSMutableDictionary *attr = [NSMutableDictionary dictionaryWithCapacity:0];
542     [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
544     [legendImage lockFocus];
545     [[NSColor clearColor] set];
546     NSRectFill(NSMakeRect(0,0,384,384));
548     [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:0.5] set];
549     panelRect = NSMakeRect(32, 16, 384-64, 384-32);
550     NSRectFill(panelRect);
551     
552     
553     legendPoint = NSMakePoint((384 - [hiScoreLegend size].width)/2,384 - [hiScoreLegend size].height*1.5 + scoreScroll);
555     [hiScoreLegend drawAtPoint:legendPoint];
556     
557     for (i = 0; i< 10; i++)
558     {
559         NSString *s1 = [NSString stringWithFormat:@"%d",[[hiScoreNumbers objectAtIndex:i] intValue]];
560         NSString *s2 = [hiScoreNames objectAtIndex:i];
561         NSPoint q1 = NSMakePoint( 192+20+1, 384 - 84 - i*30 + scoreScroll - 1); 
562         NSPoint q2 = NSMakePoint( 192-20-[s2 sizeWithAttributes:attr].width+1, 384 - 84 - i*30 + scoreScroll - 1); 
563         NSPoint p1 = NSMakePoint( 192+20, 384 - 84 - i*30 + scoreScroll); 
564         NSPoint p2 = NSMakePoint( 192-20-[s2 sizeWithAttributes:attr].width, 384 - 84 - i*30 + scoreScroll); 
566         [attr setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
568         [s1 drawAtPoint:q1 withAttributes:attr];
569         [s2 drawAtPoint:q2 withAttributes:attr];
570         
571         [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
573         [s1 drawAtPoint:p1 withAttributes:attr];
574         [s2 drawAtPoint:p2 withAttributes:attr];
575     }
576     [legendImage unlockFocus];
577     [legendSprite replaceTextureFromImage:legendImage
578                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
580     legend = legendSprite;
581     
582     showHighScores = NO;
585 - (BOOL)isOpaque {
586   return YES;
589 /* I want to add a new behaviour here, the click-drag for a square
590  * I'm prolly going to have to fake this by sending gameController two clicks
591  * I might have to change the shape of the mouse cursor too!
592  */
593 - (void)mouseDown:(NSEvent *)event {
594     NSPoint eventLocation = [event locationInWindow];
595     NSPoint center = [self convertPoint:eventLocation fromView:nil];
596     dragStartPoint = center;
599 - (void)mouseDragged:(NSEvent *)event
601   #pragma unused (event)
602   // do nothing for now
605 - (void)mouseUp:(NSEvent *)event
607     NSPoint eventLocation = [event locationInWindow];
608     NSPoint center = [self convertPoint:eventLocation fromView:nil];
609     
610     // check situation - is this a first or second mouseUp
611     if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
612     {
613         //NSLog(@"click at :%f,%f",center.x,center.y);
614         [gameController receiveClickAt:center.x:center.y];
615     }
616     else if (gameController.gameState == GAMESTATE_AWAITINGFIRSTCLICK)
617     {
618         int chx1 = floor(dragStartPoint.x / 48);
619         int chy1 = floor(dragStartPoint.y / 48);
620         int chx2 = floor(center.x / 48);
621         int chy2 = floor(center.y / 48);
622       
623       // ???: WTF, you don't really xor booleans...
624         if ((chx2 != chx1)^(chy2 != chy1))      // xor checks if a valid shove's occurred!
625         {
626             [gameController receiveClickAt:dragStartPoint.x:dragStartPoint.y];
627             [gameController receiveClickAt:center.x:center.y];
628         }
629         else {
630             [gameController receiveClickAt:center.x:center.y];
631         }
632     }
633         
636 @end