Background.m
/* |
File: Background.m |
Abstract: Background. The background image for the game. Reads and maintains landscape info; computes collisions with the background. |
Version: 1.0 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2012 Apple Inc. All Rights Reserved. |
*/ |
#import "Background.h" |
#import "Game.h" |
#import "Mine.h" |
#import "HorizMine.h" |
#define LANDSCAPEHEIGHT 175 |
#define MAXLEVELNAMELENGTH 80 |
#define MAXNUMLANDSCAPESPECS 150 |
#define LANDSCAPEWIDTHPERSPEC 25 /* pixels */ |
#define levelData ((const char *)[levelDataAsData bytes]) |
@implementation Background |
- (CGFloat)codeToHeight:(NSInteger)code { |
return (CGFloat)(LANDSCAPEHEIGHT * (code - 'a') / (CGFloat)LANDSCAPEWIDTHPERSPEC); |
} |
- (NSInteger)codeToWidth:(NSInteger)code { |
return (NSInteger)(landscapeWidthInPixels * (code) / numLandscapeSpecs); |
} |
- (NSInteger)widthToPrevCode:(CGFloat)w { |
return (NSInteger)((w) / (landscapeWidthInPixels / numLandscapeSpecs)); |
} |
- (NSInteger)widthToNextCode:(CGFloat)w { |
return (1 + (NSInteger)(((w) - 1) / (landscapeWidthInPixels / numLandscapeSpecs))); |
} |
- (id)initInGame:(Game *)g { |
NSString *levelFile; |
if ([g isDemo]) { |
levelFile = [[NSBundle mainBundle] pathForResource:@"DemoLevelData" ofType:@"txt"]; |
} else { |
levelFile = [[NSUserDefaults standardUserDefaults] stringForKey:@"LevelFile"]; |
} |
if (levelFile != nil) { |
levelFile = [levelFile stringByExpandingTildeInPath]; |
if ([self initializeLevelData:[NSData dataWithContentsOfFile:levelFile]]) { |
if (![g isDemo]) { |
levelFileIdentifier = [[levelFile lastPathComponent] copy]; |
NSLog(@"BlastApp: Using external level file %@.", levelFile); |
} |
} else { |
NSRunAlertPanel(NSLocalizedString(@"Bad Level Data File", "Title of alert if the custom level data file is corrupt"), [NSString stringWithFormat:NSLocalizedString(@"The external level data file %@ doesn't exist or is illegal. Will use the default level data instead.", "Message in alert if the custom level data file is corrupt; the argument is the name of the level file"), levelFile], NSLocalizedString(@"Bummer", "Only button in the alert if the level data file is corrupt"), nil, nil); |
} |
} |
if ([self numLevels] == 0) { // Indicating we haven't read the level data yet... |
levelFile = [[NSBundle mainBundle] pathForResource:@"LevelData" ofType:@"txt"]; |
if ((levelFile == nil) || ![self initializeLevelData:[NSData dataWithContentsOfFile:levelFile]]) { |
NSLog(@"Bad level data, aborting."); |
[[NSApplication sharedApplication] terminate:nil]; |
} |
} |
// Unfortunate that initInGame: is called so late, after some ivars are touched above (maxLevelSpecs, for instance) |
self = [self initInGame:g image:[[NSImage alloc] initWithSize:NSMakeSize(maxLevelSpecs * LANDSCAPEWIDTHPERSPEC, LANDSCAPEHEIGHT)] numFrames:1 numPoses:1 cache:NO]; |
[self setPerFrameTime:10000000]; |
bottom = malloc(maxLevelSpecs * LANDSCAPEWIDTHPERSPEC * sizeof(short)); |
top = malloc(maxLevelSpecs * LANDSCAPEWIDTHPERSPEC * sizeof(short)); |
return self; |
} |
- (void)dealloc { |
free(bottom); |
free(top); |
} |
- (NSRect)gameRect { |
return gameRect; |
} |
- (NSString *)levelFileIdentifier { |
return levelFileIdentifier; |
} |
- (NSInteger)numLevels { |
return numLevels; |
} |
// Returns the start of the next line; pins at end of the file or \0 |
static NSInteger advanceToNextLine(const char *ptr, NSInteger cnt) { |
while (ptr[cnt] != 0 && ptr[cnt++] != '\n'); |
return cnt; |
} |
// Loads the data and sets number of levels; if data is bad, returns false |
- (BOOL)initializeLevelData:(NSData *)data { |
levelDataAsData = [data copy]; |
BOOL done = NO; |
NSInteger cnt = 0, tmp; |
while (!done) { |
switch ((char)levelData[cnt]) { |
case '%': |
cnt = advanceToNextLine(levelData, cnt); |
break; |
case '+': |
numLevels++; |
cnt = advanceToNextLine(levelData, cnt); |
cnt = advanceToNextLine(levelData, cnt); |
tmp = advanceToNextLine(levelData, cnt); |
if (tmp - cnt - 2 > maxLevelSpecs) maxLevelSpecs = tmp - cnt - 2; /* 1 for end of line, 1 for the extra spec */ |
cnt = tmp; |
cnt = advanceToNextLine(levelData, cnt); |
cnt = advanceToNextLine(levelData, cnt); |
break; |
case '$': |
done = YES; |
break; |
default: |
done = YES; |
numLevels = 0; |
break; |
} |
} |
return numLevels > 0; |
} |
- (BOOL)getLevelData:(NSInteger)level { |
NSInteger cnt = 0; |
NSInteger levelCnt = 0; |
while (levelCnt != level) { |
switch ((char)levelData[cnt]) { |
case '%': |
cnt = advanceToNextLine(levelData, cnt); |
break; |
case '+': |
levelCnt++; |
NSInteger nameLoc = cnt + 1; |
cnt = advanceToNextLine(levelData, cnt); |
NSInteger colorLoc = cnt; |
cnt = advanceToNextLine(levelData, cnt); |
topSpec = cnt; |
cnt = advanceToNextLine(levelData, cnt); |
bottomSpec = cnt; |
cnt = advanceToNextLine(levelData, cnt); |
gamePieces = cnt; |
cnt = advanceToNextLine(levelData, cnt); |
if (levelCnt == level) { |
cnt = advanceToNextLine(levelData, nameLoc); |
levelDescription = [[NSString alloc] initWithBytes:levelData + nameLoc length:cnt - nameLoc - 1 encoding:NSUTF8StringEncoding]; |
numLandscapeSpecs = bottomSpec - topSpec - 2; /* One for EOL, one for the extra spec */ |
landscapeWidthInPixels = numLandscapeSpecs * LANDSCAPEWIDTHPERSPEC; |
gameRect = NSMakeRect(0, 0, landscapeWidthInPixels, LANDSCAPEHEIGHT); |
const char *colorSpec = levelData + colorLoc; |
groundColor = [NSColor brownColor]; |
NSInteger len = topSpec - colorLoc - 1; |
if (len > 6 && !strncmp(colorSpec, "random", 6)) { |
groundColor = [NSColor colorWithCalibratedHue:[Game randInt:100]/100 saturation:1.0 brightness:1.0 alpha:1.0]; |
} else { |
float r, g, b; |
if (sscanf(colorSpec, "%f %f %f", &r, &g, &b) == 3) groundColor = [NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0]; |
} |
return YES; |
} |
break; |
default: |
return NO; |
} |
} |
return NO; |
} |
- (void)setLevel:(NSInteger)level { |
if (level > [self numLevels]) { |
level = [self numLevels]; |
} |
if (![self getLevelData:level]) { |
NSRunAlertPanel(NSLocalizedString(@"Bad Level Data", "Title of alert if a level in the level data file is corrupt"), [NSString stringWithFormat:NSLocalizedString(@"Sorry, level %ld is under construction.", "Message of alert if a level in the level data file is corrupt; the argument is the level number"), (long)level], NSLocalizedString(@"Bummer", "Only button in the alert if the level data file is corrupt"), nil, nil); |
while (--level != 0 && ![self getLevelData:level]); |
if (level == 0) { |
NSLog(@"Bad level data file, aborting."); |
[[NSApplication sharedApplication] terminate:nil]; |
} |
} |
currentLevel = level; |
[self createLandscape]; |
[images lockFocus]; |
[self drawLandscape]; |
[images unlockFocus]; |
} |
- (NSString *)levelDescription { |
return levelDescription; |
} |
- (void)washToBlack:(NSColor *)color size:(NSSize)size { |
CGFloat hue = [color hueComponent]; |
CGFloat curHeight; |
NSRect rect = NSMakeRect(0.0, 0.0, size.width, 1.0); |
for (curHeight = 0; curHeight < size.height; curHeight++) { |
CGFloat brightness = 0.5 * (curHeight / size.height); |
[[NSColor colorWithCalibratedHue:hue saturation:1.0 brightness:brightness alpha:1.0] set]; |
rect.origin.y = curHeight; |
[NSBezierPath fillRect:rect]; |
} |
} |
- (void)washToWhite:(NSColor *)color size:(NSSize)size { |
CGFloat hue = [color hueComponent]; |
CGFloat curHeight; |
NSRect rect = NSMakeRect(0.0, 0.0, size.width, 1.0); |
for (curHeight = 0; curHeight < size.height; curHeight++) { |
CGFloat saturation = 0.5 * (curHeight / size.height); |
[[NSColor colorWithCalibratedHue:hue saturation:saturation brightness:1.0 alpha:1.0] set]; |
rect.origin.y = curHeight; |
[NSBezierPath fillRect:rect]; |
} |
} |
- (void)drawLandscape { |
NSColor *color; |
NSInteger cnt; |
NSBezierPath *path; |
NSPoint point = NSZeroPoint; |
color = groundColor; |
[color set]; |
[self washToBlack:color size:[self size]]; |
path = [[NSBezierPath alloc] init]; |
point.x = 0.0; |
point.y = 0.0; |
[path moveToPoint:point]; |
for (cnt = 0; cnt <= numLandscapeSpecs; cnt++) { |
point.x = (CGFloat)[self codeToWidth:cnt]; |
point.y = (CGFloat)[self codeToHeight:levelData[topSpec + cnt]]; |
[path lineToPoint:point]; |
} |
for (cnt = numLandscapeSpecs; cnt >= 0; cnt--) { |
point.x = (CGFloat)[self codeToWidth:cnt]; |
point.y = (CGFloat)[self codeToHeight:levelData[bottomSpec + cnt]]; |
[path lineToPoint:point]; |
} |
[path closePath]; |
[path setClip]; |
[self washToWhite:color size:[self size]]; |
} |
- (void)draw:(NSRect)rect { |
[images drawAtPoint:NSZeroPoint fromRect:rect operation:NSCompositeCopy fraction:1.0]; |
} |
- (void)createLandscape { |
NSInteger spec, cnt; |
CGFloat vels[] = {MAXVELY/2.0, MAXVELY/3.0, MAXVELY/4.0, MAXVELY/5.0}; // vels is used to map 4 consecutive characters to velocities |
/* First create the elevation data to be used in collision detection. */ |
for (spec = 0; spec < numLandscapeSpecs; spec++) { |
CGFloat bottomSlope = (CGFloat)([self codeToHeight:levelData[bottomSpec + spec + 1]] - [self codeToHeight:levelData[bottomSpec + spec]]) / (CGFloat)([self codeToWidth:spec + 1] - [self codeToWidth:spec]); |
CGFloat topSlope = (CGFloat)([self codeToHeight:levelData[topSpec + spec + 1]] - [self codeToHeight:levelData[topSpec + spec]]) / (CGFloat)([self codeToWidth:spec + 1] - [self codeToWidth:spec]); |
for (cnt = [self codeToWidth:spec]; cnt < [self codeToWidth:spec + 1]; cnt++) { |
bottom[cnt] = (short)([self codeToHeight:levelData[bottomSpec + spec]] + (bottomSlope * (cnt - [self codeToWidth:spec]))); |
top[cnt] = (short)([self codeToHeight:levelData[topSpec + spec]] + (topSlope * (cnt - [self codeToWidth:spec]))); |
} |
} |
spec = 0; |
while (spec < numLandscapeSpecs) { |
GamePiece *piece = nil; |
char cur = levelData[gamePieces + spec]; |
CGFloat bottomHeight = floor([self codeToHeight:levelData[bottomSpec + spec]]); |
CGFloat topHeight = floor([self codeToHeight:levelData[topSpec + spec]]); |
switch (cur) { |
case '.': |
break; |
case 'a': |
case 'b': |
case 'F': |
case '2': |
switch((char)cur) { |
case 'a': piece = [NSClassFromString(@"MissileBase") alloc]; break; |
case 'b': piece = [NSClassFromString(@"SmartMissileBase") alloc]; break; |
case 'F': piece = [NSClassFromString(@"RapidFireMissileBase") alloc]; break; |
case '2': piece = [NSClassFromString(@"KillerMissileBase") alloc]; break; |
} |
piece = [piece initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0) - ([piece size].width / 2.0), bottomHeight)]; |
[game addGamePiece:piece]; |
break; |
case 'c': |
case 'E': |
case 'J': |
case '5': |
case '6': |
switch((char)cur) { |
case 'c': piece = [NSClassFromString(@"HangingBase") alloc]; break; |
case 'E': piece = [NSClassFromString(@"RapidFireHangingBase") alloc]; break; |
case '5': piece = [NSClassFromString(@"DumHangingBase") alloc]; break; |
case 'J': piece = [NSClassFromString(@"SmartHangingBase") alloc]; break; |
case '6': piece = [NSClassFromString(@"SneakyHangingBase") alloc]; break; |
} |
piece = [piece initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0) - ([piece size].width / 2), topHeight - ([piece size].height))]; |
[game addGamePiece:piece]; |
break; |
case 'd': |
case 'e': |
case 'f': |
case 'g': |
[self putMine:[NSClassFromString(@"SmallMine") alloc] :spec :vels[cur - 'd']]; |
break; |
case 'h': |
[self putMine:[NSClassFromString(@"SmallMine") alloc] :spec :MAXVELY * (RANDINT(60) / 100)]; |
break; |
case 'j': |
case 'k': |
case 'l': |
case 'm': |
[self putMine:[NSClassFromString(@"Sentry") alloc] :spec :vels[cur - 'j']]; |
break; |
case 'n': |
[self putMine:[NSClassFromString(@"Sentry") alloc] :spec :MAXVELY * ((10.0 + RANDINT(30)) / 100.0)]; |
break; |
case 'o': |
case 'p': |
case 'q': |
case 'r': |
[self putMine:[NSClassFromString(@"FatSentry") alloc] :spec :vels[cur - 'o']]; |
break; |
case 's': |
[self putMine:[NSClassFromString(@"FatSentry") alloc] :spec :MAXVELY * ((10.0 + RANDINT(40)) / 100.0)]; |
break; |
case 't': |
[self putMine:[NSClassFromString(@"LargeMine") alloc] :spec :MAXVELY/14.0]; |
break; |
case 'u': |
[self putMine:[NSClassFromString(@"Fog") alloc] :spec :([Game oneIn:2] ? (MAXVELY * (RANDINT(30) / 200.0)) : 0.0)]; |
break; |
case 'w': |
[self putMine:[NSClassFromString(@"SmartMine") alloc] :spec :MAXVELY * ((.25 + RANDINT(30)) / 100.0)]; |
break; |
case 'z': |
[self putMine:[NSClassFromString(@"RealSmartMine") alloc] :spec :MAXVELY * ((.4 + RANDINT(40)) / 100.0)]; |
break; |
case 'v': |
piece = [[NSClassFromString(@"BackShooter") alloc] initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0) - ([piece size].width / 2.0), bottomHeight)]; |
[game addGamePiece:piece]; |
break; |
case 'A': |
case 'B': |
case 'C': |
[self putMine:[NSClassFromString(@"AmeobaMine") alloc] :spec :vels[cur - 'A']]; |
break; |
case 'D': |
[self putMine:[NSClassFromString(@"StopMine") alloc] :spec :MAXVELY * ((10.0 + RANDINT(40)) / 100.0)]; |
break; |
case 'G': |
[self putMine:[NSClassFromString(@"RandomMine") alloc] :spec :MAXVELY / 2.0]; |
break; |
case 'H': |
[self putMine:[NSClassFromString(@"Throton") alloc] :spec :MAXVELY / 4.0]; |
break; |
case '8': |
[self putMine:[NSClassFromString(@"Throton") alloc] :spec :MAXVELY / 3.0]; |
break; |
case 'I': |
[self putMine:[NSClassFromString(@"Throton") alloc] :spec :MAXVELY * ((10.0 + RANDINT(40)) / 100.0)]; |
break; |
case 'L': |
[self putMine:[NSClassFromString(@"ProximityMine") alloc] :spec :0.0 :0.0]; |
break; |
case '@': |
[self putMine:[NSClassFromString(@"DonutMine") alloc] :spec :MAXVELY :0.5]; |
break; |
case 'x': |
case 'K': |
case 'M': |
case 'O': |
case 'P': |
switch((char)cur) { |
case 'x': piece = [NSClassFromString(@"DropShip") alloc]; break; |
case 'K': piece = [NSClassFromString(@"RapidFireDropShip") alloc]; break; |
case 'M': piece = [NSClassFromString(@"AttackShip") alloc]; break; |
case 'O': piece = [NSClassFromString(@"BigAttackShip") alloc]; break; |
case 'P': piece = [NSClassFromString(@"SmartAttackShip") alloc]; break; |
} |
piece = [piece initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0), floor((bottomHeight + topHeight) / 2.0))]; |
[game addGamePiece:piece]; |
break; |
case 'y': |
piece = [[NSClassFromString(@"ArrowBase") alloc] initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0) - ([piece size].width / 2.0), bottomHeight)]; |
[game addGamePiece:piece]; |
break; |
case '+': |
piece = [[NSClassFromString(@"Switch") alloc] initInGame:game]; |
[piece setLocation:NSMakePoint(floor([self codeToWidth:spec] + [self codeToWidth:1] / 2.0), bottomHeight + 5.0)]; |
[game addGamePiece:piece]; |
break; |
case 'N': |
[self putMine:[NSClassFromString(@"Gunes") alloc] :spec :MAXVELY * ((10.0 + RANDINT(30)) / 100.0)]; |
break; |
case '&': |
[self putMine:[NSClassFromString(@"FourMines") alloc] :spec :MAXVELY / 4.0]; |
break; |
case 'Q': |
case 'R': |
[self putMine:[NSClassFromString(@"TimedVertGate") alloc] :spec :(cur == 'Q') ? 0.0 : MAXVELY / 4.0 :0.5]; |
break; |
case '*': |
[self putMine:[NSClassFromString(@"Hole") alloc] :spec :0.0 :0.5]; |
break; |
case 'S': |
[self putHorizMine:[NSClassFromString(@"GoodSheep") alloc] :spec :MAXVELX / ([Game randInt:4] + 4.0) :0.0]; |
break; |
case 'V': |
[self putHorizMine:[NSClassFromString(@"ToughGoodSheep") alloc] :spec :MAXVELX / ([Game randInt:4] + 4.0) :0.0]; |
break; |
case '4': |
[self putHorizMine:[NSClassFromString(@"BadSheep") alloc] :spec :MAXVELX / ([Game randInt:4] + 4.0) :0.0]; |
break; |
case 'U': |
[self putMine:[NSClassFromString(@"PassableVertGate") alloc] :spec :0.0 :0.5]; |
break; |
case '9': |
[self putMine:[NSClassFromString(@"SwitchedVertGate") alloc] :spec :0.0 :0.5]; |
break; |
case 'T': |
[self putMine:[NSClassFromString(@"TimedHorizGate") alloc] :spec :0.0 :0.5]; /* ??? Should this be putHorizMine */ |
break; |
case 'W': |
[self putMine:[NSClassFromString(@"WaveGenerator") alloc] :spec :MAXVELY]; |
break; |
case '$': |
[self putMine:[NSClassFromString(@"KillerWaveGenerator") alloc] :spec :MAXVELY]; |
break; |
case '3': |
[self putMine:[NSClassFromString(@"BombGenerator") alloc] :spec :0.0]; |
break; |
case 'Y': |
[self putMine:[NSClassFromString(@"BoingBall") alloc] :spec :0.0 :0.8]; |
break; |
case 'X': |
[self putMine:[NSClassFromString(@"BubbleMine") alloc] :spec :0.0 :((2.0 + RANDINT(6)) / 10.0)]; |
break; |
case 'Z': |
[self putMine:[NSClassFromString(@"BouncingBoingBall") alloc] :spec :MAXVELY :0.0]; |
break; |
case '7': |
[self putMine:[NSClassFromString(@"SneakyBoingBall") alloc] :spec :0.0 :0.95f]; |
break; |
case '0': |
[self putHorizMine:[NSClassFromString(@"Spider") alloc] :spec :MAXVELX / 3.0 :0.0]; |
break; |
case '1': |
[self putHorizMine:[NSClassFromString(@"ToughSpider") alloc] :spec :MAXVELX / 2.8f :0.0]; |
break; |
default: |
NSLog(@"Unknown game piece indicator '%c'", cur); |
break; |
} |
spec++; |
} |
} |
// If locFromBottomPercentage >= 1.0, then random placement. |
- (void)putMine:(Mine *)mine :(NSInteger)loc :(CGFloat)yVel :(CGFloat)locFromBottomPercentage { |
CGFloat bottomHeight = 0.0; |
CGFloat topHeight = 100000.0; |
NSPoint mineLocation = NSMakePoint([self codeToWidth:loc], 0.0); |
NSSize mineVelocity = NSMakeSize(0.0, yVel); |
NSSize mineSize; |
NSInteger cnt; |
mine = [mine initInGame:game]; |
mineSize = [mine size]; |
for (cnt = loc; cnt <= loc + [self widthToNextCode:mineSize.width]; cnt++) { |
bottomHeight = [Game maxFloat:bottomHeight :[self codeToHeight:levelData[bottomSpec + cnt]]]; |
topHeight = [Game minFloat:topHeight :[self codeToHeight:levelData[topSpec + cnt]]]; |
} |
bottomHeight += 2.0; |
topHeight -= 2.0; |
if (bottomHeight >= topHeight) { |
NSLog(@"Can't place mine %@ at %ld.", mine, (long)loc); |
return; |
} |
if (locFromBottomPercentage >= 1.0) { |
mineLocation.y = bottomHeight + RANDINT((NSInteger)(topHeight - bottomHeight)); |
} else { |
mineLocation.y = bottomHeight + (NSInteger)((topHeight - mineSize.height - bottomHeight) * locFromBottomPercentage); |
} |
[mine setHigh:topHeight low:bottomHeight]; |
[mine setVelocity:mineVelocity]; |
[mine setLocation:mineLocation]; |
[game addGamePiece:mine]; |
} |
- (void)putHorizMine:(HorizMine *)mine :(NSInteger)loc :(CGFloat)xVel :(CGFloat)locFromBottomPercentage { |
CGFloat bottomHeight = [self codeToHeight:levelData[bottomSpec + loc]]; |
CGFloat topHeight = 100000.0; |
CGFloat leftLoc = 100000.0; |
CGFloat rightLoc = -100000.0; |
NSPoint mineLocation = NSMakePoint([self codeToWidth:loc], 0.0); |
NSSize mineVelocity = NSMakeSize(xVel, 0.0); |
NSSize mineSize; |
NSInteger cnt; |
mine = [mine initInGame:game]; |
mineSize = [mine size]; |
for (cnt = loc; cnt <= loc + [self widthToNextCode:mineSize.width]; cnt++) { |
topHeight = [Game minFloat:topHeight :[self codeToHeight:levelData[topSpec + cnt]]]; |
} |
cnt = loc; |
while (cnt > 1 && (levelData[bottomSpec + loc] == levelData[bottomSpec + cnt - 1])) cnt--; |
leftLoc = [self codeToWidth:cnt]; |
cnt = loc; |
while (cnt < numLandscapeSpecs - 1 && (levelData[bottomSpec + loc] == levelData[bottomSpec + cnt + 1])) cnt++; |
rightLoc = [self codeToWidth:cnt]; |
if (locFromBottomPercentage >= 1.0) { |
mineLocation.y = bottomHeight + RANDINT((NSInteger)(topHeight - mineSize.height - bottomHeight)); |
} else { |
mineLocation.y = bottomHeight + (NSInteger)((topHeight - bottomHeight) * locFromBottomPercentage); |
} |
[mine setLeft:leftLoc right:rightLoc]; |
[mine setVelocity:mineVelocity]; |
[mine setLocation:mineLocation]; |
[game addGamePiece:mine]; |
} |
- (void)putMine:(Mine *)mine :(NSInteger)loc :(CGFloat)yVel { |
[self putMine:mine :loc :yVel :1.0]; |
} |
- (BOOL)touches:(GamePiece *)obj { |
return (obj != self && [obj touches:self]); |
} |
- (BOOL)touchesRect:(NSRect)rect { |
NSInteger from = [Game minInt:[Game maxInt:0 :(NSInteger)rect.origin.x] :landscapeWidthInPixels-1]; |
NSInteger to = [Game minInt:[Game maxInt:0 :(NSInteger)NSMaxX(rect)] :landscapeWidthInPixels-1]; |
while (from <= to) { |
if ((bottom[from] >= rect.origin.y) || (top[from] <= NSMaxY(rect))) return YES; |
from++; |
} |
return NO; |
} |
// Returns the area that is clear between the specified start and end locations... |
- (NSRect)clearRectFrom:(CGFloat)startLoc to:(CGFloat)endLoc { |
NSInteger to = [Game maxInt:0 :[Game minInt:(NSInteger)endLoc :landscapeWidthInPixels - 1]]; |
NSInteger from = [Game maxInt:0 :[Game minInt:(NSInteger)startLoc :landscapeWidthInPixels - 1]]; |
NSInteger tmp; |
CGFloat y1 = 0; |
CGFloat y2 = LANDSCAPEHEIGHT; |
for (tmp = from; tmp <= to; tmp++) { |
if (bottom[tmp] > y1) y1 = bottom[tmp]; |
if (top[tmp] < y2) y2 = top[tmp]; |
} |
return NSMakeRect(from, y1, to - from, y2 - y1); |
} |
@end |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-06-07