This is Player.m in view mode; [Download] [Up]
// Handles moving and rendering the Pac, whether under player or demo control. #import <libc.h> #import "Player.h" #import "Monster.h" #import "Maze.h" #import "PacManView.h" #import <gamekit/gamekit.h> #import <appkit/appkit.h> #import "PacMovement.h" // used to translate PAC_<direction> to px, py velocities static int pacxvec[9] = { 0, 2, -2, 0, 0, 0, 0, 0, 0 }; static int pacyvec[9] = { 0, 0, 0, 0, -2, 0, 0, 0, 2 }; //static int opdir[9] = { 0, 2, 1, 0, 8, 0, 0, 0, 4 }; static int bothdir[9] = { 0, 3, 3, 0, 12, 0, 0, 0, 12 }; // the y-coord in the Pacs.tiff image indexed by direction static int pacypic[9] = { 0, 5 * PAC_WIDTH, 4 * PAC_WIDTH, 0, 6 * PAC_WIDTH, 0, 0, 0, 7 * PAC_WIDTH }; static int pacydie[9] = { 0, 1 * PAC_WIDTH, 0, 0, 2 * PAC_WIDTH, 0, 0, 0, 3 * PAC_WIDTH }; @implementation Player - init // initialize the player { [super init]; pacs[1] = [NXImage findImageNamed:"Pacs.tiff"]; pacs[2] = [NXImage findImageNamed:"PacsBig.tiff"]; nextDir = PAC_STOP; curDir = PAC_STOP; playerStopped = NO; return self; } - (BOOL)newPlayer // get and set up a new Pac. Returns NO if can't { if (![pacsLeft decNumUp:self]) return NO; // no pacs left [self resetPlayer]; return YES; } - resetPlayer // reset all player info { curDir = PAC_RIGHT; nextDir = PAC_RIGHT; px = 0; py = 0; state = PAC_ALIVE; cycle = 0; lastx = - 4 * GHOST_SIZE; lasty = - 4 * GHOST_SIZE; playerStopped = NO; [maze playerPosition:&myX :&myY]; return self; } - (BOOL)pacAlive // returns YES if Pac is alive { if (state < PAC_ALIVE) return NO; return YES; } - demoMove:sender // handles Pac movement during the demo sequence { int i, zz; float xx, yy; int zi = 1; int ghost_x = 0; int ghost_y = 0; int closest = (BLOCK_WIDTH + BLOCK_HEIGHT) * GHOST_SIZE; register int tdir = 0x0f; register int sense = 0; int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE; // keep us in the track between maze walls if (dx) tdir &= 0x03; if (dy) tdir &= 0x0c; for (i=0; i<4; i++) { // find closest ghost; run away from it. [[sender ghost:i] at:&xx :&yy]; zz = abs(xx - myX) + abs(yy - myY); if (zz < closest) { zi = i; closest = zz; ghost_x = xx; ghost_y = yy; } } // first, find the directions in which pac can go if ((abs(ghost_x - myX) >= GHOST_SIZE * 2 - 2) || (abs(ghost_y - myY) >= GHOST_SIZE * 2 - 2)) { if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01; if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02; if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04; if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08; if ((random() & 0x0f) > 10) { // worry about shorter distance first. if (abs(ghost_x - myX) < abs(ghost_y - myY)) { if (tdir & 0x03) tdir &= 0x03; } else { if (tdir & 0x0c) tdir &= 0x0c; } } } else { // if a ghost is close by, we can reverse direction. if ([maze playerWall:myX :(myY-1)]) tdir &= ~0x04; if ([maze playerWall:myX :(myY+GHOST_SIZE)]) tdir &= ~0x08; if ([maze playerWall:(myX+GHOST_SIZE) :myY]) tdir &= ~0x01; if ([maze playerWall:(myX-1) :myY]) tdir &= ~0x02; } // now choose the new direction for the pac if ([[sender ghost:zi] canBeEaten]) { // chase an eatable ghost sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1]; px = chasexvec[tdir][sense]; py = chaseyvec[tdir][sense]; } else { if (((abs(ghost_x - myX) <= GHOST_SIZE * 2 - 2) && (abs(ghost_y - myY) <= GHOST_SIZE * 2 - 2)) || ((random() & 0x0f) > 2)) { // if close or random allows, go // according to program sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1]; } else { // random direction occasionally sense = random() & 0x07; } px = demoxvec[tdir][sense]; py = demoyvec[tdir][sense]; } // set up the direction: curDir = demoDir[sgn(py) + 1][sgn(px) + 1]; return self; } - move:sender // Move the PacMan one animation frame { int dx = myX % PAC_WIDTH; int dy = myY % PAC_WIDTH; register int tdir = 0x0f; if (state < PAC_DEAD) { // Pac is dying state++; } else if (state >= PAC_ALIVE) { // Pac is alive, so move it if ([gameView demoMode]) { // if demo mode, we move the pac ourselves [self demoMove:sender]; } else { // if not demo mode, we let the user decide how to move the pac. // first, find the directions in which the pac can go if (dx || dy) tdir = bothdir[curDir]; // curr. dir or opp. // direction are allowed; other dir. only at junctions. if ([maze playerWall:(myX+PAC_WIDTH) :myY]) tdir &= ~PAC_RIGHT; if ([maze playerWall:(myX-2) :myY]) tdir &= ~PAC_LEFT; if ([maze playerWall:myX :(myY-2)]) tdir &= ~PAC_DOWN; if ([maze playerWall:myX :(myY+PAC_WIDTH)]) tdir &= ~PAC_UP; // see if we can go the "next" direction the player wants or not... if (nextDir & tdir) { // we can! curDir = nextDir; } if (playerStopped) tdir = 0x00; // don't move if stopped // now choose the new direction for the pac // the "&" makes sure we stop if we can't go further in this dir. px = pacxvec[curDir & tdir]; py = pacyvec[curDir & tdir]; } } else { // Pac is dead so do nothing. px = 0; py = 0; } return self; } - newDirection:(int)newDir // send Pac in new direction. { if (newDir == PAC_STOP) { playerStopped = YES; } else { nextDir = newDir; playerStopped = NO; } return self; } - pacDie { state = PAC_DYING; return self; } // the pac will melt - renderAt:(int)posx :(int)posy move:(BOOL)moveOk // draw pac // you should lock focus on view that gets the Pac first. { NXRect from; NXPoint pos; [super renderAt:posx :posy move:moveOk]; pos.x = myX * scale + posx; pos.y = myY * scale + posy; if (state == PAC_ALIVE) { // draw living pac NXSetRect(&from, cycle * PAC_WIDTH * scale, pacypic[curDir] * scale, PAC_WIDTH * scale, PAC_WIDTH * scale); } else if (state < PAC_DEAD) { // draw dying pac NXSetRect(&from, (state - PAC_DYING) * PAC_WIDTH * scale, pacydie[curDir] * scale, PAC_WIDTH * scale, PAC_WIDTH * scale); if (!cycle) state++; if (cycle == 1) cycle = -1; // change period while we die } else { // if dead, draw no pac (upper rt. of image is blank) NXSetRect(&from, 10 * PAC_WIDTH * scale, 7 * PAC_WIDTH * scale, PAC_WIDTH * scale, PAC_WIDTH * scale); } [pacs[scale] composite:NX_SOVER fromRect:&from toPoint:&pos]; cycle++; if (cycle > 5) cycle = 0; // cycle through six images. return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.