mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
673 lines
19 KiB
C++
673 lines
19 KiB
C++
#include <algorithm>
|
|
#include "game/ht.h"
|
|
#include "game/gameform.h"
|
|
#include "game/creategameform.h"
|
|
#include "mpshared/messages.h"
|
|
|
|
namespace wi {
|
|
|
|
RoomForm::RoomForm(LoginHandler& handler, const RoomInfo& roominfo,
|
|
Chatter& chatter) : handler_(handler), roominfo_(roominfo),
|
|
chatter_(chatter), refresh_(true), creator_(false), status_(true) {
|
|
chatter_.SetChatTitle("Room Chat");
|
|
chatter_.SignalOnBlink.connect(this, &RoomForm::OnChatBlink);
|
|
chatter_.SignalOnPlayers.connect(this, &RoomForm::OnPlayers);
|
|
}
|
|
|
|
RoomForm::~RoomForm() {
|
|
chatter_.SignalOnBlink.disconnect(this);
|
|
chatter_.SignalOnPlayers.disconnect(this);
|
|
chatter_.HideChat();
|
|
}
|
|
|
|
void RoomForm::ShowJoinMessage(dword result) {
|
|
char *message = NULL;
|
|
switch (result) {
|
|
case knRoomJoinResultFail:
|
|
default:
|
|
message = "Could not join room.";
|
|
break;
|
|
|
|
case knRoomJoinResultFull:
|
|
message = "The room is full. Try again later.";
|
|
break;
|
|
|
|
case knRoomJoinResultNotFound:
|
|
message = "Can't find this room. Try another room.";
|
|
break;
|
|
|
|
case knRoomJoinResultWrongPassword:
|
|
message = "Incorrect password. Try again.";
|
|
break;
|
|
|
|
case knRoomJoinResultSuccess:
|
|
break;
|
|
}
|
|
if (message != NULL) {
|
|
HtMessageBox(kfMbWhiteBorder, "Join Room", message);
|
|
}
|
|
}
|
|
|
|
dword RoomForm::DoForm(LoginHandler& handler, const RoomInfo& roominfo,
|
|
Chatter& chatter, GameInfo *joininfo) {
|
|
|
|
if (gptra == NULL) {
|
|
return knRoomResultFail;
|
|
}
|
|
RoomForm *pfrm = (RoomForm *)gpmfrmm->LoadForm(gpiniForms, kidfRoom,
|
|
new RoomForm(handler, roominfo, chatter));
|
|
if (pfrm == NULL) {
|
|
return knRoomResultFail;
|
|
}
|
|
|
|
int result = 0;
|
|
pfrm->DoModal(&result);
|
|
*joininfo = pfrm->joininfo();
|
|
bool creator = pfrm->creator();
|
|
delete pfrm;
|
|
|
|
// Map result codes
|
|
if (result == kidcCancel) {
|
|
return knRoomResultDone;
|
|
}
|
|
if (result == kidcJoinGame) {
|
|
if (creator) {
|
|
return knRoomResultCreated;
|
|
} else {
|
|
return knRoomResultJoin;
|
|
}
|
|
}
|
|
return knRoomResultFail;
|
|
}
|
|
|
|
bool RoomForm::DoModal(int *presult) {
|
|
// Initialize controls
|
|
LabelControl *plbl = (LabelControl *)GetControlPtr(kidcRoomName);
|
|
plbl->SetText(roominfo_.name.c_str());
|
|
|
|
// Turn on ellipsis mode in list control
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList);
|
|
plstc->SetTabFlags(kfLstTabEllipsis);
|
|
|
|
// Hide Join Game until a game shows up
|
|
GetControlPtr(kidcJoinGame)->Show(false);
|
|
|
|
// Hide the list, and show the message, until a game shows up
|
|
plstc->Show(false);
|
|
GetControlPtr(kidcNoGames)->Show(true);
|
|
|
|
// Add this player to the list
|
|
char name[kcbPlayerName];
|
|
handler_.GetPlayerName(name, sizeof(name));
|
|
OnAddPlayer(name);
|
|
|
|
Show(true);
|
|
gptra->SetCallback(this);
|
|
dword result = gptra->JoinRoom(roominfo_.roomid,
|
|
roominfo_.password.c_str(), this);
|
|
if (result != knRoomJoinResultSuccess) {
|
|
gptra->SetCallback(NULL);
|
|
ShowJoinMessage(result);
|
|
*presult = 0;
|
|
return false;
|
|
}
|
|
*presult = 0;
|
|
bool success = ShellForm::DoModal(presult);
|
|
if (*presult == kidcJoinGame) {
|
|
gptra->LeaveRoom(knLeaveRoomHintJoinGame);
|
|
} else {
|
|
gptra->LeaveRoom(knLeaveRoomHintNone);
|
|
}
|
|
gptra->SetCallback(NULL);
|
|
|
|
return success;
|
|
}
|
|
|
|
void RoomForm::OnChatBlink(bool on) {
|
|
GetControlPtr(kidcChat)->Show(on);
|
|
}
|
|
|
|
void RoomForm::OnPlayers() {
|
|
std::string s = GetPlayersString();
|
|
chatter_.AddChat("", s.c_str(), true);
|
|
}
|
|
|
|
void RoomForm::OnControlSelected(word idc) {
|
|
switch (idc) {
|
|
case kidcNewGame:
|
|
OnCreateGame();
|
|
break;
|
|
|
|
case kidcJoinGame:
|
|
OnJoinGame();
|
|
break;
|
|
|
|
case kidcCancel:
|
|
EndForm(idc);
|
|
break;
|
|
|
|
case kidcChat:
|
|
chatter_.ShowChat();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnControlNotify(word idc, int nNotify) {
|
|
if (idc == kidcGameList && nNotify == knNotifySelectionChange) {
|
|
HideShowJoinGame();
|
|
}
|
|
ShellForm::OnControlNotify(idc, nNotify);
|
|
}
|
|
|
|
void RoomForm::OnJoinGame() {
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList);
|
|
dword gameid = (dword)(pword)plstc->GetSelectedItemData();
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it == map_.end()) {
|
|
return;
|
|
}
|
|
GameInfo& info = it->second;
|
|
if (!AskInstallMissionPack(&info.params.packid, "Join Game", info.title)) {
|
|
return;
|
|
}
|
|
InitiateJoinGame(info);
|
|
}
|
|
|
|
void RoomForm::OnCreateGame() {
|
|
GameParams params;
|
|
if (!CreateGameForm::DoForm(handler_, NULL, chatter_, ¶ms)) {
|
|
return;
|
|
}
|
|
|
|
while (true) {
|
|
dword gameid;
|
|
PackId packidUpgrade;
|
|
dword result = gptra->CreateGame(¶ms, &packidUpgrade, &gameid);
|
|
switch (result) {
|
|
case knRoomCreateGameResultSuccess:
|
|
{
|
|
// Update teh list and select the game, in case the join
|
|
// fails
|
|
creator_ = true;
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList);
|
|
refresh_ = false;
|
|
plstc->Select(FindIndex(gameid), true, true);
|
|
refresh_ = true;
|
|
|
|
// OnAddGame has been called already. Join the game
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it != map_.end()) {
|
|
InitiateJoinGame(it->second);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case knRoomCreateGameResultFail:
|
|
HtMessageBox(kfMbWhiteBorder, "Create Game",
|
|
"Unable to create the game.");
|
|
break;
|
|
|
|
case knRoomCreateGameResultUnknownMissionPack:
|
|
HtMessageBox(kfMbWhiteBorder, "Create Game",
|
|
"Unknown mission pack!");
|
|
break;
|
|
|
|
case knRoomCreateGameResultUpgradeMissionPack:
|
|
if (AskInstallMissionPack(&packidUpgrade, "Advertise Game",
|
|
NULL)) {
|
|
params.packid = packidUpgrade;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case knRoomCreateGameResultRoomFull:
|
|
HtMessageBox(kfMbWhiteBorder, "Create Game",
|
|
"Room full. Cannot create another game in this room.");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int RoomForm::FindIndex(dword gameid) {
|
|
int index = 0;
|
|
GameMap::iterator it = map_.begin();
|
|
for (; it != map_.end(); it++) {
|
|
if (it->first == gameid) {
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void RoomForm::InitiateJoinGame(const GameInfo& info) {
|
|
if (gptra == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Before joining, check to see if this game is joinable.
|
|
// Joining also checks, but this is a way to provide the user
|
|
// with feedback before exiting this form.
|
|
|
|
dword result = gptra->CanJoinGame(info.gameid);
|
|
GameForm::ShowJoinMessage(result);
|
|
if (result == knGameJoinResultSuccess) {
|
|
joininfo_ = info;
|
|
EndForm(kidcJoinGame);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnAddGame(const char *player, dword gameid,
|
|
const GameParams& params, dword minplayers, dword maxplayers,
|
|
const char *title, dword ctotal) {
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it != map_.end()) {
|
|
return;
|
|
}
|
|
GameInfo *info = new GameInfo;
|
|
if (info == NULL) {
|
|
return;
|
|
}
|
|
memset(info, 0, sizeof(*info));
|
|
info->roomid = roominfo_.roomid;
|
|
info->gameid = gameid;
|
|
info->params = params;
|
|
info->minplayers = minplayers;
|
|
info->maxplayers = maxplayers;
|
|
strncpyz(info->title, title, sizeof(info->title));
|
|
strncpyz(info->creator, player, sizeof(info->creator));
|
|
map_.insert(GameMap::value_type(gameid, *info));
|
|
Refresh();
|
|
|
|
if (!status_) {
|
|
chatter_.AddChat(player, base::Format::ToString(
|
|
"%s created and joined game %s.", player,
|
|
info->GetDescription()), true);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnRemoveGame(dword gameid, dword ctotal) {
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it == map_.end()) {
|
|
return;
|
|
}
|
|
map_.erase(gameid);
|
|
Refresh();
|
|
}
|
|
|
|
void RoomForm::OnGameInProgress(dword gameid) {
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it == map_.end()) {
|
|
return;
|
|
}
|
|
it->second.playing = true;
|
|
Refresh();
|
|
|
|
if (!status_) {
|
|
chatter_.AddChat("", base::Format::ToString(
|
|
"%s's game %s has started.",
|
|
it->second.creator, it->second.GetDescription()), true);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnGamePlayerNames(dword gameid, dword cnames,
|
|
const PlayerName *anames) {
|
|
GameMap::iterator it = map_.find(gameid);
|
|
if (it == map_.end()) {
|
|
return;
|
|
}
|
|
if (cnames > ARRAYSIZE(it->second.anames)) {
|
|
return;
|
|
}
|
|
GameInfo& info = it->second;
|
|
|
|
// Find the players being added to the game
|
|
int cadded = 0;
|
|
PlayerName added[kcPlayersMax];
|
|
for (int i = 0; i < cnames; i++) {
|
|
bool found = false;
|
|
for (int j = 0; j < info.cnames; j++) {
|
|
if (strcmp(anames[i].szPlayerName,
|
|
info.anames[j].szPlayerName) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
strncpyz(added[cadded].szPlayerName, anames[i].szPlayerName,
|
|
sizeof(added[cadded].szPlayerName));
|
|
cadded++;
|
|
}
|
|
}
|
|
|
|
// Find the players being removed from the game
|
|
int cremoved = 0;
|
|
PlayerName removed[kcPlayersMax];
|
|
for (int j = 0; j < info.cnames; j++) {
|
|
bool found = false;
|
|
for (int i = 0; i < cnames; i++) {
|
|
if (strcmp(anames[i].szPlayerName,
|
|
info.anames[j].szPlayerName) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
strncpyz(removed[cremoved].szPlayerName,
|
|
info.anames[j].szPlayerName,
|
|
sizeof(removed[cremoved].szPlayerName));
|
|
cremoved++;
|
|
}
|
|
}
|
|
|
|
// Update the players in the game
|
|
memcpy(info.anames, anames, cnames * sizeof(PlayerName));
|
|
info.cnames = cnames;
|
|
Refresh();
|
|
|
|
// Callbacks before Join has returned are current state, not changes
|
|
// in state.
|
|
if (status_) {
|
|
return;
|
|
}
|
|
|
|
// Add to the chat window
|
|
for (int i = 0; i < cadded; i++) {
|
|
// Don't post if this player created the game
|
|
if (strcmp(added[i].szPlayerName, info.creator) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Format: X joined Y's game Desert River Riches
|
|
chatter_.AddChat(added[i].szPlayerName,
|
|
base::Format::ToString("%s joined %s's game %s",
|
|
added[i].szPlayerName, info.creator, info.title), true);
|
|
}
|
|
|
|
for (int i = 0; i < cremoved; i++) {
|
|
if (strcmp(removed[i].szPlayerName, info.creator) == 0) {
|
|
if (!info.playing) {
|
|
// Format: X cancelled Desert River Riches.
|
|
chatter_.AddChat(removed[i].szPlayerName,
|
|
base::Format::ToString("%s cancelled %s.",
|
|
removed[i].szPlayerName, info.title), true);
|
|
} else {
|
|
// Format: X left Desert River Riches.
|
|
chatter_.AddChat(removed[i].szPlayerName,
|
|
base::Format::ToString("%s left %s.",
|
|
removed[i].szPlayerName, info.title), true);
|
|
}
|
|
} else {
|
|
if (!info.playing) {
|
|
// Format: X left Y's game Desert River Riches before it
|
|
// started.
|
|
chatter_.AddChat(removed[i].szPlayerName,
|
|
base::Format::ToString(
|
|
"%s left %s's game %s before it started.",
|
|
removed[i].szPlayerName, info.creator, info.title),
|
|
true);
|
|
} else {
|
|
// Format: X left Y's game Desert River Riches.
|
|
chatter_.AddChat(removed[i].szPlayerName,
|
|
base::Format::ToString("%s left %s's game %s.",
|
|
removed[i].szPlayerName, info.creator, info.title),
|
|
true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RoomForm::Refresh(int ct) {
|
|
if (!refresh_) {
|
|
return;
|
|
}
|
|
if (ct == -1) {
|
|
timer_.Stop();
|
|
OnTimeout(0);
|
|
return;
|
|
}
|
|
if (ct == 0) {
|
|
timer_.Stop();
|
|
}
|
|
if (timer_.IsStarted()) {
|
|
return;
|
|
}
|
|
timer_.Start(this, ct * 10);
|
|
}
|
|
|
|
void RoomForm::OnTimeout(int id) {
|
|
refresh_ = false;
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList);
|
|
dword gameid = (dword)(pword)plstc->GetSelectedItemData();
|
|
plstc->Clear();
|
|
GameMap::iterator it = map_.begin();
|
|
for (; it != map_.end(); it++) {
|
|
GameInfo& info = it->second;
|
|
char s[80];
|
|
GetString(info, s, sizeof(s));
|
|
plstc->Add(s, (void *)(pword)info.gameid);
|
|
}
|
|
int selected = FindIndex(gameid);
|
|
if (selected < 0 && map_.size() != 0) {
|
|
plstc->Select(0, true, true);
|
|
} else {
|
|
plstc->Select(selected, true);
|
|
}
|
|
refresh_ = true;
|
|
HideShowJoinGame();
|
|
|
|
// Update room label with player count.
|
|
int count = (int)players_.size();
|
|
for (GameMap::iterator it = map_.begin(); it != map_.end(); it++) {
|
|
count += it->second.cnames;
|
|
}
|
|
LabelControl *plbl = (LabelControl *)GetControlPtr(kidcRoomName);
|
|
plbl->SetText(base::Format::ToString("%s (%d %s, %d %s)",
|
|
roominfo_.name.c_str(), count,
|
|
count == 1 ? "player" : "players", map_.size(),
|
|
map_.size() == 1 ? "game" : "games"));
|
|
}
|
|
|
|
void RoomForm::HideShowJoinGame() {
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcGameList);
|
|
dword gameid = (dword)(pword)plstc->GetSelectedItemData();
|
|
GameMap::iterator it = map_.find(gameid);
|
|
bool show = true;
|
|
if (it == map_.end()) {
|
|
show = false;
|
|
} else {
|
|
GameInfo& info = it->second;
|
|
if (info.playing) {
|
|
show = false;
|
|
}
|
|
if (info.cnames == info.maxplayers) {
|
|
show = false;
|
|
}
|
|
}
|
|
Control *pctl = GetControlPtr(kidcJoinGame);
|
|
pctl->Show(show);
|
|
|
|
// Show the "no games" message if there are no games
|
|
if (map_.size() == 0) {
|
|
GetControlPtr(kidcGameList)->Show(false);
|
|
GetControlPtr(kidcNoGames)->Show(true);
|
|
} else {
|
|
GetControlPtr(kidcGameList)->Show(true);
|
|
GetControlPtr(kidcNoGames)->Show(false);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnAddPlayer(const char *player) {
|
|
players_.push_back(player);
|
|
Refresh();
|
|
|
|
if (!status_) {
|
|
chatter_.AddChat(player,
|
|
base::Format::ToString("%s entered the room.", player), true);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnRemovePlayer(dword hint, const char *player) {
|
|
// Remove the player from the list
|
|
{
|
|
std::vector<std::string>::iterator it;
|
|
it = std::find(players_.begin(), players_.end(), player);
|
|
if (it != players_.end()) {
|
|
players_.erase(it);
|
|
}
|
|
Refresh();
|
|
}
|
|
|
|
// If the player is creating or joining a game, don't add a system
|
|
// message. Otherwise, do add one.
|
|
bool add = true;
|
|
GameMap::iterator it = map_.begin();
|
|
for (; it != map_.end(); it++) {
|
|
if (strcmp(player, it->second.creator) == 0) {
|
|
add = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If joining a game, a system message will be added later with that
|
|
// notification.
|
|
if (hint == knLeaveRoomHintJoinGame) {
|
|
add = false;
|
|
}
|
|
|
|
if (add && !status_) {
|
|
chatter_.AddChat(player, base::Format::ToString("%s left the room.",
|
|
player), true);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnReceiveChat(const char *player, const char *chat) {
|
|
chatter_.AddChat(player, chat, false);
|
|
}
|
|
|
|
void RoomForm::OnStatusComplete() {
|
|
// Callbacks after this mean change in status, not current status.
|
|
status_ = false;
|
|
|
|
// Add a system message to chat. The current player is in players_[0]
|
|
std::string s = base::Format::ToString("%s has entered the %s game room. ",
|
|
players_[0].c_str(), roominfo_.name.c_str());
|
|
|
|
s += GetPlayersString();
|
|
chatter_.AddChat("", s.c_str(), true);
|
|
}
|
|
|
|
std::string RoomForm::GetPlayersString() {
|
|
std::string s;
|
|
if (players_.size() == 1) {
|
|
s += "No other players are in the room.";
|
|
return s;
|
|
}
|
|
|
|
if (players_.size() == 2) {
|
|
s += base::Format::ToString("%s is also in the room.",
|
|
players_[1].c_str());
|
|
return s;
|
|
}
|
|
|
|
for (int i = 1; i < players_.size(); i++) {
|
|
if (i != players_.size() - 1) {
|
|
s += base::Format::ToString("%s, ", players_[i].c_str());
|
|
} else {
|
|
s += base::Format::ToString("and %s ", players_[i].c_str());
|
|
}
|
|
}
|
|
s += "are also in the room.";
|
|
return s;
|
|
}
|
|
|
|
void RoomForm::GetString(GameInfo& info, char *s, int cb) {
|
|
// Format:
|
|
// Desert River Riches, 1.0x, 2-4 players: jim, bob, sam
|
|
// Desert River Riches, 1.0x, 2 players: jim, bob
|
|
// (play) Desert River Riches, 1.5x, 2-4 players: jim, bob...
|
|
|
|
const char *d = info.GetDescription();
|
|
if (info.playing) {
|
|
snprintf(s, cb, "(play) %s: ", d);
|
|
} else {
|
|
snprintf(s, cb, "%s: ", d);
|
|
}
|
|
|
|
// Add the players one by one.
|
|
for (int i = 0; i < info.cnames; i++) {
|
|
int cch = (int)strlen(s);
|
|
if (i != 0) {
|
|
if (cb - cch < 3) {
|
|
break;
|
|
}
|
|
strcat(s, ", ");
|
|
cch += 2;
|
|
}
|
|
strncpyz(&s[cch], info.anames[i].szPlayerName, cb - cch);
|
|
}
|
|
}
|
|
|
|
void RoomForm::OnStatusUpdate(char *pszStatus) {
|
|
}
|
|
|
|
void RoomForm::OnConnectionClose() {
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.idf = m_idf;
|
|
evt.eType = connectionCloseEvent;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
|
|
void RoomForm::OnShowMessage(const char *message) {
|
|
message_ = message;
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.idf = m_idf;
|
|
evt.eType = showMessageEvent;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
|
|
bool RoomForm::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;
|
|
}
|
|
|
|
|
|
const char *GameInfo::GetDescription() const {
|
|
// Format if not playing:
|
|
// Desert River Riches, 1.0x, 2-4 players:
|
|
// Format if playing:
|
|
// Desert River Riches
|
|
|
|
if (playing) {
|
|
return title;
|
|
}
|
|
|
|
char szSpeed[32];
|
|
GetSpeedMultiplierString(szSpeed, params.tGameSpeed);
|
|
|
|
if (minplayers == maxplayers) {
|
|
return base::Format::ToString("%s, %s, %d players", title,
|
|
szSpeed, minplayers);
|
|
} else {
|
|
return base::Format::ToString("%s, %s, %d-%d players",
|
|
title, szSpeed, minplayers, maxplayers);
|
|
}
|
|
}
|
|
|
|
} // namespace wi
|