Share Pastebin
Guest
Public paste!

Untitled

By: a guest | Feb 9th, 2010 | Syntax: None | Size: 48.50 KB | Hits: 32 | Expires: Never
Copy text to clipboard
  1. //-----------------------------------------------------------------------------------
  2. //
  3. // Bitfighter - A multiplayer vector graphics space game
  4. // Based on Zap demo released for Torque Network Library by GarageGames.com
  5. //
  6. // Derivative work copyright (C) 2008-2009 Chris Eykamp
  7. // Original work copyright (C) 2004 GarageGames.com, Inc.
  8. // Other code copyright as noted
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation; either version 2 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful (and fun!),
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program; if not, write to the Free Software
  22. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  23. //
  24. //------------------------------------------------------------------------------------
  25.  
  26. #include "gameConnection.h"
  27.  
  28. #include "game.h"
  29. #include "UIGame.h"
  30. #include "UIMenus.h"
  31. #include "UIInstructions.h"
  32. #include "UIChat.h"
  33. #include "UIMessage.h"
  34. #include "UIDiagnostics.h"
  35. #include "gameType.h"
  36. #include "lpc10.h"
  37.  
  38. #include "../tnl/tnlEndian.h"
  39.  
  40. #include "ship.h"
  41. #include "gameObjectRender.h"
  42. #include "input.h"
  43. #include "config.h"
  44. #include "loadoutSelect.h"
  45.  
  46. #include "md5wrapper.h"    // For submission of passwords
  47.  
  48. #include "../glut/glutInclude.h"
  49. #include <ctype.h>
  50. #include <stdio.h>
  51. #include <stdarg.h>
  52.  
  53. namespace Zap
  54. {
  55.  
  56. GameUserInterface gGameUserInterface;
  57.  
  58. // TODO: Make these static like privateF5MessageDisplayedInGameColor!
  59. Color gGlobalChatColor(0.9, 0.9, 0.9);
  60. Color gTeamChatColor(0, 1, 0);
  61. Color gCmdChatColor(1, 0, 0);
  62.  
  63. Color GameUserInterface::privateF5MessageDisplayedInGameColor(0, 0, 1);
  64.  
  65. // Used to supply names for loutout indicators --> must correspond to enum ShipModule
  66. const char *gModuleShortName[] = {
  67.    "Shield",
  68.    "Turbo",
  69.    "Sensor",
  70.    "Repair",
  71.    "Engineer",
  72.    "Cloak",
  73. };
  74.  
  75.  
  76.  
  77. // Constructor
  78. GameUserInterface::GameUserInterface()
  79. {
  80.    setMenuID(GameUI);
  81.    setPlayMode();
  82.    mInScoreboardMode = false;
  83. #if 0 //defined(TNL_OS_XBOX)
  84.    mFPSVisible = true;
  85. #else
  86.    mFPSVisible = false;
  87. #endif
  88.    mFPSAvg = 0;
  89.    mPingAvg = 0;
  90.    mFrameIndex = 0;
  91.    for(U32 i = 0; i < FPSAvgCount; i++)
  92.    {
  93.       mIdleTimeDelta[i] = 50;
  94.       mPing[i] = 100;
  95.    }
  96.  
  97.  
  98.    memset(mChatBuffer, 0, sizeof(mChatBuffer));
  99.  
  100.    // Initialize message buffers
  101.    for(S32 i = 0; i < MessageDisplayCount; i++)
  102.       mDisplayMessage[i][0] = 0;
  103.  
  104.    for(S32 i = 0; i < MessageStoreCount; i++)
  105.       mStoreMessage[i][0] = 0;
  106.  
  107.    mGotControlUpdate = false;
  108.    mRecalcFPSTimer = 0;
  109.  
  110.    mFiring = false;
  111.    for (U32 i = 0; i < (U32)ShipModuleCount; i++)
  112.       mModActivated[i] = false;
  113.  
  114.    mDisplayMessageTimer.setPeriod(DisplayMessageTimeout);    // Set the period of our message timeout timer
  115.  
  116.    populateChatCmdList();
  117. }
  118.  
  119.  
  120. extern bool gDisableShipKeyboardInput;
  121. extern Point gMousePos;
  122.  
  123. void GameUserInterface::onActivate()
  124. {
  125.    gDisableShipKeyboardInput = false;                     // Make sure our ship controls are active
  126.    mMissionOverlayActive = false;                         // Turn off the mission overlay (if it was on)
  127.    glutSetCursor(GLUT_CURSOR_NONE);                       // Turn off cursor
  128.    onMouseMoved((S32) gMousePos.x, (S32) gMousePos.y);    // Make sure ship pointed is towards mouse
  129.  
  130.    // Clear out any lingering chat messages
  131.    for(S32 i = 0; i < MessageStoreCount; i++)
  132.       mDisplayMessage[i][0] = 0;
  133.  
  134.    for(S32 i = 0; i < MessageDisplayCount; i++)
  135.       mStoreMessage[i][0] = 0;
  136.  
  137.    mMessageDisplayMode = ShortTimeout;                    // Start with normal chat msg display
  138.    setPlayMode();                                         // Make sure we're not in chat or loadout-select mode
  139.  
  140.    for(S32 i = 0; i < ShipModuleCount; i++)
  141.       mModActivated[i] = false;
  142. }
  143.  
  144.  
  145. void GameUserInterface::onReactivate()
  146. {
  147.    gDisableShipKeyboardInput = false;
  148.    glutSetCursor(GLUT_CURSOR_NONE);    // Turn off cursor
  149.    setPlayMode();
  150.  
  151.    for(S32 i = 0; i < ShipModuleCount; i++)
  152.       mModActivated[i] = false;
  153.  
  154.    // Call onMouseMoved to get ship pointed at current cursor location
  155.    onMouseMoved((S32) gMousePos.x, (S32) gMousePos.y);
  156. }
  157.  
  158.  
  159. // A new chat message is here!  We don't actually display anything here, despite the name...
  160. // just add it to the list, will be displayed in render()
  161. void GameUserInterface::displayMessage(Color theColor, const char *format, ...)
  162. {
  163.    // Create a slot for our new message
  164.    if(mDisplayMessage[0][0])
  165.       for(S32 i = MessageDisplayCount - 1; i > 0; i--)
  166.       {
  167.          strcpy(mDisplayMessage[i], mDisplayMessage[i-1]);
  168.          mDisplayMessageColor[i] = mDisplayMessageColor[i-1];
  169.       }
  170.  
  171.    for(S32 i = MessageStoreCount - 1; i > 0; i--)
  172.    {
  173.       strcpy(mStoreMessage[i], mStoreMessage[i-1]);
  174.       mStoreMessageColor[i] = mStoreMessageColor[i-1];
  175.    }
  176.  
  177.    va_list args;
  178.    va_start(args, format);
  179.  
  180.    vsnprintf(mDisplayMessage[0], sizeof(mDisplayMessage[0]), format, args);
  181.    mDisplayMessageColor[0] = theColor;
  182.  
  183.    //vsnprintf(mStoreMessage[0], sizeof(mStoreMessage[0]), format, args);
  184.    mStoreMessageColor[0] = theColor;
  185.  
  186.    mDisplayMessageTimer.reset();
  187. }
  188.  
  189.  
  190. void GameUserInterface::idle(U32 timeDelta)
  191. {
  192.    if(mDisplayMessageTimer.update(timeDelta))
  193.    {
  194.       for(S32 i = MessageDisplayCount - 1; i > 0; i--)
  195.       {
  196.          strcpy(mDisplayMessage[i], mDisplayMessage[i-1]);
  197.          mDisplayMessageColor[i] = mDisplayMessageColor[i-1];
  198.       }
  199.  
  200.       mDisplayMessage[0][0] = 0;    // Null, that is
  201.       mDisplayMessageTimer.reset();
  202.    }
  203.  
  204.    // Time to recalc FPS?
  205.    if (mFPSVisible)        // Only bother if we're displaying the value...
  206.    {
  207.       if(timeDelta > mRecalcFPSTimer)
  208.       {
  209.          U32 sum = 0, sumping = 0;
  210.  
  211.          for(U32 i = 0; i < FPSAvgCount; i++)
  212.          {
  213.             sum += mIdleTimeDelta[i];
  214.             sumping += mPing[i];
  215.          }
  216.  
  217.          mFPSAvg = (1000 * FPSAvgCount) / F32(sum);
  218.          mPingAvg = F32(sumping) / 32;
  219.          mRecalcFPSTimer = 750;
  220.       }
  221.       else
  222.          mRecalcFPSTimer -= timeDelta;
  223.    }
  224.  
  225.    if(mCurrentMode == ChatMode)
  226.       updateCursorBlink(timeDelta);    // Blink the cursor if in ChatMode
  227.    else if(mCurrentMode == QuickChatMode)
  228.       mQuickChat.idle(timeDelta);
  229.    else if(mCurrentMode == LoadoutMode)
  230.       mLoadout.idle(timeDelta);
  231.  
  232.    mVoiceRecorder.idle(timeDelta);
  233.  
  234.    U32 indx = mFrameIndex % FPSAvgCount;
  235.    mIdleTimeDelta[indx] = timeDelta;
  236.  
  237.    if(gClientGame->getConnectionToServer())
  238.       mPing[indx] = (U32)gClientGame->getConnectionToServer()->getRoundTripTime();
  239.  
  240.    mFrameIndex++;
  241.  
  242.    mWrongModeMsgDisplay.update(timeDelta);
  243.  
  244.    mProgressBarFadeTimer.update(timeDelta);
  245.  
  246.    // Should we move this timer over to UIGame??
  247.    if(gMainMenuUserInterface.levelLoadDisplayFadeTimer.update(timeDelta))
  248.       gMainMenuUserInterface.clearLevelLoadDisplay();
  249. }
  250.  
  251.  
  252. #ifdef TNL_OS_WIN32
  253. extern void checkMousePos(S32 maxdx, S32 maxdy);
  254. #endif
  255.  
  256.  
  257. // Draw main game screen (client only)
  258. void GameUserInterface::render()
  259. {
  260.    glColor3f(0.0, 0.0, 0.0);
  261.    if(!gClientGame->isConnectedToServer())
  262.    {
  263.       glColor3f(1, 1, 1);
  264.       drawCenteredString(260, 30, "Connecting to server...");
  265.  
  266.       glColor3f(0, 1, 0);
  267.       if(gClientGame->getConnectionToServer())
  268.          drawCenteredString(310, 16, gConnectStatesTable[gClientGame->getConnectionToServer()->getConnectionState()]);
  269.  
  270.       glColor3f(1, 1, 1);
  271.       drawCenteredString(346, 20, "Press <ESC> to abort");
  272.    }
  273.  
  274.    gClientGame->render();
  275.  
  276.    glMatrixMode(GL_MODELVIEW);
  277.    glLoadIdentity();          // OpenGL command to load an identity matrix (see OpenGL docs)
  278.  
  279.    renderReticle();           // Draw crosshairs if using mouse
  280.    renderMessageDisplay();    // Render incoming chat msgs
  281.    renderCurrentChat();       // Render any chat msg user is composing
  282.    renderLoadoutIndicators(); // Draw indicators for the various loadout items
  283.  
  284.    gMainMenuUserInterface.renderProgressListItems();  // This is the list of levels loaded while hosting
  285.  
  286.    renderProgressBar();       // This is the status bar that shows progress of loading this level
  287.  
  288.    mVoiceRecorder.render();   // This is the indicator that someone is sending a voice msg
  289.  
  290.    // Display running average FPS
  291.    if(mFPSVisible)
  292.    {
  293.       glColor3f(1, 1, 1);
  294.       drawStringf(canvasWidth - horizMargin - 220, vertMargin, 20, "%4.1f fps | %1.0f ms", mFPSAvg, mPingAvg);
  295.    }
  296.  
  297.    // Render QuickChat / Loadout menus
  298.    if(mCurrentMode == QuickChatMode)
  299.    {     // (braces required)
  300.       if(!mQuickChat.render())      // Render QuickChat msgs if there are any, otherwise switch back into PlayMode
  301.          setPlayMode();
  302.    }
  303.    else if(mCurrentMode == LoadoutMode)
  304.       mLoadout.render();
  305.  
  306.    GameType *theGameType = gClientGame->getGameType();
  307.  
  308.    if(theGameType)
  309.       theGameType->renderInterfaceOverlay(mInScoreboardMode);
  310.  
  311. #if 0
  312.    // Some code for outputting the position of the ship for finding good spawns
  313.    GameConnection *con = gClientGame->getConnectionToServer();
  314.  
  315.    if(con)
  316.    {
  317.       GameObject *co = con->getControlObject();
  318.       if(co)
  319.       {
  320.          Point pos = co->getActualPos() * F32(1 / 300.0f);
  321.          drawStringf(10, 550, 30, "%0.2g, %0.2g", pos.x, pos.y);
  322.       }
  323.    }
  324.  
  325.    if(mGotControlUpdate)
  326.       drawString(710, 10, 30, "CU");
  327. #endif
  328. }
  329.  
  330.  
  331. // Draws level-load progress bar across the bottom of the screen
  332. void GameUserInterface::renderProgressBar()
  333. {
  334.    GameType *gt = gClientGame->getGameType();
  335.    if((mShowProgressBar || mProgressBarFadeTimer.getCurrent() > 0) && gt && gt->mObjectsExpected > 0)
  336.    {
  337.       glEnable(GL_BLEND);
  338.       glColor4f(0, 1, 0, mShowProgressBar ? 1 : mProgressBarFadeTimer.getFraction());
  339.  
  340.       // Outline
  341.       const S32 left = 200;
  342.       const S32 width = canvasWidth - 2 * left;
  343.       const S32 height = 10;
  344.  
  345.       // For some reason, there are occasions where the status bar doesn't progress all the way over during the load process.
  346.       // The problem is that, for some reason, some objects do not add themselves to the loaded object counter, and this creates
  347.       // a disconcerting effect, as if the level did not fully load.  Rather than waste any more time on this problem, we'll just
  348.       // fill in the status bar while it's fading, to make it look like the level fully loaded.  Since the only thing that this
  349.       // whole mechanism is used for is to display something to the user, this should work fine.
  350.       F32 barWidth = mShowProgressBar ? ((F32) width * (F32) gClientGame->mObjectsLoaded / (F32) gt->mObjectsExpected) : width;
  351.  
  352.       glBegin(GL_LINE_LOOP);
  353.          glVertex2f(left, canvasHeight - vertMargin);
  354.          glVertex2f(left + width, canvasHeight - vertMargin);
  355.          glVertex2f(left + width, canvasHeight - vertMargin - height);
  356.          glVertex2f(left, canvasHeight - vertMargin - height);
  357.       glEnd();
  358.  
  359.       glBegin(GL_POLYGON);
  360.          glVertex2f(left, canvasHeight - vertMargin);
  361.          glVertex2f(left + barWidth, canvasHeight - vertMargin);
  362.          glVertex2f(left + barWidth, canvasHeight - vertMargin - height);
  363.          glVertex2f(left, canvasHeight - vertMargin - height);
  364.       glEnd();
  365.  
  366.       glDisable(GL_BLEND);
  367.    }
  368. }
  369.  
  370.  
  371. extern CmdLineSettings gCmdLineSettings;
  372. extern IniSettings gIniSettings;
  373.  
  374. // Draw the reticle (i.e. the mouse cursor) if we are using keyboard/mouse
  375. void GameUserInterface::renderReticle()
  376. {
  377.    if(gIniSettings.inputMode == Keyboard)
  378.    {
  379. #if 0 // TNL_OS_WIN32
  380.       Point realMousePoint = mMousePoint;
  381.       if(!gIniSettings.controlsRelative)
  382.       {
  383.          F32 len = mMousePoint.len();
  384.          checkMousePos(windowWidth * 100 / canvasWidth,
  385.                      windowHeight * 100 / canvasHeight);
  386.  
  387.          if(len > 100)
  388.             realMousePoint *= 100 / len;
  389.       }
  390. #endif
  391.       Point offsetMouse = mMousePoint + Point(canvasWidth / 2, canvasHeight / 2);
  392.  
  393.       glEnable(GL_BLEND);
  394.       glColor4f(0,1,0, 0.7);
  395.       glBegin(GL_LINES);
  396.  
  397.       glVertex2f(offsetMouse.x - 15, offsetMouse.y);
  398.       glVertex2f(offsetMouse.x + 15, offsetMouse.y);
  399.       glVertex2f(offsetMouse.x, offsetMouse.y - 15);
  400.       glVertex2f(offsetMouse.x, offsetMouse.y + 15);
  401.  
  402.       if(offsetMouse.x > 30)
  403.       {
  404.          glColor4f(0,1,0, 0);
  405.          glVertex2f(0, offsetMouse.y);
  406.          glColor4f(0,1,0, 0.7);
  407.          glVertex2f(offsetMouse.x - 30, offsetMouse.y);
  408.       }
  409.       if(offsetMouse.x < canvasWidth - 30)
  410.       {
  411.          glColor4f(0,1,0, 0.7);
  412.          glVertex2f(offsetMouse.x + 30, offsetMouse.y);
  413.          glColor4f(0,1,0, 0);
  414.          glVertex2f(canvasWidth, offsetMouse.y);
  415.       }
  416.       if(offsetMouse.y > 30)
  417.       {
  418.          glColor4f(0,1,0, 0);
  419.          glVertex2f(offsetMouse.x, 0);
  420.          glColor4f(0,1,0, 0.7);
  421.          glVertex2f(offsetMouse.x, offsetMouse.y - 30);
  422.       }
  423.       if(offsetMouse.y < canvasHeight - 30)
  424.       {
  425.          glColor4f(0,1,0, 0.7);
  426.          glVertex2f(offsetMouse.x, offsetMouse.y + 30);
  427.          glColor4f(0,1,0, 0);
  428.          glVertex2f(offsetMouse.x, canvasHeight);
  429.       }
  430.  
  431.       glEnd();
  432.       glDisable(GL_BLEND);
  433.    }
  434.  
  435.    if(mWrongModeMsgDisplay.getCurrent())
  436.    {
  437.       glColor3f(1,.5,.5);
  438.       drawCenteredString(225, 20, "You are in joystick mode.");
  439.       drawCenteredString(250, 20, "You can change to Keyboard input with the Options menu.");
  440.  
  441.    }
  442. }
  443.  
  444. extern LoadoutItem gLoadoutModules[];
  445.  
  446. #define fontSize 15
  447. #define gapSize 3       // Gap between text and box
  448.  
  449. // Draw weapon indicators at top of the screen, runs on client
  450. void GameUserInterface::renderLoadoutIndicators()
  451. {
  452.    if (!gIniSettings.showWeaponIndicators)      // If we're not drawing them, we've got nothing to do
  453.       return;
  454.  
  455.    U32 xPos = UserInterface::horizMargin;
  456.  
  457.    if(!gClientGame->getConnectionToServer())            // Can happen when first joining a game.  This was XelloBlue's crash...
  458.         return;
  459.  
  460.    Ship *localShip = dynamic_cast<Ship *>(gClientGame->getConnectionToServer()->getControlObject());
  461.    if (!localShip)
  462.       return;
  463.  
  464.    glEnable(GL_BLEND);
  465.  
  466.    // First, the weapons
  467.    for(U32 i = 0; i < (U32)ShipWeaponCount; i++)
  468.    {
  469.       U32 width = getStringWidth(fontSize, gWeapons[localShip->getWeapon(i)].name.getString());
  470.  
  471.       if(i == localShip->mActiveWeaponIndx)
  472.          glColor4f(1, 1, 0, 1);
  473.       else
  474.          glColor4f(1, 1, 0, 0.5);
  475.  
  476.       glBegin(GL_POLYGON);
  477.          glVertex2f(xPos, UserInterface::vertMargin);
  478.          glVertex2f(xPos + width + 2 * gapSize, UserInterface::vertMargin);
  479.          glVertex2f(xPos + width + 2 * gapSize, UserInterface::vertMargin + fontSize + 2 * gapSize);
  480.          glVertex2f(xPos, UserInterface::vertMargin + fontSize + 2 * gapSize);
  481.       glEnd();
  482.  
  483.       if(i == localShip->mActiveWeaponIndx)
  484.          glColor3f(0,0,0);
  485.       else
  486.          glColor3f(.2,.2,.2);
  487.  
  488.       // Add the weapon name
  489.       drawString(xPos + gapSize, UserInterface::vertMargin + gapSize, fontSize, gWeapons[localShip->getWeapon(i)].name.getString());
  490.  
  491.       xPos += UserInterface::vertMargin + width - 2 * gapSize;
  492.    }
  493.  
  494.    xPos += 20;    // Small horizontal gap
  495.  
  496.    // Next, loadout modules
  497.    for(U32 i = 0; i < (U32)ShipModuleCount; i++)
  498.    {
  499.       U32 width = getStringWidth(fontSize, gModuleShortName[localShip->getModule(i)]);
  500.  
  501.       if(localShip->isModuleActive(localShip->getModule(i)))
  502.          glColor4f(0, 1, 0, 1);
  503.       else
  504.          glColor4f(0, 1, 0, 0.5);
  505.  
  506.       glBegin(GL_POLYGON);
  507.          glVertex2f(xPos, UserInterface::vertMargin);
  508.          glVertex2f(xPos + width + 2 * gapSize, UserInterface::vertMargin);
  509.          glVertex2f(xPos + width + 2 * gapSize, UserInterface::vertMargin + fontSize + 2 * gapSize);
  510.          glVertex2f(xPos, UserInterface::vertMargin + fontSize + 2 * gapSize);
  511.       glEnd();
  512.  
  513.       if(i == localShip->mActiveWeaponIndx)
  514.          glColor3f(0,0,0);
  515.       else
  516.          glColor3f(.2,.2,.2);
  517.  
  518.       // Add the weapon name
  519.       drawString(xPos + gapSize, UserInterface::vertMargin + gapSize, fontSize, gModuleShortName[localShip->getModule(i)]);
  520.  
  521.       xPos += UserInterface::vertMargin + width - 2 * gapSize;
  522.    }
  523.  
  524.    glDisable(GL_BLEND);
  525. }
  526.  
  527. S32 gLoadoutIndicatorHeight = fontSize + gapSize * 2;
  528.  
  529. #undef fontSize
  530. #undef gapSize
  531.  
  532. #define FONTSIZE 14
  533. #define fontGap 4
  534.  
  535. // Render any incoming chat msgs
  536. void GameUserInterface::renderMessageDisplay()
  537. {
  538.    glColor3f(1,1,1);
  539.  
  540.    S32 y = gIniSettings.showWeaponIndicators ? UserInterface::chatMargin : UserInterface::vertMargin;
  541.    S32 msgCount;
  542.  
  543.    if(mMessageDisplayMode == LongFixed)
  544.       msgCount = MessageStoreCount;    // Long form
  545.    else
  546.       msgCount = MessageDisplayCount;  // Short form
  547.  
  548.  
  549.    if(mMessageDisplayMode == ShortTimeout)
  550.       for(S32 i = msgCount - 1; i >= 0; i--)
  551.       {
  552.          if(mDisplayMessage[i][0])
  553.          {
  554.             glColor(mDisplayMessageColor[i]);
  555.             drawString(UserInterface::horizMargin, y, FONTSIZE, mDisplayMessage[i]);
  556.             y += FONTSIZE + fontGap;
  557.          }
  558.       }
  559.    else
  560.       for(S32 i = msgCount - 1; i >= 0; i--)
  561.       {
  562.          if(mStoreMessage[i][0])
  563.          {
  564.             glColor(mStoreMessageColor[i]);
  565.             drawString(UserInterface::horizMargin, y, FONTSIZE, mStoreMessage[i]);
  566.             y += FONTSIZE + fontGap;
  567.          }
  568.       }
  569.  
  570.    // Render faint gray background box... probably better without this...
  571.    //S32 ypos1 = (gIniSettings.showWeaponIndicators ? UserInterface::chatMargin : UserInterface::vertMargin);
  572.    //S32 ypos2 = ypos1 + (FONTSIZE + fontGap) * MessageStoreCount;
  573.    //S32 width = canvasWidth - UserInterface::horizMargin;
  574.  
  575.    //glEnable(GL_BLEND);
  576.    //   glColor4f(1,1,1,.2);
  577.    //   glBegin(GL_POLYGON);
  578.    //      glVertex2f(UserInterface::horizMargin, UserInterface::vertMargin + ypos1 - 3);
  579.    //      glVertex2f(width, UserInterface::vertMargin + ypos1 - 3);
  580.    //      glVertex2f(width, UserInterface::vertMargin + ypos2);
  581.    //      glVertex2f(UserInterface::horizMargin, UserInterface::vertMargin + ypos2);
  582.    //   glEnd();
  583.    //   glColor4f(1,1,1,.4);
  584.    //   glBegin(GL_LINE_LOOP);
  585.    //      glVertex2f(UserInterface::horizMargin, UserInterface::vertMargin + ypos1 - 3);
  586.    //      glVertex2f(width, UserInterface::vertMargin + ypos1 - 3);
  587.    //      glVertex2f(width, UserInterface::vertMargin + ypos2);
  588.    //      glVertex2f(UserInterface::horizMargin, UserInterface::vertMargin + ypos2);
  589.    //   glEnd();
  590.    //glDisable(GL_BLEND);
  591. }
  592.  
  593.  
  594. // Render chat msg that user is composing
  595. void GameUserInterface::renderCurrentChat()
  596. {
  597.    if(mCurrentMode != ChatMode)
  598.       return;
  599.  
  600.    const S32 chatComposeYPos = (gIniSettings.showWeaponIndicators ? UserInterface::chatMargin : UserInterface::vertMargin) +
  601.                                (mMessageDisplayMode == LongFixed ? MessageStoreCount : MessageDisplayCount) * (FONTSIZE + fontGap);
  602.    const char *promptStr;
  603.  
  604.    Color baseColor;
  605.  
  606.    if(mChatBuffer[0] == '/' || mCurrentChatType == CmdChat)      // Whatever the underlying chat mode, seems we're entering a command here
  607.    {
  608.       baseColor = gCmdChatColor;
  609.       promptStr = mCurrentChatType ? "(Command): /" : "(Command): ";
  610.    }
  611.    else if(mCurrentChatType == TeamChat)    // Team chat (goes to all players on team)
  612.    {
  613.       baseColor = gTeamChatColor;
  614.       promptStr = "(Team): ";
  615.    }
  616.    else                                // Global in-game chat (goes to all players in game)
  617.    {
  618.       baseColor = gGlobalChatColor;
  619.       promptStr = "(Global): ";
  620.    }
  621.  
  622.    // Protect against crashes while game is initializing... is this really needed??
  623.    if(! (gClientGame && gClientGame->getConnectionToServer()))
  624.       return;
  625.  
  626.    Ship *ship = dynamic_cast<Ship *>(gClientGame->getConnectionToServer()->getControlObject());
  627.    if(!ship)
  628.       return;
  629.  
  630.    S32 promptSize = getStringWidthf(FONTSIZE, "%s", promptStr);
  631.    S32 nameSize = getStringWidthf(FONTSIZE, "%s: ", ship->getName().getString());
  632.    S32 nameWidth = max(nameSize, promptSize);
  633.    // Above block repeated below...
  634.  
  635.    S32 xpos = UserInterface::horizMargin;
  636.    S32 width = canvasWidth - 2 * UserInterface::horizMargin - (nameWidth - promptSize) + 6;
  637.  
  638.  
  639.    // Render text entry box like thingy
  640.    glEnable(GL_BLEND);
  641.       glColor4f(baseColor.r, baseColor.g, baseColor.b, .25);
  642.       glBegin(GL_POLYGON);
  643.          glVertex2f(xpos, UserInterface::vertMargin + chatComposeYPos - 3);
  644.          glVertex2f(xpos + width, UserInterface::vertMargin + chatComposeYPos - 3);
  645.          glVertex2f(xpos + width, UserInterface::vertMargin + chatComposeYPos + FONTSIZE + 7);
  646.          glVertex2f(xpos, UserInterface::vertMargin + chatComposeYPos + FONTSIZE + 7);
  647.       glEnd();
  648.  
  649.       glColor4f(baseColor.r, baseColor.g, baseColor.b, .4);
  650.       glBegin(GL_LINE_LOOP);
  651.          glVertex2f(xpos, UserInterface::vertMargin + chatComposeYPos - 3);
  652.          glVertex2f(xpos + width, UserInterface::vertMargin + chatComposeYPos - 3);
  653.          glVertex2f(xpos + width, UserInterface::vertMargin + chatComposeYPos + FONTSIZE + 7);
  654.          glVertex2f(xpos, UserInterface::vertMargin + chatComposeYPos + FONTSIZE + 7);
  655.       glEnd();
  656.  
  657.    glColor3f(baseColor.r, baseColor.g, baseColor.b);
  658.    drawString(xpos + 3, UserInterface::vertMargin + chatComposeYPos, FONTSIZE, promptStr);
  659.    drawStringf(xpos + getStringWidth(FONTSIZE, promptStr) + 3, UserInterface::vertMargin + chatComposeYPos, FONTSIZE, "%s%s", mChatBuffer, cursorBlink ? "_" : " ");
  660.    glDisable(GL_BLEND);
  661. }
  662.  
  663. #undef fontGap
  664.  
  665.  
  666. void GameUserInterface::onMouseDragged(S32 x, S32 y)
  667. {
  668.    onMouseMoved(x, y);
  669. }
  670.  
  671. // We'll store the mouse location in mMousePoint for use when we need it
  672. void GameUserInterface::onMouseMoved(S32 x, S32 y)
  673. {
  674.    mMousePoint = Point(x - (S32) windowWidth / 2, y - (S32) windowHeight / 2);
  675.    mMousePoint.x = mMousePoint.x * canvasWidth / windowWidth;
  676.    mMousePoint.y = mMousePoint.y * canvasHeight / windowHeight;
  677.  
  678.    if (gClientGame->getInCommanderMap())     // Ship not in center of the screen in cmdrs map.  Where is it?
  679.    {
  680.       // If we join a server while in commander's map, we'll be here without a gameConnection and we'll get a crash without this check
  681.       GameConnection *gameConnection = gClientGame->getConnectionToServer();
  682.       if(!gameConnection)
  683.          return;
  684.  
  685.       // Here's our ship...
  686.       Ship *ship = dynamic_cast<Ship *>(gameConnection->getControlObject());
  687.       if(!ship)      // Can sometimes happen when switching levels. This will stop the ensuing crashing.
  688.          return;
  689.  
  690.       Point p = gClientGame->worldToScreenPoint( ship->getRenderPos() );
  691.  
  692.       mCurrentMove.angle = atan2(mMousePoint.y + canvasHeight / 2 - p.y, mMousePoint.x + canvasWidth / 2 - p.x);
  693.    }
  694.  
  695.    else     // Ship is at center of the screen
  696.       mCurrentMove.angle = atan2(mMousePoint.y, mMousePoint.x);
  697. }
  698.  
  699.  
  700. // Enter quick chat mode
  701. void GameUserInterface::enterQuickChat()
  702. {
  703.    bool fromController = (gIniSettings.inputMode == Joystick);
  704.    UserInterface::playBoop();
  705.    mQuickChat.show(fromController);
  706.    mCurrentMode = QuickChatMode;
  707. }
  708.  
  709.  
  710. // Enter loadout mode
  711. void GameUserInterface::enterLoadout()
  712. {
  713.    bool fromController = (gIniSettings.inputMode == Joystick);
  714.    UserInterface::playBoop();
  715.    mLoadout.show(fromController);
  716.    mCurrentMode = LoadoutMode;
  717. }
  718.  
  719.  
  720. // Runs on client
  721. void GameUserInterface::dropItem()
  722. {
  723.    if(!gClientGame->getConnectionToServer())
  724.       return;
  725.  
  726.    Ship *ship = dynamic_cast<Ship *>(gClientGame->getConnectionToServer()->getControlObject());
  727.    if(!ship)
  728.       return;
  729.  
  730.    GameType *gt = gClientGame->getGameType();
  731.    if(!gt)
  732.       return;
  733.  
  734.    if(!gt->isCarryingItems(ship))
  735.    {
  736.       displayMessage(Color(1.0, 0.5, 0.5), "You don't have any items to drop!");
  737.       return;
  738.    }
  739.    
  740.    gt->c2sDropItem();
  741. }
  742.  
  743.  
  744. // Set current mode to playout, duh!
  745. void GameUserInterface::setPlayMode()
  746. {
  747.    mCurrentMode = PlayMode;
  748.    setBusyChatting(false);
  749.    mUpDisabled = false;
  750.    mDownDisabled = false;
  751.    mLeftDisabled = false;
  752.    mRightDisabled = false;
  753. }
  754.  
  755.  
  756. // Send a message to the server that we are (or are not) busy chatting
  757. void GameUserInterface::setBusyChatting(bool busy)
  758. {
  759.    if( gClientGame && gClientGame->getConnectionToServer() )
  760.       gClientGame->getConnectionToServer()->c2sSetIsBusy(busy);
  761. }
  762.  
  763.  
  764. // Select next weapon
  765. void GameUserInterface::advanceWeapon()
  766. {
  767.    GameType *g = gClientGame->getGameType();
  768.    if(g)
  769.       g->c2sAdvanceWeapon();
  770. }
  771.  
  772. // Select a weapon by its index
  773. void GameUserInterface::selectWeapon(U32 indx)
  774. {
  775.    GameType *g = gClientGame->getGameType();
  776.    if(g)
  777.       g->c2sSelectWeapon(indx);
  778. }
  779.  
  780. // Temporarily disable the effects of a movement key to avoid unpleasant interactions between ship movement and loadout/quick chat entry
  781. void GameUserInterface::disableMovementKey(KeyCode keyCode)
  782. {
  783.    InputMode inputMode = gIniSettings.inputMode;
  784.  
  785.    if(keyCode == keyUP[inputMode])
  786.       mUpDisabled = true;
  787.    else if(keyCode == keyDOWN[inputMode])
  788.       mDownDisabled = true;
  789.    else if(keyCode == keyLEFT[inputMode])
  790.       mLeftDisabled = true;
  791.    else if(keyCode == keyRIGHT[inputMode])
  792.       mRightDisabled = true;
  793. }
  794.  
  795. // Key pressed --> take action!
  796. // Handles all keypress events, including mouse clicks and controller button presses
  797. void GameUserInterface::onKeyDown(KeyCode keyCode, char ascii)
  798. {
  799.    S32 inputMode = gIniSettings.inputMode;
  800.  
  801.    if(keyCode == keyHELP)          // Turn on help screen
  802.    {
  803.       UserInterface::playBoop();
  804.       gInstructionsUserInterface.activate();
  805.       return;
  806.    }
  807.    else if(keyCode == keyOUTGAMECHAT)
  808.    {
  809.       setBusyChatting(true);
  810.       gChatInterface.activate();
  811.       return;
  812.    }
  813.    else if(keyCode == keyMISSION)
  814.    {
  815.       mMissionOverlayActive = true;
  816.       gClientGame->getGameType()->mLevelInfoDisplayTimer.clear();    // Clear level-start display if user hits F2
  817.    }
  818.    else if(keyCode == KEY_M && getKeyState(KEY_CTRL))    //Ctrl-M, for now, to cycle through message dispaly modes
  819.    {
  820.       S32 m = mMessageDisplayMode + 1;
  821.       if(m >= MessageDisplayModes)
  822.          m = 0;
  823.       mMessageDisplayMode = MessageDisplayMode(m);
  824.  
  825.       return;
  826.    }
  827.  
  828.  
  829.    // First, if we are in loadout mode, check the key to see if
  830.    // it does something with the loadout menu.  If not, we'll
  831.    // further process it below.
  832.  
  833.    if(mCurrentMode == LoadoutMode)
  834.    {  // (braces required)
  835.       if(mLoadout.processKeyCode(keyCode))   // Will return true if key was processed
  836.       {
  837.          disableMovementKey(keyCode);
  838.          return;                             // Leave if key did something, so we don't get "dual" effect
  839.       }
  840.    }
  841.    else if(mCurrentMode == QuickChatMode)
  842.       if(mQuickChat.processKeyCode(keyCode))     // Hand off key handling to the quick chat object
  843.       {
  844.          disableMovementKey(keyCode);
  845.          return;
  846.       }
  847.  
  848.  
  849.    if(mCurrentMode == LoadoutMode || mCurrentMode == PlayMode || mCurrentMode == QuickChatMode)
  850.    {
  851.       // The following keys are allowed in both play mode and in
  852.       // loadout or engineering menu modes if not used in the loadout
  853.       // menu above
  854.  
  855.       if (keyCode == keyMOD1[inputMode])
  856.          mModActivated[0] = true;
  857.       else if (keyCode == keyMOD2[inputMode])
  858.          mModActivated[1] = true;
  859.       else if (keyCode == keyFIRE[inputMode])
  860.          mFiring = true;
  861.       else if(keyCode == keySELWEAP1[inputMode])
  862.          selectWeapon(0);
  863.       else if(keyCode == keySELWEAP2[inputMode])
  864.          selectWeapon(1);
  865.       else if(keyCode == keySELWEAP3[inputMode])
  866.          selectWeapon(2);
  867.       else if(keyCode == keyFPS)
  868.          mFPSVisible = !mFPSVisible;
  869.       else if(keyCode == keyADVWEAP[inputMode])
  870.          advanceWeapon();
  871.       else if(keyCode == KEY_ESCAPE || keyCode == BUTTON_BACK)
  872.       {
  873.          UserInterface::playBoop();
  874.  
  875.          if(!gClientGame->isConnectedToServer())      // Perhaps we're still joining?
  876.          {
  877.             endGame();
  878.             gMainMenuUserInterface.activate();
  879.          }
  880.          else
  881.          {
  882.             setBusyChatting(true);
  883.             gGameMenuUserInterface.activate();
  884.          }
  885.       }
  886.       else if(keyCode == keyCMDRMAP[inputMode])
  887.          gClientGame->zoomCommanderMap();
  888.  
  889.       else if(keyCode == keySCRBRD[inputMode])
  890.       {     // (braces needed)
  891.          if(!mInScoreboardMode)    // We're activating the scoreboard
  892.          {
  893.             mInScoreboardMode = true;
  894.             GameType *g = gClientGame->getGameType();
  895.             if(g)
  896.                g->c2sRequestScoreboardUpdates(true);
  897.          }
  898.       }
  899.       else if(keyCode == keyTOGVOICE[inputMode])
  900.       {     // (braces needed)
  901.          if(!mVoiceRecorder.mRecordingAudio)  // Turning recorder on
  902.             mVoiceRecorder.start();
  903.       }
  904.       else if(mCurrentMode != LoadoutMode && mCurrentMode != QuickChatMode)
  905.       {
  906.          // The following keys are only allowed in PlayMode, and never work in LoadoutMode
  907.          if(keyCode == keyTEAMCHAT[inputMode])
  908.          {
  909.             mCurrentChatType = TeamChat;
  910.             mCurrentMode = ChatMode;
  911.             setBusyChatting(true);
  912.          }
  913.          else if(keyCode == keyGLOBCHAT[inputMode])
  914.          {
  915.             mCurrentChatType = GlobalChat;
  916.             mCurrentMode = ChatMode;
  917.             setBusyChatting(true);
  918.          }
  919.          else if(keyCode == keyCMDCHAT[inputMode])
  920.          {
  921.             mCurrentChatType = CmdChat;
  922.             mCurrentMode = ChatMode;
  923.             setBusyChatting(true);
  924.          }
  925.          else if(keyCode == keyQUICKCHAT[inputMode])
  926.             enterQuickChat();
  927.          else if(keyCode == keyLOADOUT[inputMode])
  928.             enterLoadout();
  929.          else if(keyCode == keyDROPITEM[inputMode])
  930.             dropItem();
  931.  
  932.          else if(inputMode == Joystick)      // Check if the user is trying to use keyboard to move when in joystick mode
  933.             if(keyCode == keyUP[Keyboard] || keyCode == keyDOWN[Keyboard] || keyCode == keyLEFT[Keyboard] || keyCode == keyRIGHT[Keyboard])
  934.                mWrongModeMsgDisplay.reset(WrongModeMsgDisplayTime);
  935.       }
  936.    }     // End if in LoadoutMode or PlayMode
  937.  
  938.    else if(mCurrentMode == ChatMode)   // Player is entering a chat message
  939.    {
  940.       if(keyCode == KEY_ENTER)
  941.          issueChat();
  942.       else if(keyCode == KEY_BACKSPACE || keyCode == KEY_DELETE)
  943.       {     // (braces required)
  944.          if(mChatCursorPos > 0)
  945.          {
  946.             mChatCursorPos--;
  947.             for(U32 i = mChatCursorPos; mChatBuffer[i]; i++)
  948.                mChatBuffer[i] = mChatBuffer[i+1];
  949.          }
  950.       }
  951.       else if(keyCode == KEY_ESCAPE || keyCode == BUTTON_BACK)
  952.          cancelChat();
  953.       else if(keyCode == KEY_TAB)      // Auto complete any commands
  954.       {
  955.          if(mChatBuffer[0] == '/' || mCurrentChatType == CmdChat)     // It's a command!
  956.          {
  957.             S32 found = -1;
  958.             S32 start = mCurrentChatType ? 1 : 0;     // Do we need to lop the leading '/' off mChatCmds item?
  959.  
  960.             size_t len = strlen(mChatBuffer);
  961.             for(S32 i = 0; i < mChatCmds.size(); i++)
  962.                if( mChatCmds[i].substr(start, len) == string(mChatBuffer) )
  963.                {
  964.                   if(found != -1)   // We've already found another command that matches this one, so that means it's not yet unique enough to autocomplete.
  965.                      return;
  966.                   found = i;
  967.                }
  968.  
  969.             if(found == -1)      // Found no match
  970.                return;
  971.  
  972.             U32 clen = (U32) mChatCmds[found].copy(mChatBuffer, mChatCmds[found].length(), start);      // Complete command
  973.             mChatBuffer[clen] = ' ';        // append trailing space for ready entry of next param
  974.             mChatBuffer[clen + 1] = '\0';   // terminate the string
  975.             mChatCursorPos = clen + 1;
  976.          }
  977.       }
  978.       else if(ascii)     // Append any other keys to the chat message
  979.       {
  980.          // Protect against crashes while game is initializing
  981.          if(gClientGame && gClientGame->getConnectionToServer())
  982.          {
  983.             for(U32 i = sizeof(mChatBuffer) - 2; i > mChatCursorPos; i--)
  984.                mChatBuffer[i] = mChatBuffer[i-1];
  985.  
  986.             S32 promptSize = getStringWidth(FONTSIZE, mCurrentChatType == TeamChat ? "(Team): " : "(Global): ");
  987.  
  988.             Ship *ship = dynamic_cast<Ship *>(gClientGame->getConnectionToServer()->getControlObject());
  989.             if(!ship)
  990.                return;
  991.  
  992.             S32 nameSize = getStringWidthf(FONTSIZE, "%s: ", ship->getName().getString());
  993.             S32 nameWidth = max(nameSize, promptSize);
  994.             // Above block repeated above
  995.  
  996.             if(mChatCursorPos < sizeof(mChatBuffer) - 2 && nameWidth + (S32) getStringWidthf(FONTSIZE, "%s%c", mChatBuffer, ascii) < canvasWidth - 2 * horizMargin - 3)
  997.             {
  998.                mChatBuffer[mChatCursorPos] = ascii;
  999.                mChatCursorPos++;
  1000.             }
  1001.          }
  1002.       }
  1003.    }     // End if in ChatMode
  1004. }
  1005.  
  1006. #undef FONTSIZE
  1007.  
  1008.  
  1009. void GameUserInterface::onKeyUp(KeyCode keyCode)
  1010. {
  1011.       S32 inputMode = gIniSettings.inputMode;
  1012.  
  1013.    // These keys works in any mode!  And why not??
  1014.  
  1015.    if(keyCode == keyMISSION)
  1016.       mMissionOverlayActive = false;
  1017.    else if (keyCode == keyMOD1[inputMode])
  1018.       mModActivated[0] = false;
  1019.    else if (keyCode == keyMOD2[inputMode])
  1020.       mModActivated[1] = false;
  1021.    else if (keyCode == keyFIRE[inputMode])
  1022.       mFiring = false;
  1023.    else if(keyCode == keySCRBRD[inputMode])
  1024.    {     // (braces required)
  1025.       if(mInScoreboardMode)     // We're turning scoreboard off
  1026.       {
  1027.          mInScoreboardMode = false;
  1028.          GameType *g = gClientGame->getGameType();
  1029.          if(g)
  1030.             g->c2sRequestScoreboardUpdates(false);
  1031.       }
  1032.    }
  1033.    else if(keyCode == keyTOGVOICE[inputMode])
  1034.    {     // (braces required)
  1035.       if(mVoiceRecorder.mRecordingAudio)  // Turning recorder off
  1036.          mVoiceRecorder.stop();
  1037.    }
  1038.    else if(keyCode == keyUP[inputMode])
  1039.       mUpDisabled = false;
  1040.    else if(keyCode == keyDOWN[inputMode])
  1041.       mDownDisabled = false;
  1042.    else if(keyCode == keyLEFT[inputMode])
  1043.       mLeftDisabled = false;
  1044.    else if(keyCode == keyRIGHT[inputMode])
  1045.       mRightDisabled = false;
  1046. }
  1047.  
  1048. // Return current move (actual move processing in ship.cpp)
  1049. // Will also transform move into "relative" mode if needed
  1050. // Note that all input supplied here will be overwritten if
  1051. // we are using a game controller.
  1052. Move *GameUserInterface::getCurrentMove()
  1053. {
  1054.    // (Possible modes = PlayMode, ChatMode, QuickChatMode, LoadoutMode)
  1055.  
  1056.    if((mCurrentMode == LoadoutMode || mCurrentMode == PlayMode || mCurrentMode == QuickChatMode ) && !gDisableShipKeyboardInput)
  1057.    {
  1058.       InputMode inputMode = gIniSettings.inputMode;
  1059.       mCurrentMove.up = !mUpDisabled && getKeyState(keyUP[inputMode]) ? 1 : 0;
  1060.       mCurrentMove.down = !mDownDisabled && getKeyState(keyDOWN[inputMode]) ? 1 : 0;
  1061.       mCurrentMove.left = !mLeftDisabled && getKeyState(keyLEFT[inputMode]) ? 1 : 0;
  1062.       mCurrentMove.right = !mRightDisabled && getKeyState(keyRIGHT[inputMode]) ? 1 : 0;
  1063.  
  1064.       mCurrentMove.fire = mFiring;
  1065.  
  1066.       for(U32 i = 0; i < (U32)ShipModuleCount; i++)
  1067.          mCurrentMove.module[i] = mModActivated[i];
  1068.    }
  1069.    else
  1070.    {
  1071.       mCurrentMove.up = 0;
  1072.       mCurrentMove.down = 0;
  1073.       mCurrentMove.left = 0;
  1074.       mCurrentMove.right = 0;
  1075.  
  1076.       mCurrentMove.fire = mFiring;     // should be false?
  1077.  
  1078.       for(U32 i = 0; i < (U32)ShipModuleCount; i++)
  1079.          mCurrentMove.module[i] = false;
  1080.    }
  1081.  
  1082.  
  1083.    if(!gIniSettings.controlsRelative)
  1084.       return &mCurrentMove;
  1085.  
  1086.    else     // Using relative controls -- all turning is done relative to the direction of the ship.
  1087.    {
  1088.       mTransformedMove = mCurrentMove;
  1089.  
  1090.       Point moveDir(mCurrentMove.right - mCurrentMove.left,
  1091.                     mCurrentMove.up - mCurrentMove.down);
  1092.  
  1093.       Point angleDir(cos(mCurrentMove.angle), sin(mCurrentMove.angle));
  1094.  
  1095.       Point rightAngleDir(-angleDir.y, angleDir.x);
  1096.       Point newMoveDir = angleDir * moveDir.y + rightAngleDir * moveDir.x;
  1097.  
  1098.       if(newMoveDir.x > 0)
  1099.       {
  1100.          mTransformedMove.right = newMoveDir.x;
  1101.          mTransformedMove.left = 0;
  1102.       }
  1103.       else
  1104.       {
  1105.          mTransformedMove.right = 0;
  1106.          mTransformedMove.left = -newMoveDir.x;
  1107.       }
  1108.       if(newMoveDir.y > 0)
  1109.       {
  1110.          mTransformedMove.down = newMoveDir.y;
  1111.          mTransformedMove.up = 0;
  1112.       }
  1113.       else
  1114.       {
  1115.          mTransformedMove.down = 0;
  1116.          mTransformedMove.up = -newMoveDir.y;
  1117.       }
  1118.       if(mTransformedMove.right > 1)
  1119.          mTransformedMove.right = 1;
  1120.       if(mTransformedMove.left > 1)
  1121.          mTransformedMove.left = 1;
  1122.       if(mTransformedMove.up > 1)
  1123.          mTransformedMove.up = 1;
  1124.       if(mTransformedMove.down > 1)
  1125.          mTransformedMove.down = 1;
  1126.  
  1127.       return &mTransformedMove;
  1128.    }
  1129. }
  1130.  
  1131.  
  1132. // User has finished entering a chat message and pressed <enter>
  1133. void GameUserInterface::issueChat()
  1134. {
  1135.    if(mChatBuffer[0])
  1136.    {
  1137.       // Check if chat buffer holds a message or a command
  1138.       if(mChatBuffer[0] != '/' && mCurrentChatType != CmdChat)     // It's a normal chat message
  1139.       {
  1140.          GameType *gt = gClientGame->getGameType();
  1141.          if(gt)
  1142.             gt->c2sSendChat(mCurrentChatType == GlobalChat, mChatBuffer);   // Broadcast message
  1143.       }
  1144.       else                          // It's a command
  1145.       {
  1146.          Vector<string> words = parseString(mChatBuffer);
  1147.          processCommand(words);
  1148.       }
  1149.    }
  1150.    cancelChat();
  1151. }
  1152.  
  1153.  
  1154. Vector<string> GameUserInterface::parseString(char buffer[])
  1155. {
  1156.    // Parse the string
  1157.    string word = "";
  1158.    Vector<string> words;
  1159.  
  1160.    S32 startIndex = (mCurrentChatType == CmdChat) ? 0 : 1;  // Start at 1 to omit the leading /
  1161.  
  1162.    for(size_t i = startIndex; i < strlen(mChatBuffer); i++)
  1163.    {
  1164.       if(mChatBuffer[i] != ' ')
  1165.          word += words.size() == 0 ? tolower(mChatBuffer[i]) : mChatBuffer[i];      // Make first word all lower case for case insensitivity
  1166.       else if(word != "")
  1167.       {
  1168.          words.push_back(word);
  1169.          word = "";
  1170.       }
  1171.    }
  1172.    if(word != "")
  1173.       words.push_back(word);
  1174.  
  1175.    return words;
  1176. }
  1177.  
  1178.  
  1179. extern md5wrapper md5;
  1180.  
  1181. // Process a command entered at the chat prompt
  1182. // Make sure any commands listed here are also included in mChatCmds for auto-completion purposes...
  1183. void GameUserInterface::processCommand(Vector<string> words)
  1184. {
  1185.    if(words.size() == 0)            // Just in case
  1186.       return;
  1187.  
  1188.    GameConnection *gc = gClientGame->getConnectionToServer();
  1189.    if(!gc)
  1190.    {
  1191.       displayMessage(gCmdChatColor, "!!! Not connected to server");
  1192.       return;
  1193.    }
  1194.  
  1195.    if(words[0] == "add")            // Add time to the game
  1196.    {
  1197.       if(words.size() < 2 || words[1] == "")
  1198.       {
  1199.          displayMessage(gCmdChatColor, "!!! Need to supply a time (in minutes)");
  1200.          return;
  1201.       }
  1202.  
  1203.       U8 mins;    // Use U8 to limit number of mins that can be added, while nominally having no limit!
  1204.       // Parse 2nd arg -- if first digit isn't a number, user probably screwed up.
  1205.       // atoi will return 0, but this probably isn't what the user wanted.
  1206.  
  1207.       bool err = false;
  1208.       if(words[1][0] >= '0' && words[1][0] <= '9')
  1209.          mins = atoi(words[1].c_str());
  1210.       else
  1211.          err = true;
  1212.  
  1213.       if(err || mins == 0)
  1214.       {
  1215.          displayMessage(gCmdChatColor, "!!! Invalid value... game time not changed");
  1216.          return;
  1217.       }
  1218.  
  1219.       displayMessage(gCmdChatColor, "Extended game by %d minute%s", mins, (mins == 1) ? "" : "s");
  1220.  
  1221.       if(gClientGame->getGameType())
  1222.          gClientGame->getGameType()->addTime(mins * 60 * 1000);
  1223.    }
  1224.  
  1225.    else if(words[0] == "next")      // Go to next level
  1226.    {
  1227.       if(!gc->isLevelChanger())
  1228.       {
  1229.          displayMessage(gCmdChatColor, "!!! You don't have permission to change levels");
  1230.          return;
  1231.       }
  1232.  
  1233.       //if(words.size() < 2 || words[1] == "")
  1234.          gc->c2sRequestLevelChange(1, true);
  1235.    }
  1236.  
  1237.    else if(words[0] == "prev")      // Go to previous level
  1238.    {
  1239.       if(!gc->isLevelChanger())
  1240.       {
  1241.          displayMessage(gCmdChatColor, "!!! You don't have permission to change levels");
  1242.          return;
  1243.       }
  1244.  
  1245.       //if(words.size() < 2 || words[1] == "")
  1246.          gc->c2sRequestLevelChange(-1, true);
  1247.    }
  1248.  
  1249.    else if(words[0] == "restart")      // Restart current level
  1250.    {
  1251.       if(!gc->isLevelChanger())
  1252.       {
  1253.          displayMessage(gCmdChatColor, "!!! You don't have permission to change levels");
  1254.          return;
  1255.       }
  1256.  
  1257.       //if(words.size() < 2 || words[1] == "")
  1258.          gc->c2sRequestLevelChange(-2, false);
  1259.    }
  1260.  
  1261.    else if(words[0] == "kick")      // Kick a player
  1262.    {
  1263.       if(!gc->isAdmin())
  1264.       {
  1265.          displayMessage(gCmdChatColor, "!!! You don't have permission to kick players");
  1266.          return;
  1267.       }
  1268.       if(words.size() < 2 || words[1] == "")
  1269.       {
  1270.          displayMessage(gCmdChatColor, "!!! Need to specify who to kick");
  1271.          return;
  1272.       }
  1273.       // Did user provide a valid, known name?
  1274.       if(!gClientGame->getGameType())
  1275.          return;
  1276.  
  1277.       ClientRef *clientRef = gClientGame->getGameType()->findClientRef(words[1].c_str());
  1278.       if(!clientRef)
  1279.       {
  1280.          displayMessage(gCmdChatColor, "!!! Could not find player %s", words[1].c_str());
  1281.          return;
  1282.       }
  1283.  
  1284.       gc->c2sAdminPlayerAction(words[1].c_str(), PlayerMenuUserInterface::Kick, 0);     // Team doesn't matter with kick!
  1285.    }
  1286.  
  1287.    else if(words[0] == "admin")     // Request admin permissions
  1288.    {
  1289.       if(words.size() < 2 || words[1] == "")
  1290.       {
  1291.          displayMessage(gCmdChatColor, "!!! Need to supply a password");
  1292.          return;
  1293.       }
  1294.       if(gc->isAdmin())
  1295.       {
  1296.          displayMessage(gCmdChatColor, "!!! You are already an admin");
  1297.          return;
  1298.       }
  1299.  
  1300.       gc->submitAdminPassword(words[1].c_str());
  1301.    }
  1302.  
  1303.    else if(words[0] == "levpass" || words[0] == "levelpass" || words[0] == "lvlpass")
  1304.    {
  1305.       if(words.size() < 2 || words[1] == "")
  1306.       {
  1307.          displayMessage(gCmdChatColor, "!!! Need to supply a password");
  1308.          return;
  1309.       }
  1310.       if(gc->isLevelChanger())
  1311.       {
  1312.          displayMessage(gCmdChatColor, "!!! You can already change levels");
  1313.          return;
  1314.       }
  1315.  
  1316.       gc->submitLevelChangePassword(words[1].c_str());
  1317.    }
  1318.    else if(words[0] == "dcoords")
  1319.       mDebugShowShipCoords = !mDebugShowShipCoords;
  1320.    else if(words[0] == "svol")      // SFX volume
  1321.       setVolume(SfxVolumeType, words);
  1322.    else if(words[0] == "mvol")      // Music volume
  1323.       setVolume(MusicVolumeType, words);
  1324.    else if(words[0] == "vvol")      // Voice chat volume
  1325.       setVolume(VoiceVolumeType, words);
  1326.    else if(words[0] == "servvol")   // Server alerts volume
  1327.       setVolume(ServerAlertVolumeType, words);
  1328.  
  1329.    else
  1330.       displayMessage(gCmdChatColor, "!!! Invalid command: %s", words[0].c_str());
  1331. }
  1332.  
  1333.  
  1334. // For auto-completion purposes
  1335. void GameUserInterface::populateChatCmdList()
  1336. {
  1337.    // Our list of commands that can be entered at the chat prompt
  1338.    mChatCmds.push_back("/add");
  1339.    mChatCmds.push_back("/admin");
  1340.    mChatCmds.push_back("/dcoords");
  1341.    mChatCmds.push_back("/kick");
  1342.    mChatCmds.push_back("/levpass");
  1343.    mChatCmds.push_back("/mvol");
  1344.    mChatCmds.push_back("/next");
  1345.    mChatCmds.push_back("/prev");
  1346.    mChatCmds.push_back("/restart");
  1347.    mChatCmds.push_back("/svol");
  1348.    mChatCmds.push_back("/servvol");
  1349.    mChatCmds.push_back("/vvol");
  1350. }
  1351.  
  1352. // Set specified volume to the specefied level
  1353. void GameUserInterface::setVolume(VolumeType volType, Vector<string> words)
  1354. {
  1355.    S32 vol;
  1356.  
  1357.    if(words.size() < 2)
  1358.    {
  1359.       displayMessage(gCmdChatColor, "!!! Need to specify volume");
  1360.       return;
  1361.    }
  1362.  
  1363.    string volstr = words[1];
  1364.  
  1365.    // Parse volstr -- if first digit isn't a number, user probably screwed up.
  1366.    // atoi will return 0, but this probably isn't what the user wanted.
  1367.    if(volstr[0] >= '0' && volstr[0] <= '9')
  1368.       vol = max(min(atoi(volstr.c_str()), 10), 0);
  1369.    else
  1370.    {
  1371.       displayMessage(gCmdChatColor, "!!! Invalid value... volume not changed");
  1372.       return;
  1373.    }
  1374.  
  1375.   switch(volType)
  1376.   {
  1377.    case SfxVolumeType:
  1378.       gIniSettings.sfxVolLevel = (F32) vol / 10.0;
  1379.       displayMessage(gCmdChatColor, "SFX volume changed to %d %s", vol, vol == 0 ? "[MUTE]" : "");
  1380.       return;
  1381.    case MusicVolumeType:
  1382.       gIniSettings.musicVolLevel = (F32) vol / 10.0;
  1383.       displayMessage(gCmdChatColor, "Music volume changed to %d %s", vol, vol == 0 ? "[MUTE]" : "");
  1384.       return;
  1385.    case VoiceVolumeType:
  1386.       gIniSettings.voiceChatVolLevel = (F32) vol / 10.0;
  1387.       displayMessage(gCmdChatColor, "Voice chat volume changed to %d %s", vol, vol == 0 ? "[MUTE]" : "");
  1388.       return;
  1389.    case ServerAlertVolumeType:
  1390.       gClientGame->getConnectionToServer()->c2sSetServerAlertVolume((S8) vol);
  1391.       displayMessage(gCmdChatColor, "Server alerts chat volume changed to %d %s", vol, vol == 0 ? "[MUTE]" : "");
  1392.       return;
  1393.   }
  1394. }
  1395.  
  1396.  
  1397. void GameUserInterface::cancelChat()
  1398. {
  1399.    memset(mChatBuffer, 0, sizeof(mChatBuffer));
  1400.    mChatCursorPos = 0;
  1401.    setPlayMode();
  1402. }
  1403.  
  1404.  
  1405. // Constructor
  1406. GameUserInterface::VoiceRecorder::VoiceRecorder()
  1407. {
  1408.    mRecordingAudio = false;
  1409.    mMaxAudioSample = 0;
  1410.    mMaxForGain = 0;
  1411.    mVoiceEncoder = new LPC10VoiceEncoder;
  1412. }
  1413.  
  1414.  
  1415. void GameUserInterface::VoiceRecorder::idle(U32 timeDelta)
  1416. {
  1417.    if(mRecordingAudio)
  1418.    {
  1419.       if(mVoiceAudioTimer.update(timeDelta))
  1420.       {
  1421.          mVoiceAudioTimer.reset(VoiceAudioSampleTime);
  1422.          process();
  1423.       }
  1424.    }
  1425. }
  1426.  
  1427.  
  1428. void GameUserInterface::VoiceRecorder::render()
  1429. {
  1430.    if(mRecordingAudio)
  1431.    {
  1432.       F32 amt = mMaxAudioSample / F32(0x7FFF);
  1433.       U32 totalLineCount = 50;
  1434.  
  1435.       glColor3f(1, 1 ,1);
  1436.       glBegin(GL_LINES);
  1437.       glVertex2f(10, 130);
  1438.       glVertex2f(10, 145);
  1439.       glVertex2f(10 + totalLineCount * 2, 130);
  1440.       glVertex2f(10 + totalLineCount * 2, 145);
  1441.  
  1442.       F32 halfway = totalLineCount * 0.5;
  1443.       F32 full = amt * totalLineCount;
  1444.       for(U32 i = 1; i < full; i++)
  1445.       {
  1446.          if(i < halfway)
  1447.             glColor3f(i / halfway, 1, 0);
  1448.          else
  1449.             glColor3f(1, 1 - (i - halfway) / halfway, 0);
  1450.  
  1451.          glVertex2f(10 + i * 2, 130);
  1452.          glVertex2f(10 + i * 2, 145);
  1453.       }
  1454.       glEnd();
  1455.    }
  1456. }
  1457.  
  1458.  
  1459. void GameUserInterface::VoiceRecorder::start()
  1460. {
  1461.    if(!mRecordingAudio)
  1462.    {
  1463.       mRecordingAudio = SFXObject::startRecording();
  1464.       if(!mRecordingAudio)
  1465.          return;
  1466.  
  1467.       mUnusedAudio = new ByteBuffer(0);
  1468.       mRecordingAudio = true;
  1469.       mMaxAudioSample = 0;
  1470.       mVoiceAudioTimer.reset(FirstVoiceAudioSampleTime);
  1471.  
  1472.       // trim the start of the capture buffer:
  1473.       SFXObject::captureSamples(mUnusedAudio);
  1474.       mUnusedAudio->resize(0);
  1475.    }
  1476. }
  1477.  
  1478. void GameUserInterface::VoiceRecorder::stop()
  1479. {
  1480.    if(mRecordingAudio)
  1481.    {
  1482.       process();
  1483.  
  1484.       mRecordingAudio = false;
  1485.       SFXObject::stopRecording();
  1486.       mVoiceSfx = NULL;
  1487.       mUnusedAudio = NULL;
  1488.    }
  1489. }
  1490.  
  1491. void GameUserInterface::VoiceRecorder::process()
  1492. {
  1493.    U32 preSampleCount = mUnusedAudio->getBufferSize() / 2;
  1494.    SFXObject::captureSamples(mUnusedAudio);
  1495.  
  1496.    U32 sampleCount = mUnusedAudio->getBufferSize() / 2;
  1497.    if(sampleCount == preSampleCount)
  1498.       return;
  1499.  
  1500.    S16 *samplePtr = (S16 *) mUnusedAudio->getBuffer();
  1501.    mMaxAudioSample = 0;
  1502.  
  1503.    for(U32 i = preSampleCount; i < sampleCount; i++)
  1504.    {
  1505.       if(samplePtr[i] > mMaxAudioSample)
  1506.          mMaxAudioSample = samplePtr[i];
  1507.       else if(-samplePtr[i] > mMaxAudioSample)
  1508.          mMaxAudioSample = -samplePtr[i];
  1509.    }
  1510.    mMaxForGain = U32(mMaxForGain * 0.95f);
  1511.    S32 boostedMax = mMaxAudioSample + 2048;
  1512.    if(boostedMax > mMaxForGain)
  1513.       mMaxForGain = boostedMax;
  1514.    if(mMaxForGain > MaxDetectionThreshold)
  1515.    {
  1516.       // apply some gain to the buffer:
  1517.       F32 gain = 0x7FFF / F32(mMaxForGain);
  1518.       for(U32 i = preSampleCount; i < sampleCount; i++)
  1519.       {
  1520.          F32 sample = gain * samplePtr[i];
  1521.          if(sample > 0x7FFF)
  1522.             samplePtr[i] = 0x7FFF;
  1523.          else if(sample < -0x7FFF)
  1524.             samplePtr[i] = -0x7FFF;
  1525.          else
  1526.             samplePtr[i] = S16(sample);
  1527.       }
  1528.       mMaxAudioSample = U32(mMaxAudioSample * gain);
  1529.    }
  1530.  
  1531.    ByteBufferPtr sendBuffer = mVoiceEncoder->compressBuffer(mUnusedAudio);
  1532.  
  1533.    if(sendBuffer.isValid())
  1534.    {
  1535.       GameType *gt = gClientGame->getGameType();
  1536.       if(gt)
  1537.          gt->c2sVoiceChat(gIniSettings.echoVoice, sendBuffer);
  1538.    }
  1539. }
  1540.  
  1541. };