Source/Bit.m
author Jens Alfke <jens@mooseyard.com>
Fri Mar 07 11:43:02 2008 -0800 (2008-03-07)
changeset 0 e9f7ba4718e1
child 1 3eb7be1dd7b6
permissions -rw-r--r--
Initial check-in into Mercurial. Branched from 1.0 release of Apple's sample code. No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game.
     1 /*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     2     http://developer.apple.com/samplecode/GeekGameBoard/
     3     Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     8     * Redistributions of source code must retain the above copyright notice, this list of conditions
     9       and the following disclaimer.
    10     * Redistributions in binary form must reproduce the above copyright notice, this list of
    11       conditions and the following disclaimer in the documentation and/or other materials provided
    12       with the distribution.
    13 
    14     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    15     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    16     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    17     BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    18     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    19     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    20     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    21     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    22 */
    23 #import "Bit.h"
    24 #import "Game.h"
    25 #import "QuartzUtils.h"
    26 
    27 
    28 @implementation Bit
    29 
    30 
    31 - (id) copyWithZone: (NSZone*)zone
    32 {
    33     // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
    34     // a temporary data block, then unarchiving a new layer from that block.
    35     
    36     // One complication is that, due to a bug in Core Animation, CALayer can't archive
    37     // a pattern-based CGColor. So as a workaround, clear the background before archiving,
    38     // then restore it afterwards.
    39     
    40     // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
    41     // As a workaround, clear the contents before archiving, then restore.
    42     
    43     CGColorRef bg = CGColorRetain(self.backgroundColor);
    44     self.backgroundColor = NULL;
    45     id contents = [self.contents retain];
    46     self.contents = nil;
    47     
    48     NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
    49     
    50     self.backgroundColor = bg;
    51     self.contents = contents;
    52 
    53     Bit *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
    54     clone.backgroundColor = bg;
    55     clone.contents = contents;
    56     CGColorRelease(bg);
    57     [contents release];
    58 
    59     clone->_owner = _owner;             // _owner is not archived
    60     return [clone retain];
    61 }
    62 
    63 
    64 - (NSString*) description
    65 {
    66     return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
    67 }
    68 
    69 
    70 @synthesize owner=_owner;
    71 
    72 - (BOOL) isFriendly         {return _owner.friendly;}
    73 - (BOOL) isUnfriendly       {return _owner.unfriendly;}
    74 
    75 
    76 - (CGFloat) scale
    77 {
    78     NSNumber *scale = [self valueForKeyPath: @"transform.scale"];
    79     return scale.floatValue;
    80 }
    81 
    82 - (void) setScale: (CGFloat)scale
    83 {
    84     [self setValue: [NSNumber numberWithFloat: scale]
    85         forKeyPath: @"transform.scale"];
    86 }
    87 
    88 
    89 - (int) rotation
    90 {
    91     NSNumber *rot = [self valueForKeyPath: @"transform.rotation"];
    92     return round( rot.doubleValue * 180.0 / M_PI );
    93 }
    94 
    95 - (void) setRotation: (int)rotation
    96 {
    97     [self setValue: [NSNumber numberWithDouble: rotation*M_PI/180.0]
    98         forKeyPath: @"transform.rotation"];
    99 }
   100 
   101 
   102 - (BOOL) pickedUp
   103 {
   104     return self.zPosition >= kPickedUpZ;
   105 }
   106 
   107 - (void) setPickedUp: (BOOL)up
   108 {
   109     if( up != self.pickedUp ) {
   110         CGFloat shadow, offset, radius, opacity, z, scale;
   111         if( up ) {
   112             shadow = 0.8;
   113             offset = 2;
   114             radius = 8;
   115             opacity = 0.9;
   116             scale = 1.2;
   117             z = kPickedUpZ;
   118             _restingZ = self.zPosition;
   119         } else {
   120             shadow = offset = radius = 0.0;
   121             opacity = 1.0;
   122             scale = 1.0/1.2;
   123             z = _restingZ;
   124         }
   125         
   126         self.zPosition = z;
   127         self.shadowOpacity = shadow;
   128         self.shadowOffset = CGSizeMake(offset,-offset);
   129         self.shadowRadius = radius;
   130         self.opacity = opacity;
   131         self.scale *= scale;
   132     }
   133 }
   134 
   135 
   136 - (BOOL)containsPoint:(CGPoint)p
   137 {
   138     // Make picked-up pieces invisible to hit-testing.
   139     // Otherwise, while dragging a Bit, hit-testing the cursor position would always return
   140     // that Bit, since it's directly under the cursor...
   141     if( self.pickedUp )
   142         return NO;
   143     else
   144         return [super containsPoint: p];
   145 }
   146 
   147 
   148 -(id<BitHolder>) holder
   149 {
   150     // Look for my nearest ancestor that's a BitHolder:
   151     for( CALayer *layer=self.superlayer; layer; layer=layer.superlayer ) {
   152         if( [layer conformsToProtocol: @protocol(BitHolder)] )
   153             return (id<BitHolder>)layer;
   154         else if( [layer isKindOfClass: [Bit class]] )
   155             return nil;
   156     }
   157     return nil;
   158 }
   159 
   160 
   161 - (void) destroy
   162 {
   163     // "Pop" the Bit by expanding it 5x as it fades away:
   164     self.scale = 5;
   165     self.opacity = 0.0;
   166     // Removing the view from its superlayer right now would cancel the animations.
   167     // Instead, defer the removal until sometime shortly after the animations finish:
   168     [self performSelector: @selector(removeFromSuperlayer) withObject: nil afterDelay: 1.0];
   169 }
   170 
   171 
   172 @end