mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-02-19 16:44:30 -07:00
607 lines
17 KiB
C++
607 lines
17 KiB
C++
#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<std::string> 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<std::string> 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::PlayerReady> GameForm::GetPlayerReadies() {
|
|
std::vector<PlayerReady> 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<PlayerReady> oldreadies = GetPlayerReadies();
|
|
gplrm.Init((PlayersUpdateNetMessage *)pnm);
|
|
std::vector<PlayerReady> 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
|