#include "ht.h" #include "game/gameform.h" #include "game/multiplayer.h" #include "mpshared/messages.h" #include "game/httppackmanager.h" #include "base/format.h" namespace wi { GameForm::GameForm(LoginHandler& handler, const GameInfo& info, bool creator, Chatter& chatter) : handler_(handler), info_(info), creator_(creator), chatter_(chatter), joined_(false) { chatter_.SignalOnBlink.connect(this, &GameForm::OnChatBlink); chatter_.SignalOnPlayers.connect(this, &GameForm::OnPlayers); } GameForm::~GameForm() { chatter_.SignalOnBlink.disconnect(this); chatter_.SignalOnPlayers.disconnect(this); chatter_.HideChat(); } void GameForm::ShowJoinMessage(dword result) { char *message = NULL; switch (result) { case knGameJoinResultSuccess: break; case knGameJoinResultFail: default: message = "Error joining this game."; break; case knGameJoinResultRoomNotFound: message = "This game room no longer available."; break; case knGameJoinResultGameNotFound: message = "This game is no longer available."; break; case knGameJoinResultGameFull: message = "This game is full."; break; case knGameJoinResultInProgress: message = "This game is already in progress."; break; } if (message != NULL) { HtMessageBox(kfMbWhiteBorder, "Join Game", message); } } dword GameForm::DoForm(LoginHandler& handler, const GameInfo& info, bool creator, Chatter& chatter) { if (gptra == NULL) { return knGameStartResultFail; } GameForm *pfrm = (GameForm *)gpmfrmm->LoadForm(gpiniForms, kidfGameStart, new GameForm(handler, info, creator, chatter)); if (pfrm == NULL) { return knGameStartResultFail; } int result = 0; pfrm->DoModal(&result); delete pfrm; if (result == kidcCancel) { return knGameStartResultDone; } if (result == kidcOk) { return knGameStartResultStart; } return knGameStartResultFail; } bool GameForm::DoModal(int *presult) { if (creator_) { ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); pbtn->SetText("BEGIN GAME"); LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameName); char name[kcbPlayerName]; handler_.GetPlayerName(name, sizeof(name)); plbl->SetText(base::Format::ToString("%s's game", name)); } // min/max players LabelControl *plbl = (LabelControl *)GetControlPtr(kidcMapName); plbl->SetText(info_.title); plbl = (LabelControl *)GetControlPtr(kidcNumPlayers); if (info_.minplayers == info_.maxplayers) { plbl->SetText(base::Format::ToString("%d", info_.minplayers)); } else { plbl->SetText(base::Format::ToString("%d-%d", info_.minplayers, info_.maxplayers)); } // Game speed char szT[16]; GetSpeedMultiplierString(szT, info_.params.tGameSpeed); plbl = (LabelControl *)GetControlPtr(kidcGameSpeedLabel); plbl->SetText(szT); // If not creator, state readiness any time if (!creator_) { ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); pbtn->Show(true); } // Join the game and bring up the form. Show(true); gptra->SetCallback(this); gptra->SetGameCallback(this); dword result = gptra->JoinGame(info_.gameid, info_.roomid); if (result != knGameJoinResultSuccess) { gptra->SetGameCallback(NULL); gptra->SetCallback(NULL); ShowJoinMessage(result); *presult = 0; return false; } joined_ = true; // Set up chat chatter_.SetChatTitle("Game Chat"); chatter_.AddChat("", base::Format::ToString("%s created game %s.", info_.creator, info_.GetDescription()), true); *presult = 0; bool success = ShellForm::DoModal(presult); if (*presult != kidcOk && joined_) { gptra->LeaveGame(); } gptra->SetGameCallback(NULL); gptra->SetCallback(NULL); return success; } void GameForm::OnChatBlink(bool on) { GetControlPtr(kidcChat)->Show(on); } void GameForm::OnPlayers() { std::string s = GetPlayersString(); chatter_.AddChat("", s.c_str(), true); } const char *GameForm::GetColorName(int side) { switch (side) { case 0: return "gray"; case 1: return "blue"; case 2: return "red"; case 3: return "yellow"; case 4: return "cyan"; } return "unknown"; } std::string GameForm::GetPlayersPlayingString() { // Extract the player names from Player objects. Make this player // player 0. std::vector names; names.push_back(base::Format::ToString("%s (%s)", gpplrLocal->GetName(), GetColorName(gpplrLocal->GetSide()))); Player *pplr = NULL; while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { if (pplr->GetFlags() & kfPlrUnfulfilled) { continue; } if (pplr == gpplrLocal) { continue; } names.push_back(base::Format::ToString("%s (%s)", pplr->GetName(), GetColorName(pplr->GetSide()))); } std::string s; if (gpplrLocal->GetFlags() & kfPlrObserver) { s = base::Format::ToString("You are %s and are observing. ", GetColorName(gpplrLocal->GetSide())); } else { s = base::Format::ToString("You are %s. ", GetColorName(gpplrLocal->GetSide())); } if (names.size() == 1) { s += "No other players are in this game."; return s; } if (names.size() == 2) { s += base::Format::ToString("%s is also in this game.", names[1].c_str()); return s; } for (int i = 1; i < names.size(); i++) { if (i != names.size() - 1) { s += base::Format::ToString("%s, ", names[i].c_str()); } else { s += base::Format::ToString("and %s ", names[i].c_str()); } } s += "are also in this game."; return s; } std::string GameForm::GetPlayersObservingString() { // Collect observing players. There are none if game is just starting // however this static method is also called in-game. Player *pplr = NULL; std::vector observers; while ((pplr = gplrm.GetNextObservingPlayer(pplr)) != NULL) { if (pplr->GetFlags() & kfPlrUnfulfilled) { continue; } if (pplr == gpplrLocal) { continue; } observers.push_back(base::Format::ToString("%s (%s)", pplr->GetName(), GetColorName(pplr->GetSide()))); } if (observers.size() == 0) { return ""; } if (observers.size() == 1) { return observers[0] + " is observing."; } std::string s; for (int i = 0; i < observers.size(); i++) { if (i != observers.size() - 1) { s += base::Format::ToString("%s, ", observers[i].c_str()); } else { s += base::Format::ToString("and %s ", observers[i].c_str()); } } return s + "are observing."; } std::string GameForm::GetPlayersString() { std::string playing = GetPlayersPlayingString(); std::string observing = GetPlayersObservingString(); if (observing.size() == 0) { return playing; } return playing + " " + observing; } void GameForm::OnControlSelected(word idc) { switch (idc) { // "Ready"/"Begin Game" button has been pressed case kidcOk: OnReadyBeginGame(); break; case kidcCancel: EndForm(kidcCancel); break; case kidcChat: chatter_.ShowChat(); break; } } void GameForm::OnReadyBeginGame() { // Players only need to say they're ready once ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); pbtn->Show(false); if (creator_) { // The server may fail this, // because a player may drop out at the last second, so // this dialog isn't dismissed until the ScBeginGame // net message is received. NetMessage nmsg(knmidCsRequestBeginGame); gptra->SendNetMessage(&nmsg); return; } // Let the server know this player is ready NetMessage nmsg(knmidCsClientReady); gptra->SendNetMessage(&nmsg); } void GameForm::OnReceiveChat(const char *player, const char *chat) { chatter_.AddChat(player, chat, false); } void GameForm::OnNetMessage(NetMessage **ppnm) { NetMessage *pnm = *ppnm; switch (pnm->nmid) { case knmidScBeginGameFail: OnBeginGameFail(); break; case knmidScPlayersUpdate: OnPlayersUpdate((PlayersUpdateNetMessage *)pnm); break; case knmidScGameParams: OnGameParams((GameParamsNetMessage *)pnm); break; case knmidScBeginGame: SetRandomSeed(((BeginGameNetMessage *)pnm)->ulRandomSeed); EndForm(kidcOk); break; case knmidScCantAcceptMoreConnections: HtMessageBox(kfMbWhiteBorder, "Game Full", "Sorry, the requested game is full and can't accept any more players."); EndForm(kidcCancel); break; } } void GameForm::OnBeginGameFail() { // Beginning the game failed; show the Begin Game button again. // This message rarely happens, in the central server // case where a game is requested to begin, but the server // fails the request (a player left the game at the last // second). ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); pbtn->Show(true); } std::vector GameForm::GetPlayerReadies() { std::vector readies; Player *pplr = NULL; while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { if (pplr->GetFlags() & (kfPlrUnfulfilled | kfPlrComputer)) { continue; } PlayerReady rdy; strcpy(rdy.szName, pplr->GetName()); rdy.side = pplr->GetSide(); rdy.fReady = (pplr->GetFlags() & kfPlrReady) != 0; rdy.fLocal = (pplr == gpplrLocal); readies.push_back(rdy); } return readies; } void GameForm::OnPlayersUpdate(PlayersUpdateNetMessage *pnm) { std::vector oldreadies = GetPlayerReadies(); gplrm.Init((PlayersUpdateNetMessage *)pnm); std::vector newreadies = GetPlayerReadies(); // Announce joins and leaves bool sfx = false; for (int i = 0; i < newreadies.size(); i++) { bool found = false; for (int j = 0; j < oldreadies.size(); j++) { if (strcmp(newreadies[i].szName, oldreadies[j].szName) == 0) { found = true; break; } } if (!found) { chatter_.AddChat("", base::Format::ToString( "%s (%s) joined this game.", newreadies[i].szName, GetColorName(newreadies[i].side)), true); // sfx on add if it isn't this player if (!newreadies[i].fLocal) { sfx = true; } } } for (int j = 0; j < oldreadies.size(); j++) { bool found = false; for (int i = 0; i < newreadies.size(); i++) { if (strcmp(newreadies[i].szName, oldreadies[j].szName) == 0) { found = true; break; } } if (!found) { chatter_.AddChat("", base::Format::ToString( "%s (%s) left this game.", oldreadies[j].szName, GetColorName(oldreadies[j].side)), true); sfx = true; } } if (sfx) { gsndm.PlaySfx(ksfxGuiScrollingListSelectItem); } // Announce ready transitions from not-ready to ready for (int i = 0; i < newreadies.size(); i++) { bool found = false; bool ready = false; for (int j = 0; j < oldreadies.size(); j++) { if (strcmp(newreadies[i].szName, oldreadies[j].szName) == 0) { if (!oldreadies[j].fReady && newreadies[i].fReady) { ready = true; break; } found = true; } } if (!found && newreadies[i].fReady) { ready = true; } if (ready) { chatter_.AddChat("", base::Format::ToString("%s (%s) is READY.", newreadies[i].szName, GetColorName(newreadies[i].side)), true); } } RefreshPlayerList(); const char *pszCreator = gplrm.GetCreatorName(); if (pszCreator != NULL) { LabelControl *plbl = (LabelControl *)GetControlPtr(kidcGameName); plbl->SetText(base::Format::ToString("%s's game", pszCreator)); } if (creator_) { ShowOrHideBeginGameButton(); } } void GameForm::OnGameParams(GameParamsNetMessage *pgpnm) { // Check to see if this client is compatible with this server, // and if this client has the same mission the server has if (!IsExpectedGameParams(pgpnm->rams)) { EndForm(kidcCancel); return; } // Join the game, NetMessage variety. This causes the server // to allocate a player for this client, and report back error // if there are no slots available. char name[kcbPlayerName]; handler_.GetPlayerName(name, sizeof(name)); PlayerJoinNetMessage pjnm; strncpyz(pjnm.szPlayerName, name, sizeof(pjnm.szPlayerName)); gptra->SendNetMessage(&pjnm); // Pretend the creator pressed the ready button if (creator_) { NetMessage nmsg(knmidCsClientReady); gptra->SendNetMessage(&nmsg); } } void GameForm::OnGameDisconnect() { chatter_.HideChat(); HtMessageBox(kfMbWhiteBorder, "Game Cancelled", "This game has been cancelled. Press OK to continue."); joined_ = false; EndForm(kidcCancel); } void GameForm::OnStatusUpdate(char *pszStatus) { // There's no status we care to display on this form } void GameForm::OnConnectionClose() { Event evt; memset(&evt, 0, sizeof(evt)); evt.idf = m_idf; evt.eType = connectionCloseEvent; gevm.PostEvent(&evt); } void GameForm::OnShowMessage(const char *message) { message_ = message; Event evt; memset(&evt, 0, sizeof(evt)); evt.idf = m_idf; evt.eType = showMessageEvent; gevm.PostEvent(&evt); } bool GameForm::OnFilterEvent(Event *pevt) { if (pevt->eType == connectionCloseEvent) { chatter_.HideChat(); HtMessageBox(kfMbWhiteBorder, "Comm Problem", "The server has closed your connection."); EndForm(kidcCancel); return true; } if (pevt->eType == showMessageEvent) { chatter_.HideChat(); HtMessageBox(kfMbWhiteBorder, "Server Message", message_.c_str()); message_ = ""; return true; } return false; } bool GameForm::IsExpectedGameParams(const GameParams& params) { // This is all really unnecessary. if (memcmp(¶ms.packid, &info_.params.packid, sizeof(PackId)) != 0) { return false; } if (params.dwVersionSimulation != info_.params.dwVersionSimulation) { return false; } if (params.tGameSpeed != info_.params.tGameSpeed) { return false; } if (strcmp(params.szLvlFilename, info_.params.szLvlFilename) != 0) { return false; } return true; } int GameForm::GetPlayerCountNeeded(bool fReady) { int cplrTotal = 0; int cplrReady = 0; Player *pplr = NULL; while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { word wf = pplr->GetFlags(); if ((wf & kfPlrUnfulfilled) != 0) { continue; } if (wf & kfPlrReady) { cplrReady++; } cplrTotal++; } int cNeeded; if (fReady) { cNeeded = info_.minplayers - cplrReady; } else { cNeeded = info_.minplayers - cplrTotal; } if (cNeeded <= 0) { return 0; } return cNeeded; } void GameForm::ShowOrHideBeginGameButton() { bool fHaveNeededPlayers = (GetPlayerCountNeeded(true) == 0); bool fAllPlayersReady = true; Player *pplr = NULL; while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { word wf = pplr->GetFlags(); if ((wf & kfPlrUnfulfilled) != 0) { continue; } if ((wf & kfPlrReady) == 0) { fAllPlayersReady = false; break; } } ButtonControl *pbtn = (ButtonControl *)GetControlPtr(kidcOk); pbtn->Show(fHaveNeededPlayers && fAllPlayersReady); } void GameForm::RefreshPlayerList() { ListControl *plstc = (ListControl *)GetControlPtr(kidcPlayerList); plstc->Clear(); Player *pplr = NULL; int cAdded = 0; while ((pplr = gplrm.GetNextHumanPlayer(pplr)) != NULL) { // Don't show unfulfilled players if (pplr->GetFlags() & kfPlrUnfulfilled) { continue; } char szT[100]; sprintf(szT, "%s (%s) is %s", pplr->GetName(), GetColorName(pplr->GetSide()), pplr->GetFlags() & kfPlrReady ? "READY" : "not ready"); plstc->Add(szT, pplr); cAdded++; } int cNeeded = GetPlayerCountNeeded(false); if (cNeeded != 0) { plstc->Add(base::Format::ToString("%d more player%s needed", cNeeded, cNeeded != 1 ? "s" : ""), NULL); } } } // namespace wi