/Volumes/Plantain/MyDocuments/Projects/MacTrek/MacTrek/Gui/GuiManager.m

00001 //
00002 //  GuiManager.m
00003 //  MacTrek
00004 //
00005 //  Created by Aqua on 27/05/2006.
00006 //  Copyright 2006 __MyCompanyName__. All rights reserved.
00007 //
00008 
00009 #import "GuiManager.h"
00010 //
00011 // The GuiManger maintains (among others) the game state machine, or to be precise the state machine that handles
00012 // the changes between the menus. It uses the following table to jump between states and specifies which events
00013 // trigger a state change
00014 //                     State          1          2          3          4          5          7          8
00015 // Event                              no       server     server     slot       login      outfit     game
00016 //                                  server    selected   contacted   found     accepted   accepted   entered
00017 //                                 selected          
00018 // MS_SERVER_SELECT                   2          2          2          2          2                    
00019 // LOGIN button pressed                                                5          5                    
00020 // CC_SERVER_CALL_SUCCESS                        3                                                  
00021 // CC_SERVER_CALL_FAILED                         1                                                  
00022 // CC_SLOT_FOUND                                            4                                        
00023 // LM_LOGIN_INVALID_SERVER                                             1                              
00024 // LM_LOGIN_COMPLETE                                                              7                              
00025 // OM_ENTER_GAME                                                                             8          
00026 // CC_GO_OUTFIT                                                                                        7
00027 // CC_GO_LOGIN                                                                                         5
00028 //
00029 // AddOn: there actually is a state 0 (begin state) that shows the splashScreen. The Splash Screen waits for
00030 //        a number of events before it raises the menu pane and allows the game to start 
00031 //
00032 
00033 @implementation GuiManager
00034 
00035 bool clientRuns = NO;
00036 MetaServerEntry *currentServer = nil;
00037 Universe *universe = nil;
00038 int startUpEvents = 0;
00039 
00040 // ROOT PLACE to turn multiThreading off, only tested for guest login
00041 // turn only off for testing!
00042 bool multiThreaded = NO; 
00043 
00044 // to be called..
00045 - (void) playIntroSoundEffect {
00046    [soundPlayer playSoundEffect:@"INTRO_SOUND"];  
00047 }
00048 
00049 - (id) init {
00050     self = [super init];
00051     if (self != nil) {
00052         // create the universe
00053         universe = [Universe defaultInstance];
00054         
00055         // setup our client
00056         client = [[ClientController alloc] initWithUniverse:universe];
00057         
00058         // create a sound player
00059         soundPlayer = [[SoundPlayer alloc] init];
00060         
00061         // reset gameState
00062         gameState = GS_NO_SERVER_SELECTED;
00063         
00064         // tag events
00065         [notificationCenter addObserver:self selector:@selector(serverSelected:) 
00066                                    name:@"MS_SERVER_SELECTED" object:nil];
00067         [notificationCenter addObserver:self selector:@selector(serverSlotFound) 
00068                                    name:@"CC_SLOT_FOUND" object:nil];
00069         [notificationCenter addObserver:self selector:@selector(serverDeSelected) 
00070                                    name:@"LC_INVALID_SERVER" object:nil];
00071         [notificationCenter addObserver:self selector:@selector(loginComplete) 
00072                                    name:@"LC_LOGIN_COMPLETE" object:nil];
00073         [notificationCenter addObserver:self selector:@selector(outfitAccepted) 
00074                                    name:@"SP_PICKOK" object:nil];
00075         [notificationCenter addObserver:self selector:@selector(loginComplete) 
00076                                    name:@"SP_PICKNOK" object:nil];
00077         // when killed
00078         [notificationCenter addObserver:self selector:@selector(iDied) 
00079                                    name:@"CC_GO_OUTFIT" object:nil];
00080         [notificationCenter addObserver:self selector:@selector(commError) 
00081                                    name:@"COMM_TCP_WRITE_ERROR" object:nil];
00082         // needed to keep polling for changed masks.. (at a very low rate)
00083         [notificationCenter addObserver:self selector:@selector(handleTeamMask:) 
00084                                    name:@"SP_MASK" object:nil];
00085         
00086         // startup events
00087         //[notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00088         //                           name:@"MS_SERVERS_READ"]; $$ sent, before i am observer..
00089         [notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00090                                    name:@"PF_IMAGES_CACHED"]; 
00091         [notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00092                                    name:@"SP_SOUNDS_CACHED"]; 
00093     }
00094     return self;
00095 }
00096 
00097 - (void) increaseStartUpCounter {
00098     
00099     startUpEvents++;
00100     [startUpProgress setIntValue:startUpEvents];
00101     [startUpProgress setNeedsDisplay:YES];
00102     NSLog(@"GuiManager.increaseStartUpCounter got event %d of %d", startUpEvents, NR_OF_EVENTS_BEFORE_SHOWING_MENU);
00103     if (startUpEvents == NR_OF_EVENTS_BEFORE_SHOWING_MENU) {
00104         [menuCntrl raiseMenu:self];
00105     } else if (startUpEvents > NR_OF_EVENTS_BEFORE_SHOWING_MENU) {
00106         NSLog(@"GuiManager.increaseStartUpCounter did not expect this event...");
00107         [menuCntrl raiseMenu:self]; // $$ try..
00108     }    
00109 }
00110 
00111 - (void) awakeFromNib { 
00112     // set the startup bar value
00113     [startUpProgress setMaxValue:NR_OF_EVENTS_BEFORE_SHOWING_MENU];
00114     
00115     // set up a timer to redraw at FRAME_RATE 
00116     // since some data is updated outside the main loop and in the receive 
00117     // thread
00118     [NSTimer scheduledTimerWithTimeInterval: (1 / FRAME_RATE)
00119                                      target:self selector:@selector(screenRefreshTimerFired:)
00120                                    userInfo:nil 
00121                                     repeats:YES];
00122     // does the controller wait, or asks for reads herself...
00123     [loginCntrl setMultiThreaded:multiThreaded];
00124 }
00125 
00126 - (void)handleTeamMask:(NSNumber *) mask {
00127     // we have received a new mask, as long as we are not in the game
00128     // the mask may change. So we keep an eye out for a change.
00129     if ((gameState == GS_LOGIN_ACCEPTED) && (!multiThreaded)) {
00130         // no outit done yet try 10ms delay
00131         NSLog(@"GuiManager.handleTeamMask firing up a read");
00132         [client performSelector: @selector(singleReadFromServer) 
00133                      withObject: self 
00134                      afterDelay: 1];
00135     }
00136 }
00137 
00138 - (void)screenRefreshTimerFired:(NSTimer*)theTimer {
00139     
00140    //static NSTimeInterval start, stop;
00141    //start = [NSDate timeIntervalSinceReferenceDate]; 
00142    //NSLog(@"GuiManager.screenRefreshTimerFired(slept): %f sec", (start-stop));        
00143 
00144     if (gameState == GS_GAME_ACTIVE) {
00145         // $$ this will work, but what about when we are killed?
00146         if (!multiThreaded) {
00147             //[notificationCenter setEnable:NO];
00148             [client singleReadFromServer]; // single threaded 
00149         }
00150 
00151         // after that, repaint
00152         [gameCntrl repaint];        
00153     } else {
00154         //[notificationCenter setEnable:YES];
00155     }
00156     //stop = [NSDate timeIntervalSinceReferenceDate];  
00157     //NSLog(@"GuiManager.screenRefreshTimerFired(spent): %f sec", (stop-start));
00158     //[mainWindow displayIfNeeded];   // not needed comm is now threadsafe 
00159 }
00160 
00161 - (void)serverSelected:(MetaServerEntry *) selectedServer {
00162     
00163     switch (gameState) {
00164         case GS_NO_SERVER_SELECTED:
00165         case GS_SERVER_SELECTED:
00166             // this event can also be a deselect 
00167             // handle it seperatly
00168             if (selectedServer == nil) {
00169                 NSLog(@"GuiManager.serverSelected forwarding to deselect");
00170                 [self serverDeSelected];
00171                 return;
00172             }
00173             // when a server is selected, connect to it
00174             // to see if we can go to the login panel
00175             gameState = GS_SERVER_SELECTED;
00176             currentServer = selectedServer;
00177             
00178             // now initiate the step to the next state 
00179             [client stop];
00180             // run the client in the main thread (no seperate)
00181             if ([client startClientAt:[selectedServer address] 
00182                                  port:[selectedServer port] 
00183                              seperate:multiThreaded]) {
00184                 // equals a CC_SERVER_CALL_SUCCESS
00185                 NSLog(@"GuiManager.serverSelected connect to server successfull"); 
00186                 // handle state entering
00187                 if (multiThreaded) {            // other wise we have already a slot found
00188                    [self serverConnected]; 
00189                 }
00190                             
00191             } else {
00192                 // equals a CC_SERVER_CALL_FAILED
00193                 NSLog(@"GuiManager.serverSelected cannot connect to server!");                
00194                 // fall back to the first state
00195                 [self serverDeSelected];
00196                 // tell the selector that this server is no good
00197                 [selectServerCntrl invalidServer];                
00198             }
00199             break;
00200         case GS_SERVER_CONNECTED:
00201         case GS_SERVER_SLOT_FOUND:
00202         case GS_LOGIN_ACCEPTED:
00203             // same server selected again?
00204             if (currentServer != selectedServer) {
00205                 
00206                 if (currentServer != nil) {
00207                      // close old connection
00208                     [self serverDeSelected];
00209                 }
00210                 // open to new
00211                 [self serverSelected:selectedServer];
00212                 currentServer = selectedServer;
00213             }
00214             break;
00215         case GS_OUTFIT_ACCEPTED:
00216         case GS_GAME_ACTIVE:
00217             NSLog(@"GuiManager.serverSelected unexpected gameState %d, reseting", gameState);
00218             [self serverDeSelected];
00219             [menuCntrl raiseMenu:self];
00220             break;
00221         default:
00222             NSLog(@"GuiManager.serverSelected unknown gameState %d", gameState);
00223             break;
00224     }   
00225     
00226     NSLog(@"GuiManager.serverSelected GAMESTATE = %d", gameState);
00227 }
00228 
00229 - (void) serverDeSelected {
00230     // if no server was selected disable login cntrl
00231     gameState = GS_NO_SERVER_SELECTED;
00232     // stop connection
00233     [client stop];
00234     //[client release];
00235     // create an empty client
00236     //client = [[ClientController alloc] initWithUniverse:universe];
00237     // cannot login
00238     [menuCntrl disableLogin];
00239     [localServerCntrl disableLogin];
00240     [selectServerCntrl disableLogin];
00241     // when we can not type our name
00242     [loginCntrl disablePlayerName];
00243     [loginCntrl reset];
00244     currentServer = nil;
00245     // get's also called to force our state back to deselection
00246     // so make sure nothing is selected after all
00247     [selectServerCntrl deselectServer:self];
00248     
00249     NSLog(@"GuiManager.serverDeSelected GAMESTATE = %d", gameState);
00250 }
00251 
00252 - (void) serverConnected {
00253 
00254     switch (gameState) {
00255 
00256         case GS_SERVER_SELECTED:            
00257             // nothing sepecial,
00258             // wait for a slot found
00259             gameState = GS_SERVER_CONNECTED;
00260             break;
00261         case GS_SERVER_SLOT_FOUND:
00262             // sometimes this happens immideatly
00263             break;            
00264         case GS_SERVER_CONNECTED:
00265         case GS_OUTFIT_ACCEPTED:            
00266         case GS_GAME_ACTIVE:
00267         case GS_LOGIN_ACCEPTED:
00268         case GS_NO_SERVER_SELECTED:
00269             NSLog(@"GuiManager.serverConnected unexpected gameState %d, reseting", gameState);
00270             [self serverDeSelected];
00271             [menuCntrl raiseMenu:self];            
00272             break;
00273         default:
00274             NSLog(@"GuiManager.serverConnected unknown gameState %d", gameState);
00275             break;
00276     }
00277  
00278     
00279     NSLog(@"GuiManager.serverConnected GAMESTATE = %d", gameState);
00280 }
00281 
00282 - (void) serverSlotFound {
00283     switch (gameState) {
00284         case GS_NO_SERVER_SELECTED:
00285 
00286         case GS_OUTFIT_ACCEPTED:
00287             NSLog(@"GuiManager.serverSlotFound unexpected gameState %d, reseting", gameState);
00288             [self serverDeSelected];
00289             [menuCntrl raiseMenu:self];            
00290             break;
00291         case GS_SERVER_CONNECTED:
00292         case GS_SERVER_SELECTED:                // can happen because of seperate thread
00293         case GS_GAME_ACTIVE:                    // after a ghostbust, reconnect is done automatically
00294             gameState = GS_SERVER_SLOT_FOUND;
00295             // now we can login
00296             [menuCntrl enableLogin];
00297             [localServerCntrl enableLogin];
00298             [selectServerCntrl enableLogin];
00299             [loginCntrl enablePlayerName];
00300             [loginCntrl startClock];
00301             // raise login window automatically
00302             // [menuCntrl raiseLogin:self];
00303             
00304             // a successfull login moves us to the next state
00305             
00306             // send our settings to the server            
00307             if (![client sendSlotSettingsToServer]) {
00308                 NSLog(@"GuiManager.serverSlotFound cannot send slot settings to server!");
00309                 [self serverDeSelected];
00310             }
00311             break;
00312         default:
00313             NSLog(@"GuiManager.serverSlotFound unknown gameState %d", gameState);
00314             break;
00315     }
00316     
00317     NSLog(@"GuiManager.serverSlotFound GAMESTATE = %d", gameState);
00318 }
00319 
00320 - (void) iDied {
00321     [self loginComplete];
00322 }
00323 
00324 - (void) loginComplete {
00325     switch (gameState) {
00326         case GS_NO_SERVER_SELECTED:
00327         case GS_SERVER_SELECTED:
00328         case GS_SERVER_CONNECTED:
00329         case GS_OUTFIT_ACCEPTED:
00330             NSLog(@"GuiManager.loginComplete unexpected gameState %d, reseting", gameState);
00331             [self serverDeSelected];
00332             [menuCntrl raiseMenu:self];            
00333             break;
00334         case GS_LOGIN_ACCEPTED: // when outfit is not accepted try again
00335             NSLog(@"GuiManager.loginComplete login was not accepted, try again");
00336             // a successfull outfit moves us to the next state
00337             break;
00338         case GS_SERVER_SLOT_FOUND:
00339             gameState = GS_LOGIN_ACCEPTED;
00340             // cannot login twice
00341             [menuCntrl disableLogin];
00342             [localServerCntrl disableLogin];
00343             [selectServerCntrl disableLogin];
00344             // when we can not type our name
00345             [loginCntrl disablePlayerName];
00346             
00347             // raise the outfit window automatically
00348             [menuCntrl raiseOutfit:self];
00349             // start with a empty login
00350             [outfitCntrl setInstructionFieldToDefault];
00351             // when we receive status messages, outfitCntrl updates the textfield 
00352             // only as long as we are in outfit!!!
00353             [notificationCenter addObserver:outfitCntrl selector:@selector(setInstructionField:) name:@"SP_WARNING"
00354                                      object:nil useLocks:NO useMainRunLoop:YES]; 
00355             // a successfull outfit moves us to the next state
00356             break;
00357         case GS_GAME_ACTIVE:    // if we are killed we get back here
00358             // start with a empty login
00359             [outfitCntrl setInstructionFieldToDefault];
00360             // when we receive status messages, outfitCntrl updates the textfield 
00361             // only as long as we are in outfit!!!
00362             [notificationCenter addObserver:outfitCntrl selector:@selector(setInstructionField:) name:@"SP_WARNING"
00363                                      object:nil useLocks:NO useMainRunLoop:YES]; 
00364             gameState = GS_LOGIN_ACCEPTED;
00365             // raise the outfit window automatically
00366             [menuCntrl raiseOutfit:self];
00367             // a successfull outfit moves us to the next state
00368             break;
00369         default:
00370             NSLog(@"GuiManager.loginComplete unknown gameState %d", gameState);
00371             break;
00372     }
00373     NSLog(@"GuiManager.loginComplete GAMESTATE = %d", gameState);
00374 }
00375 
00376 - (void) outfitAccepted {
00377     switch (gameState) {
00378         case GS_NO_SERVER_SELECTED:
00379         case GS_SERVER_SELECTED:
00380         case GS_SERVER_CONNECTED:
00381         case GS_OUTFIT_ACCEPTED:
00382         case GS_SERVER_SLOT_FOUND:
00383         case GS_GAME_ACTIVE:
00384             NSLog(@"GuiManager.outfitAccepted unexpected gameState %d, reseting", gameState);
00385             [self serverDeSelected];
00386             [menuCntrl raiseMenu:self];            
00387             break;
00388         case GS_LOGIN_ACCEPTED:
00389             gameState = GS_OUTFIT_ACCEPTED;
00390             // let outfitCntrl stop listening to warnings
00391             [notificationCenter removeObserver:outfitCntrl name:@"SP_WARNING"];
00392             [outfitCntrl setInstructionFieldToDefault];
00393             // activate the game
00394             [self gameEntered];
00395  
00396             break;
00397         default:
00398             NSLog(@"GuiManager.outfitAccepted unknown gameState %d", gameState);
00399             break;
00400     }
00401     NSLog(@"GuiManager.outfitAccepted GAMESTATE = %d", gameState);
00402 }
00403 
00404 - (void) gameEntered { 
00405 
00406     switch (gameState) {
00407         case GS_NO_SERVER_SELECTED:
00408         case GS_SERVER_SELECTED:
00409         case GS_SERVER_CONNECTED:
00410         case GS_LOGIN_ACCEPTED:
00411         case GS_SERVER_SLOT_FOUND:
00412         case GS_GAME_ACTIVE:
00413             NSLog(@"GuiManager.gameEntered unexpected gameState %d, reseting", gameState);
00414             [self serverDeSelected];
00415             [menuCntrl raiseMenu:self];            
00416             break;
00417         case GS_OUTFIT_ACCEPTED:            
00418             // save the keymap if it was changed
00419             [[settingsCntrl keyMap] writeToDefaultFileIfChanged];
00420             // first pass on the keyMap that was created in the settings
00421             [gameCntrl setKeyMap:[settingsCntrl keyMap]]; 
00422             // and the volume setting
00423             [soundPlayer setVolumeFx:[settingsCntrl fxLevel]];
00424             [soundPlayer setVolumeMusic:[settingsCntrl musicLevel]];
00425             // go and play
00426             [menuCntrl raiseGame:self];
00427             [gameCntrl startGame];
00428             gameState = GS_GAME_ACTIVE;
00429             // a kill or error will get us out of the game  
00430             [soundPlayer playSoundEffect:@"ENTER_SHIP_SOUND"];
00431             break;
00432         default:
00433             NSLog(@"GuiManager.gameEntered unknown gameState %d", gameState);
00434             break;
00435     }
00436     NSLog(@"GuiManager.gameEntered GAMESTATE = %d", gameState);
00437 }
00438 
00439 - (void) commError { 
00440     switch (gameState) {
00441         case GS_NO_SERVER_SELECTED:
00442         case GS_SERVER_SELECTED:
00443             break;                      // queued events 
00444         case GS_SERVER_CONNECTED:
00445         case GS_LOGIN_ACCEPTED:
00446         case GS_SERVER_SLOT_FOUND:
00447         case GS_GAME_ACTIVE:
00448         case GS_OUTFIT_ACCEPTED:
00449             NSLog(@"GuiManager.commError unexpected (gameState %d), reseting", gameState);
00450             [self serverDeSelected];
00451             [menuCntrl raiseMenu:self];            
00452             break;
00453         default:
00454             NSLog(@"GuiManager.gameEntered unknown gameState %d", gameState);
00455             break;
00456     }
00457     NSLog(@"GuiManager.commError GAMESTATE = %d", gameState);
00458 }
00459 
00460 // demo 
00461 /*
00462 - (void) play {
00463     [jtrekCntrl setServer:[selectServerCntrl selectedServer]];
00464     [jtrekCntrl play:self];
00465 }
00466 */
00467 @end

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