mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
Client platform version can be retrieved live with /ids and is also recorded in the playerdetail module.
1057 lines
23 KiB
C++
1057 lines
23 KiB
C++
#include "game/ht.h"
|
|
#include "game/lobby.h"
|
|
#include "game/serviceurls.h"
|
|
#include "base/misc.h"
|
|
|
|
namespace wi {
|
|
|
|
// A simple form handler for the main menu, for buttons that want to
|
|
// be processed without ending the modal form loop.
|
|
|
|
class MainMenuForm : public ShellForm
|
|
{
|
|
public:
|
|
virtual void OnControlSelected(word idc) {
|
|
// Catch this here so the form doesn't get re-created (with associated
|
|
// sound effect).
|
|
if (idc == kidcLeaderboard) {
|
|
LoginHandler handler;
|
|
std::string d = base::StringEncoder::QueryEncode(gszDeviceId);
|
|
std::string o(base::StringEncoder::QueryEncode(HostGetPlatformString()));
|
|
const char *url;
|
|
if (strlen(handler.StatsUsername()) == 0) {
|
|
url = base::Format::ToString("%s?d=%s&o=%s", kszLeaderboardUrl,
|
|
d.c_str(), o.c_str());
|
|
} else {
|
|
std::string q = base::StringEncoder::QueryEncode(
|
|
handler.StatsUsername());
|
|
url = base::Format::ToString("%s?p=%s&d=%s&o=%s", kszLeaderboardUrl,
|
|
q.c_str(), d.c_str(), o.c_str());
|
|
}
|
|
HostInitiateWebView("Hostile Takeover Statistics", url);
|
|
return;
|
|
}
|
|
ShellForm::OnControlSelected(idc);
|
|
}
|
|
};
|
|
|
|
Shell gshl;
|
|
Shell::Shell()
|
|
{
|
|
m_ppal = NULL;
|
|
m_mpiclriclrShadow = NULL;
|
|
}
|
|
|
|
bool Shell::Init()
|
|
{
|
|
m_ppal = (Palette *)gpakr.MapFile("shell.palbin", &m_fmapPalette);
|
|
Assert(m_ppal != NULL);
|
|
m_mpiclriclrShadow = (byte *)gpakr.MapFile("shell.palbin.shadowmap", &m_fmapShadowMap);
|
|
Assert(m_mpiclriclrShadow != NULL);
|
|
return true;
|
|
}
|
|
|
|
void Shell::Exit()
|
|
{
|
|
if (m_ppal != NULL)
|
|
gpakr.UnmapFile(&m_fmapPalette);
|
|
if (m_mpiclriclrShadow != NULL)
|
|
gpakr.UnmapFile(&m_fmapShadowMap);
|
|
}
|
|
|
|
void Shell::SetPalette()
|
|
{
|
|
gmpiclriclrShadow = m_mpiclriclrShadow;
|
|
SetHslAdjustedPalette(m_ppal, gnHueOffset, gnSatMultiplier, gnLumOffset);
|
|
}
|
|
|
|
int Shell::PlayGame(PlayMode pm, MissionIdentifier *pmiid, Stream *pstm,
|
|
int nRank)
|
|
{
|
|
// UNDONE: free up space-taking Shell resources
|
|
|
|
int nGo;
|
|
|
|
do {
|
|
nGo = knGoSuccess;
|
|
|
|
switch (pm) {
|
|
case kpmNormal:
|
|
nGo = ggame.PlayLevel(pmiid, NULL, nRank);
|
|
break;
|
|
|
|
case kpmSavedGame:
|
|
nGo = ggame.PlaySavedGame(pstm);
|
|
break;
|
|
}
|
|
|
|
if (nGo == knGoLoadSavedGame) {
|
|
pm = kpmSavedGame;
|
|
pstm = gpstmSavedGame;
|
|
}
|
|
} while (nGo == knGoLoadSavedGame);
|
|
|
|
// UNDONE: reload space-taking Shell resources
|
|
|
|
gmpiclriclrShadow = m_mpiclriclrShadow;
|
|
|
|
return nGo;
|
|
}
|
|
|
|
void Shell::Launch(bool fLoadReinitializeSave, MissionIdentifier *pmiid)
|
|
{
|
|
#ifdef STRESS
|
|
static char *s_aszStressLevels[] = {
|
|
"S_01.lvl", "S_02.lvl", "S_03.lvl", "S_04.lvl", "S_05.lvl", "S_06.lvl", "S_07.lvl",
|
|
"S_08.lvl", "S_09.lvl", "S_10.lvl", "S_11.lvl", "S_12.lvl", "S_13.lvl", "S_14.lvl"
|
|
};
|
|
|
|
if (gfStress) {
|
|
while (true) {
|
|
gtStressTimeout = 30 * 60 * 100L; // 30 minutes
|
|
int ilvl = GetAsyncRandom() % ARRAYSIZE(s_aszStressLevels);
|
|
strncpyz(pmiid->szLvlFilename, s_aszStressLevels[ilvl],
|
|
sizeof(pmiid->szLvlFilename));
|
|
int nGo = PlayGame(kpmNormal, pmiid, NULL, 0);
|
|
if (nGo == knGoAppStop)
|
|
return;
|
|
}
|
|
}
|
|
#endif // def STRESS
|
|
|
|
#if 0
|
|
#if defined(DEBUG) && defined(CE)
|
|
WCHAR wszDeviceName[128];
|
|
SystemParametersInfo(SPI_GETOEMINFO, sizeof(wszDeviceName), &wszDeviceName, 0);
|
|
char tst[500];
|
|
int cch = wcslen(wszDeviceName);
|
|
char *pch = (char *)wszDeviceName;
|
|
char *pdst = tst;
|
|
while (cch > 0) {
|
|
if (*pch != 0)
|
|
*pdst++ = *pch++;
|
|
else
|
|
pch++;
|
|
cch--;
|
|
}
|
|
*pdst = 0;
|
|
HtMessageBox(kfMbClearDib, "Device ID String", tst);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BETA_TIMEOUT
|
|
HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "HOSTILE TAKEOVER",
|
|
"Hostile Takeover\n\n"
|
|
"Copyright 2003-2008 Spiffcode, Inc.\n\n"
|
|
"http://www.spiffcode.com\n\n"
|
|
"Test Release. Please keep private until out of beta. Thank you :)");
|
|
#endif
|
|
|
|
// Handle DRM
|
|
|
|
if (!DrmValidate())
|
|
return;
|
|
|
|
if (gevm.IsAppStopping())
|
|
return;
|
|
|
|
// Are we launching immediately into a saved game?
|
|
|
|
Stream *pstm = HostOpenSaveGameStream(knGameReinitializeSave, true);
|
|
|
|
if (pstm != NULL) {
|
|
|
|
// Stream is closed/deleted upon return
|
|
|
|
int nGo = PlayGame(kpmSavedGame, NULL, pstm, 0);
|
|
if (nGo == knGoAppStop)
|
|
return;
|
|
|
|
//if (nGo == knGoInitFailure)
|
|
// silently fall into a normal game open
|
|
|
|
} else if (pmiid != NULL) {
|
|
int nGo = PlayGame(kpmNormal, pmiid, NULL, 0);
|
|
if (nGo == knGoAppStop)
|
|
return;
|
|
|
|
if (nGo == knGoInitFailure)
|
|
HtMessageBox(kfMbWhiteBorder, "Load Game", "Error launch level!");
|
|
}
|
|
|
|
while (true) {
|
|
if (gevm.IsAppStopping()) {
|
|
return;
|
|
}
|
|
|
|
// Show startup form (new single player, new multi player, load saved
|
|
// game, etc)
|
|
|
|
ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms,
|
|
kidfStartup, new MainMenuForm());
|
|
Assert(pfrm != NULL);
|
|
if (pfrm == NULL)
|
|
return;
|
|
|
|
#ifdef V100_MENUS
|
|
pfrm->GetControlPtr(kidcPlayMultiPlayer)->Show(false);
|
|
pfrm->GetControlPtr(kidcPlaySinglePlayer)->Show(false);
|
|
if (gfDemo) {
|
|
pfrm->GetControlPtr(kidcBeginNewGame)->Show(false);
|
|
pfrm->GetControlPtr(kidcPlayMission)->Show(false);
|
|
} else {
|
|
pfrm->GetControlPtr(kidcPlayDemo)->Show(false);
|
|
Control *pctl = pfrm->GetControlPtr(kidcBuyMe);
|
|
Rect rc;
|
|
pctl->GetRect(&rc);
|
|
pctl->Show(false);
|
|
pctl = pfrm->GetControlPtr(kidcLoadSavedGame);
|
|
pctl->SetRect(&rc);
|
|
}
|
|
#else
|
|
if (gfDemo)
|
|
pfrm->GetControlPtr(kidcLoadSavedGame)->Show(false);
|
|
else
|
|
pfrm->GetControlPtr(kidcBuyMe)->Show(false);
|
|
#endif
|
|
|
|
// Make Shell palette and shadow map active
|
|
|
|
gshl.SetPalette();
|
|
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
delete pfrm;
|
|
|
|
// While the dialog was up the player might have exited the app
|
|
|
|
if (gevm.IsAppStopping())
|
|
return;
|
|
|
|
// Before beta check
|
|
|
|
switch (idc) {
|
|
case kidcPlay:
|
|
if (DoPlay()) {
|
|
return;
|
|
}
|
|
continue;
|
|
|
|
case kidcBuyMe:
|
|
DrmValidate();
|
|
continue;
|
|
|
|
case kidcSetupGame:
|
|
DoModalGameOptionsForm(m_ppal, false);
|
|
continue;
|
|
|
|
case kidcForums:
|
|
HostOpenUrl(kszForumUrl);
|
|
continue;
|
|
|
|
case kidcHelp:
|
|
Help();
|
|
continue;
|
|
|
|
case kidcCredits:
|
|
Help("credits");
|
|
continue;
|
|
}
|
|
|
|
#ifdef BETA_TIMEOUT
|
|
if (!CheckBetaTimeout()) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
switch (idc) {
|
|
case kidcPlayDemo:
|
|
case kidcBeginNewGame:
|
|
if (BeginNewGame())
|
|
return;
|
|
break;
|
|
|
|
case kidcPlayMission:
|
|
case kidcPlaySinglePlayer:
|
|
if (PlaySinglePlayer(NULL)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case kidcPlayMultiPlayer:
|
|
if (PlayMultiplayer(NULL)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case kidcLoadSavedGame:
|
|
{
|
|
Stream *pstm = PickLoadGameStream();
|
|
if (pstm == NULL)
|
|
break;
|
|
|
|
// Stream is closed/deleted upon return
|
|
|
|
int nGo = PlayGame(kpmSavedGame, NULL, pstm, 0);
|
|
|
|
if (nGo == knGoAppStop)
|
|
return;
|
|
|
|
if (nGo == knGoInitFailure)
|
|
HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Load Game", "Error loading saved game!");
|
|
}
|
|
break;
|
|
|
|
case kidcDownloadMissions:
|
|
DownloadMissionPack();
|
|
break;
|
|
|
|
case kidcExitGame:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Shell::DoPlay()
|
|
{
|
|
while (true) {
|
|
ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms,
|
|
kidfPlay, new ShellForm());
|
|
if (pfrm == NULL) {
|
|
return kidcCancel;
|
|
}
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
delete pfrm;
|
|
|
|
if (gevm.IsAppStopping()) {
|
|
return true;
|
|
}
|
|
|
|
if (idc == kidcCancel) {
|
|
return false;
|
|
}
|
|
|
|
#ifdef BETA_TIMEOUT
|
|
if (!CheckBetaTimeout()) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (idc == kidcPlaySinglePlayer) {
|
|
if (PlaySinglePlayer(NULL)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (idc == kidcPlayMultiPlayer) {
|
|
if (PlayMultiplayer(NULL)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Shell::DownloadMissionPack()
|
|
{
|
|
// Show the download form. True is returned if the user wants to
|
|
// play the first mission in the pack identified by packid.
|
|
|
|
PackId packid;
|
|
if (!ShowDownloadMissionPackForm(&packid)) {
|
|
return;
|
|
}
|
|
|
|
// The downloaded a pack identified by packid, and wants to play it.
|
|
// Find the first mission and determine if it's single or multi
|
|
// player. Then, launch the appropriate Play form with this info.
|
|
// It will show, with the appropriate mission highlighted.
|
|
|
|
MissionList *pml = CreateMissionList(&packid, kmltAll);
|
|
if (pml == NULL) {
|
|
return;
|
|
}
|
|
if (pml->GetCount() == 0) {
|
|
delete pml;
|
|
return;
|
|
}
|
|
MissionDescription md;
|
|
pml->GetMissionDescription(0, &md);
|
|
|
|
// Show the appropriate form. This will cause the first mission from
|
|
// this pack to be highlighted.
|
|
|
|
if (pml->IsMultiplayerMissionType(md.mt)) {
|
|
PlayMultiplayer(&packid);
|
|
} else {
|
|
PlaySinglePlayer(&packid);
|
|
}
|
|
|
|
delete pml;
|
|
}
|
|
|
|
bool Shell::PlayMultiplayer(const PackId *ppackid)
|
|
{
|
|
Lobby lobby;
|
|
dword result = lobby.Shell(ppackid);
|
|
return result == knShellResultAppStop;
|
|
}
|
|
|
|
bool Shell::PlaySinglePlayer(const PackId *ppackid)
|
|
{
|
|
MissionIdentifier miidFind;
|
|
memset(&miidFind, 0, sizeof(miidFind));
|
|
if (ppackid != NULL) {
|
|
miidFind.packid = *ppackid;
|
|
}
|
|
|
|
while (true) {
|
|
// First, create a mission list, which is an enumerator for the
|
|
// missions we want to show in this form.
|
|
|
|
MissionList *pml = CreateMissionList(NULL, kmltSinglePlayer);
|
|
if (pml == NULL) {
|
|
return true;
|
|
}
|
|
SelectMissionForm *pfrm = (SelectMissionForm *)gpmfrmm->LoadForm(
|
|
gpiniForms, kidfSelectMissionWide,
|
|
new SelectMissionForm(pml, &miidFind));
|
|
if (pfrm == NULL) {
|
|
delete pml;
|
|
return true;
|
|
}
|
|
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
|
|
MissionIdentifier miid;
|
|
bool fMissionSelected = false;
|
|
if (idc == kidcOk) {
|
|
if (pfrm->GetSelectedMission(&miid)) {
|
|
miidFind = miid;
|
|
fMissionSelected = true;
|
|
}
|
|
}
|
|
|
|
delete pfrm;
|
|
delete pml;
|
|
|
|
// While the dialog was up the player might have exited the app
|
|
|
|
if (gevm.IsAppStopping())
|
|
return true;
|
|
|
|
if (!fMissionSelected)
|
|
return false;
|
|
|
|
int nGo = PlayGame(kpmNormal, &miid, NULL, 0);
|
|
|
|
// The next time the SelectMission form shows, highlight the last
|
|
// mission the user was playing.
|
|
|
|
miidFind = ggame.GetLastMissionIdentifier();
|
|
|
|
if (nGo == knGoAppStop) {
|
|
return true;
|
|
}
|
|
|
|
if (nGo == knGoInitFailure) {
|
|
HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission.");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if 0
|
|
// Returns true if the app is stopping
|
|
|
|
bool Shell::PlaySinglePlayer()
|
|
{
|
|
while (true) {
|
|
ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfPlaySolo, new ShellForm());
|
|
if (pfrm == NULL)
|
|
return false;
|
|
#if !defined(DEV_BUILD)
|
|
pfrm->GetControlPtr(kidcLoadSavedGame)->Show(false);
|
|
pfrm->GetControlPtr(kidcPlaybackGame)->Show(false);
|
|
#endif
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
delete pfrm;
|
|
|
|
// While the dialog was up the player might have exited the app
|
|
|
|
if (gevm.IsAppStopping())
|
|
return true;
|
|
|
|
switch (idc) {
|
|
case kidcCancel:
|
|
return false;
|
|
|
|
case kidcBeginNewGame:
|
|
return BeginNewGame();
|
|
|
|
case kidcPlayChallengeLevel:
|
|
if (PlayChallengeLevel(false))
|
|
return true;
|
|
break;
|
|
|
|
case kidcPlayStoryMission:
|
|
if (PlayChallengeLevel(true))
|
|
return true;
|
|
break;
|
|
|
|
case kidcPlaybackGame:
|
|
break;
|
|
|
|
case kidcLoadSavedGame:
|
|
{
|
|
Stream *pstm = PickLoadGameStream();
|
|
if (pstm == NULL)
|
|
break;
|
|
|
|
// Stream is closed/deleted upon return
|
|
|
|
int nGo = PlayGame(kpmSavedGame, pstm);
|
|
|
|
if (nGo == knGoAppStop)
|
|
return true;
|
|
|
|
if (nGo == knGoInitFailure)
|
|
HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Load Game", "Error loading saved game!");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool Shell::PlayChallengeLevel(bool fStory)
|
|
{
|
|
#if 0
|
|
while (true) {
|
|
// Have the player select a level to play
|
|
|
|
PickLevelForm *pfrm = (PickLevelForm *)gpmfrmm->LoadForm(gpiniForms, kidfPickLevel,
|
|
new PickLevelForm(fStory ? kltStory : kltChallenge));
|
|
Assert(pfrm != NULL);
|
|
if (pfrm == NULL)
|
|
return true;
|
|
|
|
if (gfDemo) {
|
|
Control *pctl = pfrm->GetControlPtr(kidcOk);
|
|
pctl->Show(false);
|
|
} else {
|
|
#if 0
|
|
// UNDONE:
|
|
pctl = GetControlPtr(kidcPleaseRegister);
|
|
pctl->Show(false);
|
|
#endif
|
|
}
|
|
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
|
|
char szLevel[kcbFilename];
|
|
if (idc != kidcCancel)
|
|
strcpy(szLevel, pfrm->m_szLevel);
|
|
delete pfrm;
|
|
|
|
// While the dialog was up the player might have exited the app
|
|
|
|
if (gevm.IsAppStopping())
|
|
return true;
|
|
|
|
if (idc == kidcCancel)
|
|
return false;
|
|
|
|
int nGo = PlayGame(kpmNormal, szLevel);
|
|
if (nGo == knGoAppStop)
|
|
return true;
|
|
|
|
if (nGo == knGoInitFailure) {
|
|
HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission.");
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool Shell::BeginNewGame()
|
|
{
|
|
#if 0
|
|
bool fNewGame = true;
|
|
int nRank = 0;
|
|
if ((gnDemoRank != 0) && (!gfDemo)) {
|
|
|
|
// would you like to replay missions?
|
|
// UNDONE: Expand HtMessageBox and use it here instead of all this.
|
|
|
|
DialogForm *pfrm = (DialogForm *)gpmfrmm->LoadForm(gpiniForms, kidfContinueGame, new DialogForm());
|
|
if (pfrm != NULL) {
|
|
pfrm->SetBorderColorIndex(kiclrWhite);
|
|
pfrm->SetTitleColor(GetColor(kiclrSide1));
|
|
pfrm->SetBackgroundColorIndex(kiclrShadow2x);
|
|
pfrm->SetClearDibFlag();
|
|
|
|
// position the form
|
|
|
|
Rect rcForm;
|
|
pfrm->GetRect(&rcForm);
|
|
DibBitmap *pbm = pfrm->GetFormMgr()->GetDib();
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
int yNew = (siz.cy - rcForm.Height()) / 2;
|
|
int xNew = (siz.cx - rcForm.Width()) / 2;
|
|
rcForm.Offset(xNew - rcForm.left, yNew - rcForm.top);
|
|
pfrm->SetRect(&rcForm);
|
|
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
gpmfrmm->RemoveForm(pfrm);
|
|
|
|
if (gevm.IsAppStopping())
|
|
return false;
|
|
|
|
|
|
fNewGame = (idc == kidcOk);
|
|
delete pfrm;
|
|
}
|
|
|
|
nRank = fNewGame ? 0 : gnDemoRank;
|
|
gnDemoRank = 0;
|
|
ggame.SavePreferences();
|
|
}
|
|
int nGo = PlayGame(kpmNormal, (void *)(fNewGame ? "S_00.lvl" : "S_03.lvl"), nRank);
|
|
if (nGo == knGoAppStop)
|
|
return true;
|
|
|
|
if (nGo == knGoInitFailure)
|
|
HtMessageBox(kfMbWhiteBorder, "Error", "Unable to load and initialize the mission.");
|
|
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// PickLevelForm implementation
|
|
//
|
|
|
|
PickLevelForm::PickLevelForm(LevelType lt)
|
|
{
|
|
m_lt = lt;
|
|
m_szLevel[0] = 0;
|
|
}
|
|
|
|
int CreateLevelList(char **ppsz)
|
|
{
|
|
// Get all the .lvl files
|
|
|
|
char szFn[kcbFilename];
|
|
int cFiles = 0;
|
|
|
|
Enum enm;
|
|
while (gpakr.EnumFiles(&enm, szFn, sizeof(szFn))) {
|
|
int cch = strlen(szFn);
|
|
if (cch < 4)
|
|
continue;
|
|
if (szFn[cch - 4] == '.' && szFn[cch - 3] == 'l' && szFn[cch - 2] == 'v' && szFn[cch - 1] == 'l') {
|
|
strncpyz((char *)&gpbScratch[cFiles * kcbFilename], szFn, kcbFilename);
|
|
cFiles++;
|
|
}
|
|
}
|
|
|
|
// Sort them based on filename!
|
|
|
|
for (int i = cFiles - 1; i >= 0; i--) {
|
|
for (int j = 1; j <= i; j++) {
|
|
char *pszBack = (char *)&gpbScratch[(j - 1) * kcbFilename];
|
|
char *pszAhead = (char *)&gpbScratch[j * kcbFilename];
|
|
|
|
if (strcmp(pszBack, pszAhead) > 0) {
|
|
char szT[kcbFilename];
|
|
strcpy(szT, pszBack);
|
|
strcpy(pszBack, pszAhead);
|
|
strcpy(pszAhead, szT);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Alloc a chunk and copy them in
|
|
|
|
char *pszT = new char[cFiles * kcbFilename];
|
|
if (pszT == NULL)
|
|
return 0;
|
|
memcpy(pszT, gpbScratch, cFiles * kcbFilename);
|
|
*ppsz = pszT;
|
|
return cFiles;
|
|
}
|
|
|
|
bool PickLevelForm::Init(FormMgr *pfrmm, IniReader *pini, word idf)
|
|
{
|
|
if (!ShellForm::Init(pfrmm, pini, idf))
|
|
return false;
|
|
|
|
// Create a entry for each level
|
|
|
|
char szLevel[kcbFilename];
|
|
char szTitle[100];
|
|
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcLevelList);
|
|
|
|
char *aszLevels = NULL;
|
|
int cLevels = CreateLevelList(&aszLevels);
|
|
for (int nLevel = 0; nLevel < cLevels; nLevel++) {
|
|
strncpyz(szLevel, (char *)&aszLevels[nLevel * kcbFilename], sizeof(szLevel));
|
|
|
|
// Only show the requested level types
|
|
|
|
LevelType lt;
|
|
if (strnicmp(szLevel, "m_", 2) == 0)
|
|
lt = kltMultiplayer;
|
|
else if (strnicmp(szLevel, "s_", 2) == 0)
|
|
lt = kltStory;
|
|
else
|
|
lt = kltChallenge;
|
|
|
|
if (lt != m_lt)
|
|
continue;
|
|
|
|
// Pull title from level.
|
|
|
|
#if 1
|
|
//faster
|
|
strcpy(szTitle, "<untitled>");
|
|
IniReader *pini = LoadIniFile(gpakr, szLevel);
|
|
if (pini != NULL) {
|
|
pini->GetPropertyValue("General", "Title", szTitle, sizeof(szTitle));
|
|
delete pini;
|
|
}
|
|
plstc->Add(szTitle, (void *)nLevel);
|
|
#else
|
|
Level *plvl = new Level();
|
|
plvl->LoadLevelInfo(szLevel);
|
|
plstc->Add(plvl->GetTitle(), (void *)nLevel);
|
|
delete plvl;
|
|
#endif
|
|
}
|
|
|
|
delete aszLevels;
|
|
return true;
|
|
}
|
|
|
|
void PickLevelForm::OnControlSelected(word idc)
|
|
{
|
|
if (idc == kidcOk) {
|
|
ListControl *plstc = (ListControl *)GetControlPtr(kidcLevelList);
|
|
int nLevel = (int)plstc->GetSelectedItemData();
|
|
char *aszLevels = NULL;
|
|
int cLevels = CreateLevelList(&aszLevels);
|
|
if (nLevel >= 0 && nLevel <= cLevels) {
|
|
strcpy(m_szLevel, (char *)&aszLevels[nLevel * kcbFilename]);
|
|
delete aszLevels;
|
|
} else {
|
|
HtMessageBox(kfMbWhiteBorder, "Error!", "First you must select a level to play.");
|
|
delete aszLevels;
|
|
return;
|
|
}
|
|
} else if (idc == kidcLevelList) {
|
|
return;
|
|
}
|
|
EndForm(idc);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// ShellForm implementation
|
|
//
|
|
|
|
ShellForm::ShellForm()
|
|
{
|
|
m_fCached = false;
|
|
m_fAnimate = true;
|
|
m_fTimerEnabled = false;
|
|
}
|
|
|
|
ShellForm::~ShellForm()
|
|
{
|
|
if (m_fTimerEnabled)
|
|
gtimm.RemoveTimer(this);
|
|
}
|
|
|
|
bool ShellForm::Init(FormMgr *pfrmm, IniReader *pini, word idf)
|
|
{
|
|
if (!Form::Init(pfrmm, pini, idf))
|
|
return false;
|
|
|
|
// Keep invisible until the controls are at the right place
|
|
|
|
Show(false);
|
|
|
|
// Shell forms draw over the whole screen, so size the form to be full screen.
|
|
// This way it is a full screen opaquing form when it comes up, which means things
|
|
// won't try to draw behind it, slowing the game down
|
|
|
|
DibBitmap *pbm = m_pfrmm->GetDib();
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
|
|
int xNew = (siz.cx - m_rc.Width()) / 2;
|
|
int yNew = (siz.cy - m_rc.Height()) / 2;
|
|
|
|
// Reposition the controls
|
|
|
|
for (int n = 0; n < m_cctl; n++) {
|
|
Control *pctl = m_apctl[n];
|
|
Rect rcCtl;
|
|
pctl->GetRect(&rcCtl);
|
|
int xNewCtl = rcCtl.left + xNew;
|
|
int yNewCtl = rcCtl.top + yNew;
|
|
pctl->SetPosition(xNewCtl, yNewCtl);
|
|
}
|
|
Rect rcNew;
|
|
rcNew.Set(0, 0, siz.cx, siz.cy);
|
|
SetRect(&rcNew);
|
|
|
|
// Set version string - hack
|
|
|
|
if (m_idf == kidfStartup) {
|
|
LabelControl *pctl = (LabelControl *)GetControlPtr(kidcVersion);
|
|
if (pctl != NULL) {
|
|
// If no version string, exe+data are unmarked. Show date/time
|
|
// Use the version string verbatim so we show extra text at the end
|
|
|
|
char szT[64];
|
|
if (!ggame.GetFormattedVersionString(gszVersion, szT)) {
|
|
szT[0] = 0;
|
|
#ifdef DEV_BUILD
|
|
strcat(szT, "DEV BUILD ");
|
|
#endif
|
|
strcat(szT, __DATE__);
|
|
strcat(szT, ", ");
|
|
strcat(szT, __TIME__);
|
|
} else {
|
|
szT[0] = 'v';
|
|
strncpyz(&szT[1], gszVersion, sizeof(szT));
|
|
}
|
|
#if __LP64__
|
|
strcat(szT, " (64 bit)");
|
|
#endif
|
|
pctl->SetText(szT);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define kctRate 2
|
|
#define kcFcZip 18
|
|
bool ShellForm::DoModal(int *pnResult, bool fAnimate, bool fShowSound)
|
|
{
|
|
m_fAnimate = fAnimate;
|
|
if (!m_fAnimate) {
|
|
if (fShowSound)
|
|
gsndm.PlaySfx(ksfxGuiFormShow);
|
|
return Form::DoModal(pnResult, (Sfx)-1, (Sfx)-1);
|
|
}
|
|
|
|
// Take over form show sound playing
|
|
|
|
Size siz;
|
|
ggame.GetPlayfieldSize(&siz);
|
|
|
|
// Reposition all the form's controls of the form so they can be zipped in
|
|
|
|
for (int i = 0; i < m_cctl; i++) {
|
|
Control *pctl = m_apctl[i];
|
|
|
|
Rect rcCtl;
|
|
pctl->GetRect(&rcCtl);
|
|
|
|
// Remember where the control is supposed to end up
|
|
|
|
m_axDst[i] = (rcCtl.left & ~1);
|
|
|
|
// Odd index controls fly in from the left, even from the right
|
|
|
|
if (i & 1) {
|
|
pctl->SetPosition(rcCtl.left - siz.cx - (i * PcFromFc(kcFcZip)), rcCtl.top);
|
|
} else {
|
|
pctl->SetPosition(rcCtl.left + siz.cx + (i * PcFromFc(kcFcZip)), rcCtl.top);
|
|
}
|
|
}
|
|
|
|
// Now that the controls are positioned right make the form visible
|
|
|
|
Show(true);
|
|
|
|
if (fShowSound)
|
|
m_wf |= kfFrmShowSound;
|
|
|
|
// Start timer
|
|
|
|
gtimm.AddTimer(this, kctRate);
|
|
m_fTimerEnabled = true;
|
|
m_fCached = false;
|
|
m_tLast = 0;
|
|
|
|
// Some controls might be added on the fly after ShellForm::Init. Since we
|
|
// won't have their destination stashed away we'll just ignore them.
|
|
|
|
m_cctlToZip = m_cctl;
|
|
|
|
return Form::DoModal(pnResult, (Sfx)-1, (Sfx)-1);
|
|
}
|
|
|
|
void ShellForm::OnTimer(long tCurrent)
|
|
{
|
|
Assert(m_fAnimate);
|
|
|
|
int ct = (int)(tCurrent - m_tLast);
|
|
m_tLast = tCurrent;
|
|
|
|
// Waiting until all the pieces have been cached before doing absolute
|
|
// timing. This gives smoother movement.
|
|
|
|
int pcMove = PcFromFc(kcFcZip);
|
|
if (m_fCached)
|
|
pcMove = ((ct + kctRate / 2) / kctRate) * PcFromFc(kcFcZip);
|
|
|
|
bool fZipping = false;
|
|
|
|
for (int i = 0; i < m_cctlToZip; i++) {
|
|
Control *pctl = m_apctl[i];
|
|
|
|
Rect rcCtl;
|
|
pctl->GetRect(&rcCtl);
|
|
|
|
int xDst = m_axDst[i];
|
|
if (rcCtl.left == xDst)
|
|
continue;
|
|
|
|
fZipping = true;
|
|
|
|
int x;
|
|
if (xDst > rcCtl.left)
|
|
x = _min(rcCtl.left + pcMove, xDst);
|
|
else
|
|
x = _max(rcCtl.left - pcMove, xDst);
|
|
|
|
pctl->SetPosition(x & ~1, rcCtl.top);
|
|
}
|
|
|
|
gevm.SetRedrawFlags(kfRedrawDirty | kfRedrawBeforeTimer);
|
|
|
|
if (!fZipping) {
|
|
gtimm.RemoveTimer(this);
|
|
m_fTimerEnabled = false;
|
|
if (m_wf & kfFrmShowSound)
|
|
gsndm.PlaySfx(ksfxGuiFormShow);
|
|
OnZipDone();
|
|
}
|
|
}
|
|
|
|
void ShellForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd)
|
|
{
|
|
// Draw the fancy background bitmap
|
|
|
|
Size sizDib;
|
|
pbm->GetSize(&sizDib);
|
|
RawBitmap *prbm = LoadRawBitmap("titlescreenbkgd.rbm");
|
|
Size sizBmp;
|
|
prbm->GetSize(&sizBmp);
|
|
|
|
// Draw middle
|
|
Rect rcBmp;
|
|
rcBmp.left = ((sizDib.cx - sizBmp.cx) / 2) & ~1;
|
|
rcBmp.top = (sizDib.cy - sizBmp.cy) / 2;
|
|
rcBmp.right = rcBmp.left + sizBmp.cx;
|
|
rcBmp.bottom = rcBmp.top + sizBmp.cy;
|
|
BltHelper(pbm, prbm, pupd, rcBmp.left, rcBmp.top);
|
|
|
|
#if 0
|
|
// Draw left
|
|
if (rcBmp.left > 0) {
|
|
BltHelper(pbm, prbm, pupd, rcBmp.left - sizBmp.cx, rcBmp.top);
|
|
}
|
|
|
|
// Draw right
|
|
if (rcBmp.right < sizDib.cx) {
|
|
BltHelper(pbm, prbm, pupd, rcBmp.right, rcBmp.top);
|
|
}
|
|
#endif
|
|
|
|
delete prbm;
|
|
|
|
// If the screen is wider than the form we clear those areas
|
|
// out first to the form's background color
|
|
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
if (rcBmp.top > 0) {
|
|
Rect rc;
|
|
rc.Set(0, 0, siz.cx, rcBmp.top);
|
|
FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack));
|
|
}
|
|
if (rcBmp.bottom < siz.cy) {
|
|
Rect rc;
|
|
rc.Set(0, rcBmp.bottom, siz.cx, siz.cy);
|
|
FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack));
|
|
}
|
|
|
|
if (rcBmp.left > 0) {
|
|
Rect rc;
|
|
rc.Set(0, rcBmp.top, rcBmp.left, rcBmp.bottom);
|
|
FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack));
|
|
}
|
|
if (rcBmp.right < siz.cx) {
|
|
Rect rc;
|
|
rc.Set(rcBmp.right, rcBmp.top, siz.cx, rcBmp.bottom);
|
|
FillHelper(pbm, pupd, &rc, GetColor(kiclrBlack));
|
|
}
|
|
|
|
// Don't start timing for absolute positioned animation until we've
|
|
// loaded the cache at least once. Gives smoother animation
|
|
|
|
if (!m_fCached) {
|
|
m_fCached = true;
|
|
m_tLast = gtimm.GetTickCount();
|
|
}
|
|
}
|
|
|
|
//
|
|
// RegisterNowForm
|
|
//
|
|
|
|
class RegisterNowForm : public ShellForm
|
|
{
|
|
public:
|
|
virtual bool OnPenEvent(Event *pevt) secGameOptionsForm;
|
|
};
|
|
|
|
void DoRegisterNowForm()
|
|
{
|
|
ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfRegisterNow, new RegisterNowForm());
|
|
if (pfrm != NULL) {
|
|
pfrm->DoModal();
|
|
delete pfrm;
|
|
}
|
|
}
|
|
|
|
bool RegisterNowForm::OnPenEvent(Event *pevt)
|
|
{
|
|
if (pevt->eType == penDownEvent) {
|
|
EndForm(kidcOk);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace wi
|