void ConnectingWindow::RenderTimeRemaining( float _fractionDone ) { static float s_timeRemaining = 0; static float s_timer = 0; float timeNow = GetHighResTime(); if( timeNow > s_timer + 0.5f ) { s_timer = timeNow; float timeSoFar = timeNow - m_stageStartTime; float timeForOnePercent = timeSoFar/_fractionDone; float percentRemaining = 1.0f - _fractionDone; s_timeRemaining = percentRemaining * timeForOnePercent; } if( s_timeRemaining > 0 && s_timeRemaining < 100000 ) { int minutes = int(s_timeRemaining / 60.0f); int seconds = s_timeRemaining - minutes * 60; char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_state_time_remaining") ); LPREPLACEINTEGERFLAG( 'M', minutes, caption ); char number[32]; sprintf( number, "%02d", seconds ); LPREPLACESTRINGFLAG( 'S', number, caption ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + m_h - 60, White, 14, caption ); } }
static void ReplaceStringFlagColorBit( char *caption, char flag, int colourDepth ) { if( colourDepth == 16 ) { LPREPLACESTRINGFLAG( flag, LANGUAGEPHRASE("dialog_colourdepth_16"), caption ); } else if( colourDepth == 24 ) { LPREPLACESTRINGFLAG( flag, LANGUAGEPHRASE("dialog_colourdepth_24"), caption ); } else if( colourDepth == 32 ) { LPREPLACESTRINGFLAG( flag, LANGUAGEPHRASE("dialog_colourdepth_32"), caption ); } else { char numberBit[128]; sprintf( numberBit, LANGUAGEPHRASE("dialog_colourdepth_X") ); LPREPLACEINTEGERFLAG( 'C', colourDepth, numberBit ); LPREPLACESTRINGFLAG( flag, numberBit, caption ); } }
void Interface::Render() { g_renderer->SetBlendMode( Renderer::BlendModeNormal ); g_renderer->SetFont( "kremlin" ); bool connectionScrewed = EclGetWindow("Connection Status"); if( !connectionScrewed ) { // // Network warning messages int myClientId = g_app->GetClientToServer()->m_clientId; if( !g_app->GetClientToServer()->IsSynchronised( myClientId ) ) { Colour col( 255, 50, 50, 255 ); float size = ( g_windowManager->WindowW() / 20.0f ); float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.2f; g_renderer->TextCentreSimple( xPos, yPos, col, size, LANGUAGEPHRASE("dialog_synchronisation_error") ); if( !EclGetWindow("Resynchronise" )) { EclRegisterWindow( new ResynchroniseWindow() ); } } if( g_app->GetClientToServer()->IsConnected() && g_app->GetClientToServer()->m_connectionState == ClientToServer::StateConnected ) { Colour col( 255, 50, 50, 255 ); float size = ( g_windowManager->WindowW() / 20.0f ); float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.3f; float yPos2 = g_windowManager->WindowH()*0.4f; static int s_connectionProblem = 0; // 0=no, 1=yes, 2=recovery static float s_maxLatency = 0.0f; static float s_connProblemDetected = -1.0f; float estimatedLatency = g_app->GetClientToServer()->GetEstimatedLatency(); if( !g_app->m_gameRunning ) s_connProblemDetected = -1.0f; if( s_connectionProblem == 0 && estimatedLatency > 5.0f ) { // Connection problem beginning s_connectionProblem = 1; s_maxLatency = 5.0f; } else if( s_connectionProblem == 1 ) { if( estimatedLatency < 1.0f ) { s_connectionProblem = 0; } else { // Connection problem getting worse float timeNow = GetHighResTime(); if( s_connProblemDetected < 0.0f ) s_connProblemDetected = timeNow; if( timeNow-s_connProblemDetected > 2.0f ) { col.m_a = 255.0f * min( 1.0f, timeNow-s_connProblemDetected-2.0f ); char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_connection_problem_seconds") ); LPREPLACEINTEGERFLAG( 'S', int(estimatedLatency), caption ); g_renderer->TextCentreSimple( xPos, yPos, col, size, caption ); } s_maxLatency = max( s_maxLatency, estimatedLatency ); float maxLat = s_maxLatency; float estLat = estimatedLatency; if( estimatedLatency < s_maxLatency - 1.0f ) { s_connectionProblem = 2; if( !EclGetWindow("Connection Status") ) { EclRegisterWindow( new ConnectingWindow() ); } } } } else if( s_connectionProblem == 2 ) { if( estimatedLatency < 1.0f ) { s_connectionProblem = 0; } else { // Connection problem recovering if( estimatedLatency > s_maxLatency + 3 ) { s_connectionProblem = 1; } else if( !EclGetWindow( "Connection Status") ) { s_connectionProblem = 0; s_maxLatency = 0.0f; } } } if( !g_app->GetServer() ) { int packetLoss = g_app->GetClientToServer()->CountPacketLoss(); int totalBad = packetLoss * 4 + estimatedLatency * 10; Clamp( totalBad, 0, 20 ); int red = totalBad*12; int green = 255 - red; col.Set(red,green,0,255); g_renderer->RectFill( 5, 5, 13, 13, col ); g_renderer->SetFont(); float yPos = 20; if( estimatedLatency > 1.0f ) { g_renderer->Text( 5, yPos, Colour(255,0,0,255), 12, LANGUAGEPHRASE("dialog_high_latency") ); yPos += 13; } if( packetLoss > 2 ) { g_renderer->Text( 5, yPos, Colour(255,0,0,255), 12, LANGUAGEPHRASE("dialog_high_packet_loss") ); } g_renderer->SetFont( "kremlin" ); } } if( m_message ) { float timeOnScreen = GetHighResTime() - m_messageTimer; if( timeOnScreen < 5.0f ) { char msg[256]; sprintf( msg, m_message ); float fractionShown = timeOnScreen/1.5f; if( fractionShown < 1.0f ) msg[ int(strlen(msg) * fractionShown) ] = '\x0'; float alpha = 1.0f; if( timeOnScreen > 4.0f ) alpha = 5.0f - timeOnScreen; alpha = max(alpha, 0.0f); alpha *= 255; Colour col( 255, 255, 255, alpha ); float size = ( g_windowManager->WindowW() / 12.0f ); float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.8f; float textWidth = g_renderer->TextWidth( m_message, size ); if( textWidth > g_windowManager->WindowW() ) { // message is longer than the window size, rescale it size *= g_windowManager->WindowW() / textWidth * 0.95f; textWidth = g_renderer->TextWidth( m_message, size ); } g_renderer->TextSimple( xPos-textWidth/2.0f, yPos, col, size, msg ); } else { delete m_message; m_message = NULL; } } // // Render Defcon counter (if applicable) if( g_app->m_gameRunning && g_app->GetWorld()->GetDefcon() > 1 && !m_message ) { int currentDefcon = g_app->GetWorld()->GetDefcon(); int minutes = g_app->GetWorld()->m_defconTime[ currentDefcon-1 ] - (g_app->GetWorld()->m_theDate.GetMinutes() + 1); int seconds = 60 - g_app->GetWorld()->m_theDate.GetSeconds(); if( seconds == 60 ) { minutes += 1; seconds = 0; } char msg[256]; sprintf( msg, LANGUAGEPHRASE("dialog_defcon_x_in_x_time") ); LPREPLACEINTEGERFLAG( 'D', currentDefcon - 1, msg ); LPREPLACEINTEGERFLAG( 'M', minutes, msg ); char number[32]; sprintf( number, "%02d", seconds ); LPREPLACESTRINGFLAG( 'S', number, msg ); float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.8f; float size = ( g_windowManager->WindowW() / 30.0f ); g_renderer->TextCentre(xPos, yPos, Colour(255,0,0,255), size, msg ); // If Defcon 3 is nearly here and we still have units left, give us some prompting if( currentDefcon == 4 ) { Team *myTeam = g_app->GetWorld()->GetMyTeam(); if( myTeam ) { int totalUnits = 0; for( int i = 0; i < WorldObject::NumObjectTypes; ++i ) { totalUnits += myTeam->m_unitsAvailable[i]; } if( totalUnits > 0 ) { yPos += size; size *= 0.35f; g_renderer->TextCentre( xPos, yPos, Colour(255,0,0,255), size, LANGUAGEPHRASE("message_placeunits") ); } } } } // // Render victory timer (if applicable) float victoryTimer = g_app->GetGame()->m_victoryTimer.DoubleValue(); if( victoryTimer > 0.0f ) { int minutes = victoryTimer / 60; int seconds = victoryTimer - minutes * 60; float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.75f; float size = ( g_windowManager->WindowW() / 30.0f ); char caption[256]; sprintf( caption, LANGUAGEPHRASE("dialog_time_remaining") ); LPREPLACEINTEGERFLAG( 'M', minutes, caption ); char number[32]; sprintf( number, "%02d", seconds ); LPREPLACESTRINGFLAG( 'S', number, caption ); g_renderer->TextCentre( xPos, yPos, Colour(255,0,0,255), size, caption ); } else if( victoryTimer == 0.0f ) { float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.75f; float size = ( g_windowManager->WindowW() / 30.0f ); g_renderer->TextCentre( xPos, yPos, Colour(255,0,0,255), size, LANGUAGEPHRASE("dialog_gameover") ); } if( g_app->m_gameRunning ) { if( g_app->GetWorld()->GetTimeScaleFactor() == 0 && g_app->GetGame()->m_winner == -1 ) { float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.5f; float size = ( g_windowManager->WindowW() / 20.0f ); g_renderer->TextCentreSimple( xPos, yPos, White, size, LANGUAGEPHRASE("dialog_paused") ); } } if( g_app->m_gameRunning && g_app->GetMapRenderer()->GetAutoCam() ) { g_renderer->TextSimple( 10.0f, 10.0f, White, 20, LANGUAGEPHRASE("dialog_autocam") ); } } // // Test Bed stuff #ifdef TESTBED if( g_app->GetServer() ) { float xPos = g_windowManager->WindowW()/2.0f; float yPos = g_windowManager->WindowH()*0.4f; float size = ( g_windowManager->WindowW() / 12.0f ); char *modPath = g_preferences->GetString( "ModPath" ); g_renderer->TextCentreSimple( xPos, yPos, White, size, "SERVER" ); g_renderer->TextCentreSimple( xPos, yPos+size, White, size/3, modPath ); ProfiledElement *element = g_profiler->m_rootElement->m_children.GetData( "Server Main Loop" ); if( element && element->m_lastNumCalls > 0 ) { float percent = element->m_lastNumCalls * 10.0f; g_renderer->TextCentre( xPos, yPos + size*3, White, size, "%d%%", (int)percent ); } } #endif // // Version //if( !g_app->m_gameRunning ) //{ // char caption[256]; // sprintf( caption, "%s %s", APP_NAME, APP_VERSION ); // float width = g_renderer->TextWidth( caption, 12 ); // g_renderer->RectFill( 18, 3, 4+width, 15, Colour(0,0,0,150) ); // g_renderer->SetFont(); // g_renderer->TextSimple( 20, 5, White, 12, caption ); // g_renderer->SetFont( "kremlin" ); //} #ifdef NON_PLAYABLE g_renderer->Text( 10, 40,Colour(255,50,50,255), 20, LANGUAGEPHRASE("dialog_non_playable_demo") ); #endif if( g_preferences->GetInt( PREFS_NETWORKTRACKSYNCRAND )) { g_renderer->Text( 10, 60, Colour(255,50,50,255), 20, LANGUAGEPHRASE("dialog_tracking_synchronisation") ); } g_renderer->SetFont(); }
void VotingWindow::Render( bool _hasFocus ) { InterfaceWindow::Render( _hasFocus ); Vote *vote = g_app->GetWorld()->m_votingSystem.LookupVote(m_voteId); if( vote ) { Team *myTeam = g_app->GetWorld()->GetMyTeam(); switch( vote->m_voteType ) { case Vote::VoteTypeJoinAlliance: if( myTeam->m_teamId == vote->m_createTeamId ) { char *allianceName = g_app->GetWorld()->GetAllianceName(vote->m_voteData); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+30, White, 20, LANGUAGEPHRASE("dialog_you_requested_alliance_1") ); char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_you_requested_alliance_2") ); LPREPLACESTRINGFLAG( 'A', allianceName, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+50, White, 20, caption ); } else if( myTeam->m_allianceId == vote->m_voteData ) { Team *team = g_app->GetWorld()->GetTeam( vote->m_createTeamId ); char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_requested_alliance_1") ); LPREPLACESTRINGFLAG( 'T', team->m_name, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+30, White, 20, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+50, White, 20, LANGUAGEPHRASE("dialog_requested_alliance_2") ); } break; case Vote::VoteTypeKickPlayer: { Team *kickTeam = g_app->GetWorld()->GetTeam(vote->m_voteData); if( myTeam->m_teamId == vote->m_createTeamId ) { g_renderer->TextCentreSimple( m_x+m_w/2, m_y+30, White, 20, LANGUAGEPHRASE("dialog_you_requested_kick_1") ); char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_you_requested_kick_2") ); LPREPLACESTRINGFLAG( 'T', kickTeam->m_name, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+50, White, 20, caption ); } else if( myTeam->m_allianceId == kickTeam->m_allianceId && myTeam->m_teamId != kickTeam->m_teamId ) { Team *team = g_app->GetWorld()->GetTeam( vote->m_createTeamId ); char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_requested_kick_1") ); LPREPLACESTRINGFLAG( 'T', team->m_name, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+30, White, 20, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_requested_kick_2") ); LPREPLACESTRINGFLAG( 'T', kickTeam->m_name, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y+50, White, 20, caption ); } break; } } g_renderer->SetFont( "kremlin" ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + 90, White, 20, LANGUAGEPHRASE("dialog_vote_yes") ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + 120, White, 20, LANGUAGEPHRASE("dialog_vote_no") ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + 150, White, 20, LANGUAGEPHRASE("dialog_vote_abstain") ); g_renderer->SetFont(); int yes, no, abstain; vote->GetCurrentVote( &yes, &no, &abstain ); g_renderer->Text( m_x + m_w - 40, m_y + 90, White, 20, "%d", yes ); g_renderer->Text( m_x + m_w - 40, m_y + 120, White, 20, "%d", no ); g_renderer->Text( m_x + m_w - 40, m_y + 150, White, 20, "%d", abstain ); if( vote->m_result == Vote::VoteUnknown ) { char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_vote_seconds_to_vote") ); LPREPLACEINTEGERFLAG( 'S', (int) vote->m_timer, caption ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + m_h - 70, White, 15, caption ); int votesRequired = vote->GetVotesRequired(); sprintf( caption, LANGUAGEPHRASE("dialog_vote_number_votes_required") ); LPREPLACEINTEGERFLAG( 'V', votesRequired, caption ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y + m_h - 50, White, 15, caption ); } else if( vote->m_result == Vote::VoteYes ) { g_renderer->SetFont( "kremlin" ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y + m_h - 70, White, 30, LANGUAGEPHRASE("dialog_vote_succeeded") ); g_renderer->SetFont(); } else if( vote->m_result == Vote::VoteNo ) { g_renderer->SetFont( "kremlin" ); g_renderer->TextCentreSimple( m_x+m_w/2, m_y + m_h - 70, White, 30, LANGUAGEPHRASE("dialog_vote_failed") ); g_renderer->SetFont(); } } }
void ConnectingWindow::Render( bool _hasFocus ) { InterfaceWindow::Render( _hasFocus ); float yPos = m_y + 40; // // Render our connection state g_renderer->SetFont( "kremlin" ); float fraction = 0.0f; char *caption = NULL; Colour col; switch( g_app->GetClientToServer()->m_connectionState ) { case ClientToServer::StateDisconnected: col.Set(255,0,0,255); caption = LANGUAGEPHRASE("dialog_state_disconnected"); fraction = 0.0f; break; case ClientToServer::StateConnecting: col.Set(255,255,0,255); caption = LANGUAGEPHRASE("dialog_state_connecting"); fraction = 0.5f; break; case ClientToServer::StateHandshaking: case ClientToServer::StateConnected: col.Set(0,255,0,255); caption = LANGUAGEPHRASE("dialog_state_connected"); fraction = 1.0f; break; } g_renderer->TextCentreSimple( m_x+m_w/2, yPos, col, 20, caption ); yPos += 20; if( fraction > 0.0f ) { g_renderer->RectFill( m_x + 30, yPos, (m_w-60)*fraction, 20, col ); g_renderer->Rect( m_x+30, yPos, (m_w-60), 20, White ); int numConnectionAttempts = g_app->GetClientToServer()->m_connectionAttempts; if( fraction < 1.0f && numConnectionAttempts > 0 ) { char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_state_attempts") ); LPREPLACEINTEGERFLAG( 'A', numConnectionAttempts, caption ); g_renderer->TextCentreSimple( m_x + m_w/2, m_y + m_h - 60, White, 14, caption ); } } // // Render our sync status (ie receiving all data from the server) yPos += 40; if( g_app->GetClientToServer()->IsConnected() ) { if( m_stage == 0 ) { m_stage = 1; m_stageStartTime = GetHighResTime(); } int serverSeqId = g_app->GetClientToServer()->m_serverSequenceId; int latestSeqId = g_app->GetClientToServer()->m_lastValidSequenceIdFromServer; int numRemaining = serverSeqId-latestSeqId; numRemaining--; if( numRemaining > m_maxUpdatesRemaining ) { m_maxUpdatesRemaining = numRemaining; } if( m_maxUpdatesRemaining > 0 ) { fraction = numRemaining / (float) m_maxUpdatesRemaining; } else { fraction = 0.0f; } Clamp( fraction, 0.0f, 1.0f ); fraction = 1.0f - fraction; col.Set( (1-fraction)*255, fraction*255, 0, 255 ); const char *caption = numRemaining > 5 ? LANGUAGEPHRASE("dialog_state_receiving") : LANGUAGEPHRASE("dialog_state_received"); g_renderer->TextCentreSimple( m_x+m_w/2, yPos, col, 20, caption ); yPos += 20; g_renderer->RectFill( m_x+30, yPos, (m_w-60)*fraction, 20, col ); g_renderer->Rect( m_x+30, yPos, (m_w-60), 20, White ); if( m_stage == 1 ) { RenderTimeRemaining(fraction); } // // Render how much of the received data we have successfully parsed int lagRemaining = 0; if( g_lastProcessedSequenceId > 0 && numRemaining < 10 ) { if( m_stage != 2 ) { m_stage = 2; m_stageStartTime = GetHighResTime(); } yPos += 40; int serverSeqId = g_app->GetClientToServer()->m_serverSequenceId; lagRemaining = serverSeqId - g_lastProcessedSequenceId; lagRemaining --; if( lagRemaining > m_maxLagRemaining ) { m_maxLagRemaining = lagRemaining; } fraction = lagRemaining / (float) m_maxLagRemaining; Clamp( fraction, 0.0f, 1.0f ); fraction = 1.0f - fraction; col.Set( (1-fraction)*255, fraction*255, 0, 255 ); const char *caption = lagRemaining > 5 ? LANGUAGEPHRASE("dialog_state_synchronising") : LANGUAGEPHRASE("dialog_state_synchronised"); g_renderer->TextCentreSimple( m_x+m_w/2, yPos, col, 20, caption ); yPos += 20; g_renderer->RectFill( m_x+30, yPos, (m_w-60)*fraction, 20, col ); g_renderer->Rect( m_x+30, yPos, (m_w-60), 20, White ); if( m_stage == 2 ) { RenderTimeRemaining( fraction ); } } // // Connection is done, we can shut down now // Pop up lobby if we were asked to do so if( g_app->GetClientToServer()->m_connectionState == ClientToServer::StateConnected ) { if( m_popupLobbyAtEnd ) { if( !EclGetWindow( "LOBBY" ) ) { LobbyWindow *lobby = new LobbyWindow(); ChatWindow *chat = new ChatWindow(); chat->SetPosition( g_windowManager->WindowW()/2 - chat->m_w/2, g_windowManager->WindowH() - chat->m_h - 30 ); EclRegisterWindow( chat ); float lobbyX = g_windowManager->WindowW()/2 - lobby->m_w/2; float lobbyY = chat->m_y - lobby->m_h - 30; lobbyY = std::max( lobbyY, 0.0f ); lobby->SetPosition(lobbyX, lobbyY); EclRegisterWindow( lobby ); } } if( numRemaining < 5 && lagRemaining < 5 ) { EclRemoveWindow(m_name); } } } else { if( g_app->GetClientToServer()->m_connectionState == ClientToServer::StateDisconnected ) { EclRemoveWindow(m_name); } } g_renderer->SetFont(); }
void NetworkWindow::Render( bool hasFocus ) { InterfaceWindow::Render( hasFocus ); int y = m_y+15; int h = 15; if( g_app->GetServer() ) { char caption[128]; char number[32]; sprintf( caption, LANGUAGEPHRASE("dialog_network_server_seqid") ); LPREPLACEINTEGERFLAG( 'S', g_app->GetServer()->m_sequenceId, caption ); g_renderer->TextSimple( m_x + 10, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_server_send") ); sprintf( number, "%2.1f", g_app->GetServer()->m_sendRate/1024.0f ); LPREPLACESTRINGFLAG( 'R', number, caption ); g_renderer->TextSimple( m_x + 10, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_server_receive") ); sprintf( number, "%2.1f", g_app->GetServer()->m_receiveRate/1024.0f ); LPREPLACESTRINGFLAG( 'R', number, caption ); g_renderer->TextSimple( m_x + 10, y+=h, White, 12, caption ); g_renderer->Line( m_x + 10, y + 20, m_x + m_w - 10, y + 20, White, 1 ); int clientX = m_x + 20; int ipX = clientX + 60; int seqX = ipX + 140; int playerX = seqX + 60; int lagX = playerX + 150; int syncX = lagX + 90; y+=h*2; g_renderer->TextSimple( clientX, y, White, 14, LANGUAGEPHRASE("dialog_network_id") ); g_renderer->TextSimple( ipX, y, White, 14, LANGUAGEPHRASE("dialog_network_ip_port") ); g_renderer->TextSimple( seqX, y, White, 14, LANGUAGEPHRASE("dialog_network_seqid") ); g_renderer->TextSimple( playerX, y, White, 14, LANGUAGEPHRASE("dialog_network_name") ); g_renderer->TextSimple( lagX, y, White, 14, LANGUAGEPHRASE("dialog_network_status") ); y+=h*2; int maxClients = g_app->GetGame()->GetOptionValue("MaxTeams") + g_app->GetGame()->GetOptionValue("MaxSpectators"); for( int i = 0; i < maxClients; ++i ) { if( g_app->GetServer()->m_clients.ValidIndex(i) ) { ServerToClient *sToc = g_app->GetServer()->m_clients[i]; char netLocation[256]; sprintf( netLocation, "%s:%d", sToc->m_ip, sToc->m_port); char caption[256]; Colour col; float timeBehind = GetHighResTime() - sToc->m_lastMessageReceived; if( timeBehind > 2.0f ) { col.Set( 255, 0, 0, 255 ); sprintf( caption, LANGUAGEPHRASE("dialog_network_lost_con") ); LPREPLACEINTEGERFLAG( 'S', (int)timeBehind, caption ); } else if( !sToc->m_caughtUp ) { col.Set( 200, 200, 30, 255 ); int percent = 100 * (sToc->m_lastKnownSequenceId / (float)g_app->GetServer()->m_sequenceId); sprintf( caption, LANGUAGEPHRASE("dialog_network_synching") ); LPREPLACEINTEGERFLAG( 'P', percent, caption ); } else { float latency = (g_app->GetServer()->m_sequenceId - sToc->m_lastKnownSequenceId); float latencyMs = latency * 100.0f; sprintf( caption, LANGUAGEPHRASE("dialog_network_ping") ); LPREPLACEINTEGERFLAG( 'P', (int)latencyMs, caption ); int red = std::min(255,(int) latency*20); int green = 255 - red; col.Set(red,green,0,255); } Colour normalCol(200,200,255,200); char *playerName = NULL; if( sToc->m_spectator ) { for( int j = 0; j < g_app->GetWorld()->m_spectators.Size(); ++j ) { Spectator *thisTeam = g_app->GetWorld()->m_spectators[j]; if( thisTeam->m_clientId == sToc->m_clientId ) { playerName = thisTeam->m_name; break; } } } else { for( int j = 0; j < g_app->GetWorld()->m_teams.Size(); ++j ) { Team *thisTeam = g_app->GetWorld()->m_teams[j]; if( thisTeam->m_clientId == sToc->m_clientId ) { playerName = thisTeam->m_name; break; } } } g_renderer->Text( clientX, y, normalCol, 12, "%d", sToc->m_clientId ); if( playerName ) { g_renderer->TextSimple( playerX, y, normalCol, 12, playerName ); } g_renderer->TextSimple( ipX, y, normalCol, 12, netLocation ); g_renderer->Text( seqX, y, normalCol, 12, "%d", sToc->m_lastKnownSequenceId ); g_renderer->TextSimple( lagX, y, col, 12, caption ); if( sToc->m_spectator ) { g_renderer->TextSimple( clientX+10, y, normalCol, 12, LANGUAGEPHRASE("dialog_network_spec") ); } if( sToc->m_syncErrorSeqId != -1 ) { g_renderer->Text( syncX, y, Colour(255,0,0,255), 12, LANGUAGEPHRASE("dialog_worldstatus_out_of_sync_2") ); } } else { g_renderer->Text( clientX, y, Colour(255,255,255,50), 12, LANGUAGEPHRASE("dialog_network_empty") ); } y += 20; } } // // Show which packets are queued up if we're a client if( g_app->GetClientToServer() && g_lastProcessedSequenceId >= 0 ) { float yPos = m_y + m_h - 25; g_renderer->TextSimple( m_x + 10, yPos-25, White, 13, LANGUAGEPHRASE("dialog_network_sequence_msg_queue") ); float xPos = m_x + 10; float width = (m_w - 120) / 10; float gap = width * 0.2f; for( int i = 0; i <= 10; ++i ) { if( i != 0 ) { g_renderer->RectFill( xPos, yPos-5, width-gap, 20, Colour(20,20,50,255) ); if( g_app->GetClientToServer()->IsSequenceIdInQueue( g_lastProcessedSequenceId+i ) ) { g_renderer->RectFill( xPos, yPos-5, width-gap, 20, Colour(0,255,0,255) ); } g_renderer->Rect( xPos, yPos-5, width-gap, 20, Colour(255,255,255,100) ); } if( i == 0 ) { char caption[128]; sprintf( caption, LANGUAGEPHRASE("dialog_network_seqid_number") ); LPREPLACEINTEGERFLAG( 'S', g_lastProcessedSequenceId, caption ); g_renderer->TextSimple( xPos, yPos, White, 13, caption ); xPos += width * 1; } xPos += width; } } if( g_app->GetClientToServer() ) { char caption[128]; char number[32]; if( !g_app->GetServer() ) { sprintf( caption, LANGUAGEPHRASE("dialog_network_svr_known_seqid") ); LPREPLACEINTEGERFLAG( 'S', g_app->GetClientToServer()->m_serverSequenceId, caption ); g_renderer->TextSimple( m_x + 10, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_svr_estimated_seqid") ); LPREPLACEINTEGERFLAG( 'S', g_app->GetClientToServer()->GetEstimatedServerSeqId(), caption ); g_renderer->TextSimple( m_x + 10, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_estimated_latency") ); sprintf( number, "%2.1f", g_app->GetClientToServer()->GetEstimatedLatency() ); LPREPLACESTRINGFLAG( 'L', number, caption ); g_renderer->TextCentreSimple( m_x + m_w/2, y+40, White, 15, caption ); } y = m_y + 15; sprintf( caption, LANGUAGEPHRASE("dialog_network_client_seqid") ); LPREPLACEINTEGERFLAG( 'S', g_app->GetClientToServer()->m_lastValidSequenceIdFromServer, caption ); g_renderer->TextSimple( m_x + 250, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_client_send") ); sprintf( number, "%2.1f", g_app->GetClientToServer()->m_sendRate/1024.0f ); LPREPLACESTRINGFLAG( 'R', number, caption ); g_renderer->TextSimple( m_x + 250, y+=h, White, 12, caption ); sprintf( caption, LANGUAGEPHRASE("dialog_network_client_receive") ); sprintf( number, "%2.1f", g_app->GetClientToServer()->m_receiveRate/1024.0f ); LPREPLACESTRINGFLAG( 'R', number, caption ); g_renderer->TextSimple( m_x + 250, y+=h, White, 12, caption ); } }
bool ProcessServerLetters( Directory *letter ) { if( strcmp( letter->m_name, NET_DEFCON_MESSAGE ) != 0 || !letter->HasData( NET_DEFCON_COMMAND ) ) { AppDebugOut( "Client received bogus message, discarded (4)\n" ); return true; } char *cmd = letter->GetDataString( NET_DEFCON_COMMAND ); if( strcmp( cmd, NET_DEFCON_CLIENTHELLO ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); if( clientId == g_app->GetClientToServer()->m_clientId ) { g_app->GetClientToServer()->m_connectionState = ClientToServer::StateConnected; AppDebugOut( "CLIENT : Received HelloClient from Server\n" ); if( !g_app->GetTutorial() ) { g_app->GetClientToServer()->RequestTeam( Team::TypeLocalPlayer ); } } // If this is a Demo client, make a note of that if( letter->HasData( NET_DEFCON_CLIENTISDEMO ) ) { g_app->GetClientToServer()->SetClientDemo( clientId ); } // This might be a client rejoining the game // Need to give him his teams back g_app->GetWorld()->ReassignTeams( clientId ); return true; } else if( strcmp( cmd, NET_DEFCON_CLIENTGOODBYE ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); int reason = letter->GetDataInt(NET_DEFCON_DISCONNECT); AppDebugOut( "CLIENT : Client %d left the game\n", clientId ); g_app->GetWorld()->RemoveTeams( clientId, reason ); g_app->GetWorld()->RemoveSpectator( clientId ); g_app->GetClientToServer()->SetSyncState( clientId, true ); return true; } else if( strcmp( cmd, NET_DEFCON_CLIENTID ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); AppDebugOut( "CLIENT : Received ClientID of %d\n", clientId ); if( g_app->GetClientToServer()->m_clientId != -1 ) { AppAssert( g_app->GetClientToServer()->m_clientId == clientId ); return true; } g_app->GetClientToServer()->m_clientId = clientId; g_app->GetClientToServer()->m_connectionState = ClientToServer::StateHandshaking; g_lastProcessedSequenceId = -1; if( letter->HasData( NET_DEFCON_VERSION, DIRECTORY_TYPE_STRING ) ) { char *serverVersion = letter->GetDataString( NET_DEFCON_VERSION ); strcpy( g_app->GetClientToServer()->m_serverVersion, serverVersion ); AppDebugOut( "CLIENT : Server version is %s\n", serverVersion ); } if( !VersionManager::DoesSupportModSystem( g_app->GetClientToServer()->m_serverVersion ) ) { // This server is too old to support Mods, so make sure we de-activate any critical ones g_modSystem->DeActivateAllCriticalMods(); if( g_modSystem->CommitRequired() ) { g_modSystem->Commit(); g_app->RestartAmbienceMusic(); } } return true; } else if( strcmp( cmd, NET_DEFCON_TEAMASSIGN ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); int teamId = letter->GetDataInt(NET_DEFCON_TEAMID); int teamType = letter->GetDataInt(NET_DEFCON_TEAMTYPE); if( teamType != Team::TypeAI && clientId != g_app->GetClientToServer()->m_clientId ) { teamType = Team::TypeRemotePlayer; } g_app->GetWorld()->InitialiseTeam(teamId, teamType, clientId ); return true; } else if( strcmp( cmd, NET_DEFCON_SPECTATORASSIGN ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); g_app->GetWorld()->InitialiseSpectator(clientId); if( clientId == g_app->GetClientToServer()->m_clientId ) { AppDebugOut( "CLIENT: I am a spectator\n" ); } return true; } else if( strcmp( cmd, NET_DEFCON_NETSYNCERROR ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); AppDebugOut( "SYNCERROR Server informed us that Client %d is out of Sync\n", clientId ); g_app->GetClientToServer()->SetSyncState( clientId, false ); char *syncId = letter->GetDataString( NET_DEFCON_SYNCERRORID ); DumpSyncLogs(syncId); return true; } else if( strcmp( cmd, NET_DEFCON_NETSYNCFIXED ) == 0 ) { int clientId = letter->GetDataInt(NET_DEFCON_CLIENTID); AppDebugOut( "SYNCFIXED Server informed us that Client %d has repaired his Sync Error\n", clientId ); g_app->GetClientToServer()->SetSyncState( clientId, true ); return true; } else if( strcmp( cmd, NET_DEFCON_DISCONNECT ) == 0 ) { if( g_app->GetClientToServer()->m_connectionState > ClientToServer::StateDisconnected ) { g_app->ShutdownCurrentGame(); if ( EclGetWindow("LOBBY") ) EclRemoveWindow( "LOBBY" ); if ( EclGetWindow("Comms Window") ) EclRemoveWindow( "Comms Window" ); if ( EclGetWindow("Preparing Game...") ) EclRemoveWindow("Preparing Game..." ); g_app->GetInterface()->OpenSetupWindows(); char *reason; char *reasonLanguagePhrase; int reasonDisconnectInt = letter->GetDataInt(NET_DEFCON_DISCONNECT); switch( reasonDisconnectInt ) { case Disconnect_ClientLeave: reason = "You have left the game"; reasonLanguagePhrase = "dialog_disconnect_client_leave"; break; case Disconnect_ServerShutdown: reason = "The server has shutdown"; reasonLanguagePhrase = "dialog_disconnect_server_shutdown"; break; case Disconnect_InvalidKey: reason = "You are using an invalid key"; reasonLanguagePhrase = "dialog_disconnect_invalid_key"; break; case Disconnect_DuplicateKey: reason = "You are using a duplicate key"; reasonLanguagePhrase = "dialog_disconnect_duplicate_key"; break; case Disconnect_KeyAuthFailed: reason = "Key authentication failed"; reasonLanguagePhrase = "dialog_disconnect_key_auth_failed"; break; case Disconnect_BadPassword: reason = "Invalid Password Entered"; reasonLanguagePhrase = "dialog_disconnect_bad_password"; break; case Disconnect_GameFull: reason = "Game is already full"; reasonLanguagePhrase = "dialog_disconnect_game_full"; break; case Disconnect_KickedFromGame: reason = "Kicked by the Server"; reasonLanguagePhrase = "dialog_disconnect_kicked_from_game"; break; case Disconnect_DemoFull: reason = "Too many Demo Players already"; reasonLanguagePhrase = "dialog_disconnect_demo_full"; break; default: reason = "Unknown"; reasonLanguagePhrase = "dialog_disconnect_unknown"; break; } if( reasonDisconnectInt == Disconnect_KeyAuthFailed || reasonDisconnectInt == Disconnect_InvalidKey || reasonDisconnectInt == Disconnect_DuplicateKey ) { char authKey[256]; if( Authentication_IsKeyFound() ) { Authentication_GetKey( authKey ); } else { sprintf( authKey, LANGUAGEPHRASE("dialog_authkey_not_found") ); } BadKeyWindow *badKey = new BadKeyWindow(); sprintf( badKey->m_extraMessage, LANGUAGEPHRASE("dialog_auth_error") ); LPREPLACESTRINGFLAG( 'E', LANGUAGEPHRASE(reasonLanguagePhrase), badKey->m_extraMessage ); LPREPLACESTRINGFLAG( 'K', authKey, badKey->m_extraMessage ); EclRegisterWindow(badKey); } else if( reasonDisconnectInt == Disconnect_DemoFull ) { int maxGameSize; int maxDemoPlayers; bool allowDemoServers; g_app->GetClientToServer()->GetDemoLimitations( maxGameSize, maxDemoPlayers, allowDemoServers ); BadKeyWindow *badKey = new BadKeyWindow(); sprintf( badKey->m_extraMessage, LANGUAGEPHRASE("dialog_server_demo_restricted") ); LPREPLACEINTEGERFLAG( 'N', maxDemoPlayers, badKey->m_extraMessage ); badKey->m_offerDemo = false; EclRegisterWindow(badKey); } else { MessageDialog *dialog = new MessageDialog( "Disconnected", reasonLanguagePhrase, true, "dialog_disconnected", true ); EclRegisterWindow( dialog ); } AppDebugOut( "CLIENT : Received Disconnect from server : %s\n", reason ); #ifdef TESTBED if( letter->GetDataInt(NET_DEFCON_DISCONNECT) == Disconnect_ServerShutdown ) { RestartTestBed(1, "Client Disconnect"); } #endif } return true; } else if( strcmp( cmd, NET_DEFCON_SETMODPATH ) == 0 ) { char *modPath = letter->GetDataString( NET_DEFCON_SETMODPATH ); AppDebugOut( "Server has set the MOD path: '%s'\n", modPath ); if( !g_modSystem->IsCriticalModPathSet( modPath ) ) { if( g_modSystem->CanSetModPath( modPath ) ) { g_modSystem->DeActivateAllCriticalMods(); g_modSystem->SetModPath( modPath ); } else { char reason[8192]; sprintf( reason, LANGUAGEPHRASE("dialog_error_mod_path_caption") ); char modPathCopy[4096]; strcpy( modPathCopy, modPath ); LList<char *> *tokens = g_modSystem->ParseModPath( modPathCopy ); for( int i = 0; i < tokens->Size(); i+=2 ) { char *modName = tokens->GetData(i); char *version = tokens->GetData(i+1); strcat( reason, modName ); strcat( reason, " " ); strcat( reason, version ); if( !g_modSystem->IsModInstalled( modName, version ) ) { strcat( reason, " " ); strcat( reason, LANGUAGEPHRASE("dialog_mod_not_installed") ); } strcat( reason, "\n" ); } delete tokens; MessageDialog *dialog = new MessageDialog( "Error setting MOD path", reason, false, "dialog_error_mod_path_title", true ); EclRegisterWindow( dialog ); } } return true; } else { return false; } }
void App::InitialiseWindow() { int screenW = g_preferences->GetInt( PREFS_SCREEN_WIDTH ); int screenH = g_preferences->GetInt( PREFS_SCREEN_HEIGHT ); int colourDepth = g_preferences->GetInt( PREFS_SCREEN_COLOUR_DEPTH ); int screenRefresh = g_preferences->GetInt( PREFS_SCREEN_REFRESH ); int zDepth = g_preferences->GetInt( PREFS_SCREEN_Z_DEPTH ); int antiAlias = g_preferences->GetInt( PREFS_SCREEN_ANTIALIAS, 0 ); bool windowed = g_preferences->GetInt( PREFS_SCREEN_WINDOWED ); if( screenW == 0 || screenH == 0 ) { g_windowManager->SuggestDefaultRes( &screenW, &screenH, &screenRefresh, &colourDepth ); g_preferences->SetInt( PREFS_SCREEN_WIDTH, screenW ); g_preferences->SetInt( PREFS_SCREEN_HEIGHT, screenH ); g_preferences->SetInt( PREFS_SCREEN_REFRESH, screenRefresh ); g_preferences->SetInt( PREFS_SCREEN_COLOUR_DEPTH, colourDepth ); } if(m_gameRunning && m_game->GetOptionValue("GameMode") == GAMEMODE_OFFICEMODE ) { windowed = true; } bool success = g_windowManager->CreateWin( screenW, screenH, windowed, colourDepth, screenRefresh, zDepth, antiAlias, "DEFCON" ); if( !success ) { // Safety values int safeScreenW = 800; int safeScreenH = 600; bool safeWindowed = 1; int safeColourDepth = 16; int safeZDepth = 16; int safeScreenRefresh = 60; char caption[512]; sprintf( caption, LANGUAGEPHRASE("dialog_screen_error_caption") ); LPREPLACEINTEGERFLAG( 'W', screenW, caption ); LPREPLACEINTEGERFLAG( 'H', screenH, caption ); ReplaceStringFlagColorBit( caption, 'C', colourDepth ); ReplaceStringFlagWindowed( caption, 'S', windowed ); LPREPLACEINTEGERFLAG( 'w', safeScreenW, caption ); LPREPLACEINTEGERFLAG( 'h', safeScreenH, caption ); ReplaceStringFlagColorBit( caption, 'c', safeColourDepth ); ReplaceStringFlagWindowed( caption, 's', safeWindowed ); MessageDialog *dialog = new MessageDialog( "Screen Error", caption, false, "dialog_screen_error_title", true ); EclRegisterWindow( dialog ); dialog->m_x = 100; dialog->m_y = 100; // Go for safety values screenW = safeScreenW; screenH = safeScreenH; windowed = safeWindowed; colourDepth = safeColourDepth; zDepth = safeZDepth; screenRefresh = safeScreenRefresh; antiAlias = 0; success = g_windowManager->CreateWin(screenW, screenH, windowed, colourDepth, screenRefresh, zDepth, antiAlias, "DEFCON"); AppReleaseAssert( success, "Failed to set screen mode" ); g_preferences->SetInt( PREFS_SCREEN_WIDTH, screenW ); g_preferences->SetInt( PREFS_SCREEN_HEIGHT, screenH ); g_preferences->SetInt( PREFS_SCREEN_WINDOWED, windowed ); g_preferences->SetInt( PREFS_SCREEN_COLOUR_DEPTH, colourDepth ); g_preferences->SetInt( PREFS_SCREEN_Z_DEPTH, zDepth ); g_preferences->SetInt( PREFS_SCREEN_REFRESH, screenRefresh ); g_preferences->Save(); } #ifdef TARGET_MSVC WindowManagerWin32 *wm32 = (WindowManagerWin32*) g_windowManager; DWORD dwStyle = GetWindowLong( wm32->m_hWnd, GWL_STYLE ); dwStyle &= ~(WS_MAXIMIZEBOX); SetWindowLong( wm32->m_hWnd, GWL_STYLE, dwStyle ); HICON hIcon = LoadIcon( wm32->GethInstance(), MAKEINTRESOURCE(IDI_ICON1) ); //SendMessage( wm32->m_hWnd, (UINT)WM_SETICON, ICON_SMALL, (LPARAM)hIcon ); SendMessage( wm32->m_hWnd, (UINT)WM_SETICON, ICON_BIG, (LPARAM)hIcon ); #endif // TARGET_MSVC g_windowManager->HideMousePointer(); SetMousePointerVisible(true); }
void SidePanel::Render( bool hasFocus ) { int currentSelectionId = g_app->GetMapRenderer()->GetCurrentSelectionId(); if( currentSelectionId == -1 ) { if( m_mode != ModeUnitPlacement && m_mode != ModeFleetPlacement ) { ChangeMode( ModeUnitPlacement ); m_currentFleetId = -1; } } Team *myTeam = g_app->GetWorld()->GetMyTeam(); if( m_mode == ModeUnitPlacement ) { PanelModeButton *button = (PanelModeButton *)GetButton("FleetMode"); //if( !button->m_disabled ) { int shipsRemaining = 0; if( myTeam ) { shipsRemaining += myTeam->m_unitsAvailable[WorldObject::TypeBattleShip]; shipsRemaining += myTeam->m_unitsAvailable[WorldObject::TypeCarrier]; shipsRemaining += myTeam->m_unitsAvailable[WorldObject::TypeSub]; bool unplacedFleets = false; bool outOfCredits = myTeam->m_unitCredits <= 0; if( !g_app->GetGame()->GetOptionValue("VariableUnitCounts") ) outOfCredits = false; if( myTeam->m_fleets.Size() > 0 && myTeam->GetFleet( myTeam->m_fleets.Size() - 1 )->m_active == false ) { unplacedFleets = true; } if( (shipsRemaining <= 0 || myTeam->m_unitCredits <= 0 )&& !unplacedFleets ) { button->m_disabled = true; } else { button->m_disabled = false; } } } } int w = m_w; m_w = 100; FadingWindow::Render( hasFocus, true ); m_w = w; char text[128]; switch(m_mode) { case ModeUnitPlacement: sprintf(text, LANGUAGEPHRASE("dialog_placeunit")); break; case ModeFleetPlacement: sprintf(text, LANGUAGEPHRASE("dialog_createfleet")); break; } g_renderer->TextCentreSimple(m_x+50, m_y+10, White, m_fontsize, text ); if( m_mode == ModeFleetPlacement ) { if( m_currentFleetId != -1 ) { int x = 0; int y = 0; int yMod = 0; Team *myTeam = g_app->GetWorld()->GetTeam( g_app->GetWorld()->m_myTeamId ); if( myTeam && myTeam->m_fleets.ValidIndex(m_currentFleetId) ) { if( m_mode == ModeFleetPlacement ) { x = m_x + 120; y = m_y + 20; yMod = 50; for( int i = 0; i < 6; ++i ) { g_renderer->RectFill( x, y, 40, 40, Colour(90,90,170,200) ); g_renderer->Rect(x, y, 40, 40, White ); y += yMod; } y = m_y+20; } else { x = m_x + 10; y = m_y + 40; yMod = 60; } for( int i = 0; i < myTeam->m_fleets[ m_currentFleetId ]->m_memberType.Size(); ++i ) { int type = myTeam->m_fleets[ m_currentFleetId ]->m_memberType[i]; Image *bmpImage = g_resource->GetImage( g_app->GetMapRenderer()->m_imageFiles[type] ); g_renderer->SetBlendMode( Renderer::BlendModeAdditive ); g_renderer->Blit( bmpImage, x+3, y, 35, 35, myTeam->GetTeamColour() ); g_renderer->SetBlendMode( Renderer::BlendModeNormal ); y += yMod; } } } } int variableTeamUnits = g_app->GetGame()->GetOptionValue("VariableUnitCounts"); if( myTeam ) { if( variableTeamUnits == 1 ) { Colour col = Colour(255,255,0,255); char msg[128]; sprintf( msg, LANGUAGEPHRASE("dialog_credits_1") ); LPREPLACEINTEGERFLAG( 'C', myTeam->m_unitCredits, msg ); g_renderer->TextCentreSimple( m_x+50, m_y+m_h-28, col, m_fontsize, msg ); sprintf( msg, LANGUAGEPHRASE("dialog_credits_2")); g_renderer->TextCentreSimple( m_x+50, m_y+m_h-15, col, m_fontsize, msg ); } int totalUnits = 0; for( int i = 0; i < WorldObject::NumObjectTypes; ++i ) { totalUnits += myTeam->m_unitsAvailable[i]; } if( totalUnits == 0 && m_previousUnitCount > 0 && m_currentFleetId == -1 && !g_app->GetTutorial() ) { EclRemoveWindow("Side Panel" ); return; } m_previousUnitCount = totalUnits; } }