/Volumes/Plantain/MyDocuments/Projects/MacTrek/MacTrek/Views/GameView.m

00001 //
00002 //  GameView.m
00003 //  MacTrek
00004 //
00005 //  Created by Aqua on 02/06/2006.
00006 //  Copyright 2006 __MyCompanyName__. All rights reserved.
00007 //
00008 
00009 #import "GameView.h"
00010 
00011 
00012 @implementation GameView
00013 
00014 - (void) awakeFromNib {
00015     
00016     [super awakeFromNib];
00017     
00018     step = GV_SCALE_STEP;
00019     keyMap = nil;
00020     //universe = nil; this will kill the universe that has been set
00021     // since i cannot predict the sequence in which the obects are awoken
00022     scale = 40; // default
00023     trigonometry = [LLTrigonometry defaultInstance];
00024     // $$ temp linked to Netrek needs to go through selection mechanism..
00025     painter = [[PainterFactoryForNetrek alloc] init]; 
00026     angleConvertor = [[Entity alloc] init];
00027     busyDrawing = NO;
00028 }
00029 
00030 - (NSPoint) gamePointRepresentingCentreOfView {
00031     return [[universe playerThatIsMe] predictedPosition];
00032 }
00033 
00034 - (void) setScaleFullView {
00035     NSSize viewSize = [self bounds].size;
00036     
00037     int minSize = (viewSize.height < viewSize.width ? viewSize.height : viewSize.width);
00038     
00039     // minSize must cover UNIVERSE_PIXEL_SIZE thus zoom is
00040     scale = UNIVERSE_PIXEL_SIZE / minSize;
00041 }
00042 
00043 - (void) setScale:(int)newScale {
00044     scale = newScale;
00045 }
00046 
00047 - (int) scale {
00048     return scale;
00049 }
00050 
00051 // draw the view
00052 - (void)drawRect:(NSRect)aRect {
00053     
00054     // sometimes the drawing takes so long the timer invokes another go
00055     // maybe should use locks..
00056     if (busyDrawing) {
00057         NSLog(@"GameView.drawRect busy drawing");
00058         return;
00059     }    
00060     busyDrawing = YES;
00061     
00062     // during this draw i want to lock universal access (try for 200ms)
00063     if ([[universe synchronizeAccess] lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]]) { 
00064         
00065         // first set up the gamebounds based on my position
00066         NSRect gameBounds = [painter gameRectAround:[self gamePointRepresentingCentreOfView]
00067                                             forView:[self bounds]
00068                                           withScale:scale]; 
00069         // then draw it        
00070         [painter drawRect:aRect 
00071              ofViewBounds:[self bounds] 
00072 whichRepresentsGameBounds:gameBounds
00073                 withScale:scale]; 
00074         
00075         [[universe synchronizeAccess] unlock];
00076     } else {
00077         NSLog(@"GameView.drawRect waited %d seconds for lock, discarding lock");
00078         
00079         // first set up the gamebounds based on my position
00080         NSRect gameBounds = [painter gameRectAround:[self gamePointRepresentingCentreOfView]
00081                                             forView:[self bounds]
00082                                           withScale:scale]; 
00083         // then draw it        
00084         [painter drawRect:aRect 
00085              ofViewBounds:[self bounds] 
00086 whichRepresentsGameBounds:gameBounds
00087                 withScale:scale]; 
00088         
00089         // no lock obtained, so no need to unlock
00090     }
00091     busyDrawing = NO;
00092 }
00093 
00094 - (void) setKeyMap:(MTKeyMap *)newKeyMap {
00095     keyMap = newKeyMap;
00096 }
00097 
00098 // view functions
00099 - (void) keyDown:(NSEvent *)theEvent {
00100     
00101     if (keyMap == nil) {
00102         NSLog(@"GameView.keyDown have no keymap");
00103         [super keyDown:theEvent];
00104         return;
00105     }
00106     
00107     // check all characters in the event
00108     NSString *characters = [theEvent characters];
00109     
00110     for (int i = 0; i < [characters length]; i++) {
00111         unichar theChar = [characters characterAtIndex:i];
00112         unsigned int modifierFlags = [theEvent modifierFlags];
00113         
00114         int action = [keyMap actionForKey:theChar withModifierFlags:modifierFlags];
00115         
00116         // only valid keys
00117         if (action == ACTION_UNKNOWN) {
00118             [super keyDown:theEvent];
00119         }
00120         else {
00121             if ([self performAction: action] == NO) {
00122                 [super keyDown:theEvent];
00123             }
00124         }        
00125     }  
00126 }
00127 
00128 // mouse events $$ currently static allocated
00129 - (void) mouseDown:(NSEvent *)theEvent {
00130     [self performAction:ACTION_FIRE_TORPEDO];
00131 }
00132 
00133 - (void) otherMouseDown:(NSEvent *)theEvent {
00134     [self performAction:ACTION_FIRE_PHASER];
00135 }
00136 
00137 - (void) dummyMouseAction {
00138     
00139     // must become the first responder,
00140     // or a arbitrary view crashes...
00141     [[self window] makeKeyWindow];
00142     [self becomeFirstResponder];
00143    
00144     NSEvent *evt = [NSEvent mouseEventWithType:NSRightMouseDown 
00145                                       location:[painter centreOfRect:[self bounds]]
00146                                  modifierFlags:256 
00147                                      timestamp:GetCurrentEventTime()
00148                                   windowNumber:[[self window] windowNumber]
00149                                        context:[NSGraphicsContext currentContext] 
00150                                    eventNumber:0 
00151                                     clickCount:1 
00152                                       pressure:0];
00153     
00154     [[NSApplication sharedApplication] postEvent:evt atStart:YES];
00155 }
00156 
00157 - (void) rightMouseDown:(NSEvent *)theEvent {
00158     
00159     [self performAction:ACTION_SET_COURSE];
00160 }
00161 
00162 - (void) scrollWheel:(NSEvent *)theEvent {
00163     float mouseRole = [theEvent deltaY];
00164     // 1.0 means zoom in
00165     // -1.0 means zoom out
00166     if (mouseRole > 0) {
00167         // zoom in means smaller scale factor
00168         int newScale = scale - step*scale;
00169         if (newScale == scale) {
00170             newScale = scale - 1; // at least 1
00171         }
00172         // if scale is small it may not be possible to zoom in
00173         // scale*step = 0;
00174         if (newScale > [painter minScale]) {
00175             scale = newScale; 
00176         }
00177     } else {
00178         // zoom out means larger factor
00179         // if scale is small it may not be possible to zoom out
00180         // scale*step = 0 this means you'll stay zoomed in
00181         int newScale = scale + step*scale;
00182         if (newScale == scale) {
00183             newScale = scale + 1; // at least 1
00184         }
00185         if (newScale < [painter maxScale]) {
00186             scale = newScale;
00187         }
00188     }
00189     NSLog(@"GameView.scrollWheel setting scale to %d", scale);
00190 }
00191 
00192 
00193 - (void) sendSpeedReq:(int)speed {
00194     if (universe == nil) {
00195         NSLog(@"GameView.sendSpeedReq have no universe?");
00196     }
00197     
00198     int maxSpeed = [[[universe playerThatIsMe] ship] maxSpeed];   
00199     if (speed > maxSpeed) {
00200         speed = maxSpeed;
00201     }
00202     
00203     [[universe playerThatIsMe] setRequestedSpeed:speed]; // store here so we can track accelertaion in the future
00204     [notificationCenter postNotificationName:@"COMM_SEND_SPEED_REQ" userInfo:[NSNumber numberWithInt:speed]];
00205 }
00206 
00207 - (float) mouseDir {
00208 
00209     NSPoint mouseLocation = [self mousePos];
00210     
00211     // we are at the center
00212     NSPoint ourLocation;
00213     NSRect bounds = [self bounds];
00214     ourLocation.x = bounds.size.width / 2;
00215     ourLocation.y = bounds.size.height / 2;
00216     
00217     // the direction is
00218     float dir = [trigonometry angleDegBetween:mouseLocation andPoint:ourLocation];
00219     dir -= 90;  // north is 0 deg
00220     if (dir < 0) {
00221         dir += 360;
00222     }
00223     //NSLog(@"GameView.mouseDir = %f", dir);
00224     
00225     return dir;
00226 }
00227 
00228 // $$ may need locks here if the code becomes multi threaded
00229 - (bool) performAction:(int) action {
00230     
00231     //NSLog(@"GameView.performAction performing action %d", action);
00232     
00233     int maxSpeed, speed;
00234     Player *target = nil;
00235     Planet *planet = nil;
00236     NSPoint targetGamePoint;
00237     Player *me = [universe playerThatIsMe];    // IS used sometimes, compiler just complains..
00238     
00239     switch (action) {
00240         case ACTION_UNKNOWN:
00241             NSLog(@"GameView.performAction unknown action %d", action);
00242             return NO;
00243             break;
00244             case ACTION_CLOAK:
00245             if ([[universe playerThatIsMe] flags] & PLAYER_CLOAK) {
00246                 [notificationCenter postNotificationName:@"COMM_SEND_CLOAK_REQ" userInfo:[NSNumber numberWithBool:NO]];                
00247             } else {
00248                 [notificationCenter postNotificationName:@"COMM_SEND_CLOAK_REQ" userInfo:[NSNumber numberWithBool:YES]];
00249             }
00250             break;
00251             case ACTION_DET_ENEMY:
00252             // $$ JTrek checks if current time - last det time > 100 ms before sending this again
00253             // sounds sensible...
00254             [notificationCenter postNotificationName:@"COMM_SEND_DETONATE_REQ" userInfo:nil];
00255             break;
00256             case ACTION_DET_OWN: // detting ALL
00257             [notificationCenter postNotificationName:@"COMM_SEND_DET_MINE_ALL_REQ" userInfo:nil];
00258             break;
00259             case ACTION_FIRE_PLASMA:
00260             // $$ we can check if we are able to fire plasmas at all before poluting the network
00261             [angleConvertor setCourse:[self mouseDir]];            
00262             [notificationCenter postNotificationName:@"COMM_SEND_PLASMA_REQ" userInfo:[NSNumber numberWithChar:[angleConvertor netrekFormatCourse]]];
00263             break;
00264             case ACTION_FIRE_TORPEDO:
00265             [angleConvertor setCourse:[self mouseDir]];            
00266             [notificationCenter postNotificationName:@"COMM_SEND_TORPS_REQ" userInfo:[NSNumber numberWithChar:[angleConvertor netrekFormatCourse]]];
00267             break;
00268             case ACTION_FIRE_PHASER:
00269             [angleConvertor setCourse:[self mouseDir]];            
00270             [notificationCenter postNotificationName:@"COMM_SEND_PHASER_REQ" userInfo:[NSNumber numberWithChar:[angleConvertor netrekFormatCourse]]];
00271             break;
00272             case ACTION_SHIELDS:
00273             if ([me flags] & PLAYER_SHIELD) {
00274                 [notificationCenter postNotificationName:@"COMM_SEND_SHIELD_REQ" userInfo:[NSNumber numberWithBool:NO]];                
00275             } else {
00276                 [notificationCenter postNotificationName:@"COMM_SEND_SHIELD_REQ" userInfo:[NSNumber numberWithBool:YES]];
00277             }
00278             break;
00279             case ACTION_TRACTOR:
00280             // convert the mouse pointer to a point in the game grid
00281             targetGamePoint = [painter gamePointFromViewPoint:[self mousePos] 
00282                                                      viewRect:[self bounds]
00283                                         gamePosInCentreOfView:[self gamePointRepresentingCentreOfView] 
00284                                                     withScale:scale];
00285             // find the nearest player
00286             target = [universe playerNearPosition:targetGamePoint ofType:UNIVERSE_TARG_PLAYER];
00287             
00288             // if we are already tracktoring/pressoring, disable
00289             if ([[universe playerThatIsMe] flags] & PLAYER_TRACT) {
00290                 // $$ it is probaly not needed to figure out the correct playerId..
00291                 [notificationCenter postNotificationName:@"COMM_SEND_TRACTOR_OFF_REQ" 
00292                                                 userInfo:[NSNumber numberWithInt:[target playerId]]];
00293             } else {
00294                 [notificationCenter postNotificationName:@"COMM_SEND_TRACTOR_ON_REQ" 
00295                                                 userInfo:[NSNumber numberWithInt:[target playerId]]];
00296                 [[universe playerThatIsMe] setTractorTarget:target];
00297             }
00298                 break;
00299             case ACTION_PRESSOR:
00300             // convert the mouse pointer to a point in the game grid
00301             targetGamePoint = [painter gamePointFromViewPoint:[self mousePos] 
00302                                                      viewRect:[self bounds]
00303                                         gamePosInCentreOfView:[self gamePointRepresentingCentreOfView] 
00304                                                     withScale:scale];
00305             // find the nearest player
00306             target = [universe playerNearPosition:targetGamePoint ofType:UNIVERSE_TARG_PLAYER];
00307             
00308             // if we are already tracktoring/pressoring, disable
00309             if ([[universe playerThatIsMe] flags] & PLAYER_PRESS) {
00310                 // $$ it is probaly not needed to figure out the correct playerId..
00311                 [notificationCenter postNotificationName:@"COMM_SEND_REPRESSOR_OFF_REQ" 
00312                                                 userInfo:[NSNumber numberWithInt:[target playerId]]];
00313             } else {
00314                 [notificationCenter postNotificationName:@"COMM_SEND_REPRESSOR_ON_REQ" 
00315                                                 userInfo:[NSNumber numberWithInt:[target playerId]]];
00316                 // $$ bit strange, but i assume we cannot pressor one target and tractor another at
00317                 // the same time
00318                 [[universe playerThatIsMe] setTractorTarget:target];
00319             }
00320             break;
00321         case ACTION_WARP_0:  
00322             [self sendSpeedReq:0];
00323             break;
00324             case ACTION_WARP_1:
00325             [self sendSpeedReq:1];
00326             break;
00327             case ACTION_WARP_2:
00328             [self sendSpeedReq:2];
00329             break;
00330             case ACTION_WARP_3:
00331             [self sendSpeedReq:3];
00332             break;
00333         case ACTION_WARP_4:
00334             [self sendSpeedReq:4];
00335             break;
00336         case ACTION_WARP_5:
00337             [self sendSpeedReq:5];
00338             break;
00339         case ACTION_WARP_6:
00340             [self sendSpeedReq:6];
00341             break;
00342         case ACTION_WARP_7:
00343             [self sendSpeedReq:7];
00344             break;
00345         case ACTION_WARP_8:
00346             [self sendSpeedReq:8];
00347             break;
00348         case ACTION_WARP_9:
00349             [self sendSpeedReq:9];
00350             break;
00351         case ACTION_WARP_10:
00352             [self sendSpeedReq:10];
00353             break;
00354         case ACTION_WARP_11:
00355             [self sendSpeedReq:11];
00356             break;
00357         case ACTION_WARP_12:
00358             [self sendSpeedReq:0];
00359             break;
00360         case ACTION_WARP_MAX:
00361             maxSpeed = [[[universe playerThatIsMe] ship] maxSpeed];
00362             [self sendSpeedReq:maxSpeed];
00363             break;
00364         case ACTION_WARP_HALF_MAX:
00365             maxSpeed = [[[universe playerThatIsMe] ship] maxSpeed];
00366             [self sendSpeedReq:maxSpeed / 2];
00367             break;
00368         case ACTION_WARP_INCREASE:
00369             speed = [[universe playerThatIsMe] speed];
00370             [self sendSpeedReq:speed + 1];
00371             break;
00372         case ACTION_WARP_DECREASE:
00373             speed = [[universe playerThatIsMe] speed];
00374             [self sendSpeedReq:speed - 1];
00375             break;
00376         case ACTION_SET_COURSE:
00377             [angleConvertor setCourse:[self mouseDir]];            
00378             [notificationCenter postNotificationName:@"COMM_SEND_DIR_REQ" userInfo:[NSNumber numberWithChar:[angleConvertor netrekFormatCourse]]];
00379             // remove the planet lock
00380             [me setFlags:[me flags] & ~(PLAYER_PLOCK | PLAYER_PLLOCK)];
00381             break;
00382         case ACTION_LOCK:
00383             // convert the mouse pointer to a point in the game grid
00384             targetGamePoint = [painter gamePointFromViewPoint:[self mousePos] 
00385                                                      viewRect:[self bounds]
00386                                         gamePosInCentreOfView:[self gamePointRepresentingCentreOfView] 
00387                                                     withScale:scale];
00388             // find the nearest player
00389             target = [universe playerNearPosition:targetGamePoint ofType:UNIVERSE_TARG_PLAYER];
00390             // find the nearest planet
00391             planet = [universe planetNearPosition:targetGamePoint];
00392             // lock on the closest
00393             if ([universe entity:target closerToPos:targetGamePoint than:planet]) {
00394                 // lock on player
00395                 [notificationCenter postNotificationName:@"COMM_SEND_PLAYER_LOCK_REQ" 
00396                                                 userInfo:[NSNumber numberWithInt:[target playerId]]];
00397                 [me setPlayerLock:target];
00398             } else {
00399                 // lock on planet
00400                 [notificationCenter postNotificationName:@"COMM_SEND_PLANET_LOCK_REQ" 
00401                                                 userInfo:[NSNumber numberWithInt:[planet planetId]]];
00402                 [me setPlanetLock:planet];
00403             }
00404             break;
00405         case ACTION_PRACTICE_BOT:
00406             [notificationCenter postNotificationName:@"COMM_SEND_PRACTICE_REQ"];
00407             break;
00408         case ACTION_TRANSWARP:
00409             // netrek uses the same message for this, it could lead to very
00410             // funny results now we seperate it.
00411             [notificationCenter postNotificationName:@"COMM_SEND_PRACTICE_REQ"];
00412             break;
00413         case ACTION_BOMB:
00414             if (([me flags] & PLAYER_BOMB) == 0) { // already bombing?
00415                 [notificationCenter postNotificationName:@"COMM_SEND_BOMB_REQ" 
00416                                                 userInfo:[NSNumber numberWithBool:YES]];
00417             }
00418             break;
00419         case ACTION_ORBIT:
00420             [notificationCenter postNotificationName:@"COMM_SEND_ORBIT_REQ" 
00421                                             userInfo:[NSNumber numberWithBool:YES]];
00422             break;
00423         case ACTION_BEAM_DOWN:
00424             if (([me flags] & PLAYER_BEAMDOWN) == 0) { // already beaming?
00425                 [notificationCenter postNotificationName:@"COMM_SEND_BEAM_REQ" 
00426                                                 userInfo:[NSNumber numberWithBool:NO]]; // no means down
00427             }
00428             break;
00429         case ACTION_BEAM_UP:
00430             if (([me flags] & PLAYER_BEAMUP) == 0) { // already beaming?
00431                 [notificationCenter postNotificationName:@"COMM_SEND_BEAM_REQ" 
00432                                                 userInfo:[NSNumber numberWithBool:YES]]; // no means down
00433             }
00434             break;
00435         case ACTION_DISTRESS_CALL:
00436             NSLog(@"GameView.performAction Marcro's not implemented"); // $$ todo
00437             break;
00438         case ACTION_ARMIES_CARRIED_REPORT:
00439             NSLog(@"GameView.performAction Marcro's not implemented"); // $$ todo
00440             break;
00441         case ACTION_MESSAGE:
00442             NSLog(@"GameView.performAction MESSAGE not implemented"); // $$ todo
00443             break;
00444         case ACTION_DOCK_PERMISSION:
00445             if (([me flags] & PLAYER_DOCKOK) == 0) {  // toggle
00446                 [notificationCenter postNotificationName:@"COMM_SEND_DOCK_REQ" 
00447                                                 userInfo:[NSNumber numberWithBool:YES]]; 
00448             } else {
00449                 [notificationCenter postNotificationName:@"COMM_SEND_DOCK_REQ" 
00450                                                 userInfo:[NSNumber numberWithBool:NO]]; 
00451             }
00452             break;
00453         case ACTION_INFO:
00454             // convert the mouse pointer to a point in the game grid
00455             targetGamePoint = [painter gamePointFromViewPoint:[self mousePos] 
00456                                                      viewRect:[self bounds]
00457                                         gamePosInCentreOfView:[self gamePointRepresentingCentreOfView] 
00458                                                     withScale:scale];
00459             // find the nearest player
00460             target = [universe playerNearPosition:targetGamePoint ofType:(UNIVERSE_TARG_PLAYER | UNIVERSE_TARG_SELF)];
00461             // find the nearest planet
00462             planet = [universe planetNearPosition:targetGamePoint];
00463             // lock on the closest            
00464             if ([universe entity:target closerToPos:targetGamePoint than:planet]) {
00465                 // toggle info on player
00466                 [target setShowInfo:![target showInfo]];
00467             } else {
00468                 [planet setShowInfo:![planet showInfo]];
00469             }
00470             break;
00471         case ACTION_REFIT:
00472             // swich input do REFIT mode, probably need something similar for
00473             // message, we could generate a pop-up though...
00474             NSLog(@"GameView.performAction REFIT not implemented"); // $$ todo
00475             break;
00476         case ACTION_REPAIR:
00477             // $$ no toggle ?
00478             [notificationCenter postNotificationName:@"COMM_SEND_REPAIR_REQ" 
00479                                             userInfo:[NSNumber numberWithBool:YES]];
00480             break;
00481         case ACTION_QUIT:
00482             // $$ how do we quit ? send BYE, reset some data and flip to the outfit
00483             // or go through state machine to main menu...
00484             break;
00485         case ACTION_HELP:
00486             NSLog(@"GameView.performAction HELP not implemented"); // $$ todo
00487         case ACTION_DEBUG:
00488             [painter setDebugLabels:![painter debugLabels]];
00489             break;
00490         default:
00491             NSLog(@"GameView.performAction unknown action %d", action);
00492             return NO;
00493             break;
00494     }
00495     return YES;
00496 }
00497 
00498 @end

Generated on Fri Jul 28 19:15:24 2006 for MacTrek by  doxygen 1.4.7