mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
1213 lines
31 KiB
C++
1213 lines
31 KiB
C++
#include "ht.h"
|
|
#include "mpshared/netmessage.h"
|
|
#include "game/stateframe.h"
|
|
#include "game/completemanager.h"
|
|
#include "base/md5.h"
|
|
|
|
namespace wi {
|
|
|
|
#define knCreditDelta 18 //TUNE:
|
|
PlayerMgr gplrm;
|
|
Player *gpplrLocal;
|
|
Player gplrDummy;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// PlayerMgr Implementation
|
|
|
|
PlayerMgr::PlayerMgr()
|
|
{
|
|
m_aplr = NULL;
|
|
Reset();
|
|
}
|
|
|
|
PlayerMgr::~PlayerMgr()
|
|
{
|
|
delete[] m_aplr;
|
|
}
|
|
|
|
void PlayerMgr::Reset()
|
|
{
|
|
delete[] m_aplr;
|
|
m_aplr = new Player[kcPlayersMax];
|
|
Assert(m_aplr != NULL, "out of memory!");
|
|
if (m_aplr != NULL)
|
|
memset(m_aplr, 0, sizeof(Player) * kcPlayersMax);
|
|
gpplrLocal = NULL;
|
|
}
|
|
|
|
Player *PlayerMgr::AllocPlayer(word wf)
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_wf & kfPlrInUse)
|
|
continue;
|
|
|
|
pplr->Init(i);
|
|
pplr->m_wf |= kfPlrInUse | wf;
|
|
return pplr;
|
|
}
|
|
|
|
Assert("this shouldn't happen!");
|
|
return NULL;
|
|
}
|
|
|
|
Player *PlayerMgr::GetPlayer(Side side)
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_side == side)
|
|
return pplr;
|
|
}
|
|
|
|
Assert("No player playing this side???");
|
|
return NULL;
|
|
}
|
|
|
|
Player *PlayerMgr::GetNextPlayer(Player *pplr)
|
|
{
|
|
int i = (pplr == NULL) ? 0 : pplr->m_pid + 1;
|
|
pplr = &m_aplr[i];
|
|
for (; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_wf & kfPlrInUse)
|
|
return pplr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Player *PlayerMgr::GetNextHumanPlayer(Player *pplr)
|
|
{
|
|
int i = pplr == NULL ? 0 : pplr->m_pid + 1;
|
|
pplr = &m_aplr[i];
|
|
for (; i < kcPlayersMax; i++, pplr++) {
|
|
if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer)) == kfPlrInUse)
|
|
return pplr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Player *PlayerMgr::GetNextObservingPlayer(Player *pplr)
|
|
{
|
|
int i = pplr == NULL ? 0 : pplr->m_pid + 1;
|
|
pplr = &m_aplr[i];
|
|
for (; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_fLeftGame) {
|
|
continue;
|
|
}
|
|
if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer | kfPlrObserver)) ==
|
|
(kfPlrInUse | kfPlrComputer | kfPlrObserver)) {
|
|
return pplr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int PlayerMgr::GetPlayerCount()
|
|
{
|
|
int cplr = 0;
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_wf & kfPlrInUse)
|
|
cplr++;
|
|
}
|
|
|
|
return cplr;
|
|
}
|
|
|
|
int PlayerMgr::GetHumanTeamCount(bool fExtra, Pid pidExtra) {
|
|
// Count the # of unique ally masks among active human players,
|
|
// even though it is a mask. This simulates team count.
|
|
SideMask asidmAlliesUnique[kcPlayersMax];
|
|
int cUnique = 0;
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if ((pplr->m_wf & kfPlrInUse) == 0) {
|
|
continue;
|
|
}
|
|
bool fSkipCheck = false;
|
|
if (fExtra && pplr->m_pid == pidExtra) {
|
|
fSkipCheck = true;
|
|
}
|
|
if (!fSkipCheck) {
|
|
if ((pplr->m_wf & (kfPlrInUse | kfPlrComputer | kfPlrUnfulfilled))
|
|
!= kfPlrInUse) {
|
|
continue;
|
|
}
|
|
}
|
|
bool fFound = false;
|
|
for (int j = 0; j < cUnique; j++) {
|
|
if (pplr->GetAllies() == asidmAlliesUnique[j]) {
|
|
fFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!fFound) {
|
|
asidmAlliesUnique[cUnique] = pplr->GetAllies();
|
|
cUnique++;
|
|
}
|
|
}
|
|
return cUnique;
|
|
}
|
|
|
|
bool PlayerMgr::DetectTransitionToSingleHumanTeam(Pid pidLeft) {
|
|
// Detect if there were two or more opposing sides, and now there are
|
|
// no opposing sides.
|
|
|
|
int cBefore = GetHumanTeamCount(true, pidLeft);
|
|
int cAfter = GetHumanTeamCount(false, 0);
|
|
return (cAfter == 1 && cBefore > 1);
|
|
}
|
|
|
|
void PlayerMgr::Init(PlayersUpdateNetMessage *ppunm)
|
|
{
|
|
Reset();
|
|
|
|
PlayerRecord *pplrr = ppunm->aplrr;
|
|
for (int i = 0; i < ppunm->cplrr; i++, pplrr++) {
|
|
Player *pplr = &m_aplr[pplrr->pid];
|
|
pplr->Init(pplrr->pid);
|
|
|
|
pplr->m_wf |= kfPlrInUse;
|
|
pplr->SetSide(pplrr->side);
|
|
|
|
strncpyz(pplr->m_szName, pplrr->szName, sizeof(pplr->m_szName));
|
|
if (pplrr->wf & kfPlrrReady)
|
|
pplr->m_wf |= kfPlrReady;
|
|
if (pplrr->wf & kfPlrrComputer)
|
|
pplr->m_wf |= kfPlrComputer;
|
|
if (pplrr->wf & kfPlrrComputerOvermind)
|
|
pplr->m_wf |= kfPlrComputerOvermind;
|
|
if (pplrr->wf & kfPlrrUnfulfilled)
|
|
pplr->m_wf |= kfPlrUnfulfilled;
|
|
if (pplrr->wf & kfPlrrCreator)
|
|
pplr->m_wf |= kfPlrCreator;
|
|
if (pplrr->wf & kfPlrrLocal)
|
|
gpplrLocal = pplr;
|
|
|
|
// Life is hard for everybody in a multiplayer game!
|
|
|
|
pplr->SetHandicap(kfHcapHard);
|
|
}
|
|
}
|
|
|
|
bool PlayerMgr::Init(char *pszLevel)
|
|
{
|
|
Reset();
|
|
|
|
Level *plvl = new Level();
|
|
Assert(plvl != NULL, "out of memory!");
|
|
if (plvl == NULL)
|
|
return false;
|
|
if (!plvl->LoadLevelInfo(pszLevel)) {
|
|
delete plvl;
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < kcSides; i++) {
|
|
SideInfo *psidi = plvl->GetSideInfo(i);
|
|
word wf = 0;
|
|
switch (psidi->nIntelligence) {
|
|
case knIntelligenceComputer:
|
|
case knIntelligenceComputerNeutral:
|
|
wf = kfPlrComputer;
|
|
break;
|
|
|
|
case knIntelligenceComputerOvermind:
|
|
wf = kfPlrComputer | kfPlrComputerOvermind;
|
|
break;
|
|
}
|
|
|
|
Player *pplr = AllocPlayer(wf);
|
|
|
|
// The local player is the first human player allocated
|
|
|
|
if (wf & kfPlrComputer) {
|
|
pplr->SetHandicap(kfHcapHard); // life is hard for computers because that's how the levels have been tuned
|
|
} else if (gpplrLocal == NULL) {
|
|
gpplrLocal = pplr;
|
|
gpplrLocal->SetHandicap(gwfHandicap);
|
|
}
|
|
|
|
pplr->SetSide(i);
|
|
}
|
|
|
|
delete plvl;
|
|
return true;
|
|
}
|
|
|
|
#define knVerPlayerMgrState 1
|
|
bool PlayerMgr::LoadState(Stream *pstm)
|
|
{
|
|
byte nVer = pstm->ReadByte();
|
|
if (nVer != knVerPlayerMgrState)
|
|
return false;
|
|
|
|
Reset();
|
|
|
|
int cPlayers = pstm->ReadWord();
|
|
Assert(cPlayers <= kcPlayersMax);
|
|
|
|
while (cPlayers-- != 0) {
|
|
Player *pplr = gplrm.AllocPlayer();
|
|
if (pplr == NULL || !pplr->LoadState(pstm))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PlayerMgr::SetAllies(Player **applr, int cplrs, SideMask sidmAllies)
|
|
{
|
|
// Set allies
|
|
|
|
for (int n = 0; n < cplrs; n++) {
|
|
applr[n]->SetAllies(sidmAllies);
|
|
|
|
// Send this to the server so it can use it for ally chat
|
|
if (gptra != NULL) {
|
|
gptra->UpdateAllies(applr[n]->GetSide(), sidmAllies);
|
|
}
|
|
}
|
|
|
|
// Recalc enemies. Clear first
|
|
|
|
Gob *pgobT;
|
|
for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) {
|
|
if (!(pgobT->GetFlags() & kfGobUnit))
|
|
continue;
|
|
UnitGob *punt = (UnitGob *)pgobT;
|
|
punt->RecalcEnemyNearby(true);
|
|
}
|
|
|
|
// Now recalc
|
|
|
|
for (pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) {
|
|
if (!(pgobT->GetFlags() & kfGobUnit))
|
|
continue;
|
|
UnitGob *punt = (UnitGob *)pgobT;
|
|
punt->RecalcEnemyNearby(false);
|
|
}
|
|
}
|
|
|
|
bool PlayerMgr::SaveState(Stream *pstm)
|
|
{
|
|
pstm->WriteByte(knVerPlayerMgrState);
|
|
pstm->WriteWord(gplrm.GetPlayerCount());
|
|
for (Player *pplr = gplrm.GetNextPlayer(NULL); pplr != NULL; pplr = gplrm.GetNextPlayer(pplr))
|
|
pplr->SaveState(pstm);
|
|
return true;
|
|
}
|
|
|
|
#if 0 // not using anymore
|
|
bool PlayerMgr::IsSideInUse(Side side)
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_side == side)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
const char *PlayerMgr::GetCreatorName()
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_wf & kfPlrCreator) {
|
|
return pplr->m_szName;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void PlayerMgr::Update(int cUpdates)
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (pplr->m_wf & kfPlrInUse)
|
|
pplr->Update(cUpdates);
|
|
}
|
|
}
|
|
|
|
int PlayerMgr::GetUnitInstanceCountFromMask(UnitMask um, word wfPlr)
|
|
{
|
|
int cUnits = 0;
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
// If kfPlrComputer matches in both...
|
|
if (((pplr->m_wf ^ wfPlr) & kfPlrComputer) == 0)
|
|
cUnits += pplr->GetUnitInstanceCountFromMask(um);
|
|
}
|
|
return cUnits;
|
|
}
|
|
|
|
#ifdef TRACKSTATE
|
|
void PlayerMgr::TrackState(StateFrame *frame)
|
|
{
|
|
Player *pplr = m_aplr;
|
|
for (int i = 0; i < kcPlayersMax; i++, pplr++) {
|
|
if (!(pplr->m_wf & kfPlrInUse)) {
|
|
continue;
|
|
}
|
|
pplr->TrackState(frame);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void PlayerMgr::SendWinStats()
|
|
{
|
|
if (!gfMultiplayer || gptra == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Find the allies of the winner, if any. They are all winners.
|
|
// Since kfPlrWinner only gets set by the human winner, this will
|
|
// only be != 0 by the winning player.
|
|
SideMask sidmWinners = 0;
|
|
Player *pplr = NULL;
|
|
while ((pplr = GetNextPlayer(pplr)) != NULL) {
|
|
if (pplr->GetFlags() & kfPlrWinner) {
|
|
sidmWinners = pplr->GetAllies();
|
|
break;
|
|
}
|
|
}
|
|
|
|
pplr = NULL;
|
|
while ((pplr = GetNextPlayer(pplr)) != NULL) {
|
|
// Collect and send stats for each player; let the server sort it out
|
|
WinStatsNetMessage wsnm;
|
|
wsnm.pid = pplr->m_pid;
|
|
wsnm.ws.sidm = GetSideMask(pplr->m_side);
|
|
wsnm.ws.sidmAllies = pplr->m_sidmAllies;
|
|
wsnm.ws.cCreditsAcquired = pplr->GetTotalCreditsAcquired();
|
|
wsnm.ws.cCreditsConsumed = pplr->GetTotalCreditsConsumed();
|
|
wsnm.ws.cEnemyMobileUnitsKilled = pplr->GetEnemyMobileUnitsKilled();
|
|
wsnm.ws.cEnemyStructuresKilled = pplr->GetEnemyStructuresKilled();
|
|
wsnm.ws.cMobileUnitsLost = pplr->GetMobileUnitsLost();
|
|
wsnm.ws.cStructuresLost = pplr->GetStructuresLost();
|
|
Assert(ARRAYSIZE(pplr->m_acut) == ARRAYSIZE(wsnm.ws.acut));
|
|
for (int i = 0; i < ARRAYSIZE(wsnm.ws.acut); i++) {
|
|
wsnm.ws.acut[i] = pplr->m_acut[i];
|
|
}
|
|
Assert(ARRAYSIZE(pplr->m_acutBuilt) == ARRAYSIZE(wsnm.ws.acutBuilt));
|
|
for (int i = 0; i < ARRAYSIZE(wsnm.ws.acutBuilt); i++) {
|
|
wsnm.ws.acutBuilt[i] = pplr->m_acutBuilt[i];
|
|
}
|
|
wsnm.ws.ff = 0;
|
|
|
|
// If there is a winner team, then the rest are losers
|
|
if (sidmWinners != 0) {
|
|
if (GetSideMask(pplr->GetSide()) & sidmWinners) {
|
|
wsnm.ws.ff |= kfwsWinner;
|
|
} else {
|
|
wsnm.ws.ff |= kfwsLoser;
|
|
}
|
|
} else {
|
|
// This side isn't the winner. It doesn't know who the winners
|
|
// are, but it does know it is a loser.
|
|
if (pplr->m_wf & kfPlrLoser) {
|
|
wsnm.ws.ff |= kfwsLoser;
|
|
}
|
|
}
|
|
|
|
// Note: Update wsnm.hash with whatever is helpful to validate these
|
|
// results on the server. See validating code in server/game.cpp.
|
|
|
|
gptra->SendNetMessage(&wsnm);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Player Implementation
|
|
|
|
Player::Player()
|
|
{
|
|
Init(0);
|
|
}
|
|
|
|
Player::~Player()
|
|
{
|
|
if (m_szFormalObjectiveInfo != NULL)
|
|
gmmgr.FreePtr(m_szFormalObjectiveInfo);
|
|
|
|
for (int i = 0; i < kcFormalObjectivesMax; i++) {
|
|
if (m_aszFormalObjectiveText[i] != NULL)
|
|
gmmgr.FreePtr(m_aszFormalObjectiveText[i]);
|
|
if (m_aszFormalObjectiveStatus[i] != NULL)
|
|
gmmgr.FreePtr(m_aszFormalObjectiveStatus[i]);
|
|
}
|
|
}
|
|
|
|
#ifdef TRACKSTATE
|
|
void Player::TrackState(StateFrame *frame) {
|
|
int i = frame->AddCountedValue('PLYR');
|
|
frame->AddValue('WFUP', (dword)m_wfUpgrades, i);
|
|
frame->AddValue('SIDE', (dword)m_side, i);
|
|
frame->AddValue('SALI', (dword)m_sidmAllies, i);
|
|
frame->AddValue('SDIS', (dword)m_sidmDiscovered, i);
|
|
frame->AddValue('CRED', (dword)m_nCredits, i);
|
|
frame->AddValue('POWR', (dword)m_nPowerSupply, i);
|
|
frame->AddValue('DMND', (dword)m_nPowerDemand, i);
|
|
frame->AddValue('UPGR', (dword)m_upgm, i);
|
|
frame->AddValue('CMUK', (dword)m_cmuntKilled, i);
|
|
frame->AddValue('CSTK', (dword)m_cstruKilled, i);
|
|
frame->AddValue('CMLS', (dword)m_cmuntLost, i);
|
|
frame->AddValue('CSLS', (dword)m_cstruLost, i);
|
|
frame->AddValue('TCAQ', (dword)m_nTotalCreditsAcquired, i);
|
|
frame->AddValue('TCCN', (dword)m_nTotalCreditsConsumed, i);
|
|
frame->AddValue('CURL', (dword)m_cUpdatesRepairLast, i);
|
|
frame->AddValue('CSNC', (dword)m_cStructsNeedCredits, i);
|
|
frame->AddValue('WFHN', (dword)m_wfHandicap, i);
|
|
}
|
|
#endif
|
|
|
|
void Player::Init(Pid pid)
|
|
{
|
|
m_wf = 0;
|
|
m_pid = pid;
|
|
m_nCredits = 0;
|
|
m_nCreditsConsumed = m_nCreditsAcquired = 0;
|
|
m_nDirCredits = 0;
|
|
m_nConsumerCredits = knConsumerMax;
|
|
m_side = ksideNeutral;
|
|
m_wfUpgrades = 0;
|
|
m_nPowerDemand = 0;
|
|
m_nPowerSupply = 0;
|
|
m_szName[0] = 0;
|
|
m_sidmAllies = 0;
|
|
m_umAllowed = kumAll;
|
|
m_szObjective[0] = 0;
|
|
m_tptDiscover.tx = m_tptDiscover.ty = 0;
|
|
m_upgm = 0;
|
|
m_upgmAllowed = kupgmAll;
|
|
m_szFormalObjectiveInfo = NULL;
|
|
m_cUpdatesRepairLast = 0;
|
|
m_cStructsNeedCredits = 0;
|
|
m_wfHandicap = kfHcapDefault;
|
|
m_cUpdates = -1;
|
|
m_nLagState = knLagNone;
|
|
m_fLeftGame = false;
|
|
|
|
memset(m_acut, 0, ARRAYSIZE(m_acut));
|
|
memset(m_acutBuilt, 0, ARRAYSIZE(m_acutBuilt));
|
|
|
|
for (int i = 0; i < kcFormalObjectivesMax; i++) {
|
|
m_aszFormalObjectiveText[i] = NULL;
|
|
m_aszFormalObjectiveStatus[i] = NULL;
|
|
}
|
|
}
|
|
|
|
#define knVerPlayerState 11
|
|
bool Player::LoadState(Stream *pstm)
|
|
{
|
|
byte nVer = pstm->ReadByte();
|
|
if (nVer != knVerPlayerState)
|
|
return false;
|
|
m_wfUpgrades = pstm->ReadWord();
|
|
m_side = pstm->ReadWord();
|
|
m_sidmAllies = pstm->ReadWord();
|
|
m_sidmDiscovered = pstm->ReadWord();
|
|
m_tptDiscover.tx = pstm->ReadWord();
|
|
m_tptDiscover.ty = pstm->ReadWord();
|
|
pstm->ReadString(m_szName, sizeof(m_szName));
|
|
m_wf = pstm->ReadWord();
|
|
m_pid = pstm->ReadWord();
|
|
m_nCredits = pstm->ReadDword();
|
|
|
|
m_nCreditsAcquired = 0;
|
|
m_nCreditsConsumed = 0;
|
|
m_nPowerSupply = (short)pstm->ReadWord();
|
|
m_nPowerDemand = (short)pstm->ReadWord();
|
|
pstm->Read(m_acut, sizeof(m_acut));
|
|
m_umAllowed = pstm->ReadDword();
|
|
pstm->ReadString(m_szObjective, sizeof(m_szObjective));
|
|
m_upgm = pstm->ReadWord();
|
|
m_upgmAllowed = pstm->ReadWord();
|
|
m_cUpdatesRepairLast = pstm->ReadDword();
|
|
|
|
// Elaborate b.s. to save memory from dynamic heap on palm
|
|
|
|
char szFormalObjectiveInfo[kcchObjectiveInfoMax];
|
|
pstm->ReadString(szFormalObjectiveInfo, sizeof(szFormalObjectiveInfo));
|
|
if (szFormalObjectiveInfo[0] == (char)0xff) {
|
|
m_szFormalObjectiveInfo = NULL;
|
|
} else {
|
|
m_szFormalObjectiveInfo = (char *)gmmgr.AllocPtr(
|
|
strlen(szFormalObjectiveInfo) + 1);
|
|
gmmgr.WritePtr(m_szFormalObjectiveInfo, 0, szFormalObjectiveInfo,
|
|
strlen(szFormalObjectiveInfo) + 1);
|
|
}
|
|
|
|
for (int i = 0; i < kcFormalObjectivesMax; i++) {
|
|
char szFormalObjectiveText[kcchObjectiveMax];
|
|
pstm->ReadString(szFormalObjectiveText, sizeof(szFormalObjectiveText));
|
|
if (szFormalObjectiveText[0] == (char)0xff) {
|
|
m_aszFormalObjectiveText[i] = NULL;
|
|
} else {
|
|
m_aszFormalObjectiveText[i] = (char *)gmmgr.AllocPtr(
|
|
strlen(szFormalObjectiveText) + 1);
|
|
gmmgr.WritePtr(m_aszFormalObjectiveText[i], 0,
|
|
szFormalObjectiveText, strlen(szFormalObjectiveText) + 1);
|
|
}
|
|
|
|
char szFormalObjectiveStatus[kcchObjectiveStatusMax];
|
|
pstm->ReadString(szFormalObjectiveStatus,
|
|
sizeof(szFormalObjectiveStatus));
|
|
if (szFormalObjectiveStatus[0] == (char)0xff) {
|
|
m_aszFormalObjectiveStatus[i] = NULL;
|
|
} else {
|
|
m_aszFormalObjectiveStatus[i] = (char *)gmmgr.AllocPtr(
|
|
strlen(szFormalObjectiveStatus) + 1);
|
|
gmmgr.WritePtr(m_aszFormalObjectiveStatus[i], 0,
|
|
szFormalObjectiveStatus,
|
|
strlen(szFormalObjectiveStatus) + 1);
|
|
}
|
|
}
|
|
|
|
m_cmuntKilled = pstm->ReadWord();
|
|
m_cstruKilled = pstm->ReadWord();
|
|
m_cmuntLost = pstm->ReadWord();
|
|
m_cstruLost = pstm->ReadWord();
|
|
m_nTotalCreditsAcquired = pstm->ReadDword();
|
|
m_nTotalCreditsConsumed = pstm->ReadDword();
|
|
m_wfHandicap = pstm->ReadWord();
|
|
|
|
// The local player is the first human player allocated
|
|
|
|
if (!(m_wf & kfPlrComputer) && gpplrLocal == NULL) {
|
|
gpplrLocal = this;
|
|
gwfHandicap = m_wfHandicap;
|
|
}
|
|
|
|
return pstm->IsSuccess();
|
|
}
|
|
|
|
bool Player::SaveState(Stream *pstm)
|
|
{
|
|
pstm->WriteByte(knVerPlayerState);
|
|
pstm->WriteWord(m_wfUpgrades);
|
|
pstm->WriteWord(m_side);
|
|
pstm->WriteWord(m_sidmAllies);
|
|
pstm->WriteWord(m_sidmDiscovered);
|
|
pstm->WriteWord(m_tptDiscover.tx);
|
|
pstm->WriteWord(m_tptDiscover.ty);
|
|
pstm->WriteString(m_szName);
|
|
pstm->WriteWord(m_wf);
|
|
pstm->WriteWord(m_pid);
|
|
pstm->WriteDword(m_nCredits);
|
|
pstm->WriteWord(m_nPowerSupply);
|
|
pstm->WriteWord(m_nPowerDemand);
|
|
pstm->Write(m_acut, sizeof(m_acut));
|
|
pstm->WriteDword(m_umAllowed);
|
|
pstm->WriteString(m_szObjective);
|
|
pstm->WriteWord(m_upgm);
|
|
pstm->WriteWord(m_upgmAllowed);
|
|
pstm->WriteDword(m_cUpdatesRepairLast);
|
|
|
|
if (m_szFormalObjectiveInfo == NULL) {
|
|
pstm->WriteByte(0xff);
|
|
pstm->WriteByte(0);
|
|
} else {
|
|
pstm->WriteString(m_szFormalObjectiveInfo);
|
|
}
|
|
|
|
for (int i = 0; i < kcFormalObjectivesMax; i++) {
|
|
if (m_aszFormalObjectiveText[i] == NULL) {
|
|
pstm->WriteByte(0xff);
|
|
pstm->WriteByte(0);
|
|
} else {
|
|
pstm->WriteString(m_aszFormalObjectiveText[i]);
|
|
}
|
|
if (m_aszFormalObjectiveStatus[i] == NULL) {
|
|
pstm->WriteByte(0xff);
|
|
pstm->WriteByte(0);
|
|
} else {
|
|
pstm->WriteString(m_aszFormalObjectiveStatus[i]);
|
|
}
|
|
}
|
|
|
|
pstm->WriteWord(m_cmuntKilled);
|
|
pstm->WriteWord(m_cstruKilled);
|
|
pstm->WriteWord(m_cmuntLost);
|
|
pstm->WriteWord(m_cstruLost);
|
|
pstm->WriteDword(m_nTotalCreditsAcquired);
|
|
pstm->WriteDword(m_nTotalCreditsConsumed);
|
|
pstm->WriteWord(m_wfHandicap);
|
|
|
|
return pstm->IsSuccess();
|
|
}
|
|
|
|
// TUNE:
|
|
#define kctLagGrace 100 // lag time period to be declared laggy player
|
|
#define kctLagRedemption 0 // 300 // to become non-laggy, the player must be not laggy for this period
|
|
#define kctLagKill 800 // if still laggy after this period, recommend kill
|
|
|
|
bool Player::IsBehind(int cUpdates)
|
|
{
|
|
// If this player has already broadcasted a disconnect message, force it
|
|
// into a no-lag state
|
|
|
|
bool fBehind = m_cUpdates < cUpdates;
|
|
if (m_wf & kfPlrDisconnectBroadcasted) {
|
|
SetLagState(knLagNone);
|
|
return fBehind;
|
|
}
|
|
|
|
if (fBehind) {
|
|
// Player is behind
|
|
|
|
switch (m_nLagState) {
|
|
case knLagNone:
|
|
// Remember that pplr is lagging and when this started
|
|
|
|
m_tLagStart = HostGetTickCount();
|
|
m_nLagState = knLagGrace;
|
|
break;
|
|
|
|
case knLagGrace:
|
|
// This state is effectively a grace period. If the player remains
|
|
// laggy through this period, it becomes guilty of lag
|
|
|
|
if (HostGetTickCount() - m_tLagStart >= kctLagGrace) { m_nLagState
|
|
= knLagGuilty; m_tLastLag = HostGetTickCount(); } break;
|
|
|
|
case knLagGuilty:
|
|
case knLagKill:
|
|
// This player has lagged long enough to be guilty as charged. Now
|
|
// the player needs to be *not* laggy over a fixed period to get
|
|
// out of this state
|
|
|
|
m_tLastLag = HostGetTickCount();
|
|
if (HostGetTickCount() - m_tLagStart >= kctLagKill)
|
|
m_nLagState = knLagKill;
|
|
break;
|
|
}
|
|
} else {
|
|
// Player is not behind
|
|
|
|
switch (m_nLagState) {
|
|
case knLagNone:
|
|
// All is ok
|
|
|
|
break;
|
|
|
|
case knLagGrace:
|
|
// This player has "caught up" during the grace period. Assume there is no lag now.
|
|
|
|
m_nLagState = knLagNone;
|
|
break;
|
|
|
|
case knLagGuilty:
|
|
case knLagKill:
|
|
// Guilty of lag and yet the player has caught up. If the player can keep this state
|
|
// it will be declared not laggy
|
|
|
|
if (HostGetTickCount() - m_tLastLag >= kctLagRedemption) {
|
|
m_nLagState = knLagNone;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fBehind;
|
|
}
|
|
|
|
int Player::GetLagState()
|
|
{
|
|
return m_nLagState;
|
|
}
|
|
|
|
void Player::SetLagState(int nLagState)
|
|
{
|
|
m_nLagState = nLagState;
|
|
m_tLastLag = HostGetTickCount();
|
|
m_tLagStart = m_tLastLag;
|
|
}
|
|
|
|
int Player::GetLagTimeout()
|
|
{
|
|
// Return seconds until this player gets to the kill state
|
|
|
|
long ctElapsed = HostGetTickCount() - m_tLagStart;
|
|
if (ctElapsed > kctLagKill)
|
|
return 0;
|
|
return (int)((kctLagKill - ctElapsed + 50) / 100);
|
|
}
|
|
|
|
void Player::SetUpgrades(word wfUpgrades)
|
|
{
|
|
word wfChange = wfUpgrades ^ m_wfUpgrades;
|
|
if (wfChange == 0)
|
|
return;
|
|
m_wfUpgrades = wfUpgrades;
|
|
|
|
if (wfUpgrades & kfUpgradeHrc)
|
|
m_upgm |= kupgmAdvancedHRC;
|
|
else
|
|
m_upgm &= ~kupgmAdvancedHRC;
|
|
if (wfUpgrades & kfUpgradeVts)
|
|
m_upgm |= kupgmAdvancedVTS;
|
|
else
|
|
m_upgm &= ~kupgmAdvancedVTS;
|
|
|
|
// Tell interested units that they are being upgraded; this gets them to wake up
|
|
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if (pgob->GetOwner() != this)
|
|
continue;
|
|
if ((pgob->GetFlags() & (kfGobActive | kfGobUnit)) == (kfGobActive | kfGobUnit)) {
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
MobileUnitBuilderConsts *pmubc = (MobileUnitBuilderConsts *)punt->GetConsts();
|
|
switch (punt->GetType()) {
|
|
case kgtVehicleTransportStation:
|
|
if (wfChange & kfUpgradeVtsInProgress)
|
|
gsmm.SendMsg((m_wfUpgrades & kfUpgradeVtsInProgress) ? kmidBeingUpgraded : kmidUpgradeComplete, punt->GetId());
|
|
break;
|
|
|
|
case kgtHumanResourceCenter:
|
|
if (wfChange & kfUpgradeHrcInProgress)
|
|
gsmm.SendMsg((m_wfUpgrades & kfUpgradeHrcInProgress) ? kmidBeingUpgraded : kmidUpgradeComplete, punt->GetId());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::ModifyNeedCreditsCount(int cDelta)
|
|
{
|
|
// track how many units are waiting for credits so we can
|
|
// flash the UI if needed. Keep here so it can be re-updated
|
|
// when loading a save game which happens before the UI is initialized.
|
|
|
|
m_cStructsNeedCredits += cDelta;
|
|
Assert((short)m_cStructsNeedCredits >= 0);
|
|
}
|
|
|
|
void Player::AddPowerSupplyAndDemand(int nPowerSupply, int nPowerDemand)
|
|
{
|
|
bool fLowBefore = IsPowerLow();
|
|
m_nPowerSupply += nPowerSupply;
|
|
m_nPowerDemand += nPowerDemand;
|
|
bool fLowAfter = IsPowerLow();
|
|
|
|
// Notify this player's gobs that want to know that the power situation has changed
|
|
|
|
if (fLowBefore != fLowAfter) {
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if (pgob->GetOwner() != this)
|
|
continue;
|
|
if ((pgob->GetFlags() & (kfGobActive | kfGobUnit)) == (kfGobActive | kfGobUnit)) {
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
if (punt->GetConsts()->wf & kfUntcNotifyPowerLowHigh) {
|
|
gsmm.SendMsg(kmidPowerLowHigh, punt->GetId());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify the of a power change that may affect if the radar is powered
|
|
|
|
if (this == gpplrLocal) {
|
|
gpmm->CalcPoweredRadar();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::SetName(const char *pszName)
|
|
{
|
|
strncpyz(m_szName, (char *)pszName, sizeof(m_szName));
|
|
}
|
|
|
|
UnitMask Player::GetUnitMask()
|
|
{
|
|
UnitMask um = 0;
|
|
|
|
for (int i = 0; i < kutMax; i++) {
|
|
if (m_acut[i] != 0)
|
|
um |= gapuntc[i]->um;
|
|
}
|
|
|
|
return um;
|
|
}
|
|
|
|
int Player::GetUnitActiveCountFromMask(UnitMask um)
|
|
{
|
|
int c = 0;
|
|
for (int i = 0; i < kutMax; i++) {
|
|
if (gapuntc[i]->um & um)
|
|
c += m_acut[i];
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
int Player::GetUnitInstanceCountFromMask(UnitMask um)
|
|
{
|
|
if (!(m_wf & kfPlrInUse))
|
|
return 0;
|
|
|
|
int cUnits = 0;
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if (pgob->GetFlags() & kfGobUnit) {
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
if (punt->GetOwner() == this) {
|
|
if (punt->GetConsts()->um & um)
|
|
cUnits++;
|
|
}
|
|
}
|
|
}
|
|
return cUnits;
|
|
}
|
|
|
|
int Player::GetCreditsDirection()
|
|
{
|
|
int nDir = m_nDirCredits;
|
|
m_nDirCredits = 0;
|
|
return nDir;
|
|
}
|
|
|
|
int Player::GetCreditsConsumer()
|
|
{
|
|
int nConsumer = m_nConsumerCredits;
|
|
m_nConsumerCredits = knConsumerMax;
|
|
return nConsumer;
|
|
}
|
|
|
|
void Player::SetCredits(int nCredits, bool fAffectTotals, int nConsumer)
|
|
{
|
|
Assert(nCredits >= 0);
|
|
|
|
if (nConsumer < m_nConsumerCredits)
|
|
m_nConsumerCredits = nConsumer;
|
|
|
|
if (nCredits > m_nCredits && m_nCredits != 0) {
|
|
m_nDirCredits = 1;
|
|
} else if (nCredits < m_nCredits && nCredits != 0) {
|
|
if (m_nDirCredits == 0)
|
|
m_nDirCredits = -1;
|
|
}
|
|
|
|
int dCredits = nCredits - m_nCredits;
|
|
if (dCredits > 0)
|
|
m_nTotalCreditsAcquired += dCredits;
|
|
else
|
|
m_nTotalCreditsConsumed -= dCredits;
|
|
|
|
m_nCredits = nCredits;
|
|
|
|
if (this == gpplrLocal && fAffectTotals) {
|
|
if (dCredits < 0)
|
|
m_nCreditsConsumed += -dCredits;
|
|
else if (dCredits > 0)
|
|
m_nCreditsAcquired += dCredits;
|
|
}
|
|
}
|
|
|
|
void Player::SetObjective(char *psz)
|
|
{
|
|
strncpyz(m_szObjective, psz, sizeof(m_szObjective));
|
|
}
|
|
|
|
void Player::Update(int cUpdates)
|
|
{
|
|
// If auto repair is on, repair all damaged structures owned by this player.
|
|
// TUNE:
|
|
|
|
#define kcupdRepair 6
|
|
|
|
if (m_cUpdatesRepairLast == 0 || abs(m_cUpdatesRepairLast - cUpdates) >= kcupdRepair) {
|
|
m_cUpdatesRepairLast = cUpdates;
|
|
if (m_wf & kfPlrAutoRepair)
|
|
Repair(true);
|
|
}
|
|
|
|
// new theory. Play a credit noise every delta credits
|
|
|
|
if (m_nCreditsConsumed >= knCreditDelta) {
|
|
m_nCreditsConsumed %= knCreditDelta;
|
|
gsndm.PlaySfx(ksfxGameCreditsDecreasing);
|
|
}
|
|
|
|
if (m_nCreditsAcquired >= knCreditDelta) {
|
|
m_nCreditsAcquired %= knCreditDelta;
|
|
gsndm.PlaySfx(ksfxGameCreditsIncreasing);
|
|
}
|
|
}
|
|
|
|
void Player::Repair(bool fOn)
|
|
{
|
|
if (fOn)
|
|
m_wf |= kfPlrAutoRepair;
|
|
else
|
|
m_wf &= ~kfPlrAutoRepair;
|
|
|
|
for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) {
|
|
if (pgobT->GetOwner() != this)
|
|
continue;
|
|
|
|
if ((pgobT->GetFlags() & (kfGobStructure | kfGobActive)) != (kfGobStructure | kfGobActive))
|
|
continue;
|
|
|
|
StructGob *pstru = (StructGob *)pgobT;
|
|
|
|
if (fOn) {
|
|
if (!(pstru->GetUnitFlags() & kfUnitRepairing)) {
|
|
if (pstru->GetHealth() < divfx(pstru->GetConsts()->GetArmorStrength(), itofx(3)))
|
|
pstru->Repair(true);
|
|
}
|
|
} else {
|
|
if (pstru->GetUnitFlags() & kfUnitRepairing)
|
|
pstru->Repair(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::SetFormalObjectiveText(int iObjective, char *pszText)
|
|
{
|
|
if (m_aszFormalObjectiveText[iObjective] != NULL)
|
|
gmmgr.FreePtr(m_aszFormalObjectiveText[iObjective]);
|
|
m_aszFormalObjectiveText[iObjective] = (char *)gmmgr.AllocPtr(strlen(pszText) + 1);
|
|
gmmgr.WritePtr(m_aszFormalObjectiveText[iObjective], 0, pszText, strlen(pszText) + 1);
|
|
SetFormalObjectiveStatus(iObjective, "Incomplete");
|
|
}
|
|
|
|
void Player::SetFormalObjectiveStatus(int iObjective, char *pszText)
|
|
{
|
|
if (m_aszFormalObjectiveStatus[iObjective] != NULL)
|
|
gmmgr.FreePtr(m_aszFormalObjectiveStatus[iObjective]);
|
|
m_aszFormalObjectiveStatus[iObjective] = (char *)gmmgr.AllocPtr(strlen(pszText) + 1);
|
|
gmmgr.WritePtr(m_aszFormalObjectiveStatus[iObjective], 0, pszText, strlen(pszText) + 1);
|
|
}
|
|
|
|
void Player::SetFormalObjectiveInfo(char *pszText)
|
|
{
|
|
if (m_szFormalObjectiveInfo != NULL)
|
|
gmmgr.FreePtr(m_szFormalObjectiveInfo);
|
|
m_szFormalObjectiveInfo = (char *)gmmgr.AllocPtr(strlen(pszText) + 1);
|
|
gmmgr.WritePtr(m_szFormalObjectiveInfo, 0, pszText, strlen(pszText) + 1);
|
|
}
|
|
|
|
//
|
|
// Objectives / Win Summary / Lose Summary form
|
|
//
|
|
|
|
class ObjectivesForm : public ShellForm {
|
|
public:
|
|
ObjectivesForm(Player *pplr, int so, bool fForceInfoDisplay) secPlayer;
|
|
void UpdateStatistics() secPlayer;
|
|
|
|
// ShellForm overrides
|
|
|
|
virtual bool Init(FormMgr *pfrmm, IniReader *pini, word idf) secPlayer;
|
|
virtual void OnControlSelected(word idc) secPlayer;
|
|
|
|
private:
|
|
Player *m_pplr;
|
|
int m_so;
|
|
static bool s_fStatistics;
|
|
};
|
|
|
|
bool ObjectivesForm::s_fStatistics = false;
|
|
|
|
ObjectivesForm::ObjectivesForm(Player *pplr, int so, bool fForceInfoDisplay)
|
|
{
|
|
m_pplr = pplr;
|
|
m_so = so;
|
|
if (fForceInfoDisplay)
|
|
s_fStatistics = false;
|
|
}
|
|
|
|
bool ObjectivesForm::Init(FormMgr *pfrmm, IniReader *pini, word idf)
|
|
{
|
|
if (!ShellForm::Init(pfrmm, pini, idf))
|
|
return false;
|
|
|
|
LabelControl *plbl;
|
|
plbl = (LabelControl *)GetControlPtr(kidcMissionTitle);
|
|
char szT[kcbLevelTitle];
|
|
strncpyz(szT, gsim.GetLevel()->GetTitle(), sizeof(szT));
|
|
HtStrupr(szT);
|
|
plbl->SetText(szT);
|
|
|
|
for (int i = 0; i < kcFormalObjectivesMax; i++) {
|
|
plbl = (LabelControl *)GetControlPtr(kidcObjectiveText1 + i);
|
|
plbl->SetText(m_pplr->GetFormalObjectiveText(i));
|
|
plbl = (LabelControl *)GetControlPtr(kidcObjectiveStatus1 + i);
|
|
plbl->SetText(m_pplr->GetFormalObjectiveStatus(i));
|
|
}
|
|
|
|
UpdateStatistics();
|
|
|
|
// Stop the animation. Will anyone people notice?
|
|
|
|
m_fAnimate = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ObjectivesForm::UpdateStatistics()
|
|
{
|
|
char szT[100];
|
|
LabelControl *plbl;
|
|
if (m_so == ksoObjectives) {
|
|
plbl = (LabelControl *)GetControlPtr(kidcObjectiveInfo);
|
|
plbl->SetText(m_pplr->GetFormalObjectiveInfo());
|
|
|
|
for (int i = 0; i < m_cctl; i++) {
|
|
Control *pctl = m_apctl[i];
|
|
int idc = pctl->GetId();
|
|
if (idc >= kidcPage1 && idc < kidcPage2)
|
|
pctl->Show(!s_fStatistics);
|
|
else if (idc >= kidcPage2 && idc < kidcPage3)
|
|
pctl->Show(s_fStatistics);
|
|
}
|
|
}
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcMobileUnitsKilled);
|
|
itoa(m_pplr->GetEnemyMobileUnitsKilled(), szT, 10);
|
|
plbl->SetText(szT);
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcStructuresKilled);
|
|
itoa(m_pplr->GetEnemyStructuresKilled(), szT, 10);
|
|
plbl->SetText(szT);
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcMobileUnitsLost);
|
|
itoa(m_pplr->GetMobileUnitsLost(), szT, 10);
|
|
plbl->SetText(szT);
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcStructuresLost);
|
|
itoa(m_pplr->GetStructuresLost(), szT, 10);
|
|
plbl->SetText(szT);
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcCreditsAction);
|
|
sprintf(szT, "%d / %d", m_pplr->GetTotalCreditsAcquired(), m_pplr->GetTotalCreditsConsumed());
|
|
plbl->SetText(szT);
|
|
|
|
plbl = (LabelControl *)GetControlPtr(kidcGameTime);
|
|
long t = gsim.GetTickCount();
|
|
int nHour = (int)(t / (100 * 60 * 60));
|
|
t -= nHour * (100L * 60 * 60);
|
|
int nMin = (int)(t / (100 * 60));
|
|
Assert(nMin < 60);
|
|
t -= nMin * (100L * 60);
|
|
int nSec = (int)(t / 100);
|
|
Assert(nSec < 60);
|
|
sprintf(szT, "%02d:%02d:%02d", nHour, nMin, nSec);
|
|
plbl->SetText(szT);
|
|
}
|
|
|
|
void ObjectivesForm::OnControlSelected(word idc)
|
|
{
|
|
if (idc == kidcStatistics || idc == kidcInfo) {
|
|
s_fStatistics = !s_fStatistics;
|
|
UpdateStatistics();
|
|
} else {
|
|
ShellForm::OnControlSelected(idc);
|
|
}
|
|
}
|
|
|
|
int Player::ShowObjectives(int so, bool fForceInfoDisplay, bool fAborting)
|
|
{
|
|
// The default for losing is knGoAbortLevel. This way, if someone exits
|
|
// while the form is up, knGoAbortLevel will be returned and the game
|
|
// won't be saved. The default for winning, is knGoSuccess. This way,
|
|
// if someone exits while the form is up, the game will be saved, which
|
|
// will mean the mission re-loads and can auto-advance.
|
|
|
|
int idf;
|
|
int nGo;
|
|
|
|
switch (so) {
|
|
case ksoObjectives:
|
|
idf = gfMultiplayer ? kidfMultiplayerObjectives : kidfObjectives;
|
|
nGo = knGoSuccess;
|
|
break;
|
|
|
|
case ksoWinSummary:
|
|
if (m_wf & kfPlrSummaryShown)
|
|
return knGoSuccess;
|
|
m_wf |= kfPlrSummaryShown | kfPlrWinner;
|
|
idf = gfMultiplayer ? kidfMultiplayerWinSummary : kidfWinSummary;
|
|
gplrm.SendWinStats();
|
|
if (!gfMultiplayer && !fAborting) {
|
|
// Mark current game complete.
|
|
// Doing it here ensures it is done when the mission is
|
|
// completed.
|
|
gpcptm->MarkComplete(&ggame.GetLastMissionIdentifier());
|
|
}
|
|
nGo = knGoSuccess;
|
|
break;
|
|
|
|
case ksoLoseSummary:
|
|
if (m_wf & kfPlrSummaryShown)
|
|
return knGoSuccess;
|
|
m_wf |= kfPlrSummaryShown | kfPlrLoser;
|
|
idf = gfMultiplayer ? kidfMultiplayerLoseSummary : kidfLoseSummary;
|
|
gplrm.SendWinStats();
|
|
nGo = knGoAbortLevel;
|
|
break;
|
|
|
|
default:
|
|
Assert("Invalid objective screen type passed to ShowObjectives");
|
|
return knGoInitFailure;
|
|
}
|
|
|
|
lbTryAgain:
|
|
ObjectivesForm *pfrm = (ObjectivesForm *)gpmfrmm->LoadForm(gpiniForms, idf,
|
|
new ObjectivesForm(this, so, fForceInfoDisplay));
|
|
if (pfrm == NULL) {
|
|
Assert("Objectives form failed to load!");
|
|
return knGoInitFailure;
|
|
}
|
|
|
|
if (fAborting) {
|
|
LabelControl *plbl;
|
|
plbl = (LabelControl *)pfrm->GetControlPtr(kidcMissionResult);
|
|
if (plbl != NULL)
|
|
plbl->SetText("MISSION ABORTED");
|
|
}
|
|
|
|
if (so != ksoObjectives) {
|
|
gsndm.PlaySfx((idf == kidfLoseSummary || idf == kidfMultiplayerLoseSummary || fAborting) ? ksfxGameLoseLevel : ksfxGameWinLevel);
|
|
}
|
|
|
|
int idc;
|
|
pfrm->DoModal(&idc, true, so == ksoObjectives);
|
|
delete pfrm;
|
|
|
|
if (!gevm.IsAppStopping()) {
|
|
switch (idc) {
|
|
case kidcLoadGame:
|
|
{
|
|
Stream *pstm = PickLoadGameStream();
|
|
if (pstm == NULL)
|
|
goto lbTryAgain;
|
|
|
|
gpstmSavedGame = pstm;
|
|
nGo = knGoLoadSavedGame;
|
|
}
|
|
break;
|
|
|
|
case kidcAbortMission:
|
|
case kidcRestartMission:
|
|
nGo = idc == kidcAbortMission ? knGoAbortLevel : knGoRetryLevel;
|
|
break;
|
|
}
|
|
} else {
|
|
// The game is exiting. If the level was lost, then don't save
|
|
// the re-initialize game, otherwise the user will be able
|
|
// to exit while this form is up, restart the game, and advance
|
|
// to the next level.
|
|
|
|
if (nGo != knGoSuccess) {
|
|
ggame.SkipSaveReinitializeGame();
|
|
}
|
|
}
|
|
|
|
return nGo;
|
|
}
|
|
|
|
} // namespace wi
|