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