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 ----====----====----====----====----====----====----====----====----====---- */
22 //#import <OpenGL/gl.h>
23 //#import <OpenGL/glu.h>
26 #import "GameController.h"
31 #import "ScoreBubble.h"
33 #import "OpenGLSprite.h"
37 @implementation GameView
39 @synthesize animating = _animating;
41 - (id)initWithFrame:(NSRect)frame {
45 NSOpenGLPixelFormatAttribute attrs[] = {
46 NSOpenGLPFADepthSize, 1,
47 NSOpenGLPFAAccelerated,
49 NSOpenGLPixelFormat *pixFmt;
51 pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
52 self = [super initWithFrame:frame pixelFormat:pixFmt];
54 m_glContextInitialized = NO;
59 ticksSinceLastMove = 0;
61 docTypeDictionary = [NSDictionary dictionary];
69 [backgroundColor release];
71 [gemImageArray release];
73 [gemImageArray release];
74 if (backgroundImagePathArray)
75 [backgroundImagePathArray release];
84 backgroundColor = [[NSColor purpleColor] retain];
86 [self loadImageArray];
88 tiffData = [[NSUserDefaults standardUserDefaults] dataForKey:@"backgroundTiffData"];
90 backgroundImage = [[NSImage alloc] initWithData:tiffData];
92 backgroundImage = [NSImage imageNamed:@"background"];
94 crosshairImage = [NSImage imageNamed:@"cross"];
95 movehintImage = [NSImage imageNamed:@"movehint"];
99 // Make the Open GL Sprites!
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)];
112 [gemSpriteArray release];
113 gemSpriteArray = [[NSMutableArray arrayWithCapacity:0] retain];
114 for (i = 0; i < 7; i++)
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)];
121 [gemSpriteArray addObject:sprite];
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]];
136 [self setLegend:[NSImage imageNamed:@"title"]];
142 // if custom backgrounds are to be used initialise the array of paths to images
144 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"useCustomBackgrounds"])
146 NSString *customBackgroundFolderPath = [[[NSUserDefaults standardUserDefaults] stringForKey:@"customBackgroundFolderPath"] stringByResolvingSymlinksInPath];
148 //NSLog(@"customBackgroundFolderPath = ",customBackgroundFolderPath);
150 if (customBackgroundFolderPath)
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];
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)
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)])
180 [backgroundImagePathArray addObject:fullPath];
182 relativeFilePath=[picturesFolderEnum nextObject];
185 //NSLog(@"[backgroundImagePathArray count]= %d",[backgroundImagePathArray count]);
192 [self newBackground];
197 - (void) loadImageArray
199 BOOL useAlternateGraphics, useImportedGraphics;
200 useAlternateGraphics = [[NSUserDefaults standardUserDefaults]
201 boolForKey:@"useAlternateGraphics"];
202 useImportedGraphics = [[NSUserDefaults standardUserDefaults]
203 boolForKey:@"useImportedGraphics"];
205 [gemImageArray release];
206 gemImageArray = [[NSMutableArray arrayWithCapacity:0] retain];
207 if (!useAlternateGraphics)
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"]];
220 //NSData *tiffData = [[[NSUserDefaults standardUserDefaults]
221 // dataForKey:@"tiffData"] retain];
222 if (!useImportedGraphics)
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"]];
236 //NSLog(@"Loading custom graphics");
237 for (i = 0; i < 7; i++)
239 NSString *key = [NSString stringWithFormat:@"tiffGemImage%d", i];
240 NSData *tiffData = [[NSUserDefaults standardUserDefaults]
242 NSImage *gemImage = [[NSImage alloc] initWithData:tiffData];
243 [gemImageArray addObject:gemImage];
251 - (void) setMuted:(BOOL)value
256 - (void) setShowHint:(BOOL)value
261 - (void) setPaused:(BOOL)value
266 animationStatus = _animating;
270 _animating = animationStatus;
273 // ANIMATE called by the Timer
279 // MIKE WESSLER'S Scorebubbles
283 // needsUpdate added so setNeedsDisplay gets called once at most
285 BOOL needsUpdate = FALSE;
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];
293 [[game scoreBubbles] removeObjectAtIndex:b];
300 //NSLog(@"GameView.animate");
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];
309 [gameController animationEnded];
315 ticksSinceLastMove++;
316 if (ticksSinceLastMove > 500)
322 [self setNeedsDisplay:YES];
326 - (void) setGame:(Game *) agame
334 - (NSArray *) imageArray
336 return gemImageArray;
338 - (NSArray *) spriteArray
340 return gemSpriteArray;
343 - (void) newBackground
345 if (([gameController useCustomBackgrounds])&&(backgroundImagePathArray)&&([backgroundImagePathArray count] > 0))
347 NSString *imagePath = [backgroundImagePathArray objectAtIndex:0];
348 [backgroundImagePathArray addObject:imagePath];
349 [backgroundImagePathArray removeObjectAtIndex:0];
350 //NSLog(@"Taking image from path: %@",imagePath);
352 backgroundImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
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)];
363 NSData *tiffData = [[NSUserDefaults standardUserDefaults] dataForKey:@"backgroundTiffData"];
365 backgroundImage = [[NSImage alloc] initWithData:tiffData];
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)];
374 - (void) setLegend:(id)value
376 // NEED TO DRAW LEGEND INTO LEGENDIMAGE THEN REPLACE THE TEXTURE IN LEGENDSPRITE
378 if (!value) // is null
380 //NSLog(@"Legend cleared");
382 [self setNeedsDisplay:YES];
388 [legendImage lockFocus];
389 [[NSColor clearColor] set];
390 NSRectFill(NSMakeRect(0,0,384,384));
391 if ([value isKindOfClass:[NSAttributedString class]])
393 NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
394 [(NSAttributedString *)value drawAtPoint:legendPoint];
396 if ([value isKindOfClass:[NSImage class]])
398 NSPoint legendPoint = NSMakePoint((384 - [value size].width)/2,(384 - [value size].height)/2);
399 [(NSImage *)value compositeToPoint:legendPoint operation:NSCompositeSourceOver];
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;
410 [self setNeedsDisplay:YES];
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;
445 [self setNeedsDisplay:YES];
448 - (void)drawRect:(NSRect)rect
450 #pragma unused (rect)
455 float size = 384.0/2.0; // screenSize/2;
456 float clearDepth = 1.0;
458 // try to fix image loading problem
464 if (!m_glContextInitialized)
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;
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)
492 [backgroundSprite blitToX:0.0
496 if ((game)&&(!paused))
498 for (i = 0; i < 8; i++)
499 for (j = 0; j < 8; j++)
500 [[game gemAt:i :j] drawSprite];
504 for (i=0; i<[[game scoreBubbles] count]; i++) {
505 ScoreBubble *sb= [[game scoreBubbles] objectAtIndex:i];
512 if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
514 NSPoint crossHairPosition = gameController.crossHair1Position;
516 [crosshairSprite blitToX:crossHairPosition.x
517 Y:crossHairPosition.y
522 [self showScores]; // draws the HighScores in the current focused view (Quartz)
523 //return; // now legendSprite contains the drawing...
527 if ((ticksSinceLastMove > 500)&&(showHint))
529 NSPoint hintPoint = game.hintPoint;
530 [movehintSprite blitToX:hintPoint.x
533 Alpha:(sin((ticksSinceLastMove-497.0)/4.0)+1.0)/2.0];
538 if (ticksSinceLastMove > 500)
539 [self setLegend:[NSImage imageNamed:@"title"]]; // show Logo
540 if ([legend isKindOfClass:[OpenGLSprite class]])
542 //NSLog(@"Blitting legend");
543 [legend blitToX:0.0 Y:0.0 Z:-0.75];
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);
568 legendPoint = NSMakePoint((384 - [hiScoreLegend size].width)/2,384 - [hiScoreLegend size].height*1.5 + scoreScroll);
570 [hiScoreLegend drawAtPoint:legendPoint];
572 for (i = 0; i< 10; i++)
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];
586 [attr setObject:[NSColor yellowColor] forKey:NSForegroundColorAttributeName];
588 [s1 drawAtPoint:p1 withAttributes:attr];
589 [s2 drawAtPoint:p2 withAttributes:attr];
591 [legendImage unlockFocus];
592 [legendSprite replaceTextureFromImage:legendImage
593 cropRectangle:NSMakeRect(0.0, 0.0, [legendImage size].width, [legendImage size].height)];
595 legend = legendSprite;
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!
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];
625 // check situation - is this a first or second mouseUp
626 if (gameController.gameState == GAMESTATE_AWAITINGSECONDCLICK)
628 //NSLog(@"click at :%f,%f",center.x,center.y);
629 [gameController receiveClickAt:center.x:center.y];
631 else if (gameController.gameState == GAMESTATE_AWAITINGFIRSTCLICK)
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);
638 // ???: WTF, you don't really xor booleans...
639 if ((chx2 != chx1)^(chy2 != chy1)) // xor checks if a valid shove's occurred!
641 [gameController receiveClickAt:dragStartPoint.x:dragStartPoint.y];
642 [gameController receiveClickAt:center.x:center.y];
645 [gameController receiveClickAt:center.x:center.y];