// 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)); }
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(); }
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; }
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); }
// 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(); }
int main(int, char **) { try { ClientGame game; game.run(); return (0); } catch (std::exception &e) { std::cerr << e.what() << std::endl; return (1); } }
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; }
// 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; }
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"; }
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()); }