1 /* ----====----====----====----====----====----====----====----====----====----
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 ----====----====----====----====----====----====----====----====----====---- */
24 #import "GameController.h"
29 #import "ScoreBubble.h"
31 #import "OpenGLSprite.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
49 NSOpenGLPixelFormatAttribute attrs[] = {
50 NSOpenGLPFADepthSize, 1,
51 NSOpenGLPFAAccelerated,
53 NSOpenGLPixelFormat *pixFmt;
55 pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
57 self = [super initWithFrame:frame pixelFormat:pixFmt];
59 m_glContextInitialized = NO;
64 ticksSinceLastMove = 0;
66 docTypeDictionary = [NSDictionary dictionary];
75 [backgroundColor release];
77 [_gemImageArray release];
79 // ???: bug in the original code: [gemImageArray release];
80 [_gemSpriteArray release];
81 if (backgroundImagePathArray)
82 [backgroundImagePathArray release];
91 backgroundColor = [[NSColor purpleColor] retain];
93 [self loadImageArray];
95 tiffData = [[NSUserDefaults standardUserDefaults] dataForKey:@"backgroundTiffData"];
97 backgroundImage = [[NSImage alloc] initWithData:tiffData];
99 backgroundImage = [NSImage imageNamed:@"background"];
101 crosshairImage = [NSImage imageNamed:@"cross"];
102 movehintImage = [NSImage imageNamed:@"movehint"];
106 // Make the Open GL Sprites!
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)];
119 [_gemSpriteArray release];
120 _gemSpriteArray = [[NSMutableArray arrayWithCapacity:0] retain];
121 for (i = 0; i < 7; i++)
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)];
128 [_gemSpriteArray addObject:sprite];
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]];
143 [self setLegend:[NSImage imageNamed:@"title"]];
149 // if custom backgrounds are to be used initialise the array of paths to images
151 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"useCustomBackgrounds"])
153 NSString *customBackgroundFolderPath = [[[NSUserDefaults standardUserDefaults] stringForKey:@"customBackgroundFolderPath"] stringByResolvingSymlinksInPath];
155 //NSLog(@"customBackgroundFolderPath = ",customBackgroundFolderPath);
157 if (customBackgroundFolderPath)
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];
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)
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)])
187 [backgroundImagePathArray addObject:fullPath];
189 relativeFilePath=[picturesFolderEnum nextObject];
192 //NSLog(@"[backgroundImagePathArray count]= %d",[backgroundImagePathArray count]);
199 [self newBackground];
204 - (void) loadImageArray
206 BOOL useAlternateGraphics, useImportedGraphics;
207 useAlternateGraphics = [[NSUserDefaults standardUserDefaults]
208 boolForKey:@"useAlternateGraphics"];
209 useImportedGraphics = [[NSUserDefaults standardUserDefaults]
210 boolForKey:@"useImportedGraphics"];
212 [_gemImageArray release];
213 _gemImageArray = [[NSMutableArray arrayWithCapacity:0] retain];
214 if (!useAlternateGraphics)
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"]];
227 //NSData *tiffData = [[[NSUserDefaults standardUserDefaults]
228 // dataForKey:@"tiffData"] retain];
229 if (!useImportedGraphics)
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"]];
243 //NSLog(@"Loading custom graphics");
244 for (i = 0; i < 7; i++)
246 NSString *key = [NSString stringWithFormat:@"tiffGemImage%d", i];
247 NSData *tiffData = [[NSUserDefaults standardUserDefaults]
249 NSImage *gemImage = [[NSImage alloc] initWithData:tiffData];
250 [_gemImageArray addObject:gemImage];
258 - (void) setShowHint:(BOOL)value
263 - (void) setPaused:(BOOL)value
268 animationStatus = _animating;
272 _animating = animationStatus;
275 // ANIMATE called by the Timer
281 // MIKE WESSLER'S Scorebubbles
285 // needsUpdate added so setNeedsDisplay gets called once at most
287 BOOL needsUpdate = FALSE;
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];
295 [[_game scoreBubbles] removeObjectAtIndex:b];
302 //NSLog(@"GameView.animate");
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];
311 [gameController animationEnded];
317 ticksSinceLastMove++;
318 if (ticksSinceLastMove > 500)
324 [self setNeedsDisplay:YES];
328 - (void) newBackground
330 if (([gameController useCustomBackgrounds])&&(backgroundImagePathArray)&&([backgroundImagePathArray count] > 0))
332 NSString *imagePath = [backgroundImagePathArray objectAtIndex:0];
333 [backgroundImagePathArray addObject:imagePath];
334 [backgroundImagePathArray removeObjectAtIndex:0];
335 //NSLog(@"Taking image from path: %@",imagePath);
337 backgroundImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
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)];
348 NSData *tiffData = [[NSUserDefaults standardUserDefaults] dataForKey:@"backgroundTiffData"];
350 backgroundImage = [[NSImage alloc] initWithData:tiffData];
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)];
359 - (void) setLegend:(id)value
361 // NEED TO DRAW LEGEND INTO LEGENDIMAGE THEN REPLACE THE TEXTURE IN LEGENDSPRITE
363 if (!value) // is null
365 //NSLog(@"Legend cleared");
367 [self setNeedsDisplay:YES];
373 [legendImage lockFocus];
374 [[NSColor clearColor] set];
375 NSRectFill(NSMakeRect(0,0,384,384));
376 if ([value isKindOfClass:[NSAttributedString class]])
378 NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
379 [(NSAttributedString *)value drawAtPoint:legendPoint];
381 if ([value isKindOfClass:[NSImage class]])
383 NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
384 [(NSImage *)value compositeToPoint:legendPoint operation:NSCompositeSourceOver];
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;
395 [self setNeedsDisplay:YES];
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;
430 [self setNeedsDisplay:YES];
433 - (void)drawRect:(NSRect)rect
435 #pragma unused (rect)
440 float size = 384.0/2.0; // screenSize/2;
441 float clearDepth = 1.0;
443 // try to fix image loading problem
449 if (!m_glContextInitialized)
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;
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)
477 [backgroundSprite blitToX:0.0
481 if ((_game != nil) && (!paused))
483 for (i = 0; i < 8; i++)
484 for (j = 0; j < 8; j++)
485 [[_game gemAt:i :j] drawSprite];
489 for (i=0; i<[[_game scoreBubbles] count]; i++) {
490 ScoreBubble *sb= [[_game scoreBubbles] objectAtIndex:i];
497 if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
499 NSPoint crossHairPosition = gameController.crossHair1Position;
501 [crosshairSprite blitToX:crossHairPosition.x
502 Y:crossHairPosition.y
507 [self showScores]; // draws the HighScores in the current focused view (Quartz)
508 //return; // now legendSprite contains the drawing...
512 if ((ticksSinceLastMove > 500)&&(showHint))
514 NSPoint hintPoint = _game.hintPoint;
515 [movehintSprite blitToX:hintPoint.x
518 Alpha:(sin((ticksSinceLastMove-497.0)/4.0)+1.0)/2.0];
523 if (ticksSinceLastMove > 500)
524 [self setLegend:[NSImage imageNamed:@"title"]]; // show Logo
525 if ([legend isKindOfClass:[OpenGLSprite class]])
527 //NSLog(@"Blitting legend");
528 [legend blitToX:0.0 Y:0.0 Z:-0.75];
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);
553 legendPoint = NSMakePoint((384 - [hiScoreLegend size].width)/2,384 - [hiScoreLegend size].height*1.5 + scoreScroll);
555 [hiScoreLegend drawAtPoint:legendPoint];
557 for (i = 0; i< 10; i++)
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];
571 [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
573 [s1 drawAtPoint:p1 withAttributes:attr];
574 [s2 drawAtPoint:p2 withAttributes:attr];
576 [legendImage unlockFocus];
577 [legendSprite replaceTextureFromImage:legendImage
578 cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
580 legend = legendSprite;
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!
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];
610 // check situation - is this a first or second mouseUp
611 if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
613 //NSLog(@"click at :%f,%f",center.x,center.y);
614 [gameController receiveClickAt:center.x:center.y];
616 else if (gameController.gameState == GAMESTATE_AWAITINGFIRSTCLICK)
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);
623 // ???: WTF, you don't really xor booleans...
624 if ((chx2 != chx1)^(chy2 != chy1)) // xor checks if a valid shove's occurred!
626 [gameController receiveClickAt:dragStartPoint.x:dragStartPoint.y];
627 [gameController receiveClickAt:center.x:center.y];
630 [gameController receiveClickAt:center.x:center.y];