// Scenario 11 -- Player is editing loadout when level changes --> TEST NOT COMPLETE!!!!
static void doScenario11(GamePair &gamePair)
{
   ClientGame *clientGame = gamePair.getClient(0);
   ServerGame *serverGame = gamePair.server;
   serverGame->setGameTime(.5);     // 30 seconds

   GameUserInterface *gameUI = clientGame->getUIManager()->getUI<GameUserInterface>();
   ASSERT_FALSE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));

   gameUI->activateHelper(HelperMenu::LoadoutHelperType);
   ASSERT_TRUE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));
   ASSERT_EQ("", serverGame->getClientInfo(0)->getOnDeckLoadout().toString(true));   // Prove there's no on deck loadout

   // See static const OverlayMenuItem loadoutModuleMenuItems[] in loadoutHelper.cpp
   // Feed the UI some keys... like we're configuring a loadout!  
   gameUI->onKeyDown(KEY_1);     gameUI->onKeyDown(KEY_3);                                // First 2 modules...
   gameUI->onKeyDown(KEY_1);     gameUI->onKeyDown(KEY_2);     gameUI->onKeyDown(KEY_3);  // ...then 3 weapons
   ASSERT_FALSE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));

   gamePair.idle(10, 10);
   // Check the on deck loadout on server -- does not get set on the client
   ASSERT_EQ("Turbo,Repair,Phaser,Bouncer,Triple", serverGame->getClientInfo(0)->getOnDeckLoadout().toString(true));

   gameUI->activateHelper(HelperMenu::LoadoutHelperType);
   ASSERT_TRUE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));
   gamePair.idle(100, 350);        // Idle until game ends

   gameUI->onKeyDown(KEY_1);     gameUI->onKeyDown(KEY_4);
   gameUI->onKeyDown(KEY_1);     gameUI->onKeyDown(KEY_2);     gameUI->onKeyDown(KEY_3);

   ASSERT_FALSE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));

   gamePair.idle(10, 10);   
   ASSERT_EQ("Turbo,Sensor,Phaser,Bouncer,Triple", serverGame->getClientInfo(0)->getOnDeckLoadout().toString(true));
}
Beispiel #2
0
TEST(EditorTest, findSnapVertexTest)
{
    GamePair pair;
    ClientGame *clientGame = pair.getClient(0);
    ASSERT_TRUE(clientGame)  << "Shouldn't be NULL here...";
    EditorUserInterface *editorUi = clientGame->getUIManager()->getUI<EditorUserInterface>();
    editorUi->setLevel(boost::shared_ptr<Level>(new Level()));

    ASSERT_EQ(0, editorUi->getLevel()->getObjectCount());     // Confirm level starts empty

    // 5 vertex wall in stair-step pattern, with middle vertex on 0,0
    editorUi->getLevel()->parseLevelLine("BarrierMaker 10 -100 -100  0 -100  0 0  100 0  100 100", "NoFile");

    ASSERT_EQ(1, editorUi->getLevel()->getObjectCount());     // Confirm object was added properly

    // Mark first 3 vertices as being selected
    WallItem *wall = static_cast<WallItem *>(editorUi->getLevel()->getObjectByIndex(0));
    wall->aselectVert(0);
    wall->aselectVert(1);
    wall->aselectVert(2);

    ASSERT_TRUE(wall->vertSelected(0));
    ASSERT_TRUE(wall->vertSelected(1));
    ASSERT_TRUE(wall->vertSelected(2));
    ASSERT_FALSE(wall->vertSelected(3));
    ASSERT_FALSE(wall->vertSelected(4));

    editorUi->mMousePos.set(3,3);          // In canvas coords; near vertex 2
    editorUi->findSnapVertex();
    EXPECT_EQ(2, editorUi->mSnapVertexIndex);

    editorUi->mMousePos.set(95,105);       // Near vertex 4, which is not selected --> closest selected is 2
    editorUi->findSnapVertex();
    EXPECT_EQ(2, editorUi->mSnapVertexIndex);

    editorUi->mMousePos.set(-88,-106);     // Near vertex 0, which is selected
    editorUi->findSnapVertex();
    EXPECT_EQ(0, editorUi->mSnapVertexIndex);

    wall->unselectVert(0);
    ASSERT_FALSE(wall->vertSelected(0));
    ASSERT_TRUE(wall->vertSelected(1));
    ASSERT_TRUE(wall->vertSelected(2));
    ASSERT_FALSE(wall->vertSelected(3));
    ASSERT_FALSE(wall->vertSelected(4));

    editorUi->mMousePos.set(-88, -106);    // Near vertex 0, which is not selected   --> closest selected is 1
    editorUi->findSnapVertex();
    EXPECT_EQ(1, editorUi->mSnapVertexIndex);

    editorUi->mMousePos.set(3, 3);         // In canvas coords; near vertex 2
    editorUi->findSnapVertex();
    EXPECT_EQ(2, editorUi->mSnapVertexIndex);

    // Cleanup
    delete newClientGame();
}
Beispiel #3
0
GameUserInterface *GamePair::getGameUI(S32 clientIndex)
{
   EXPECT_TRUE(clientIndex >= 0 && clientIndex < getClientCount()) << "Index out of bounds!";
   ClientGame *client = getClient(clientIndex);
   GameUserInterface *ui = dynamic_cast<GameUserInterface *>(client->getUIManager()->getCurrentUI());
   EXPECT_TRUE(ui != NULL) << "Are we in the game?";

   return ui;
}
Beispiel #4
0
void GamePair::removeClient(S32 index)
{
   // Disconnect before deleting
   ClientGame *clientGame = GameManager::getClientGames()->get(index);

   if(clientGame->getConnectionToServer())
      clientGame->getConnectionToServer()->disconnect(NetConnection::ReasonSelfDisconnect, "");

   this->idle(5, 5);      // Let things settle

   GameManager::deleteClientGame(index);
}
Beispiel #5
0
// Simulates player joining game from new client
ClientGame *GamePair::addClient(const string &name)
{
   ClientGame *clientGame = newClientGame();
   clientGame->userEnteredLoginCredentials(name, "password", false);    // Simulates entry from NameEntryUserInterface

   // Get a base UI going, so if we enter the game, and exit again, we'll have a place to land
   clientGame->activateMainMenuUI();

   GameManager::addClientGame(clientGame);

   return addClient(clientGame);
}
void LevelMenuSelectUserInterface::onActivate()
{
   Parent::onActivate();

   // Replace with a getLevelCount() method on Game?
   ClientGame *game = getGame();
   GameConnection *gc = game->getConnectionToServer();

   if(gc == NULL || gc->mLevelInfos.size() == 0)
      return;

   mMenuTitle = "CHOOSE LEVEL [" + mCategory + "]";
   mMenuDisplayItems.clear();

   char c[2];
   c[1] = 0;   // null termination

   if(mCategory == UPLOAD_LEVELS)
   {
      // Get all the playable levels in levelDir
      mMenuDisplayItems = mGameSettings->getLevelList();

      for(S32 i = 0; i < mMenuDisplayItems.size(); i++)
      {
         c[0] = mMenuDisplayItems[i][0];
         addMenuItem(new MenuItem(i | UPLOAD_LEVELS_BIT, 
                                  mMenuDisplayItems[i], 
                                  processLevelSelectionCallback, 
                                  "", 
                                  InputCodeManager::stringToInputCode(c)));
      }
   }
 
   for(S32 i = 0; i < gc->mLevelInfos.size(); i++)
   {
      if(gc->mLevelInfos[i].mLevelName == "")   // Skip levels with blank names --> but all should have names now!
         continue;

      if(strcmp(gc->mLevelInfos[i].getLevelTypeName(), mCategory.c_str()) == 0 || mCategory == ALL_LEVELS)
      {
         const char *levelName = gc->mLevelInfos[i].mLevelName.getString();
         c[0] = levelName[0];
         addMenuItem(new MenuItem(i, levelName, processLevelSelectionCallback, "", InputCodeManager::stringToInputCode(c)));
      }
   }

   if(!getGame()->isUsingPlaylist())
      sortMenuItems();
}
Beispiel #7
0
int main(int, char **)
{
  try
  {
    ClientGame  game;
    
    game.run();
    return (0);
  }
  catch (std::exception &e)
  {
    std::cerr << e.what() << std::endl;
    return (1);
  }
}
Beispiel #8
0
int main (int /* argc */, char** /* argv */)
{

	//auto v = Vector2<int>::Zero;


	ClientGame* game = ClientGame::GetInstance();

	game->Initialize();
	game->LoadContent();
	//game->Run();
	game->UnloadContent();

	delete game;


	GLFWwindow* window;

	if(!glfwInit())
		return EXIT_FAILURE;

	window = glfwCreateWindow(g_windowwidth, g_windowheight, "Tribble", nullptr, nullptr);
	if(!window)
	{
		glfwTerminate();
		return EXIT_FAILURE;
	}

	glfwMakeContextCurrent(window);

	bool32 running = true;
	while(running)
	{
		glClearColor(0.5f, 0.6f, 1.0f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		
		glfwSwapBuffers(window);
		glfwPollEvents();

		if (glfwWindowShouldClose(window) ||
			glfwGetKey(window, GLFW_KEY_ESCAPE))
			running = false;
	}

	glfwDestroyWindow(window);
	glfwTerminate();
	return EXIT_SUCCESS;
}
Beispiel #9
0
// Checks collisions with a SpeedZone
bool SpeedZone::collide(BfObject *hitObject)
{
    if(ignoreThisCollision)
        return false;

    // This is run on both server and client side to reduce lag
    if(isShipType(hitObject->getObjectTypeNumber()))     // Only ships & robots collide
    {
#ifndef ZAP_DEDICATED
        if(isGhost()) // On client, don't process speedZone on all moveObjects except the controlling one
        {
            ClientGame *client = static_cast<ClientGame *>(getGame());
            GameConnection *gc = client->getConnectionToServer();
            if(gc && gc->getControlObject() != hitObject)
                return false;
        }
#endif
        return true;
    }
    return false;
}
Beispiel #10
0
void doTest(const Vector<S32> &loadoutZoneCount, S32 neutralLoadoutZoneCount, S32 hostileLoadoutZoneCount, const Vector<S32> &results)
{
   ASSERT_EQ(loadoutZoneCount.size(), results.size()) << "Malformed test!";      // Sanity check

   string level = getLevelWithVariableNumberOfLoadoutZones(loadoutZoneCount, neutralLoadoutZoneCount, hostileLoadoutZoneCount);

   GamePair pair(level);

   ClientGame *client = pair.getClient(0);
   ServerGame *server = pair.server;

   for(S32 i = 0; i < results.size(); i++)
      if(results[i])
      {
         EXPECT_TRUE(client->levelHasLoadoutZoneForTeam(i));
         EXPECT_TRUE(server->levelHasLoadoutZoneForTeam(i));
      }
      else
      {
         EXPECT_FALSE(client->levelHasLoadoutZoneForTeam(i));
         EXPECT_FALSE(server->levelHasLoadoutZoneForTeam(i));
      }
}
// Scenario 1: Player is idle and gets suspended -- no other players in game
static void doScenario1(GamePair &gamePair)
{
   ClientGame *clientGame = gamePair.getClient(0);
   ServerGame *serverGame = gamePair.server;

   // Idle for a while -- ship should become spawn delayed.  GameConnection::SPAWN_DELAY_TIME is measured in ms.
   gamePair.idle(10, GameConnection::SPAWN_DELAY_TIME / 10);

   ASSERT_TRUE(serverGame->getClientInfo(0)->isPlayerInactive());    // No input from player, should be flagged as inactive
   // Note that spawn delay does not get set until the delayed ship tries to spawn, even if player is marked as inactive

   // Kill the ship again -- should be delayed when it tries to respawn because client has been inactive
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   EXPECT_EQ(1, fillVector.size());    // Should only be one ship

   Ship *ship = static_cast<Ship *>(fillVector[0]);      // Server's copy of the ship

   killShip(ship);

   gamePair.idle(10, GameType::RespawnDelay / 10 + 5);
   // Since server has received no input from client for GameConnection::SPAWN_DELAY_TIME ms, and ship has attempted to respawn, should be spawn-delayed
   ASSERT_TRUE(serverGame->getClientInfo(0)->isSpawnDelayed());
   ASSERT_TRUE(clientGame->isSpawnDelayed());      // Status should have propagated to client by now

   // At this point, client and server are both aware that the spawn is delayed due to player inactivity

   gamePair.idle(10, 10);              // Idle 10x
   // If spawn were not delayed, ship would have respawned.  Check for it on the server.
   // (Dead ship may linger on client while exploding, so we won't check there.)
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(0, fillVector.size());                   // Ship is spawn delayed and won't spawn... hence no ships
   ASSERT_EQ(0, serverGame->getClientInfo(0)->getReturnToGameTime());      // No returnToGamePenalty in this scenario
   ASSERT_EQ(0, clientGame->getReturnToGameDelay());
   ASSERT_FALSE(static_cast<FullClientInfo *>(serverGame->getClientInfo(0))->hasReturnToGamePenalty());   // No penalty in the works

   // Undelay spawn
   clientGame->undelaySpawn();         // This is what gets run when player presses a key
   gamePair.idle(10, 5);               // Idle 5x; give things time to propagate

   ASSERT_FALSE(serverGame->getClientInfo(0)->isSpawnDelayed());     // Player should no longer be spawn delayed
   ASSERT_FALSE(clientGame->inReturnToGameCountdown());
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    // Ship should have spawned and be available on client and server
   fillVector.clear();
   clientGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    // Ship should have spawned and be available on client and server
}
   // This one is a little more involved, as we will be simulating an entire editor session, including level testing
   TEST(ObjectCleanupTests, TestEditorObjectManagement)
   {
      GameSettingsPtr settings = GameSettingsPtr(new GameSettings());
      settings->setSetting(IniKey::Nickname, string("Alfonso"));     // Set this to bypass startup screen
      settings->getFolderManager()->setLevelDir("levels");           // Need something for hosting to work
      settings->updatePlayerName("Alfonso");

      Address addr;
      ClientGame *clientGame = new ClientGame(addr, settings, new UIManager());    // ClientGame destructor will clean up UIManager


      GameManager::addClientGame(clientGame);
      UIManager *uiMgr = clientGame->getUIManager();

      uiMgr->activate<MainMenuUserInterface>();
      ASSERT_TRUE(uiMgr->isCurrentUI<MainMenuUserInterface>());
      // Cheat a little; go directly to editor
      uiMgr->getUI<EditorUserInterface>()->setLevelFileName("xyzzy");      // Reset this so we get the level entry screen
      uiMgr->activate<EditorUserInterface>();

      ASSERT_TRUE(uiMgr->isCurrentUI<EditorUserInterface>());
      EditorUserInterface *editor = uiMgr->getUI<EditorUserInterface>();
      ///// Test basic object deletion, undo system not activated
      SafePtr<TestItem> testItem = new TestItem();    // To track deletion... see "Basics" test above
      ASSERT_FALSE(testItem.isNull()) << "Just created this!";
      editor->addToEditor(testItem.getPointer());
      editor->deleteItem(0, false);                   // Low level method doesn't save undo state; object should be deleted
      ASSERT_TRUE(testItem.isNull()) << "Obj should be gone!";

      ///// Check deleting object with undo system -- do things get deleted as expected?
      testItem = new TestItem();
      ASSERT_FALSE(testItem.isNull()) << "Just created this -- shouldn't be gone yet!";
      editor->addToEditor(testItem.getPointer());
      testItem->setSelected(true);
      ASSERT_EQ(1, editor->getLevel()->findObjects_fast()->size()) << "Started with one object";
      editor->handleKeyPress(KEY_Z, "Del");     // Triggers deleteSelection(false)
      ASSERT_TRUE(testItem.isNull()) << "Obj should be gone!";
      ASSERT_EQ(0, editor->getLevel()->findObjects_fast()->size()) << "All objects deleted... none should be present";
      editor->handleKeyPress(KEY_Z, editor->getEditorBindingString(BINDING_UNDO_ACTION));  // Undo
      ASSERT_EQ(1, editor->getLevel()->findObjects_fast()->size()) << "Undeleted one object";
      ASSERT_FALSE(testItem.isValid());
      testItem = dynamic_cast<TestItem *>(editor->getLevel()->findObjects_fast()->get(0));
      ASSERT_TRUE(testItem.isValid());
      editor->handleKeyPress(KEY_Z, editor->getEditorBindingString(BINDING_REDO_ACTION));
      ASSERT_EQ(0, editor->getLevel()->findObjects_fast()->size()) << "Redid delete... none should be present";
      ASSERT_FALSE(testItem.isValid());

      ///// Test level
      Level *level = editor->getLevel();     // Keep track of the level we were editing
      editor->testLevelStart();
      ASSERT_TRUE(uiMgr->isCurrentUI<EditorUserInterface>()) << "Still in editor";
      ASSERT_TRUE(editor->getLevel()->getGameType()) << "Have valid GameType";
      GameType *gt = editor->getLevel()->getGameType();

      GameManager::getServerGame()->loadNextLevelInfo();
      ASSERT_EQ(GameManager::DoneLoadingLevels, GameManager::getHostingModePhase()) << "Only loading one level here...";
      ASSERT_TRUE(GameManager::hostGame()) << "Failure to host game!";
      ASSERT_TRUE(uiMgr->isCurrentUI<GameUserInterface>()) << "In game UI";
      GameManager::localClientQuits(GameManager::getClientGames()->get(0));
      ASSERT_TRUE(uiMgr->isCurrentUI<EditorUserInterface>()) << "Back to the editor";
      ASSERT_EQ(editor, uiMgr->getCurrentUI()) << "Expect same object";
      ASSERT_EQ(level, editor->getLevel()) << "Level should not have changed";
      ASSERT_TRUE(editor->getLevel()->getGameType()) << "Expect valid GameType";
      ASSERT_EQ(gt, editor->getLevel()->getGameType()) << "GameType should not have changed";
   }
Beispiel #13
0
TEST(ObjectScopeTest, TestItemPropagation)
{
   for(S32 i = 0; i < testInfos.size(); i++)
   {
      // Create a GamePair using our text item code, with 2 clients; one will be red, the other blue.
      // The test will confirm which players get the red text item, the blue line, and the zone object.
      string levelCode = getLevelCodeForItemPropagationTests(testInfos[i].itemToTest);

      GamePair gamePair(levelCode, 2);

      // First, ensure we have two players, one red, one blue
      ServerGame *serverGame = gamePair.server;

      ASSERT_EQ(2, serverGame->getPlayerCount());
      ASSERT_EQ(Colors::blue.toHexString(), serverGame->getTeamColor(0).toHexString());
      ASSERT_EQ(Colors::red.toHexString(), serverGame->getTeamColor(1).toHexString());

      // Do the following rigamarole to break dependency on assumption client0 is blue and client1 is red
      ClientGame *client0 = gamePair.getClient(0);
      ClientGame *client1 = gamePair.getClient(1);

      ClientGame *blue = (serverGame->getTeamColor(client0->getLocalRemoteClientInfo()->getTeamIndex()).toHexString() ==
         Colors::blue.toHexString()) ? client0 : client1;

      ClientGame *red = (serverGame->getTeamColor(client0->getLocalRemoteClientInfo()->getTeamIndex()).toHexString() ==
         Colors::red.toHexString()) ? client0 : client1;

      ASSERT_EQ(Colors::blue.toHexString(), serverGame->getTeamColor(blue->getLocalRemoteClientInfo()->getTeamIndex()).toHexString());
      ASSERT_EQ(Colors::red.toHexString(), serverGame->getTeamColor(red->getLocalRemoteClientInfo()->getTeamIndex()).toHexString());

      ASSERT_FALSE(serverGame->getClientInfos()->get(0)->getConnection()->mInCommanderMap);
      ASSERT_FALSE(serverGame->getClientInfos()->get(1)->getConnection()->mInCommanderMap);

      // Now that we know which client is which, we can check to see which objects are available where
      {
         SCOPED_TRACE("Not in CommandersMap // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].startingCondition);
      }

      // Turn on cmdrs map... should not affect results
      red->setUsingCommandersMap(true);
      blue->setUsingCommandersMap(true);

      gamePair.idle(10, 5);     // Let things settle

      ASSERT_TRUE(serverGame->getClientInfos()->get(0)->getConnection()->mInCommanderMap);
      ASSERT_TRUE(serverGame->getClientInfos()->get(1)->getConnection()->mInCommanderMap);
      {
         SCOPED_TRACE("In CommandersMap // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].startingCondition);
      }

      Vector<DatabaseObject *> fillVector;

      // Change the textitem from red to blue
      serverGame->getLevel()->findObjects(testInfos[i].objectTypeNumber, fillVector);
      ASSERT_EQ(1, fillVector.size());
      BfObject *obj = static_cast<BfObject *>(fillVector[0]);
      obj->setTeam(0);     // Blue team

      gamePair.idle(10, 5);     // Let things settle
      {
         SCOPED_TRACE("Item Moved To Blue // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].itemMovedToBlue);
      }
      obj->setTeam(1);          // Back to red

      gamePair.idle(10, 5);     // Let things settle
      {
         SCOPED_TRACE("Item Moved To Red // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].itemMovedToRed);
      }

      // Now change the player's team from blue to red
      blue->changeOwnTeam(1);
      EXPECT_EQ(0, blue->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 0 (change hasn't propagated yet)";
      gamePair.idle(10, 5);     // Let things settle
      EXPECT_EQ(1, blue->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 1 (change should have propagated)";

      EXPECT_EQ(1, serverGame->getClientInfos()->get(0)->getTeamIndex());
      EXPECT_EQ(1, serverGame->getClientInfos()->get(1)->getTeamIndex());
      {
         SCOPED_TRACE("Blue Player Moved To Red, Items On Red // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].playersMovedToRed);
      }

      // And put both players on blue
      blue->changeOwnTeam(0);
      red->changeOwnTeam(0);

      EXPECT_EQ(1, blue->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 1 (change hasn't propagated yet)";
      EXPECT_EQ(1, red->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 1 (change hasn't propagated yet)";
      gamePair.idle(10, 5);     // Let things settle
      EXPECT_EQ(0, blue->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 0 (change should have propagated)";
      EXPECT_EQ(0, blue->getLocalRemoteClientInfo()->getTeamIndex()) << "Expect this client to be on team 0 (change should have propagated)";

      EXPECT_EQ(0, serverGame->getClientInfos()->get(0)->getTeamIndex());
      EXPECT_EQ(0, serverGame->getClientInfos()->get(1)->getTeamIndex());
      {
         SCOPED_TRACE("Both Players Moved To Blue, Items On Red // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].playersMovedToBlue);
      }

      // Make items neutral and see if they propagate properly
      fillVector.clear();
      serverGame->getLevel()->findObjects(testInfos[i].objectTypeNumber, fillVector);
      ASSERT_EQ(1, fillVector.size());
      for(S32 j = 0; j < fillVector.size(); j++)
         static_cast<BfObject *>(fillVector[j])->setTeam(TEAM_NEUTRAL);
      gamePair.idle(10, 5);     // Let things settle
      {
         SCOPED_TRACE("Items On Neutral // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].itemMovedToNeutral);
      }

      for(S32 j = 0; j < fillVector.size(); j++)
         static_cast<BfObject *>(fillVector[j])->setTeam(TEAM_HOSTILE);
      gamePair.idle(10, 5);     // Let things settle
      {
         SCOPED_TRACE("Items On Hostile // " + testInfos[i].itemToTest);
         testScope(serverGame, blue, red, testInfos[i], testInfos[i].itemsMovedToHostile);
      }
   }
}
TEST(LevelMenuSelectUserInterfaceTests, GetIndexOfNext)
{
   ClientGame *game = newClientGame();

   // Want to test getIndexOfNext(), which is a slightly complex function.  Need to start by setting up a menu.
   LevelMenuSelectUserInterface *ui = game->getUIManager()->getUI<LevelMenuSelectUserInterface>();      // Cleaned up when game goes out of scope

   // These should be alphabetically sorted
   ui->addMenuItem(new MenuItem("Aardvark"));    //  0
   ui->addMenuItem(new MenuItem("Assinine"));    //  1
   ui->addMenuItem(new MenuItem("Bouy"));        //  2
   ui->addMenuItem(new MenuItem("Boy"));         //  3
   ui->addMenuItem(new MenuItem("C"));           //  4
   ui->addMenuItem(new MenuItem("Cat"));         //  5
   ui->addMenuItem(new MenuItem("Cc"));          //  6
   ui->addMenuItem(new MenuItem("Chop"));        //  7
   ui->addMenuItem(new MenuItem("Chump"));       //  8
   ui->addMenuItem(new MenuItem("Dog"));         //  9
   ui->addMenuItem(new MenuItem("Doug"));        // 10
   ui->addMenuItem(new MenuItem("Eat"));         // 11
   ui->addMenuItem(new MenuItem("Eating"));      // 12
   ui->addMenuItem(new MenuItem("Eel"));         // 13
   ui->addMenuItem(new MenuItem("Eels"));        // 14
   ui->addMenuItem(new MenuItem("Eggs"));        // 15


   // Some random checks
   ui->selectedIndex = 1;
   ASSERT_EQ(ui->getIndexOfNext("a"), 0);
   ASSERT_EQ(ui->getIndexOfNext("boy"), 3);
   ASSERT_EQ(ui->getIndexOfNext("c"), 4);
   ASSERT_EQ(ui->getIndexOfNext("ch"), 7);
   ASSERT_EQ(ui->getIndexOfNext("cho"), 7);
   ASSERT_EQ(ui->getIndexOfNext("chop"), 7);

   // Check cycling of the Cs
   ui->selectedIndex = 3;
   ASSERT_EQ(ui->getIndexOfNext("c"), 4);
   ui->selectedIndex = 4;
   ASSERT_EQ(ui->getIndexOfNext("c"), 5);
   ui->selectedIndex = 5;
   ASSERT_EQ(ui->getIndexOfNext("c"), 6);
   ui->selectedIndex = 6;
   ASSERT_EQ(ui->getIndexOfNext("c"), 7);
   ui->selectedIndex = 7;
   ASSERT_EQ(ui->getIndexOfNext("c"), 8);
   ui->selectedIndex = 8;
   ASSERT_EQ(ui->getIndexOfNext("c"), 4);

   // Check wrapping
   ui->selectedIndex = 9;
   ASSERT_EQ(ui->getIndexOfNext("a"), 0);
   ui->selectedIndex = 15;     // last item
   ASSERT_EQ(ui->getIndexOfNext("a"), 0);

   // Check repeated hammering on current item
   ui->selectedIndex = 12;
   ASSERT_EQ(ui->getIndexOfNext("e"), 13);    // Single letter advances to next of that letter
   ASSERT_EQ(ui->getIndexOfNext("ea"), 12);
   ASSERT_EQ(ui->getIndexOfNext("eat"), 12);
   ASSERT_EQ(ui->getIndexOfNext("eati"), 12);
   ASSERT_EQ(ui->getIndexOfNext("eatin"), 12);
   ASSERT_EQ(ui->getIndexOfNext("eating"), 12);

   // Check for not found items -- should return current index
   ASSERT_EQ(ui->getIndexOfNext("eatingx"), 12); 
   ASSERT_EQ(ui->getIndexOfNext("flummoxed"), 12); 

   ui->selectedIndex = 8;
   ASSERT_EQ(ui->getIndexOfNext("chop"), 7);

   delete game;
}
// The spawnDelay mechansim is complex and interacts with other weird things like levelUp messages and server suspension
TEST(SpawnDelayTest, SpawnDelayTests)
{
   GamePair gamePair;     // An empty level should work fine here
   ClientGame *clientGame = gamePair.getClient(0);
   ServerGame *serverGame = gamePair.server;

   ASSERT_TRUE(serverGame->getGameType()) << "Expect a GameType by now!";

   // Idle for a while, let things settle
   gamePair.idle(10, 5);

   ASSERT_TRUE(clientGame->getGameType()) << "Expect a GameType by now!";

   Vector<DatabaseObject *> fillVector;

   // Ship should have spawned by now... check for it on the client and server
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    // Ship should have spawned by now

   Ship *ship = static_cast<Ship *>(fillVector[0]);      // Server's copy of the ship

   fillVector.clear();
   clientGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    // And it should be on the client, too

   ASSERT_FALSE(clientGame->isSpawnDelayed());     // Should not be spawn-delayed at this point

   killShip(ship);

   gamePair.idle(10, 5);      // 5 cycles

   // Ship should have spawned by now... check for it on the client and server
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    //  Ship should have respawned and be available on the server...
   fillVector.clear();
   clientGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());    // ...and also on the client

   // Scenario 1: Player is idle and gets spawn delayed -- no other players in game
   // TODO: Also need a scenario where palyer goes idle and gets spawn delayed, but game does not get suspended due to other players
   doScenario1(gamePair);

   // Scenario 2: Player enters idle command, other players, so server does not suspend itself.  Since
   // player used idle command, a 5 second penalty will be levied against them.
   doScenario2(gamePair);

   // Scenarios 3 & 4 -- Player enters /idle command, no other players, so server suspends itself partially (3) or fully (4)
   // See https://code.google.com/p/googletest/wiki/AdvancedGuide#Using_Assertions_in_Sub-routines for details on this technique
   {
      SCOPED_TRACE("Scenario 3, letGameSlipIntoFullSuspendMode is false");
      doScenario34(gamePair, false);
   }
   {
      SCOPED_TRACE("Scenario 4, letGameSlipIntoFullSuspendMode is true");
      doScenario34(gamePair, true);
   }

   // Scenario 5 -- Player enters idle when in punishment delay period for pervious /idle command
   doScenario5(gamePair);  // Not complete

   // Scenario 6 -- Player is shown a new levelup screen
   doScenario6(gamePair);  // Not complete

   // Scenario 7 -- Player is shown a levelup screen they have already seen
   doScenario7(gamePair);  // Not complete

   // Scenario 8 -- Player is shown (new) levelup screen while they are idle due to inaction

   // Scenario 9 -- Player is shown (new) levelup screen while they are idle due to idle command

   // Scenario 10 -- Player is shown (new) levelup screen while they are in penalty phase of idle command

   // Scenario 11 -- Player is changing their loadout when the level changes.  This triggers a delayed spawn.
   doScenario11(gamePair);
}
TEST(GameUserInterfaceTest, Engineer)
{
   InputCodeManager::initializeKeyNames();  

   GamePair gamePair(getLevelCodeForTestingEngineer1(), 3); // See def for description of level
   ServerGame *serverGame                  = GameManager::getServerGame();
   const Vector<ClientGame *> *clientGames = GameManager::getClientGames();
   GameSettings *clientSettings            = clientGames->first()->getSettings();
   GameUserInterface *gameUI               = clientGames->first()->getUIManager()->getUI<GameUserInterface>();

   DEFINE_KEYS_AND_EVENTS(clientSettings);

   // Idle for a while, let things settle
   GamePair::idle(10, 5);

   // Verify that engineer is enabled
   ASSERT_TRUE(serverGame->getGameType()->isEngineerEnabled()) << "Engineer should be enabled on server!";

   for(S32 i = 0; i < clientGames->size(); i++)
   {
      SCOPED_TRACE("i = " + itos(i));
      ASSERT_TRUE(clientGames->get(i)->getGameType())                      << "Clients should have a GameType by now!";
      ASSERT_TRUE(clientGames->get(i)->getGameType()->isEngineerEnabled()) << "Engineer mode not propagating to clients!";
   }

   ASSERT_TRUE(serverGame->getClientInfo(0)->getShip()->isInZone(LoadoutZoneTypeNumber)); // Check level is as we expected

   // Add engineer to current loadout
   ASSERT_FALSE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));
   gameUI->activateHelper(HelperMenu::LoadoutHelperType);
   ASSERT_TRUE(gameUI->isHelperActive(HelperMenu::LoadoutHelperType));

   gameUI->onKeyDown(LOADOUT_KEY_ENGR);      gameUI->onKeyDown(LOADOUT_KEY_REPAIR);                                           // First 2 modules...
   gameUI->onKeyDown(LOADOUT_KEY_PHASER);    gameUI->onKeyDown(LOADOUT_KEY_BOUNCE);    gameUI->onKeyDown(LOADOUT_KEY_TRIPLE); // ...then 3 weapons

   // On this level, the ship spawn is inside a loadout zone, so loadout should take effect immediately
   GamePair::idle(10, 5);
   ASSERT_EQ("Engineer,Repair,Phaser,Bouncer,Triple", serverGame->getClientInfo(0)->getShip()->getLoadoutString());
   for(S32 i = 0; i < clientGames->size(); i++)
   {
      SCOPED_TRACE("i = " + itos(i));
      ASSERT_EQ("Engineer,Repair,Phaser,Bouncer,Triple", clientGames->get(0)->getLocalPlayerShip()->getLoadoutString());
   }

   // Fly down to pick up resource item -- to get flying to work, need to create events at a very basic level
   ClientGame *clientGame = clientGames->get(0);
   Point startPos = clientGame->getLocalPlayerShip()->getActualPos();
   Event::onEvent(clientGame, &EventDownPressed);
   GamePair::idle(100, 5);
   Event::onEvent(clientGame, &EventDownReleased);
   Point endPos = clientGame->getLocalPlayerShip()->getActualPos();
   ASSERT_TRUE(startPos.distSquared(endPos) > 0) << "Ship did not move!!";
   for(S32 i = 0; i < clientGames->size(); i++)
   {
      SCOPED_TRACE("i = " + itos(i));
      ASSERT_TRUE(clientGames->get(0)->getLocalPlayerShip()->isCarryingItem(ResourceItemTypeNumber));
   }

   // Time to engineer!
   gameUI->onKeyDown(KEY_MOD1);
   ASSERT_TRUE(gameUI->isHelperActive(HelperMenu::EngineerHelperType)) << "Expect engineer helper menu to be active!";

   InputCode key = EngineerHelper::getInputCodeForOption(EngineeredTeleporterEntrance, true);
   gameUI->onKeyDown(key);
   ASSERT_FALSE(static_cast<const EngineerHelper *>(gameUI->getActiveHelper())->isMenuBeingDisplayed());
   gameUI->onKeyDown(KEY_MOD1);     // Place entrance
   gameUI->onKeyDown(KEY_MOD1);     // Place exit
   GamePair::idle(100, 5);           // Let things mellow

   for(S32 i = 0; i < clientGames->size(); i++)
   {
      SCOPED_TRACE("i = " + itos(i));
      Vector<DatabaseObject *> fillVector;
      clientGames->get(i)->getLevel()->findObjects(TeleporterTypeNumber, fillVector);
      EXPECT_EQ(1, fillVector.size()) << "Expected a teleporter!";
   }
}
// Scenario 3, 4 -- Player enters /idle command, no other players, so server suspends itself
// In this case, no returnToGame penalty should be levied
// In this scenario 3, player un-idles during the suspend game timer countdown (there is a 2 second delay after all players are idle)
// In scenario 4, player enters full suspend mode before unidling
static void doScenario34(GamePair &gamePair, bool letGameSlipIntoFullSuspendMode)
{
   ClientGame *clientGame = gamePair.getClient(0);
   ServerGame *serverGame = gamePair.server;

   // Make sure we start off in a "normal" state
   ASSERT_FALSE(serverGame->isOrIsAboutToBeSuspended());
   ASSERT_FALSE(clientGame->isSpawnDelayed());         

   gamePair.idle(Ship::KillDeleteDelay / 15, 20);     // Idle; give things time to propagate
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());                   // Now that player 2 has left, should only be one ship
   fillVector.clear();
   clientGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());

   Vector<string> words;
   ChatCommands::idleHandler(clientGame, words);
   gamePair.idle(10, 10);     // Idle; give things time to propagate, timers to time out, etc.
   ASSERT_TRUE(serverGame->getClientInfo(0)->isSpawnDelayed());
   ASSERT_TRUE(serverGame->isOrIsAboutToBeSuspended());
   EXPECT_FALSE(serverGame->isSuspended());
   ASSERT_TRUE(clientGame->isSpawnDelayed());         // Status should have propagated to client by now
   EXPECT_FALSE(clientGame->isSuspended());

   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(0, fillVector.size());                   // No ships remaining in game -- don't check client as it may have exploding ship there
   fillVector.clear();

   // ReturnToGame penalty has been set, but won't start to count down until ship attempts to spawn
   ASSERT_FALSE(clientGame->inReturnToGameCountdown());
   ASSERT_TRUE(static_cast<FullClientInfo *>(serverGame->getClientInfo(0))->hasReturnToGamePenalty()); // Penalty has been primed
   ASSERT_EQ(0, serverGame->getClientInfo(0)->getReturnToGameTime());

   if(letGameSlipIntoFullSuspendMode)
		gamePair.idle(ServerGame::PreSuspendSettlingPeriod / 20, 25);

   // Player presses a key to rejoin the game; since game was suspended, player can resume without penalty
   ASSERT_TRUE(serverGame->isOrIsAboutToBeSuspended()) << "Game should be suspended";

   if(letGameSlipIntoFullSuspendMode)
   {
      // In here, game is suspended

      EXPECT_TRUE(serverGame->isSuspended());
      EXPECT_TRUE(clientGame->isSuspended());

      // Check if server clocks are still counting down... should be stopped
      S32 snow = serverGame->getGameType()->getRemainingGameTimeInMs();
      S32 cnow = clientGame->getGameType()->getRemainingGameTimeInMs();
      gamePair.idle(10, 10);
      S32 slater = serverGame->getGameType()->getRemainingGameTimeInMs();
      S32 clater = clientGame->getGameType()->getRemainingGameTimeInMs();
      EXPECT_EQ(snow, slater) << "Looks like server clock is still running (should be stopped)!";
      EXPECT_EQ(cnow, clater) << "Looks like client clock is still running (should be stopped)!";
   }
   else
   {
      // In here, game is not (yet) suspended, but is in the 2 second cooldown period that comes after 
      // all players have left or are idle, but before full suspension

      EXPECT_FALSE(serverGame->isSuspended());
      EXPECT_FALSE(clientGame->isSuspended());

      // Check if clocks are still counting down... should be still running
      S32 snow = serverGame->getGameType()->getRemainingGameTimeInMs();
      S32 cnow = clientGame->getGameType()->getRemainingGameTimeInMs();
      gamePair.idle(10, 10);
      S32 slater = serverGame->getGameType()->getRemainingGameTimeInMs();
      S32 clater = clientGame->getGameType()->getRemainingGameTimeInMs();
      EXPECT_NE(snow, slater) << "Looks like server clock is stopped (should be running)!";
      EXPECT_NE(cnow, clater) << "Looks like client clock is stopped (should be running)!";
   }

   clientGame->undelaySpawn();                                          // Simulate effects of key press
   gamePair.idle(10, 5);                                                // Idle; give things time to propagate
   ASSERT_FALSE(serverGame->isOrIsAboutToBeSuspended());

   ASSERT_EQ(0, serverGame->getClientInfo(0)->getReturnToGameTime());   // No returnToGame penalty
   ASSERT_FALSE(clientGame->inReturnToGameCountdown());

   gamePair.idle(Ship::KillDeleteDelay / 15, 20);     // Idle; give dead ships time to be cleaned up

   // Check to ensure ship spawned
   fillVector.clear();
   serverGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());   
   fillVector.clear();
   clientGame->getLevel()->findObjects(PlayerShipTypeNumber, fillVector);
   ASSERT_EQ(1, fillVector.size());

   ASSERT_FALSE(serverGame->isOrIsAboutToBeSuspended());
   ASSERT_FALSE(clientGame->isSpawnDelayed());         
}