add koko_legumes
[exterlulz-kokogems.git] / src / GameView.m
blobd71beed9b1470c0e1bb4a79238565679296dc90e
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 #include <math.h>
23 //#import <OpenGL/gl.h>
24 //#import <OpenGL/glu.h>
26 #import "GameView.h"
27 #import "GameController.h"
28 #import "Game.h"
29 #import "Gem.h"
31 // MW...
33 #import "ScoreBubble.h"
35 // OpenGLSprites
37 #import "OpenGLSprite.h"
39 @implementation GameView
41 @synthesize _animating;
43 - (id)initWithFrame:(NSRect)frame {
45     //NSData    *tiffData;
47     NSOpenGLPixelFormatAttribute attrs[] = {
48         NSOpenGLPFADepthSize, 1,
49         NSOpenGLPFAAccelerated,
50         0};
51     NSOpenGLPixelFormat *pixFmt;
53     pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
54     self = [super initWithFrame:frame pixelFormat:pixFmt];
56     m_glContextInitialized = NO;
58     _animating = NO;
59     showHighScores = NO;
60     showHint = YES;
61     ticksSinceLastMove = 0;
62     
63     docTypeDictionary = [NSDictionary dictionary];
64     
65     return self;
68 - (void) dealloc
70     if (backgroundColor)
71         [backgroundColor release];
72     if (gemImageArray)
73         [gemImageArray release];
74     if (gemSpriteArray)
75         [gemImageArray release];
76     if (backgroundImagePathArray)
77         [backgroundImagePathArray release];
78     [super dealloc];
81 - (void) graphicSetUp
83     int         i;
84     NSData      *tiffData;
86     backgroundColor = [[NSColor purpleColor] retain];
88     [self loadImageArray];
90         tiffData = [[NSUserDefaults standardUserDefaults]       dataForKey:@"backgroundTiffData"];
91     if (tiffData)
92         backgroundImage = [[NSImage alloc] initWithData:tiffData];
93     else
94         backgroundImage = [NSImage imageNamed:@"background"];
96     crosshairImage = [NSImage imageNamed:@"cross"];
97     movehintImage = [NSImage imageNamed:@"movehint"];
99     //////
100     //
101     // Make the Open GL Sprites!
102     //
103     backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
104                                              cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
105                                                       size:NSMakeSize(384.0,384.0)];
107     crosshairSprite = [[OpenGLSprite alloc] initWithImage:crosshairImage
108                                             cropRectangle:NSMakeRect(0.0, 0.0, [crosshairImage size].width, [crosshairImage size].height)
109                                                      size:NSMakeSize(48.0,48.0)];
110     movehintSprite = [[OpenGLSprite alloc] initWithImage:movehintImage
111                                            cropRectangle:NSMakeRect(0.0, 0.0, [movehintImage size].width, [movehintImage size].height)
112                                                     size:NSMakeSize(48.0,48.0)];
113     if (gemSpriteArray)
114         [gemSpriteArray release];
115     gemSpriteArray = [[NSMutableArray arrayWithCapacity:0] retain];
116     for (i = 0; i < 7; i++)
117     {
118         NSImage *image = [gemImageArray objectAtIndex:i];
119         OpenGLSprite *sprite = [[OpenGLSprite alloc] initWithImage:image
120                                                      cropRectangle:NSMakeRect(0.0, 0.0, [image size].width, [image size].height)
121                                                               size:NSMakeSize(48.0,48.0)];
122         
123         [gemSpriteArray addObject:sprite];
124         [sprite release];
125     }
127     if (!legendImage)
128     {
129         legendImage = [[NSImage alloc] initWithSize:NSMakeSize(384,384)];
130         [legendImage lockFocus];
131         [[NSColor clearColor] set];
132         NSRectFill(NSMakeRect(0,0,384,384));
133         [legendImage unlockFocus];
134         legendSprite = [[OpenGLSprite alloc] initWithImage:legendImage
135                                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)
136                                                     size:[legendImage size]];
137         
138         [self setLegend:[NSImage imageNamed:@"title"]];
139     }
140     //
141     //
142     //////
144     // if custom backgrounds are to be used initialise the array of paths to images
145     //
146     if ([[NSUserDefaults standardUserDefaults]  boolForKey:@"useCustomBackgrounds"])
147     {
148         NSString *customBackgroundFolderPath = [[[NSUserDefaults standardUserDefaults] stringForKey:@"customBackgroundFolderPath"] stringByResolvingSymlinksInPath];
150         //NSLog(@"customBackgroundFolderPath = ",customBackgroundFolderPath);
151         
152         if (customBackgroundFolderPath)
153         {
154             // borrowed code here
155             NSDirectoryEnumerator *picturesFolderEnum;
156             NSString *relativeFilePath,*fullPath;
157             // grab all picture formats NSImage knows about - we'll assume that if we can read them,
158             // we can set them to be the desktop picture
159             NSArray *imageFormats=[NSImage imageFileTypes];
161             if (backgroundImagePathArray)
162                 [backgroundImagePathArray autorelease];
163             backgroundImagePathArray = [[NSMutableArray arrayWithCapacity:0] retain];
164             // build the array
166             // borrowed code here
167             // now we need to go scan the folder chosen, enumerating through to find all picture files
168             picturesFolderEnum=[[NSFileManager defaultManager] enumeratorAtPath:customBackgroundFolderPath];
170             relativeFilePath=[picturesFolderEnum nextObject];
171             while (relativeFilePath)
172             {
173                 fullPath=[NSString stringWithFormat:@"%@/%@",customBackgroundFolderPath,relativeFilePath];
175                 // If the file's extension or type matches a format that NSImage understands,
176                 // then we're good to go, and we add a new menu item, using the display name
177                 // (which may have a hidden extension) for the menu item's title and passing
178                 // the full path to the picture to store with the menu item
179                 if ([imageFormats containsObject:[relativeFilePath pathExtension]] ||
180                     [imageFormats containsObject:NSHFSTypeOfFile(fullPath)])
181                 {
182                     [backgroundImagePathArray addObject:fullPath];
183                 }
184                 relativeFilePath=[picturesFolderEnum nextObject];
185             }
187             //NSLog(@"[backgroundImagePathArray count]= %d",[backgroundImagePathArray count]);
188             
189             //
190         }
192     }
194     [self newBackground];
195     
196     
199 - (void) loadImageArray
201     BOOL        useAlternateGraphics, useImportedGraphics;
202     useAlternateGraphics = [[NSUserDefaults standardUserDefaults]
203                                         boolForKey:@"useAlternateGraphics"];
204     useImportedGraphics = [[NSUserDefaults standardUserDefaults]
205                                         boolForKey:@"useImportedGraphics"];
206     if (gemImageArray)
207         [gemImageArray release];
208     gemImageArray = [[NSMutableArray arrayWithCapacity:0] retain];
209     if (!useAlternateGraphics)
210     {
211         //NSLog(@"Loading regular graphics");
212         [gemImageArray addObject:[NSImage imageNamed:@"1gem"]];
213         [gemImageArray addObject:[NSImage imageNamed:@"2gem"]];
214         [gemImageArray addObject:[NSImage imageNamed:@"3gem"]];
215         [gemImageArray addObject:[NSImage imageNamed:@"4gem"]];
216         [gemImageArray addObject:[NSImage imageNamed:@"5gem"]];
217         [gemImageArray addObject:[NSImage imageNamed:@"6gem"]];
218         [gemImageArray addObject:[NSImage imageNamed:@"7gem"]];
219     }
220     else
221     {
222         //NSData        *tiffData = [[[NSUserDefaults standardUserDefaults]
223         //                                dataForKey:@"tiffData"] retain];
224         if (!useImportedGraphics)
225         {
226             //NSLog(@"Loading alternate graphics");
227             [gemImageArray addObject:[NSImage imageNamed:@"1gemA"]];
228             [gemImageArray addObject:[NSImage imageNamed:@"2gemA"]];
229             [gemImageArray addObject:[NSImage imageNamed:@"3gemA"]];
230             [gemImageArray addObject:[NSImage imageNamed:@"4gemA"]];
231             [gemImageArray addObject:[NSImage imageNamed:@"5gemA"]];
232             [gemImageArray addObject:[NSImage imageNamed:@"6gemA"]];
233             [gemImageArray addObject:[NSImage imageNamed:@"7gemA"]];
234         }
235         else
236         {
237             int i = 0;
238             //NSLog(@"Loading custom graphics");
239             for (i = 0; i < 7; i++)
240             {
241                 NSString *key = [NSString stringWithFormat:@"tiffGemImage%d", i];
242                 NSData  *tiffData = [[NSUserDefaults standardUserDefaults]
243                                         dataForKey:key];
244                 NSImage *gemImage = [[NSImage alloc] initWithData:tiffData];
245                 [gemImageArray addObject:gemImage];
246                 [gemImage release];
247             }
248             
249         }
250     }
253 - (void) setMuted:(BOOL)value
255     muted = value;
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)
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) setGame:(Game *) agame
330     game = agame;
332 - (Game *) game
334     return game;
336 - (NSArray *) imageArray
338     return gemImageArray;
340 - (NSArray *) spriteArray
342     return gemSpriteArray;
345 - (void) newBackground
347     if (([gameController useCustomBackgrounds])&&(backgroundImagePathArray)&&([backgroundImagePathArray count] > 0))
348     {
349         NSString *imagePath = [backgroundImagePathArray objectAtIndex:0];
350         [backgroundImagePathArray addObject:imagePath];
351         [backgroundImagePathArray removeObjectAtIndex:0];
352         //NSLog(@"Taking image from path: %@",imagePath);
353         
354         backgroundImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
355         
356         //NSLog(@"Image size is %f x %f",[backgroundImage size].width, [backgroundImage size].height);
358         [backgroundSprite substituteTextureFromImage:backgroundImage];
359         //backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
360         //                                         cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
361         //                                                 size:NSMakeSize(384.0,384.0)];
362     }
363     else
364     {
365         NSData *tiffData = [[NSUserDefaults standardUserDefaults]       dataForKey:@"backgroundTiffData"];
366         if (tiffData)
367             backgroundImage = [[NSImage alloc] initWithData:tiffData];
368         else
369             backgroundImage = [NSImage imageNamed:@"background"];
370         backgroundSprite = [[OpenGLSprite alloc] initWithImage:backgroundImage
371                                                  cropRectangle:NSMakeRect(0.0, 0.0, [backgroundImage size].width, [backgroundImage size].height)
372                                                           size:NSMakeSize(384.0,384.0)];
373     }    
376 - (void) setLegend:(id)value
378     // NEED TO DRAW LEGEND INTO LEGENDIMAGE THEN REPLACE THE TEXTURE IN LEGENDSPRITE
380     if (!value)         // is null
381     {
382         //NSLog(@"Legend cleared");
383         legend = value;
384         [self setNeedsDisplay:YES];
385         return;
386     }
388     //
389     //
390     [legendImage lockFocus];
391     [[NSColor clearColor] set];
392     NSRectFill(NSMakeRect(0,0,384,384));
393     if ([value isKindOfClass:[NSAttributedString class]])
394     {
395         NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
396         [(NSAttributedString *)value drawAtPoint:legendPoint];
397     }
398     if ([value isKindOfClass:[NSImage class]])
399     {
400         NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
401         [(NSImage *)value compositeToPoint:legendPoint operation:NSCompositeSourceOver];
402     }
403     [legendImage unlockFocus];
404     [legendSprite replaceTextureFromImage:legendImage
405                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
407     legend = legendSprite;
408     ticksSinceLastMove = 0;
409     showHighScores = NO;
410     _animating = NO;
412     [self setNeedsDisplay:YES];
413     
414     //
415     //
418 - (void) setHTMLLegend:(NSString *)value
420     NSData              *htmlData = [NSData dataWithBytes:[value UTF8String] length:[value length]];
421     [self setLegend:[[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL]];
424 - (void) setHiScoreLegend:(NSAttributedString *)value
426     hiScoreLegend = value;
429 - (void) setHTMLHiScoreLegend:(NSString *)value
431     NSData *htmlData = [NSData dataWithBytes:[value UTF8String] length:[value length]];
432     [self setHiScoreLegend:[[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL]];
435 - (void) setLastMoveDate
437     ticksSinceLastMove = 0;
440 - (void) showHighScores:(NSArray *)scores andNames:(NSArray *)names
442     hiScoreNumbers = scores;
443     hiScoreNames = names;
444     showHighScores = YES;
445     _animating = NO;
446     scoreScroll = 0;
447     [self setNeedsDisplay:YES];
450 // drawRect: should be overridden in subclassers of NSView to do necessary
451 // drawing in order to recreate the the look of the view. It will be called
452 // to draw the whole view or parts of it (pay attention the rect argument);
453 // it will also be called during printing if your app is set up to print.
455 - (void)drawRect:(NSRect)rect {
456     int i,j;
458     // Open GL
459     float       size = 384.0/2.0;       // screenSize/2;
460     float       clearDepth = 1.0;
462     // try to fix image loading problem
463     if (!legendImage)
464         [self graphicSetUp];
466     //////////
468     if (!m_glContextInitialized)
469     {
470         glShadeModel(GL_FLAT);
472         glMatrixMode(GL_PROJECTION);
473         glLoadIdentity();       // reset matrix
475         glFrustum(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);     // set projection matrix
476         glMatrixMode(GL_MODELVIEW);
478         //glEnable(GL_DEPTH_TEST);              // depth buffer
480         glEnable(GL_BLEND);                     // alpha blending
481         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);              // alpha blending
482         m_glContextInitialized = YES;
483     }
485     glClearColor(0.3, 0.3, 0.3, 0.0);
486     glClearDepth(clearDepth);
487     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
489     glLoadIdentity();   // allows me to resize the window but keep position OK
491     glTranslatef(-1.0,-1.0,0.0);
492     glScalef(1/size,1/size,1.0);// scale to screen size and width
494     if (backgroundSprite)
495     {
496         [backgroundSprite blitToX:0.0
497                                 Y:0.0
498                                 Z:0.0];
499     }
500     if ((game)&&(!paused))
501     {
502         for (i = 0; i < 8; i++)
503             for (j = 0; j < 8; j++)
504                 [[game gemAt:i :j] drawSprite];
505         //
506         // MW - Scorebubbles
507         //
508         for (i=0; i<[[game scoreBubbles] count]; i++) {
509             ScoreBubble *sb= [[game scoreBubbles] objectAtIndex:i];
510             [sb drawSprite];
511         }
512         //
513         ////
514     }
516     if ([gameController gameState] == GAMESTATE_AWAITINGSECONDCLICK)
517     {
518         [crosshairSprite blitToX:[gameController crossHair1Position].x
519                                Y:[gameController crossHair1Position].y
520                                Z:-0.5];
521     }
522     if (showHighScores)
523     {
524         [self showScores];      // draws the HighScores in the current focused view (Quartz)
525         //return;               // now legendSprite contains the drawing...
526     }
527     if (!legend)
528     {
529         if ((ticksSinceLastMove > 500)&&(showHint))
530         {
531             [movehintSprite blitToX:[game hintPoint].x
532                                    Y:[game hintPoint].y
533                                    Z:-0.4
534                               Alpha:(sin((ticksSinceLastMove-497.0)/4.0)+1.0)/2.0];
535         }
536     }
537     else
538     {
539         if (ticksSinceLastMove > 500)
540             [self setLegend:[NSImage imageNamed:@"title"]];     // show Logo
541         if ([legend isKindOfClass:[OpenGLSprite class]])
542         {
543             //NSLog(@"Blitting legend");
544             [legend blitToX:0.0 Y:0.0 Z:-0.75];
545         }
546     }
547     glFlush();
548     
551 - (void) showScores
553     int i;
554     NSPoint legendPoint;
555     NSRect panelRect;
556     NSMutableDictionary *attr = [NSMutableDictionary dictionaryWithCapacity:0];
558     [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
560     [legendImage lockFocus];
561     [[NSColor clearColor] set];
562     NSRectFill(NSMakeRect(0,0,384,384));
564     [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:0.5] set];
565     panelRect = NSMakeRect(32, 16, 384-64, 384-32);
566     NSRectFill(panelRect);
567     
568     
569     legendPoint = NSMakePoint((384 - [hiScoreLegend size].width)/2,384 - [hiScoreLegend size].height*1.5 + scoreScroll);
571     [hiScoreLegend drawAtPoint:legendPoint];
572     
573     for (i = 0; i< 10; i++)
574     {
575         NSString *s1 = [NSString stringWithFormat:@"%d",[[hiScoreNumbers objectAtIndex:i] intValue]];
576         NSString *s2 = [hiScoreNames objectAtIndex:i];
577         NSPoint q1 = NSMakePoint( 192+20+1, 384 - 84 - i*30 + scoreScroll - 1); 
578         NSPoint q2 = NSMakePoint( 192-20-[s2 sizeWithAttributes:attr].width+1, 384 - 84 - i*30 + scoreScroll - 1); 
579         NSPoint p1 = NSMakePoint( 192+20, 384 - 84 - i*30 + scoreScroll); 
580         NSPoint p2 = NSMakePoint( 192-20-[s2 sizeWithAttributes:attr].width, 384 - 84 - i*30 + scoreScroll); 
582         [attr setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
584         [s1 drawAtPoint:q1 withAttributes:attr];
585         [s2 drawAtPoint:q2 withAttributes:attr];
586         
587         [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
589         [s1 drawAtPoint:p1 withAttributes:attr];
590         [s2 drawAtPoint:p2 withAttributes:attr];
591     }
592     [legendImage unlockFocus];
593     [legendSprite replaceTextureFromImage:legendImage
594                             cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
596     legend = legendSprite;
597     
598     showHighScores = NO;
601 - (BOOL)isOpaque {
602     return YES;
605 /* I want to add a new behaviour here, the click-drag for a square
606  * I'm prolly going to have to fake this by sending gameController two clicks
607  * I might have to change the shape of the mouse cursor too!
608  */
609 - (void)mouseDown:(NSEvent *)event {
610     NSPoint eventLocation = [event locationInWindow];
611     NSPoint center = [self convertPoint:eventLocation fromView:nil];
612     dragStartPoint = center;
615 - (void)mouseDragged:(NSEvent *)event {
616     // do nothing for now
619 - (void)mouseUp:(NSEvent *)event
621     NSPoint eventLocation = [event locationInWindow];
622     NSPoint center = [self convertPoint:eventLocation fromView:nil];
623     
624     // check situation - is this a first or second mouseUp
625     if ([gameController gameState] == GAMESTATE_AWAITINGSECONDCLICK)
626     {
627         //NSLog(@"click at :%f,%f",center.x,center.y);
628         [gameController receiveClickAt:center.x:center.y];
629     }
630     else if ([gameController gameState] == GAMESTATE_AWAITINGFIRSTCLICK)
631     {
632         int chx1 = floor(dragStartPoint.x / 48);
633         int chy1 = floor(dragStartPoint.y / 48);
634         int chx2 = floor(center.x / 48);
635         int chy2 = floor(center.y / 48);
636       
637       // ???: WTF, you don't really xor booleans...
638         if ((chx2 != chx1)^(chy2 != chy1))      // xor checks if a valid shove's occurred!
639         {
640             [gameController receiveClickAt:dragStartPoint.x:dragStartPoint.y];
641             [gameController receiveClickAt:center.x:center.y];
642         }
643         else {
644             [gameController receiveClickAt:center.x:center.y];
645         }
646     }
647         
650 @end