Hello everyone... I know this is probably too much to ask, but there is probably and expert Objective-C programmer here lurking about that can spot my mistake instantly.
I am working on writing a 2d game engine in Objective C and using "CocoaQuest" the template for doing this. I am finding that this thingy is filled with bugs. So, in the code below... it draws a little map, and I am able to move around with the 'w', 'a', 'd', 'x' keys. When you press Shift and these keys it fires a bullet.
The problem is two fold:
1.)
The [super keyDown: theEvent]; is getting in the way. When this is in there the bullets don't work. When it is taken out, another problem happens.
2.)
The bullets ONLY fire up (eUp)
I have tracked down the offender somewhat... in the '-moveBullets' method right before the switch, 'direction' is not being set. It just blows through the switch statement. So I am thinking the logic of the keydown method is written incorrectly and not transferring the direction into the object.
the Bizarro thing is that this code is straight out of a book I paid good money for. Anywho, if there is someone here that can spot the mistake, I would be very grateful.
I am a total newbie by the way if your wondering.
I am working on writing a 2d game engine in Objective C and using "CocoaQuest" the template for doing this. I am finding that this thingy is filled with bugs. So, in the code below... it draws a little map, and I am able to move around with the 'w', 'a', 'd', 'x' keys. When you press Shift and these keys it fires a bullet.
The problem is two fold:
1.)
The [super keyDown: theEvent]; is getting in the way. When this is in there the bullets don't work. When it is taken out, another problem happens.
2.)
The bullets ONLY fire up (eUp)
I have tracked down the offender somewhat... in the '-moveBullets' method right before the switch, 'direction' is not being set. It just blows through the switch statement. So I am thinking the logic of the keydown method is written incorrectly and not transferring the direction into the object.
the Bizarro thing is that this code is straight out of a book I paid good money for. Anywho, if there is someone here that can spot the mistake, I would be very grateful.
I am a total newbie by the way if your wondering.
Code:
#import "GameView.h"
#define kMapHeight 20
#define kMapWidth 20
char gMapData[kMapWidth][kMapHeight] = {
"XXXXXXXXXXXXXXXXXXXX",
"XPPPP....MMM.......X",
"X.............T....X",
"X....T.............X",
"X..................X",
"X..........T.......X",
"X......T...........X",
"X...........X......X",
"X...........X......X",
"X.XXXXXXXXXXXXXXXX.X",
"X...........X......X",
"X...X.XXX...X......X",
"X...X...X..........X",
"X...XXXXX..........X",
"X...........TTTTTT.X",
"X...XXXXXXX........X",
"X...X.....X........X",
"X...X..X.XX........X",
"X...X..X...........X",
"XXXXXXXXXXXXXXXXXXXX" };
#define kBulletColumnKey @"bullet column"
#define kBulletRowKey @"bullet row"
#define kBulletDirectionKey @"bullet direction"
enum { eUp, eDown, eRight, eLeft };
char mapDataAtLocation ( int column, int row )
{
return gMapData[ kMapHeight - 1 - row][column];
}
void setMapDataAtLocation ( char newData, int column, int row )
{
gMapData[ kMapHeight - 1 - row][column] = newData;
}
BOOL isMapLocationPassableForPlayer ( int column, int row )
{
char locationData = mapDataAtLocation( column, row );
return locationData != 'X' && locationData != 'T';
}
@implementation GameView
- (id)initWithFrame:(NSRect)frameRect
{
if ((self = [super initWithFrame:frameRect]) != nil)
{
// Add initialization code here
playerColumn_ = 1;
playerRow_ = 1;
playerHealth_ = 10;
playerScore_ = 0;
bullets_ = [[NSMutableArray alloc] init];
}
return self;
}
- (NSRect) rectForMapCellAtColumn: (int) col row: (int) row
{
return NSMakeRect( col * cellWidth_,
row * cellHeight_,
cellWidth_, cellHeight_ );
}
- (void) drawBullseyeInRect: (NSRect) rect
{
float width = NSWidth( rect );
float height = NSHeight( rect );
NSBezierPath* path = [[NSBezierPath alloc] init];
while ( width > 0 && height > 0 )
{
[path appendBezierPathWithOvalInRect: rect];
rect = NSInsetRect( rect, 3, 3 );
width -= 3;
height -= 3;
}
[[NSColor redColor] set];
[path stroke];
[path release];
}
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
float viewWidth = NSWidth( bounds );
float viewHeight = NSHeight( bounds );
cellWidth_ = viewWidth / kMapWidth;
cellHeight_ = viewHeight / kMapHeight;
int row, col;
// draw the map
for ( row = 0; row < kMapHeight; row++ )
{
for ( col = 0; col < kMapWidth; col++ )
{
char cellData = mapDataAtLocation( col, row );
NSRect cellRect = [self rectForMapCellAtColumn: col row: row];
if ( cellData == 'X' )
{
[[NSColor blackColor] set];
[NSBezierPath fillRect: cellRect];
}
else if ( cellData == 'P' )
{
cellRect = NSInsetRect( cellRect, NSWidth( cellRect ) / 4, NSHeight( cellRect ) / 4 );
[[NSColor purpleColor] set];
NSBezierPath* potionPath = [[NSBezierPath alloc] init];
[potionPath appendBezierPathWithOvalInRect: cellRect];
[potionPath fill];
[potionPath release];
}
else if ( cellData == 'T' )
{
[self drawBullseyeInRect: cellRect ];
}
else if ( cellData == 'M' )
{
[[NSColor brownColor] set];
[NSBezierPath fillRect: cellRect];
}
}
}
// draw the player
NSRect playerRect = [self rectForMapCellAtColumn: playerColumn_ row: playerRow_];
NSBezierPath* playerPath = [[NSBezierPath alloc] init];
[playerPath appendBezierPathWithOvalInRect: playerRect];
[playerPath stroke];
[playerPath release];
// draw the bullets
[[NSColor redColor] set];
NSEnumerator* bulletEnumerator = [bullets_ objectEnumerator];
NSMutableDictionary* bulletDictionary = [bulletEnumerator nextObject];
while ( bulletDictionary )
{
int column = [[bulletDictionary objectForKey: kBulletColumnKey] intValue];
int row = [[bulletDictionary objectForKey: kBulletRowKey] intValue];
// NSLog(@"column = %i", column);
// NSLog(@"row= %i", row);
NSRect bulletRect = [self rectForMapCellAtColumn: column row: row];
bulletRect = NSInsetRect( bulletRect, NSWidth( bulletRect ) / 3.0, NSHeight( bulletRect ) / 3.0 );
NSBezierPath* bulletPath = [[NSBezierPath alloc] init];
[bulletPath appendBezierPathWithOvalInRect: bulletRect];
[bulletPath fill];
[bulletPath release];
bulletDictionary = [bulletEnumerator nextObject];
}
}
- (void) keyDown: (NSEvent*) theEvent
{
int newPlayerCol = playerColumn_;
int newPlayerRow = playerRow_;
int direction = eUp;
NSString* eventChars = [theEvent charactersIgnoringModifiers];
if ( [eventChars isEqualTo: @"w"] )
{
// move the player up
newPlayerRow++;
direction = eUp;
}
else if ( [eventChars isEqualTo: @"x"] )
{
// move the player down
newPlayerRow--;
direction = eDown;
}
else if ( [eventChars isEqualTo: @"a"] )
{
// move the player left
newPlayerCol--;
direction = eLeft;
}
else if ( [eventChars isEqualTo: @"d"] )
{
// move the player right
newPlayerCol++;
direction = eRight;
}
else
{
[super keyDown: theEvent];
return;
}
// handle a player shot
if ( [theEvent modifierFlags] & NSShiftKeyMask )
{
// the player moved while holding the SHIFT key
// fire a bullet in the appropriate direction
[self fireBulletAtColumn: newPlayerCol row: newPlayerRow inDirection: direction];
[self setNeedsDisplay: YES];
}
else
{
// validate player movement
if ( newPlayerCol >= 0 && newPlayerCol < kMapWidth && newPlayerRow >= 0 && newPlayerRow < kMapHeight )
{
if ( isMapLocationPassableForPlayer( newPlayerCol, newPlayerRow ) )
{
// valid move
playerColumn_ = newPlayerCol;
playerRow_ = newPlayerRow;
if ( mapDataAtLocation( newPlayerCol, newPlayerRow ) == 'P' )
{
// we've stepped on a potion
playerHealth_ += 5;
setMapDataAtLocation( '.', newPlayerCol, newPlayerRow );
[self showPlayerStatus];
}
[self setNeedsDisplay: YES];
}
}
}
}
- (BOOL) acceptsFirstResponder
{
return YES;
}
- (void) fireBulletTimer: (NSTimer*) timer
{
[self moveBullets];
[self moveMonsters];
[self setNeedsDisplay: YES];
}
- (void) awakeFromNib
{
[self showPlayerStatus];
bulletTimer_ = [NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: @selector(fireBulletTimer:) userInfo: nil repeats: YES];
}
- (void) showPlayerStatus
{
// show the player's status
NSString* titleString = [NSString stringWithFormat: @"Cocoa Quest: Health = %d, Score = %d", playerHealth_, playerScore_];
[[self window] setTitle: titleString];
}
- (void) fireBulletAtColumn: (int) column row: (int) row inDirection: (int) direction
{
NSMutableDictionary* newBullet = [[NSMutableDictionary alloc] init];
[newBullet setObject: [NSNumber numberWithInt: column] forKey: kBulletColumnKey];
[newBullet setObject: [NSNumber numberWithInt: row] forKey: kBulletRowKey];
[newBullet setObject: [NSNumber numberWithInt: direction] forKey: kBulletDirectionKey];
[bullets_ addObject: newBullet];
[newBullet release];
}
- (BOOL) validBulletColumn: (int) column row: (int) row
{
BOOL validPosition = YES;
if ( column < 0 || column >= kMapWidth )
{
validPosition = NO;
}
if ( row < 0 || row >= kMapHeight )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'X' )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'T' )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'M' )
{
validPosition = NO;
}
return validPosition;
}
- (void) moveBullets
{
NSMutableArray* bulletsToRemove = [[NSMutableArray alloc] init];
NSEnumerator* bulletEnumerator = [bullets_ objectEnumerator];
NSMutableDictionary* bulletDictionary = [bulletEnumerator nextObject];
while ( bulletDictionary )
{
int column = [[bulletDictionary objectForKey: kBulletColumnKey] intValue];
int row = [[bulletDictionary objectForKey: kBulletRowKey] intValue];
int direction = [[bulletDictionary objectForKey: kBulletDirectionKey] intValue];
// this works here also int direction = eRight;
// direction is not getting set here...
switch ( direction )
{
case eUp:
row++;
break;
case eDown:
row--;
break;
case eRight:
column++;
break;
case eLeft:
column--;
break;
default:
NSLog( @"Invalid bullet direction" );
}
// update the bullet's position
[bulletDictionary setObject: [NSNumber numberWithInt: column] forKey: kBulletColumnKey];
[bulletDictionary setObject: [NSNumber numberWithInt: row] forKey: kBulletRowKey];
if ( ![self validBulletColumn: column row: row] )
{
// check to see if the bullet hits a target
if ( mapDataAtLocation( column, row ) == 'T' )
{
// remove the target and replace with floor ('.')
setMapDataAtLocation( '.', column, row );
// give the player a few points
playerScore_ += 4;
[self showPlayerStatus];
}
else if ( mapDataAtLocation( column, row ) == 'M' )
{
// remove the target and replace with floor ('.')
setMapDataAtLocation( '.', column, row );
// give the player a few points
playerScore_ += 25;
[self showPlayerStatus];
}
// remove the bullet from the list
[bulletsToRemove addObject: bulletDictionary];
}
bulletDictionary = [bulletEnumerator nextObject];
}
// clean up after any bullets that need to be removed
[bullets_ removeObjectsInArray: bulletsToRemove];
[bulletsToRemove release];
}
- (BOOL) validMonsterColumn: (int) column row: (int) row
{
BOOL validPosition = YES;
if ( column < 0 || column >= kMapWidth )
{
validPosition = NO;
}
if ( row < 0 || row >= kMapHeight )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'X' )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'T' )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'P' )
{
validPosition = NO;
}
if ( mapDataAtLocation( column, row ) == 'M' )
{
validPosition = NO;
}
if ( column == playerColumn_ && row == playerRow_ )
{
validPosition = NO;
}
return validPosition;
}
- (void) moveMonsters
{
int column, row, newColumn, newRow;
for ( row = 0; row < kMapHeight; row++ )
{
for ( column = 0; column < kMapWidth; column++ )
{
char mapData = mapDataAtLocation( column, row );
if ( mapData == 'M' )
{
// found a monster
// try to move in a random direction
newColumn = column + (random() % 3) - 1;
newRow = row + (random() % 3) - 1;
if ( [self validMonsterColumn: newColumn row: newRow] )
{
// move the monster
setMapDataAtLocation( '.', column, row );
setMapDataAtLocation( 'M', newColumn, newRow );
}
else
{
// we ran into something
// if it was the player,
// damage both of us
if ( newColumn == playerColumn_ && newRow == playerRow_ )
{
playerHealth_ -= 3;
[self showPlayerStatus];
// monsters currently don't have health
// they're just alive or dead
// so remove the monster
// from the map
setMapDataAtLocation( '.', column, row );
}
}
}
}
}
}
@end