Gui/GuiManager.m

00001 //
00002 //  GuiManager.m
00003 //  MacTrek
00004 //
00005 //  Created by Aqua on 27/05/2006.
00006 //  Copyright 2006 Luky Soft. 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 Luky Softhreading off, only tested for guest login
00041 // turn only off for testing!
00042 bool multiThreaded = YES; 
00043 
00044 - (id) init {
00045     self = [super init];
00046     if (self != nil) {
00047         // create the universe
00048         universe = [Universe defaultInstance];
00049         
00050         // setup our client
00051         client = [[ClientController alloc] initWithUniverse:universe];
00052         
00053         // load the themes
00054         soundPlayerTheme1 = [[SoundPlayerForNetrek alloc] init];
00055         painterTheme1     = [[PainterFactoryForNetrek alloc] init];
00056         soundPlayerTheme2 = [[SoundPlayerForMacTrek alloc] init];
00057         painterTheme2     = [[PainterFactoryForMacTrek alloc] init];
00058         soundPlayerTheme3 = [[SoundPlayerForTac alloc] init];
00059         painterTheme3     = [[PainterFactoryForTac alloc] init];
00060         // pick a default
00061         soundPlayerActiveTheme = soundPlayerTheme1;
00062         painterActiveTheme = painterTheme1;
00063         activeTheme = -1;
00064         
00065         // reset gameState
00066         gameState = GS_NO_SERVER_SELECTED;
00067         
00068         // tag events
00069         [notificationCenter addObserver:self selector:@selector(serverSelected:) 
00070                                    name:@"MS_SERVER_SELECTED" object:nil];
00071         [notificationCenter addObserver:self selector:@selector(serverSlotFound) 
00072                                    name:@"CC_SLOT_FOUND" object:nil];
00073         [notificationCenter addObserver:self selector:@selector(serverDeSelected) 
00074                                    name:@"LC_INVALID_SERVER" object:nil];
00075         [notificationCenter addObserver:self selector:@selector(loginComplete) 
00076                                    name:@"LC_LOGIN_COMPLETE" object:nil];
00077         [notificationCenter addObserver:self selector:@selector(outfitAccepted) 
00078                                    name:@"SP_PICKOK" object:nil];
00079         [notificationCenter addObserver:self selector:@selector(loginComplete) 
00080                                    name:@"SP_PICKNOK" object:nil];
00081         // when killed
00082         [notificationCenter addObserver:self selector:@selector(iDied) 
00083                                    name:@"CC_GO_OUTFIT" object:nil];
00084         [notificationCenter addObserver:self selector:@selector(commError) 
00085                                    name:@"COMM_TCP_WRITE_ERROR" object:nil];
00086         // needed to keep polling for changed masks.. (at a very low rate)
00087         [notificationCenter addObserver:self selector:@selector(handleTeamMask:) 
00088                                    name:@"SP_MASK" object:nil];
00089         
00090         // startup events
00091         //[notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00092         //                           name:@"MS_SERVERS_READ"]; $$ sent, before i am observer..
00093         [notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00094                                    name:@"PF_IMAGES_CACHED"]; 
00095         [notificationCenter addObserver:self selector:@selector(increaseStartUpCounter) 
00096                                    name:@"SP_SOUNDS_CACHED"]; 
00097     }
00098     return self;
00099 }
00100 
00101 
00102 // to be called..
00103 - (void) playIntroSoundEffect {
00104     [soundPlayerActiveTheme playSoundEffect:@"INTRO_SOUND"];  
00105 }
00106 
00107 - (void) increaseStartUpCounter {
00108     
00109     startUpEvents++;
00110     [startUpProgress setIntValue:startUpEvents];
00111     [startUpProgress setNeedsDisplay:YES];
00112     NSLog(@"GuiManager.increaseStartUpCounter got event %d of %d", startUpEvents, NR_OF_EVENTS_BEFORE_SHOWING_MENU);
00113     
00114     if (startUpEvents == NR_OF_EVENTS_BEFORE_SHOWING_MENU) {
00115         
00116         //[startUpProgress setHidden:YES];
00117         //[startUpProgress setNeedsDisplay:YES];
00118         [self playIntroSoundEffect];
00119         
00120         //[menuCntrl raiseMenu:self]; // $$ should be on mouseclick
00121         [menuButton setHidden:NO]; // enable going to the memu
00122         [menuButton setEnabled:YES];
00123         [menuButton setNeedsDisplay:YES];
00124         // or go automaticaly after 10 seconds (use obj nil since menu does not know us
00125         [menuCntrl  performSelector:@selector(leaveSplashScreen) withObject:nil afterDelay: 10];
00126         [splashView performSelector:@selector(stop:) withObject:self afterDelay: 10];
00127         
00128     } else if (startUpEvents > NR_OF_EVENTS_BEFORE_SHOWING_MENU) {
00129         
00130         NSLog(@"GuiManager.increaseStartUpCounter did not expect this event... %d", startUpEvents);
00131         [menuCntrl raiseMenu:self]; // $$ try..
00132     }    
00133 }
00134 
00135 - (void) awakeFromNib { 
00136     // set the startup bar value
00137     [startUpProgress setMaxValue:NR_OF_EVENTS_BEFORE_SHOWING_MENU];
00138     
00139     // set up a timer to redraw at FRAME_RATE 
00140     // since some data is updated outside the main loop and in the receive 
00141     // thread
00142     [NSTimer scheduledTimerWithTimeInterval: (1 / FRAME_RATE)
00143                                      target:self selector:@selector(screenRefreshTimerFired:)
00144                                    userInfo:nil 
00145                                     repeats:YES];
00146     // does the controller wait, or asks for reads herself...
00147     [loginCntrl setMultiThreaded:multiThreaded];
00148 }
00149 
00150 - (void)handleTeamMask:(NSNumber *) mask {
00151     // we have received a new mask, as long as we are not in the game
00152     // the mask may change. So we keep an eye out for a change.
00153     if ((gameState == GS_LOGIN_ACCEPTED) && (!multiThreaded)) {
00154         // no outit done yet try 10ms delay
00155         NSLog(@"GuiManager.handleTeamMask firing up a read");
00156         [client performSelector: @selector(singleReadFromServer) 
00157                      withObject: self 
00158                      afterDelay: 1];
00159     }
00160 }
00161 
00162 - (void)screenRefreshTimerFired:(NSTimer*)theTimer {
00163     
00164    //static NSTimeInterval start, stop;
00165    //start = [NSDate timeIntervalSinceReferenceDate]; 
00166    //NSLog(@"GuiManager.screenRefreshTimerFired(slept): %f sec", (start-stop));        
00167 
00168     if (gameState == GS_GAME_ACTIVE) {
00169         // $$ this will work, but what about when we are killed?
00170         if (!multiThreaded) {
00171             //[notificationCenter setEnable:NO];
00172             [client singleReadFromServer]; // single threaded 
00173         }
00174 
00175         // after that, repaint
00176         [gameCntrl repaint];        
00177     } else {
00178         //[notificationCenter setEnable:YES];
00179     }
00180     //stop = [NSDate timeIntervalSinceReferenceDate];  
00181     //NSLog(@"GuiManager.screenRefreshTimerFired(spent): %f sec", (stop-start));
00182     //[mainWindow displayIfNeeded];   // not needed comm is now threadsafe 
00183 }
00184 
00185 - (void)serverSelected:(MetaServerEntry *) selectedServer {
00186     
00187     switch (gameState) {
00188         case GS_NO_SERVER_SELECTED:
00189         case GS_SERVER_SELECTED:
00190             // this event can also be a deselect 
00191             // handle it seperatly
00192             if (selectedServer == nil) {
00193                 NSLog(@"GuiManager.serverSelected forwarding to deselect");
00194                 [self serverDeSelected];
00195                 return;
00196             }
00197             // when a server is selected, connect to it
00198             // to see if we can go to the login panel
00199             gameState = GS_SERVER_SELECTED;
00200             currentServer = selectedServer;
00201             
00202             // now initiate the step to the next state 
00203             [client stop];
00204             // run the client in the main thread (no seperate)
00205             if ([client startClientAt:[selectedServer address] 
00206                                  port:[selectedServer port] 
00207                              seperate:multiThreaded]) {
00208                 // equals a CC_SERVER_CALL_SUCCESS
00209                 NSLog(@"GuiManager.serverSelected connect to server successfull"); 
00210                 // handle state entering
00211                 if (multiThreaded) {            // other wise we have already a slot found
00212                    [self serverConnected]; 
00213                 }
00214                             
00215             } else {
00216                 // equals a CC_SERVER_CALL_FAILED
00217                 NSLog(@"GuiManager.serverSelected cannot connect to server!");                
00218                 // fall back to the first state
00219                 [self serverDeSelected];
00220                 // tell the selector that this server is no good
00221                 [selectServerCntrl invalidServer];                
00222             }
00223             break;
00224         case GS_SERVER_CONNECTED:
00225         case GS_SERVER_SLOT_FOUND:
00226         case GS_LOGIN_ACCEPTED:
00227             // same server selected again?
00228             if (currentServer != selectedServer) {
00229                 
00230                 if (currentServer != nil) {
00231                      // close old connection
00232                     [self serverDeSelected];
00233                 }
00234                 // open to new
00235                 [self serverSelected:selectedServer];
00236                 currentServer = selectedServer;
00237             }
00238             break;
00239         case GS_OUTFIT_ACCEPTED:
00240         case GS_GAME_ACTIVE:
00241             NSLog(@"GuiManager.serverSelected unexpected gameState %d, reseting", gameState);
00242             [self serverDeSelected];
00243             [menuCntrl raiseMenu:self];
00244             break;
00245         default:
00246             NSLog(@"GuiManager.serverSelected unknown gameState %d", gameState);
00247             break;
00248     }   
00249     
00250     NSLog(@"GuiManager.serverSelected GAMESTATE = %d", gameState);
00251 }
00252 
00253 - (void) serverDeSelected {
00254     // if no server was selected disable login cntrl
00255     gameState = GS_NO_SERVER_SELECTED;
00256     // stop connection
00257     [client stop];
00258     //[client release];
00259     // create an empty client
00260     //client = [[ClientController alloc] initWithUniverse:universe];
00261     // cannot login
00262     [menuCntrl disableLogin];
00263     [localServerCntrl disableLogin];
00264     [selectServerCntrl disableLogin];
00265     // when we can not type our name
00266     [loginCntrl disablePlayerName];
00267     [loginCntrl reset];
00268     currentServer = nil;
00269     // get's also called to force our state back to deselection
00270     // so make sure nothing is selected after all
00271     [selectServerCntrl deselectServer:self];
00272     
00273     NSLog(@"GuiManager.serverDeSelected GAMESTATE = %d", gameState);
00274 }
00275 
00276 - (void) serverConnected {
00277 
00278     switch (gameState) {
00279 
00280         case GS_SERVER_SELECTED:            
00281             // nothing sepecial,
00282             // wait for a slot found
00283             gameState = GS_SERVER_CONNECTED;
00284             break;
00285         case GS_SERVER_SLOT_FOUND:
00286             // sometimes this happens immideatly
00287             break;            
00288         case GS_SERVER_CONNECTED:
00289         case GS_OUTFIT_ACCEPTED:            
00290         case GS_GAME_ACTIVE:
00291         case GS_LOGIN_ACCEPTED:
00292         case GS_NO_SERVER_SELECTED:
00293             NSLog(@"GuiManager.serverConnected unexpected gameState %d, reseting", gameState);
00294             [self serverDeSelected];
00295             [menuCntrl raiseMenu:self];            
00296             break;
00297         default:
00298             NSLog(@"GuiManager.serverConnected unknown gameState %d", gameState);
00299             break;
00300     }
00301  
00302     
00303     NSLog(@"GuiManager.serverConnected GAMESTATE = %d", gameState);
00304 }
00305 
00306 - (void) serverSlotFound {
00307     switch (gameState) {
00308         case GS_NO_SERVER_SELECTED:
00309 
00310         case GS_OUTFIT_ACCEPTED:
00311             NSLog(@"GuiManager.serverSlotFound unexpected gameState %d, reseting", gameState);
00312             [self serverDeSelected];
00313             [menuCntrl raiseMenu:self];            
00314             break;
00315         case GS_SERVER_CONNECTED:
00316         case GS_SERVER_SELECTED:                // can happen because of seperate thread
00317         case GS_GAME_ACTIVE:                    // after a ghostbust, reconnect is done automatically
00318             gameState = GS_SERVER_SLOT_FOUND;
00319             // now we can login
00320             [menuCntrl enableLogin];
00321             [localServerCntrl enableLogin];
00322             [selectServerCntrl enableLogin];
00323             [loginCntrl enablePlayerName];
00324             [loginCntrl startClock];
00325             // raise login window automatically
00326             // [menuCntrl raiseLogin:self];
00327             
00328             // a successfull login moves us to the next state
00329             
00330             // send our settings to the server            
00331             if (![client sendSlotSettingsToServer]) {
00332                 NSLog(@"GuiManager.serverSlotFound cannot send slot settings to server!");
00333                 [self serverDeSelected];
00334             }
00335             break;
00336         default:
00337             NSLog(@"GuiManager.serverSlotFound unknown gameState %d", gameState);
00338             break;
00339     }
00340     
00341     NSLog(@"GuiManager.serverSlotFound GAMESTATE = %d", gameState);
00342 }
00343 
00344 - (void) iDied {
00345     // restore the cursor if needed
00346     if ([NSCursor currentCursor] == [NSCursor crosshairCursor]) {
00347         [[NSCursor crosshairCursor] pop];
00348     }
00349     [self loginComplete];
00350 }
00351 
00352 - (void) loginComplete {
00353     switch (gameState) {
00354         case GS_NO_SERVER_SELECTED:
00355         case GS_SERVER_SELECTED:
00356         case GS_SERVER_CONNECTED:
00357         case GS_OUTFIT_ACCEPTED:
00358             NSLog(@"GuiManager.loginComplete unexpected gameState %d, reseting", gameState);
00359             [self serverDeSelected];
00360             [menuCntrl raiseMenu:self];            
00361             break;
00362         case GS_LOGIN_ACCEPTED: // when outfit is not accepted try again
00363             NSLog(@"GuiManager.loginComplete login was not accepted, try again");
00364             // a successfull outfit moves us to the next state
00365             break;
00366         case GS_SERVER_SLOT_FOUND:
00367             gameState = GS_LOGIN_ACCEPTED;
00368             // cannot login twice
00369             [menuCntrl disableLogin];
00370             [localServerCntrl disableLogin];
00371             [selectServerCntrl disableLogin];
00372             // when we can not type our name
00373             [loginCntrl disablePlayerName];
00374             
00375             // raise the outfit window automatically
00376             [menuCntrl raiseOutfit:self];
00377             // start with a empty login
00378             [outfitCntrl setInstructionFieldToDefault];
00379             // when we receive status messages, outfitCntrl updates the textfield 
00380             // only as long as we are in outfit!!!
00381             [notificationCenter addObserver:outfitCntrl selector:@selector(setInstructionField:) name:@"SP_WARNING"
00382                                      object:nil useLocks:NO useMainRunLoop:YES]; 
00383             // a successfull outfit moves us to the next state
00384             break;
00385         case GS_GAME_ACTIVE:    // if we are killed we get back here
00386             // start with a empty login
00387             [outfitCntrl setInstructionFieldToDefault];
00388             // when we receive status messages, outfitCntrl updates the textfield 
00389             // only as long as we are in outfit!!!
00390             [notificationCenter addObserver:outfitCntrl selector:@selector(setInstructionField:) name:@"SP_WARNING"
00391                                      object:nil useLocks:NO useMainRunLoop:YES]; 
00392             gameState = GS_LOGIN_ACCEPTED;
00393             // raise the outfit window automatically
00394             [menuCntrl raiseOutfit:self];
00395             // a successfull outfit moves us to the next state
00396             break;
00397         default:
00398             NSLog(@"GuiManager.loginComplete unknown gameState %d", gameState);
00399             break;
00400     }
00401     NSLog(@"GuiManager.loginComplete GAMESTATE = %d", gameState);
00402 }
00403 
00404 - (void) outfitAccepted {
00405     switch (gameState) {
00406         case GS_NO_SERVER_SELECTED:
00407         case GS_SERVER_SELECTED:
00408         case GS_SERVER_CONNECTED:
00409         case GS_OUTFIT_ACCEPTED:
00410         case GS_SERVER_SLOT_FOUND:
00411         case GS_GAME_ACTIVE:
00412             NSLog(@"GuiManager.outfitAccepted unexpected gameState %d, reseting", gameState);
00413             [self serverDeSelected];
00414             [menuCntrl raiseMenu:self];            
00415             break;
00416         case GS_LOGIN_ACCEPTED:
00417             gameState = GS_OUTFIT_ACCEPTED;
00418             // let outfitCntrl stop listening to warnings
00419             [notificationCenter removeObserver:outfitCntrl name:@"SP_WARNING"];
00420             [outfitCntrl setInstructionFieldToDefault];
00421             // activate the game
00422             [self gameEntered];
00423  
00424             break;
00425         default:
00426             NSLog(@"GuiManager.outfitAccepted unknown gameState %d", gameState);
00427             break;
00428     }
00429     NSLog(@"GuiManager.outfitAccepted GAMESTATE = %d", gameState);
00430 }
00431 
00432 - (void) gameEntered { 
00433 
00434     switch (gameState) {
00435         case GS_NO_SERVER_SELECTED:
00436         case GS_SERVER_SELECTED:
00437         case GS_SERVER_CONNECTED:
00438         case GS_LOGIN_ACCEPTED:
00439         case GS_SERVER_SLOT_FOUND:
00440         case GS_GAME_ACTIVE:
00441             NSLog(@"GuiManager.gameEntered unexpected gameState %d, reseting", gameState);
00442             [self serverDeSelected];
00443             [menuCntrl raiseMenu:self];            
00444             break;
00445         case GS_OUTFIT_ACCEPTED:     
00446             // activate the theme if needed
00447             [self setTheme];
00448             // save the keymap if it was changed
00449             [[settingsCntrl keyMap] writeToDefaultFileIfChanged];
00450             // first pass on the keyMap that was created in the settings
00451             [gameCntrl setKeyMap:[settingsCntrl keyMap]]; 
00452             // and the volume setting
00453             [soundPlayerActiveTheme setVolumeFx:[settingsCntrl fxLevel]];
00454             [soundPlayerActiveTheme setVolumeMusic:[settingsCntrl musicLevel]];
00455             // go and play
00456             [menuCntrl raiseGame:self];
00457             [gameCntrl startGame];
00458             gameState = GS_GAME_ACTIVE;
00459             // a kill or error will get us out of the game  
00460             [soundPlayerActiveTheme playSoundEffect:@"ENTER_SHIP_SOUND"];
00461             break;
00462         default:
00463             NSLog(@"GuiManager.gameEntered unknown gameState %d", gameState);
00464             break;
00465     }
00466     NSLog(@"GuiManager.gameEntered GAMESTATE = %d", gameState);
00467 }
00468 
00469 - (void) commError { 
00470     switch (gameState) {
00471         case GS_NO_SERVER_SELECTED:
00472         case GS_SERVER_SELECTED:
00473             break;                      // queued events 
00474         case GS_SERVER_CONNECTED:
00475         case GS_LOGIN_ACCEPTED:
00476         case GS_SERVER_SLOT_FOUND:
00477         case GS_GAME_ACTIVE:
00478         case GS_OUTFIT_ACCEPTED:
00479             NSLog(@"GuiManager.commError unexpected (gameState %d), reseting", gameState);
00480             [self serverDeSelected];
00481             [menuCntrl raiseMenu:self];            
00482             break;
00483         default:
00484             NSLog(@"GuiManager.gameEntered unknown gameState %d", gameState);
00485             break;
00486     }
00487     NSLog(@"GuiManager.commError GAMESTATE = %d", gameState);
00488 }
00489 
00490 - (void) setTheme {
00491     
00492     // we are not yet setting the mapview to a themed painter,
00493     // can do so in the future if desired.    
00494     int theme = [settingsCntrl graphicsModel] + 1; // we count 1,2,3 graphicsModel starts at 0
00495     
00496     if (theme != activeTheme) {
00497         NSLog(@"GuiManager.setTheme to theme %d", theme);
00498         switch (theme) {
00499         case 1:            
00500             [gameCntrl setPainter:painterTheme1];
00501             [gameCntrl setSpeakComputerMessages:NO];
00502             painterActiveTheme = painterTheme1;
00503             [soundPlayerActiveTheme unSubscibeToNotifications]; // silence
00504             soundPlayerActiveTheme = soundPlayerTheme1;
00505             [soundPlayerActiveTheme subscribeToNotifications];  // activate            
00506             break;
00507         case 2:            
00508             [gameCntrl setPainter:painterTheme2];
00509             [gameCntrl setSpeakComputerMessages:YES]; // mactrek speaks
00510             painterActiveTheme = painterTheme2;
00511             [soundPlayerActiveTheme unSubscibeToNotifications]; // silence
00512             soundPlayerActiveTheme = soundPlayerTheme2;
00513             [soundPlayerActiveTheme subscribeToNotifications];  // activate
00514             break;
00515         case 3:            
00516             [gameCntrl setPainter:painterTheme3];
00517             [gameCntrl setSpeakComputerMessages:NO];
00518             painterActiveTheme = painterTheme3;
00519             [soundPlayerActiveTheme unSubscibeToNotifications]; // silence
00520             soundPlayerActiveTheme = soundPlayerTheme3;
00521             [soundPlayerActiveTheme subscribeToNotifications];  // activate            
00522             break;            
00523         default:
00524             NSLog(@"GuiManager.setTheme ERROR, do not know of theme %d", theme);
00525             break;
00526         }        
00527     }
00528     
00529     if ([settingsCntrl fxLevel] == 0.0) {
00530         // special case disable all sound
00531         [soundPlayerActiveTheme unSubscibeToNotifications]; // silence
00532         NSLog(@"GuiManager.setTheme silencing SOUND");
00533     }
00534 }
00535 
00536 
00537 // demo 
00538 /*
00539 - (void) play {
00540     [jtrekCntrl setServer:[selectServerCntrl selectedServer]];
00541     [jtrekCntrl play:self];
00542 }
00543 */
00544 @end

Generated on Sat Aug 26 21:14:15 2006 for MacTrek by  doxygen 1.4.7