You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1345 lines
41 KiB
1345 lines
41 KiB
#include "qlimits.h" |
|
#include "tier1/strtools.h" |
|
#include "tier1/utlvector.h" |
|
#include "asw_mission_chooser_source_local.h" |
|
#include "KeyValues.h" |
|
#include "filesystem.h" |
|
#include "convar.h" |
|
#include "asw_system.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
static FileFindHandle_t g_hmapfind = FILESYSTEM_INVALID_FIND_HANDLE; |
|
static FileFindHandle_t g_hcampaignfind = FILESYSTEM_INVALID_FIND_HANDLE; |
|
static FileFindHandle_t g_hsavedfind = FILESYSTEM_INVALID_FIND_HANDLE; |
|
|
|
#define ASW_SKILL_POINTS_PER_MISSION 2 // keep in sync with asw_shareddefs.h (we need a h shared between missionchooser and game dlls...) |
|
|
|
ConVar asw_max_saves("asw_max_saves", "200", FCVAR_ARCHIVE, "Maximum number of multiplayer saves that will be stored on this server."); |
|
|
|
namespace |
|
{ |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Slightly modified strtok. Does not modify the input string. Does |
|
// not skip over more than one separator at a time. This allows parsing |
|
// strings where tokens between separators may or may not be present: |
|
// |
|
// Door01,,,0 would be parsed as "Door01" "" "" "0" |
|
// Door01,Open,,0 would be parsed as "Door01" "Open" "" "0" |
|
// |
|
// Input : token - Returns with a token, or zero length if the token was missing. |
|
// str - String to parse. |
|
// sep - Character to use as separator. UNDONE: allow multiple separator chars |
|
// Output : Returns a pointer to the next token to be parsed. |
|
//----------------------------------------------------------------------------- |
|
const char *nexttoken(char *token, const char *str, char sep) |
|
{ |
|
if ((str == NULL) || (*str == '\0')) |
|
{ |
|
*token = '\0'; |
|
return(NULL); |
|
} |
|
|
|
// |
|
// Copy everything up to the first separator into the return buffer. |
|
// Do not include separators in the return buffer. |
|
// |
|
while ((*str != sep) && (*str != '\0')) |
|
{ |
|
*token++ = *str++; |
|
} |
|
*token = '\0'; |
|
|
|
// |
|
// Advance the pointer unless we hit the end of the input string. |
|
// |
|
if (*str == '\0') |
|
{ |
|
return(str); |
|
} |
|
|
|
return(++str); |
|
} |
|
}; |
|
|
|
CASW_Mission_Chooser_Source_Local::CASW_Mission_Chooser_Source_Local() |
|
{ |
|
for (int i=0;i<ASW_SAVES_PER_PAGE;i++) // this array needs to be large enough for either ASW_SAVES_PER_PAGE (for when showing save games), or ASW_MISSIONS_PER_PAGE (for when showing missions) or ASW_CAMPAIGNS_PER_SCREEN (for when showing campaigns) |
|
{ |
|
Q_snprintf(m_missions[i].m_szMissionName, sizeof(m_missions[i].m_szMissionName), ""); |
|
} |
|
m_bBuiltMapList = false; |
|
m_bBuiltCampaignList = false; |
|
m_bBuiltSavedCampaignList = false; |
|
|
|
m_bBuildingMapList = false; |
|
m_bBuildingCampaignList = false; |
|
m_bBuildingSavedCampaignList = false; |
|
|
|
m_pszMapFind = NULL; |
|
m_pszCampaignFind = NULL; |
|
m_pszSavedFind = NULL; |
|
} |
|
|
|
CASW_Mission_Chooser_Source_Local::~CASW_Mission_Chooser_Source_Local() |
|
{ |
|
m_MissionDetails.PurgeAndDeleteElements(); |
|
m_CampaignDetails.PurgeAndDeleteElements(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::OnSaveDeleted(const char *szSaveName) |
|
{ |
|
char fixedname[ 256 ]; |
|
Q_strncpy( fixedname, szSaveName, sizeof( fixedname ) ); |
|
Q_SetExtension( fixedname, ".campaignsave", sizeof( fixedname ) ); |
|
// check if this save is in the list of summaries, if so, remove it |
|
for (int i=0;i<m_SavedCampaignList.Count();i++) |
|
{ |
|
if (!Q_stricmp(m_SavedCampaignList[i].m_szSaveName, fixedname)) |
|
{ |
|
m_SavedCampaignList.Remove(i); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::OnSaveUpdated(const char *szSaveName) |
|
{ |
|
// if we haven't started scanning for saves yet, don't worry about it |
|
if (!m_bBuiltSavedCampaignList && !m_bBuildingSavedCampaignList) |
|
return; |
|
|
|
// make sure it has the campaignsave extension |
|
char stripped[256]; |
|
V_StripExtension(szSaveName, stripped, sizeof(stripped)); |
|
char szWithExtension[256]; |
|
Q_snprintf(szWithExtension, sizeof(szWithExtension), "%s.campaignsave", stripped); |
|
// check it's not already in the saved list |
|
for (int i=0;i<m_SavedCampaignList.Count();i++) |
|
{ |
|
if (!Q_strcmp(m_SavedCampaignList[i].m_szSaveName, szWithExtension)) |
|
{ |
|
m_SavedCampaignList.Remove(i); |
|
break; |
|
} |
|
} |
|
Msg("Updating save game summary %s\n", szSaveName); |
|
AddToSavedCampaignList(szWithExtension); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::RefreshSavedCampaigns() |
|
{ |
|
m_bBuildingSavedCampaignList = false; |
|
m_bBuiltSavedCampaignList = false; |
|
BuildSavedCampaignList(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::ClearMapList() |
|
{ |
|
m_Items.Purge(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::AddToMapList(const char *szMapName) |
|
{ |
|
MapListName item; |
|
Q_snprintf(item.szMapName, sizeof(item.szMapName), "%s", szMapName); |
|
m_Items.Insert( item ); |
|
|
|
// check if it has an overview txt |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szMapName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", stripped ); |
|
bool bHasOverviewOnHost = (g_pFullFileSystem->FileExists(tempfile)); |
|
//bool bNoOverview = false; |
|
if ( !bHasOverviewOnHost ) |
|
{ |
|
// try to load it directly from the maps folder |
|
Q_snprintf( tempfile, sizeof( tempfile ), "maps/%s.txt", stripped ); |
|
bHasOverviewOnHost = (g_pFullFileSystem->FileExists(tempfile)); |
|
} |
|
|
|
// add it to our overview list if it has an overview |
|
if (bHasOverviewOnHost) |
|
{ |
|
m_OverviewItems.Insert( item ); |
|
} |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::BuildMapList() |
|
{ |
|
if (m_bBuildingMapList || m_bBuiltMapList) // already building |
|
return; |
|
|
|
ClearMapList(); |
|
|
|
m_bBuildingMapList = true; |
|
|
|
// Search the directory structure. |
|
char mapwild[MAX_QPATH]; |
|
Q_strncpy(mapwild,"maps/*.bsp", sizeof( mapwild ) ); |
|
m_pszMapFind = Sys_FindFirst( g_hmapfind, mapwild, NULL, 0 ); |
|
// think will continue the search |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::Think() |
|
{ |
|
// if we're building the map list, continue our search of files |
|
if (m_bBuildingMapList) |
|
{ |
|
if (m_pszMapFind) |
|
{ |
|
//Msg("Adding map: %s\n", m_pszMapFind); |
|
AddToMapList(m_pszMapFind); |
|
m_pszMapFind = Sys_FindNext(g_hmapfind, NULL, 0); |
|
} |
|
else |
|
{ |
|
//Msg("Ending search for maps\n"); |
|
Sys_FindClose(g_hmapfind); |
|
m_bBuildingMapList = false; |
|
m_bBuiltMapList= true; |
|
} |
|
} |
|
// if we're building the campaign list, continue our search of files |
|
if (m_bBuildingCampaignList) |
|
{ |
|
if (m_pszCampaignFind) |
|
{ |
|
//Msg("Adding campaign: %s\n", m_pszCampaignFind); |
|
AddToCampaignList(m_pszCampaignFind); |
|
m_pszCampaignFind = Sys_FindNext(g_hcampaignfind, NULL, 0); |
|
} |
|
else |
|
{ |
|
//Msg("Ending search for campaigns\n"); |
|
Sys_FindClose(g_hcampaignfind); |
|
m_bBuildingCampaignList = false; |
|
m_bBuiltCampaignList= true; |
|
} |
|
} |
|
// if we're building the saved campaign list, continue our search of files |
|
if (m_bBuildingSavedCampaignList) |
|
{ |
|
if (m_pszSavedFind) |
|
{ |
|
//Msg("Adding saved campaign: %s\n", m_pszSavedFind); |
|
AddToSavedCampaignList(m_pszSavedFind); |
|
m_pszSavedFind = Sys_FindNext(g_hsavedfind, NULL, 0); |
|
} |
|
else |
|
{ |
|
//Msg("Ending search for saved campaigns\n"); |
|
Sys_FindClose(g_hsavedfind); |
|
m_bBuildingSavedCampaignList = false; |
|
m_bBuiltSavedCampaignList= true; |
|
} |
|
} |
|
} |
|
|
|
// called when server is in a relatively idle state (like during the briefing) |
|
// we can use this time to scan for missions if we need to |
|
void CASW_Mission_Chooser_Source_Local::IdleThink() |
|
{ |
|
// if we're not currently building any lists, try to start building one |
|
if (!m_bBuildingMapList && !m_bBuildingCampaignList && !m_bBuildingSavedCampaignList) |
|
{ |
|
if (!m_bBuiltMapList) |
|
{ |
|
BuildMapList(); |
|
} |
|
else |
|
{ |
|
if (!m_bBuiltCampaignList) |
|
{ |
|
BuildCampaignList(); |
|
} |
|
else if (!m_bBuiltSavedCampaignList) |
|
{ |
|
BuildSavedCampaignList(); |
|
} |
|
} |
|
} |
|
Think(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::FindMissionsInCampaign( int iCampaignIndex, int nMissionOffset, int iNumSlots ) |
|
{ |
|
if (!m_bBuiltMapList) |
|
BuildMapList(); |
|
|
|
ASW_Mission_Chooser_Mission* pCampaign = GetCampaign( iCampaignIndex ); |
|
if ( !pCampaign ) |
|
return; |
|
|
|
KeyValues *pCampaignDetails = GetCampaignDetails( pCampaign->m_szMissionName ); |
|
if ( !pCampaignDetails ) |
|
return; |
|
|
|
CUtlVector<KeyValues*> aMissionKeys; |
|
bool bSkippedFirst = false; |
|
for ( KeyValues *pMission = pCampaignDetails->GetFirstSubKey(); pMission; pMission = pMission->GetNextKey() ) |
|
{ |
|
if ( !Q_stricmp( pMission->GetName(), "MISSION" ) ) |
|
{ |
|
if ( !bSkippedFirst ) |
|
{ |
|
bSkippedFirst = true; |
|
} |
|
else |
|
{ |
|
aMissionKeys.AddToTail( pMission ); |
|
} |
|
} |
|
} |
|
|
|
int max_items = aMissionKeys.Count(); |
|
for ( int stored=0;stored<iNumSlots;stored++ ) |
|
{ |
|
int realoffset = nMissionOffset; |
|
if ( realoffset >= max_items || realoffset < 0 ) |
|
{ |
|
// no more missions... |
|
Q_snprintf( m_missions[stored].m_szMissionName, sizeof( m_missions[stored].m_szMissionName ), "" ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( m_missions[stored].m_szMissionName, sizeof( m_missions[stored].m_szMissionName ), "%s", aMissionKeys[realoffset]->GetString( "MapName" ) ); |
|
} |
|
nMissionOffset++; |
|
} |
|
} |
|
|
|
int CASW_Mission_Chooser_Source_Local::GetNumMissionsInCampaign( int iCampaignIndex ) |
|
{ |
|
if (!m_bBuiltMapList) |
|
BuildMapList(); |
|
|
|
ASW_Mission_Chooser_Mission* pCampaign = GetCampaign( iCampaignIndex ); |
|
if ( !pCampaign ) |
|
return 0; |
|
|
|
KeyValues *pCampaignDetails = GetCampaignDetails( pCampaign->m_szMissionName ); |
|
if ( !pCampaignDetails ) |
|
return 0; |
|
|
|
CUtlVector<KeyValues*> aMissionKeys; |
|
bool bSkippedFirst = false; |
|
for ( KeyValues *pMission = pCampaignDetails->GetFirstSubKey(); pMission; pMission = pMission->GetNextKey() ) |
|
{ |
|
if ( !Q_stricmp( pMission->GetName(), "MISSION" ) ) |
|
{ |
|
if ( !bSkippedFirst ) |
|
{ |
|
bSkippedFirst = true; |
|
} |
|
else |
|
{ |
|
aMissionKeys.AddToTail( pMission ); |
|
} |
|
} |
|
} |
|
|
|
return aMissionKeys.Count(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::FindMissions(int nMissionOffset, int iNumSlots, bool bRequireOverview) |
|
{ |
|
if (!m_bBuiltMapList) |
|
BuildMapList(); |
|
|
|
int max_items = bRequireOverview ? m_OverviewItems.Count() : m_Items.Count(); |
|
for (int stored=0;stored<iNumSlots;stored++) |
|
{ |
|
int realoffset = nMissionOffset; |
|
if (realoffset >= max_items || realoffset < 0) |
|
{ |
|
// no more missions... |
|
Q_snprintf(m_missions[stored].m_szMissionName, sizeof(m_missions[stored].m_szMissionName), ""); |
|
} |
|
else |
|
{ |
|
if (bRequireOverview) |
|
{ |
|
|
|
Q_snprintf(m_missions[stored].m_szMissionName, sizeof(m_missions[stored].m_szMissionName), "%s", m_OverviewItems[realoffset].szMapName); |
|
} |
|
else |
|
{ |
|
Q_snprintf(m_missions[stored].m_szMissionName, sizeof(m_missions[stored].m_szMissionName), "%s", m_Items[realoffset].szMapName); |
|
} |
|
} |
|
nMissionOffset++; |
|
} |
|
} |
|
|
|
|
|
// pass an array of mission names back |
|
ASW_Mission_Chooser_Mission* CASW_Mission_Chooser_Source_Local::GetMissions() |
|
{ |
|
return m_missions; |
|
} |
|
|
|
ASW_Mission_Chooser_Mission* CASW_Mission_Chooser_Source_Local::GetMission( int nIndex, bool bRequireOverview ) |
|
{ |
|
if (!m_bBuiltMapList) |
|
BuildMapList(); |
|
|
|
int max_items = bRequireOverview ? m_OverviewItems.Count() : m_Items.Count(); |
|
static ASW_Mission_Chooser_Mission mission; |
|
if (nIndex >= max_items || nIndex < 0) |
|
{ |
|
// no more missions... |
|
Q_snprintf(mission.m_szMissionName, sizeof(mission.m_szMissionName), ""); |
|
} |
|
else |
|
{ |
|
if (bRequireOverview) |
|
{ |
|
|
|
Q_snprintf(mission.m_szMissionName, sizeof(mission.m_szMissionName), "%s", m_OverviewItems[nIndex].szMapName); |
|
} |
|
else |
|
{ |
|
Q_snprintf(mission.m_szMissionName, sizeof(mission.m_szMissionName), "%s", m_Items[nIndex].szMapName); |
|
} |
|
} |
|
return &mission; |
|
} |
|
|
|
int CASW_Mission_Chooser_Source_Local::GetNumMissions(bool bRequireOverview) |
|
{ |
|
if (!m_bBuiltMapList) |
|
BuildMapList(); |
|
|
|
if (bRequireOverview) |
|
return m_OverviewItems.Count(); |
|
return m_Items.Count(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::ClearCampaignList() |
|
{ |
|
m_CampaignList.Purge(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::AddToCampaignList(const char *szCampaignName) |
|
{ |
|
MapListName item; |
|
Q_snprintf(item.szMapName, sizeof(item.szMapName), "%s", szCampaignName); |
|
m_CampaignList.Insert( item ); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::BuildCampaignList() |
|
{ |
|
if (m_bBuildingCampaignList || m_bBuiltCampaignList) // already building |
|
return; |
|
|
|
ClearCampaignList(); |
|
|
|
m_bBuildingCampaignList= true; |
|
|
|
// Search the directory structure. |
|
char mapwild[MAX_QPATH]; |
|
Q_strncpy(mapwild,"resource/campaigns/*.txt", sizeof( mapwild ) ); |
|
m_pszCampaignFind = Sys_FindFirst( g_hcampaignfind, mapwild, NULL, 0 ); |
|
// think will continue the search |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::FindCampaigns(int nCampaignOffset, int iNumSlots) |
|
{ |
|
if (!m_bBuiltCampaignList) |
|
BuildCampaignList(); |
|
|
|
int max_items = m_CampaignList.Count(); |
|
for (int stored=0;stored<iNumSlots;stored++) |
|
{ |
|
int realoffset = nCampaignOffset; |
|
if (stored < ASW_CAMPAIGNS_PER_PAGE) |
|
{ |
|
if (realoffset >= max_items || realoffset < 0) |
|
{ |
|
Q_snprintf(m_campaigns[stored].m_szMissionName, sizeof(m_campaigns[stored].m_szMissionName), ""); |
|
} |
|
else |
|
{ |
|
Q_snprintf(m_campaigns[stored].m_szMissionName, sizeof(m_campaigns[stored].m_szMissionName), "%s", m_CampaignList[realoffset].szMapName); |
|
|
|
} |
|
nCampaignOffset++; |
|
} |
|
} |
|
} |
|
|
|
// Passes an array of campaign names back |
|
ASW_Mission_Chooser_Mission* CASW_Mission_Chooser_Source_Local::GetCampaigns() |
|
{ |
|
if (!m_bBuiltCampaignList) |
|
BuildCampaignList(); |
|
return m_campaigns; |
|
} |
|
|
|
ASW_Mission_Chooser_Mission* CASW_Mission_Chooser_Source_Local::GetCampaign( int nIndex ) |
|
{ |
|
if (!m_bBuiltCampaignList) |
|
BuildCampaignList(); |
|
|
|
static ASW_Mission_Chooser_Mission campaign; |
|
int max_items = m_CampaignList.Count(); |
|
if (nIndex >= max_items || nIndex < 0) |
|
{ |
|
Q_snprintf(campaign.m_szMissionName, sizeof(campaign.m_szMissionName), ""); |
|
} |
|
else |
|
{ |
|
Q_snprintf(campaign.m_szMissionName, sizeof(campaign.m_szMissionName), "%s", m_CampaignList[nIndex].szMapName); |
|
|
|
} |
|
return &campaign; |
|
} |
|
|
|
int CASW_Mission_Chooser_Source_Local::GetNumCampaigns() |
|
{ |
|
if (!m_bBuiltCampaignList) |
|
BuildCampaignList(); |
|
|
|
return m_CampaignList.Count(); |
|
} |
|
|
|
// todo: accept a steam ID and only find saves from that person? |
|
void CASW_Mission_Chooser_Source_Local::FindSavedCampaigns( int nSaveOffset, int iNumSlots, bool bMultiplayer, const char *szFilterID) |
|
{ |
|
if (!m_bBuiltSavedCampaignList) |
|
BuildSavedCampaignList(); |
|
int skip_entries = nSaveOffset; // how many filter matching entries we're skipping |
|
int max_items = m_SavedCampaignList.Count(); |
|
int stored = 0; |
|
int offset = 0; |
|
|
|
// count the offset up until we've skipped the desired number of filter matching entries |
|
while (skip_entries > 0 && offset < max_items) |
|
{ |
|
if (bMultiplayer == m_SavedCampaignList[offset].m_bMultiplayer && SavePassesFilter(&m_SavedCampaignList[offset], szFilterID)) |
|
{ |
|
skip_entries--; |
|
} |
|
offset++; |
|
} |
|
|
|
while (stored < iNumSlots && offset < max_items) |
|
{ |
|
//Msg("testing save %d multi = %d we want multi = %d\n", offset, m_SavedCampaignList[offset].m_bMultiplayer, bMultiplayer); |
|
if (bMultiplayer == m_SavedCampaignList[offset].m_bMultiplayer && SavePassesFilter(&m_SavedCampaignList[offset], szFilterID)) |
|
{ |
|
Q_snprintf(m_savedcampaigns[stored].m_szSaveName, sizeof(m_savedcampaigns[stored].m_szSaveName), "%s", m_SavedCampaignList[offset].m_szSaveName); |
|
Q_snprintf(m_savedcampaigns[stored].m_szCampaignName, sizeof(m_savedcampaigns[stored].m_szCampaignName), "%s", m_SavedCampaignList[offset].m_szCampaignName); |
|
Q_snprintf(m_savedcampaigns[stored].m_szDateTime, sizeof(m_savedcampaigns[stored].m_szDateTime), "%s", m_SavedCampaignList[offset].m_szDateTime); |
|
Q_snprintf(m_savedcampaigns[stored].m_szPlayerNames, sizeof(m_savedcampaigns[stored].m_szPlayerNames), "%s", m_SavedCampaignList[offset].m_szPlayerNames); |
|
m_savedcampaigns[stored].m_iMissionsComplete = m_SavedCampaignList[offset].m_iMissionsComplete; |
|
stored++; |
|
} |
|
offset++; |
|
} |
|
|
|
// blank out any slots that didn't get filled |
|
for (int i=stored;i<iNumSlots;i++) |
|
{ |
|
Q_snprintf(m_savedcampaigns[i].m_szSaveName, sizeof(m_savedcampaigns[i].m_szSaveName), ""); |
|
Q_snprintf(m_savedcampaigns[i].m_szCampaignName, sizeof(m_savedcampaigns[i].m_szCampaignName), ""); |
|
Q_snprintf(m_savedcampaigns[i].m_szDateTime, sizeof(m_savedcampaigns[i].m_szDateTime), ""); |
|
Q_snprintf(m_savedcampaigns[i].m_szPlayerNames, sizeof(m_savedcampaigns[i].m_szPlayerNames), ""); |
|
m_savedcampaigns[i].m_iMissionsComplete = 0; |
|
} |
|
} |
|
|
|
ASW_Mission_Chooser_Saved_Campaign* CASW_Mission_Chooser_Source_Local::GetSavedCampaigns() |
|
{ |
|
if (!m_bBuiltSavedCampaignList) |
|
BuildSavedCampaignList(); |
|
|
|
return m_savedcampaigns; |
|
} |
|
|
|
ASW_Mission_Chooser_Saved_Campaign* CASW_Mission_Chooser_Source_Local::GetSavedCampaign( int nIndex, bool bMultiplayer, const char *szFilterID ) |
|
{ |
|
if (!m_bBuiltSavedCampaignList) |
|
BuildSavedCampaignList(); |
|
|
|
int skip_entries = nIndex; // how many filter matching entries we're skipping |
|
int max_items = m_SavedCampaignList.Count(); |
|
int offset = 0; |
|
|
|
// count the offset up until we've skipped the desired number of filter matching entries |
|
while (skip_entries > 0 && offset < max_items) |
|
{ |
|
if (bMultiplayer == m_SavedCampaignList[offset].m_bMultiplayer && SavePassesFilter(&m_SavedCampaignList[offset], szFilterID)) |
|
{ |
|
skip_entries--; |
|
} |
|
offset++; |
|
} |
|
|
|
static ASW_Mission_Chooser_Saved_Campaign save; |
|
Q_snprintf(save.m_szSaveName, sizeof(save.m_szSaveName), "%s", m_SavedCampaignList[offset].m_szSaveName); |
|
Q_snprintf(save.m_szCampaignName, sizeof(save.m_szCampaignName), "%s", m_SavedCampaignList[offset].m_szCampaignName); |
|
Q_snprintf(save.m_szDateTime, sizeof(save.m_szDateTime), "%s", m_SavedCampaignList[offset].m_szDateTime); |
|
Q_snprintf(save.m_szPlayerNames, sizeof(save.m_szPlayerNames), "%s", m_SavedCampaignList[offset].m_szPlayerNames); |
|
save.m_iMissionsComplete = m_SavedCampaignList[offset].m_iMissionsComplete; |
|
|
|
return &save; |
|
} |
|
|
|
bool CASW_Mission_Chooser_Source_Local::SavePassesFilter(ASW_Mission_Chooser_Saved_Campaign* pSaved, const char *szFilterID) |
|
{ |
|
if (!pSaved) |
|
return false; |
|
|
|
if (!szFilterID || Q_strlen(szFilterID) < 1) |
|
return true; |
|
|
|
// check our filterID is present in the playerids with this save |
|
const char *p = pSaved->m_szPlayerIDs; |
|
char token[128]; |
|
|
|
p = nexttoken( token, p, ' ' ); |
|
|
|
while ( Q_strlen( token ) > 0 ) |
|
{ |
|
// found a match |
|
if (!Q_stricmp(szFilterID, token)) |
|
return true; |
|
if (p) |
|
p = nexttoken( token, p, ' ' ); |
|
else |
|
token[0] = '\0'; |
|
} |
|
|
|
|
|
return false; |
|
} |
|
|
|
int CASW_Mission_Chooser_Source_Local::GetNumSavedCampaigns(bool bMultiplayer, const char *szFilterID) |
|
{ |
|
int iNumSaves = 0; |
|
for (int i=0;i<m_SavedCampaignList.Count();i++) |
|
{ |
|
if (m_SavedCampaignList[i].m_bMultiplayer == bMultiplayer && SavePassesFilter(&m_SavedCampaignList[i], szFilterID)) |
|
{ |
|
iNumSaves++; |
|
} |
|
} |
|
|
|
return iNumSaves; |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::ClearSavedCampaignList() |
|
{ |
|
m_SavedCampaignList.Purge(); |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::AddToSavedCampaignList(const char *szSaveName) |
|
{ |
|
// find out what campaign this save is for |
|
char szFullFileName[256]; |
|
Q_snprintf(szFullFileName, sizeof(szFullFileName), "save/%s", szSaveName); |
|
KeyValues *pSaveKeyValues = new KeyValues( szSaveName ); |
|
if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, szFullFileName)) |
|
{ |
|
const char *pCampaignName = pSaveKeyValues->GetString("CampaignName"); |
|
// check the campaign exists |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/campaigns/%s.txt", pCampaignName ); |
|
if (g_pFullFileSystem->FileExists(tempfile)) |
|
{ |
|
ASW_Mission_Chooser_Saved_Campaign item; |
|
Q_snprintf(item.m_szSaveName, sizeof(item.m_szSaveName), "%s", szSaveName); |
|
Q_snprintf(item.m_szCampaignName, sizeof(item.m_szCampaignName), "%s", pCampaignName); |
|
Q_snprintf(item.m_szDateTime, sizeof(item.m_szDateTime), "%s", pSaveKeyValues->GetString("DateTime")); |
|
//Msg("save %s multiplayer %d\n", szSaveName, pSaveKeyValues->GetInt("Multiplayer")); |
|
item.m_bMultiplayer = (pSaveKeyValues->GetInt("Multiplayer") > 0); |
|
//Msg("item multiplayer = %d\n", item.m_bMultiplayer); |
|
|
|
// check subsections for player names and player IDs, concat them into two strings |
|
char namebuffer[256]; |
|
char idbuffer[512]; |
|
namebuffer[0] = '\0'; |
|
idbuffer[0] = '\0'; |
|
int namepos = 0; |
|
int idpos = 0; |
|
KeyValues *pkvSubSection = pSaveKeyValues->GetFirstSubKey(); |
|
while ( pkvSubSection ) |
|
{ |
|
if ((Q_stricmp(pkvSubSection->GetName(), "PLAYER")==0) && namepos < 253) |
|
{ |
|
const char *pName = pkvSubSection->GetString("PlayerName"); |
|
if (pName && pName[0] != '\0') |
|
{ |
|
int namelength = Q_strlen(pName); |
|
for (int charcopy=0; charcopy<namelength && namepos<253; charcopy++) |
|
{ |
|
namebuffer[namepos] = pName[charcopy]; |
|
namepos++; |
|
} |
|
namebuffer[namepos] = ' '; namepos++; |
|
namebuffer[namepos] = '\0'; |
|
} |
|
} |
|
if ((Q_stricmp(pkvSubSection->GetName(), "DATA")==0) && idpos < 253) |
|
{ |
|
const char *pID = pkvSubSection->GetString("DataBlock"); |
|
if (pID && pID[0] != '\0') |
|
{ |
|
int idlength = Q_strlen(pID); |
|
for (int charcopy=0; charcopy<idlength && idpos<253; charcopy++) |
|
{ |
|
idbuffer[idpos] = pID[charcopy]; |
|
idpos++; |
|
} |
|
idbuffer[idpos] = ' '; idpos++; |
|
idbuffer[idpos] = '\0'; |
|
} |
|
} |
|
pkvSubSection = pkvSubSection->GetNextKey(); |
|
} |
|
Q_snprintf(item.m_szPlayerNames, sizeof(item.m_szPlayerNames), "%s", namebuffer); |
|
Q_snprintf(item.m_szPlayerIDs, sizeof(item.m_szPlayerIDs), "%s", idbuffer); |
|
item.m_iMissionsComplete = pSaveKeyValues->GetInt("NumMissionsComplete"); |
|
m_SavedCampaignList.Insert( item ); |
|
} |
|
} |
|
pSaveKeyValues->deleteThis(); |
|
|
|
// check if there's now too many save games |
|
int iNumMultiplayer = GetNumSavedCampaigns(true, NULL); |
|
if (iNumMultiplayer > asw_max_saves.GetInt()) |
|
{ |
|
// find the oldest one |
|
ASW_Mission_Chooser_Saved_Campaign* pChosen = false; |
|
int iChosen = -1; |
|
for (int i=m_SavedCampaignList.Count()-1; i>=0; i--) |
|
{ |
|
if (m_SavedCampaignList[i].m_bMultiplayer) |
|
{ |
|
pChosen = &m_SavedCampaignList[i]; |
|
iChosen = i; |
|
break; |
|
} |
|
} |
|
// delete if found |
|
if (iChosen != -1 && pChosen) |
|
{ |
|
char buffer[MAX_PATH]; |
|
Q_snprintf(buffer, sizeof(buffer), "save/%s", pChosen->m_szSaveName); |
|
Msg("Deleting save %s as we have too many\n", buffer); |
|
g_pFullFileSystem->RemoveFile( buffer, "GAME" ); |
|
m_SavedCampaignList.Remove(iChosen); |
|
} |
|
} |
|
} |
|
|
|
void CASW_Mission_Chooser_Source_Local::BuildSavedCampaignList() |
|
{ |
|
// don't start searching for saves until we've loaded in all the campaigns |
|
if (!m_bBuiltCampaignList) |
|
{ |
|
BuildCampaignList(); |
|
return; |
|
} |
|
if (m_bBuildingSavedCampaignList || m_bBuiltSavedCampaignList) |
|
return; |
|
|
|
ClearSavedCampaignList(); |
|
|
|
m_bBuildingSavedCampaignList = true; |
|
|
|
if (m_CampaignList.Count() <= 0) |
|
{ |
|
//Msg("Error: Cannot build saved campaign list until the campaign list is built\n"); |
|
return; |
|
} |
|
|
|
// Search the directory structure for save games |
|
char mapwild[MAX_QPATH]; |
|
Q_strncpy(mapwild,"save/*.campaignsave", sizeof( mapwild ) ); |
|
m_pszSavedFind = Sys_FindFirst( g_hsavedfind, mapwild, NULL, 0 ); |
|
// think will continue the search |
|
} |
|
|
|
bool CASW_Mission_Chooser_Source_Local::MissionExists(const char *szMapName, bool bRequireOverview) |
|
{ |
|
if ( !szMapName ) |
|
return false; |
|
|
|
// check it has an overview txt |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szMapName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
if (bRequireOverview) |
|
{ |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", stripped ); |
|
if (!g_pFullFileSystem->FileExists(tempfile)) |
|
return false; |
|
} |
|
|
|
// check the map exists |
|
Q_snprintf( tempfile, sizeof( tempfile ), "maps/%s.bsp", stripped ); |
|
return (g_pFullFileSystem->FileExists(tempfile)); |
|
} |
|
|
|
bool CASW_Mission_Chooser_Source_Local::CampaignExists(const char *szCampaignName) |
|
{ |
|
if ( !szCampaignName ) |
|
return false; |
|
|
|
// check the campaign txt exists |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szCampaignName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/campaigns/%s.txt", stripped ); |
|
return (g_pFullFileSystem->FileExists(tempfile)); |
|
} |
|
|
|
bool CASW_Mission_Chooser_Source_Local::SavedCampaignExists(const char *szSaveName) |
|
{ |
|
// check the save file exists |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szSaveName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "save/%s.campaignsave", stripped ); |
|
return (g_pFullFileSystem->FileExists(tempfile)); |
|
} |
|
|
|
int CASW_Mission_Chooser_Source_Local::GetNumMissionsCompleted(const char *szSaveName) |
|
{ |
|
Msg("GetNumMissionsCompleted %s\n", szSaveName); |
|
// check the save file exists |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szSaveName, stripped, MAX_PATH ); |
|
Msg(" stripped = %s\n", stripped); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "save/%s.campaignsave", stripped ); |
|
Msg(" tempfile = %s\n", tempfile); |
|
Q_strlower( tempfile ); |
|
Msg(" tempfile lowered = %s\n", tempfile); |
|
if (!g_pFullFileSystem->FileExists(tempfile)) |
|
{ |
|
Msg(" this save doesn't exist! returning -1 missions\n"); |
|
return -1; |
|
} |
|
|
|
KeyValues *pSaveKeyValues = new KeyValues( szSaveName ); |
|
if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, tempfile)) |
|
{ |
|
int iMissions = pSaveKeyValues->GetInt("NumMissionsComplete"); |
|
pSaveKeyValues->deleteThis(); |
|
Msg(" loaded keyvalues from file and it thinks num missions is %d\n", iMissions); |
|
return iMissions; |
|
} |
|
|
|
Msg(" Couldn't load save keyvalues from file, returning -1\n"); |
|
if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, tempfile, "MOD")) |
|
Msg(" but it loaded if we use the MOD path\n"); |
|
else if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, tempfile, "GAME")) |
|
Msg(" but it loaded if we use the GAME path\n"); |
|
pSaveKeyValues->deleteThis(); |
|
return -1; |
|
} |
|
|
|
const char* CASW_Mission_Chooser_Source_Local::GetPrettyMissionName(const char *szMapName) |
|
{ |
|
static char szPrettyName[64]; |
|
szPrettyName[0] = '\0'; |
|
|
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szMapName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", stripped ); |
|
|
|
KeyValues *pOverviewKeyValues = new KeyValues( szMapName ); |
|
if (pOverviewKeyValues->LoadFromFile(g_pFullFileSystem, tempfile)) |
|
{ |
|
Q_snprintf(szPrettyName, sizeof(szPrettyName), "%s", pOverviewKeyValues->GetString("missiontitle")); |
|
} |
|
pOverviewKeyValues->deleteThis(); |
|
|
|
return szPrettyName; |
|
} |
|
|
|
const char* CASW_Mission_Chooser_Source_Local::GetPrettyCampaignName(const char *szCampaignName) |
|
{ |
|
static char szPrettyName[64]; |
|
szPrettyName[0] = '\0'; |
|
|
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szCampaignName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/campaigns/%s.txt", stripped ); |
|
|
|
KeyValues *pCampaignKeyValues = new KeyValues( szCampaignName ); |
|
if (pCampaignKeyValues->LoadFromFile(g_pFullFileSystem, tempfile)) |
|
{ |
|
Q_snprintf(szPrettyName, sizeof(szPrettyName), "%s", pCampaignKeyValues->GetString("CampaignName")); |
|
} |
|
pCampaignKeyValues->deleteThis(); |
|
|
|
return szPrettyName; |
|
} |
|
|
|
const char* CASW_Mission_Chooser_Source_Local::GetPrettySavedCampaignName(const char *szSaveName) |
|
{ |
|
static char szPrettyName[256]; |
|
szPrettyName[0] = '\0'; |
|
|
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szSaveName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "save/%s.campaignsave", stripped ); |
|
|
|
KeyValues *pSaveKeyValues = new KeyValues( szSaveName ); |
|
if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, tempfile)) |
|
{ |
|
const char *szCampaignName = pSaveKeyValues->GetString("CampaignName"); |
|
const char *szPrettyCampaignName = szCampaignName; |
|
if (szCampaignName && Q_strlen(szCampaignName) > 0) |
|
{ |
|
szPrettyCampaignName = GetPrettyCampaignName(szCampaignName); |
|
} |
|
const char *szDate = pSaveKeyValues->GetString("DateTime"); |
|
char namebuffer[256]; |
|
namebuffer[0] = '\0'; |
|
int namepos = 0; |
|
KeyValues *pkvSubSection = pSaveKeyValues->GetFirstSubKey(); |
|
while ( pkvSubSection && namepos < 253) |
|
{ |
|
if (Q_stricmp(pkvSubSection->GetName(), "PLAYER")==0) |
|
{ |
|
const char *pName = pkvSubSection->GetString("PlayerName"); |
|
if (pName && pName[0] != '\0') |
|
{ |
|
if (namepos != 0) |
|
{ |
|
namebuffer[namepos] = ' '; namepos++; |
|
} |
|
int namelength = Q_strlen(pName); |
|
for (int charcopy=0; charcopy<namelength && namepos<253; charcopy++) |
|
{ |
|
namebuffer[namepos] = pName[charcopy]; |
|
namepos++; |
|
} |
|
namebuffer[namepos] = '\0'; |
|
} |
|
} |
|
pkvSubSection = pkvSubSection->GetNextKey(); |
|
} |
|
Q_snprintf(szPrettyName, sizeof(szPrettyName), |
|
"%s (%s) (%s)", |
|
szPrettyCampaignName, szDate, namebuffer); |
|
} |
|
pSaveKeyValues->deleteThis(); |
|
|
|
return szPrettyName; |
|
} |
|
|
|
// a new save has been created, add it to our summary list |
|
void CASW_Mission_Chooser_Source_Local::NotifyNewSave(const char *szSaveName) |
|
{ |
|
// if we haven't started scanning for saves yet, don't worry about it |
|
if (!m_bBuiltSavedCampaignList && !m_bBuildingSavedCampaignList) |
|
return; |
|
|
|
// make sure it has the campaignsave extension |
|
char stripped[256]; |
|
V_StripExtension(szSaveName, stripped, sizeof(stripped)); |
|
char szWithExtension[256]; |
|
Q_snprintf(szWithExtension, sizeof(szWithExtension), "%s.campaignsave", stripped); |
|
// check it's not already in the saved list |
|
for (int i=0;i<m_SavedCampaignList.Count();i++) |
|
{ |
|
if (!Q_strcmp(m_SavedCampaignList[i].m_szSaveName, szWithExtension)) |
|
return; |
|
} |
|
Msg("New save created, adding it to the list of saved campaigns: %s\n", szSaveName); |
|
AddToSavedCampaignList(szWithExtension); |
|
} |
|
|
|
// a new save has been created, add it to our summary list |
|
void CASW_Mission_Chooser_Source_Local::NotifySaveDeleted(const char *szSaveName) |
|
{ |
|
// if we haven't started scanning for saves yet, don't worry about it |
|
if (!m_bBuiltSavedCampaignList && !m_bBuildingSavedCampaignList) |
|
return; |
|
|
|
// make sure it has the campaignsave extension |
|
char stripped[256]; |
|
V_StripExtension(szSaveName, stripped, sizeof(stripped)); |
|
char szWithExtension[256]; |
|
Q_snprintf(szWithExtension, sizeof(szWithExtension), "%s.campaignsave", stripped); |
|
// find it in the list |
|
for (int i=0;i<m_SavedCampaignList.Count();i++) |
|
{ |
|
if (!Q_strcmp(m_SavedCampaignList[i].m_szSaveName, szWithExtension)) |
|
{ |
|
m_SavedCampaignList.Remove(i); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
// !!NOTE!! - these numbers are from asw_campaign_save.h. Duplicated constants for the lose. |
|
#define ASW_MAX_PLAYERS_PER_SAVE 10 |
|
#define ASW_MAX_MISSIONS_PER_CAMPAIGN 32 |
|
#define ASW_CURRENT_SAVE_VERSION 1 |
|
#define ASW_NUM_MARINE_PROFILES 9 // from asw_shareddefs.h |
|
|
|
bool CASW_Mission_Chooser_Source_Local::ASW_Campaign_CreateNewSaveGame(char *szFileName, int iFileNameMaxLen, const char *szCampaignName, bool bMultiplayerGame, const char *szStartingMission) // szFileName arg is the desired filename or NULL for an autogenerated one. Function sets szFileName with the filename used. |
|
{ |
|
if (!szFileName) |
|
return false; |
|
|
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szCampaignName, stripped, MAX_PATH ); |
|
|
|
char szStartingMissionStripped[64]; |
|
if ( szStartingMission ) |
|
{ |
|
V_StripExtension( szStartingMission, szStartingMissionStripped, sizeof( szStartingMissionStripped ) ); |
|
} |
|
else |
|
{ |
|
szStartingMissionStripped[0] = 0; |
|
} |
|
|
|
// check the campaign file exists |
|
char campbuffer[MAX_PATH]; |
|
Q_snprintf(campbuffer, sizeof(campbuffer), "resource/campaigns/%s.txt", stripped); |
|
if (!g_pFullFileSystem->FileExists(campbuffer)) |
|
{ |
|
Msg("No such campaign: %s\n", campbuffer); |
|
return false; |
|
} |
|
|
|
// Get the current time and date as a string |
|
char szDateTime[256]; |
|
int year, month, dayOfWeek, day, hour, minute, second; |
|
ASW_System_GetCurrentTimeAndDate(&year, &month, &dayOfWeek, &day, &hour, &minute, &second); |
|
Q_snprintf(szDateTime, sizeof(szDateTime), "%02d/%02d/%02d %02d:%02d", month, day, year, hour, minute); |
|
|
|
if (szFileName[0] == '\0') |
|
{ |
|
// autogenerate a filename based on the current time and date |
|
Q_snprintf(szFileName, iFileNameMaxLen, "%s_save_%02d_%02d_%02d_%02d_%02d_%02d", stripped, year, month, day, hour, minute, second); |
|
} |
|
|
|
// make sure the path and extension are correct |
|
Q_SetExtension( szFileName, ".campaignsave", iFileNameMaxLen ); |
|
char tempbuffer[256]; |
|
Q_snprintf(tempbuffer, sizeof(tempbuffer), "%s", szFileName); |
|
const char *pszNoPathName = Q_UnqualifiedFileName(tempbuffer); |
|
Msg("Unqualified = %s\n", pszNoPathName); |
|
char szFullFileName[256]; |
|
Q_snprintf(szFullFileName, sizeof(szFullFileName), "save/%s", pszNoPathName); |
|
Msg("Creating new save with filename: %s\n", szFullFileName); |
|
|
|
KeyValues *pSaveKeyValues = new KeyValues( pszNoPathName ); |
|
|
|
int nMissionsComplete = 0; |
|
if ( szStartingMission && szStartingMission[0] ) |
|
{ |
|
KeyValues *pCampaignDetails = GetCampaignDetails( stripped ); |
|
if ( pCampaignDetails ) |
|
{ |
|
int nMissionSections = 0; |
|
for ( KeyValues *pMission = pCampaignDetails->GetFirstSubKey(); pMission; pMission = pMission->GetNextKey() ) |
|
{ |
|
if ( !Q_stricmp( pMission->GetName(), "MISSION" ) ) |
|
{ |
|
if ( !Q_stricmp( pMission->GetString( "MapName", "" ), szStartingMissionStripped ) ) |
|
{ |
|
nMissionsComplete = nMissionSections - 1; // skip first dummy mission |
|
break; |
|
} |
|
nMissionSections++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
pSaveKeyValues->SetInt("Version", ASW_CURRENT_SAVE_VERSION); |
|
pSaveKeyValues->SetString("CampaignName", stripped); |
|
pSaveKeyValues->SetInt("CurrentPosition", nMissionsComplete + 1); // position squad on the first uncompleted mission |
|
pSaveKeyValues->SetInt("NumMissionsComplete", nMissionsComplete); |
|
pSaveKeyValues->SetInt("InitialNumMissionsComplete", nMissionsComplete); |
|
pSaveKeyValues->SetInt("Multiplayer", bMultiplayerGame ? 1 : 0); |
|
pSaveKeyValues->SetString("DateTime", szDateTime); |
|
pSaveKeyValues->SetInt("NumPlayers", 0); |
|
|
|
// write out each mission's status |
|
KeyValues *pSubSection; |
|
for (int i=0; i<ASW_MAX_MISSIONS_PER_CAMPAIGN; i++) |
|
{ |
|
pSubSection = new KeyValues("MISSION"); |
|
pSubSection->SetInt("MissionID", i); |
|
bool bComplete = ( i != 0 ) && ( i <= nMissionsComplete ); |
|
pSubSection->SetInt("MissionComplete", bComplete ? 1 : 0 ); |
|
pSaveKeyValues->AddSubKey(pSubSection); |
|
} |
|
|
|
const int nInitialSkillPoints = 0; |
|
|
|
// write out each marine's stats |
|
for (int i=0; i<ASW_NUM_MARINE_PROFILES; i++) |
|
{ |
|
pSubSection = new KeyValues("MARINE"); |
|
pSubSection->SetInt("MarineID", i); |
|
pSubSection->SetInt("SkillSlot0", 0); |
|
pSubSection->SetInt("SkillSlot1", 0); |
|
pSubSection->SetInt("SkillSlot2", 0); |
|
pSubSection->SetInt("SkillSlot3", 0); |
|
pSubSection->SetInt("SkillSlot4", 0); |
|
|
|
pSubSection->SetInt("SkillSlotSpare", nInitialSkillPoints + nMissionsComplete * ASW_SKILL_POINTS_PER_MISSION ); |
|
|
|
pSubSection->SetInt("UndoSkillSlot0", 0); |
|
pSubSection->SetInt("UndoSkillSlot1", 0); |
|
pSubSection->SetInt("UndoSkillSlot2", 0); |
|
pSubSection->SetInt("UndoSkillSlot3", 0); |
|
pSubSection->SetInt("UndoSkillSlot4", 0); |
|
|
|
pSubSection->SetInt("UndoSkillSlotSpare", nInitialSkillPoints + nMissionsComplete * ASW_SKILL_POINTS_PER_MISSION ); |
|
pSubSection->SetString("MissionsCompleted", ""); |
|
pSubSection->SetString("Medals", ""); |
|
pSubSection->SetInt("Wounded", 0); |
|
pSubSection->SetInt("Dead", 0); |
|
pSubSection->SetInt("ParasitesKilled", 0); |
|
pSaveKeyValues->AddSubKey(pSubSection); |
|
} |
|
// players section is empty at first |
|
|
|
// Create the save sub-directory |
|
if (!g_pFullFileSystem->IsDirectory( "save", "MOD" )) |
|
{ |
|
g_pFullFileSystem->CreateDirHierarchy( "save", "MOD" ); |
|
} |
|
|
|
// save it |
|
if (pSaveKeyValues->SaveToFile(g_pFullFileSystem, szFullFileName)) |
|
{ |
|
// make sure our save summary list adds this to it, if needed |
|
Msg("New save created: %s\n", szFullFileName); |
|
NotifyNewSave(pszNoPathName); |
|
return true; |
|
} |
|
Msg("Save to file failed. Filename=%s\n", szFullFileName); |
|
return false; |
|
} |
|
|
|
// normal alphabetical sorting for map/campaigns |
|
bool CASW_Mission_Chooser_Source_Local::MapNameLess::Less( MapListName const& src1, MapListName const& src2, void *pCtx ) |
|
{ |
|
return !!Q_strcmp(src1.szMapName,src2.szMapName); |
|
} |
|
|
|
// sort by the datetime string |
|
bool CASW_Mission_Chooser_Source_Local::SavedCampaignLess::Less( ASW_Mission_Chooser_Saved_Campaign const& src1, ASW_Mission_Chooser_Saved_Campaign const& src2, void *pCtx ) |
|
{ |
|
int month, day, year, hour, minute; |
|
int month2, day2, year2, hour2, minute2; |
|
|
|
if ( sscanf( src1.m_szDateTime, "%d/%d/%d %d:%d", &month, &day, &year, &hour, &minute ) != 5 ) |
|
return false; |
|
if ( sscanf( src2.m_szDateTime, "%d/%d/%d %d:%d", &month2, &day2, &year2, &hour2, &minute2 ) != 5 ) |
|
return false; |
|
|
|
//Msg("src1 month:%d day:%d year:%d hour:%d minute:%d\n", month, day, year, hour, minute); |
|
//Msg("src2 month:%d day:%d year:%d hour:%d minute:%d\n", month2, day2, year2, hour2, minute2); |
|
|
|
if (year > year2) |
|
{ |
|
//Msg("src1 is newer because of year\n"); |
|
return true; |
|
} |
|
else if (year < year2) |
|
{ |
|
//Msg("src2 is newer because of year\n"); |
|
return false; |
|
} |
|
|
|
if (month > month2) |
|
{ |
|
//Msg("src1 is newer because of month\n"); |
|
return true; |
|
} |
|
else if (month < month2) |
|
{ |
|
//Msg("src2 is newer because of month\n"); |
|
return false; |
|
} |
|
|
|
if (day > day2) |
|
{ |
|
//Msg("src1 is newer because of day\n"); |
|
return true; |
|
} |
|
else if (day < day2) |
|
{ |
|
//Msg("src2 is newer because of day\n"); |
|
return false; |
|
} |
|
|
|
if (hour > hour2) |
|
{ |
|
//Msg("src1 is newer because of hour\n"); |
|
return true; |
|
} |
|
else if (hour < hour2) |
|
{ |
|
//Msg("src2 is newer because of hour\n"); |
|
return false; |
|
} |
|
|
|
if (minute > minute2) |
|
{ |
|
//Msg("src1 is newer because of minute\n"); |
|
return true; |
|
} |
|
else if (minute < minute2) |
|
{ |
|
//Msg("src2 is newer because of minute\n"); |
|
return false; |
|
} |
|
|
|
//if ((year > year2) || (month > month2) || (day > day2) || (hour > hour2) || (minute > minute2)) |
|
//return true; |
|
//Msg("neither is newer\n"); |
|
|
|
return false; |
|
} |
|
|
|
#define ASW_DEFAULT_INTRO_MAP "intro_jacob" |
|
const char* CASW_Mission_Chooser_Source_Local::GetCampaignSaveIntroMap(const char *szSaveName) |
|
{ |
|
// check the save file exists |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szSaveName, stripped, MAX_PATH ); |
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "save/%s.campaignsave", stripped ); |
|
if (!g_pFullFileSystem->FileExists(tempfile)) |
|
return ASW_DEFAULT_INTRO_MAP; |
|
|
|
KeyValues *pSaveKeyValues = new KeyValues( szSaveName ); |
|
const char* pszCampaign = NULL; |
|
if (pSaveKeyValues->LoadFromFile(g_pFullFileSystem, tempfile)) |
|
{ |
|
pszCampaign = pSaveKeyValues->GetString("CampaignName"); |
|
} |
|
if (!pszCampaign) |
|
{ |
|
pSaveKeyValues->deleteThis(); |
|
return ASW_DEFAULT_INTRO_MAP; |
|
} |
|
|
|
char ctempfile[MAX_PATH]; |
|
Q_snprintf( ctempfile, sizeof( ctempfile ), "resource/campaigns/%s.txt", pszCampaign ); |
|
if (!g_pFullFileSystem->FileExists(ctempfile)) /// check it exists |
|
{ |
|
pSaveKeyValues->deleteThis(); |
|
return ASW_DEFAULT_INTRO_MAP; |
|
} |
|
|
|
// now read in the campaign txt and find the intro map name |
|
KeyValues *pCampaignKeyValues = new KeyValues( pszCampaign ); |
|
if (pCampaignKeyValues->LoadFromFile(g_pFullFileSystem, ctempfile)) |
|
{ |
|
static char s_introname[128]; |
|
Q_strncpy( s_introname, pCampaignKeyValues->GetString("IntroMap"), 128 ); |
|
// check we actually got a valid intro map name string |
|
if ( Q_strlen(s_introname) > 5 && !Q_strnicmp( s_introname, "intro", 5 ) ) |
|
{ |
|
pSaveKeyValues->deleteThis(); |
|
pCampaignKeyValues->deleteThis(); |
|
return s_introname; |
|
} |
|
} |
|
|
|
pSaveKeyValues->deleteThis(); |
|
pCampaignKeyValues->deleteThis(); |
|
return ASW_DEFAULT_INTRO_MAP; |
|
} |
|
|
|
KeyValues *CASW_Mission_Chooser_Source_Local::GetMissionDetails( const char *szMissionName ) |
|
{ |
|
// see if we have this cached already |
|
for ( int i = 0; i < m_MissionDetails.Count(); i++ ) |
|
{ |
|
if ( !Q_stricmp( m_MissionDetails[ i ]->szMissionName, szMissionName ) ) |
|
{ |
|
return m_MissionDetails[ i ]->m_pMissionKeys; |
|
} |
|
} |
|
|
|
// strip off the extension |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szMissionName, stripped, MAX_PATH ); |
|
|
|
KeyValues *pMissionKeys = new KeyValues( stripped ); |
|
|
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", stripped ); |
|
|
|
bool bNoOverview = false; |
|
if ( !pMissionKeys->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) ) |
|
{ |
|
// try to load it directly from the maps folder |
|
Q_snprintf( tempfile, sizeof( tempfile ), "maps/%s.txt", stripped ); |
|
if ( !pMissionKeys->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) ) |
|
{ |
|
bNoOverview = true; |
|
} |
|
} |
|
|
|
MissionDetails_t *pDetails = new MissionDetails_t; |
|
pDetails->m_pMissionKeys = pMissionKeys; |
|
Q_snprintf( pDetails->szMissionName, sizeof( pDetails->szMissionName ), "%s", szMissionName ); |
|
m_MissionDetails.AddToTail( pDetails ); |
|
|
|
return pMissionKeys; |
|
} |
|
|
|
KeyValues *CASW_Mission_Chooser_Source_Local::GetCampaignDetails( const char *szCampaignName ) |
|
{ |
|
// see if we have this cached already |
|
for ( int i = 0; i < m_CampaignDetails.Count(); i++ ) |
|
{ |
|
if ( !Q_stricmp( m_CampaignDetails[ i ]->szCampaignName, szCampaignName ) ) |
|
{ |
|
return m_CampaignDetails[ i ]->m_pCampaignKeys; |
|
} |
|
} |
|
|
|
// strip off the extension |
|
char stripped[MAX_PATH]; |
|
V_StripExtension( szCampaignName, stripped, MAX_PATH ); |
|
|
|
KeyValues *pCampaignKeys = new KeyValues( stripped ); |
|
|
|
char tempfile[MAX_PATH]; |
|
Q_snprintf( tempfile, sizeof( tempfile ), "resource/campaigns/%s.txt", stripped ); |
|
|
|
bool bNoOverview = false; |
|
if ( !pCampaignKeys->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) ) |
|
{ |
|
// try to load it directly from the maps folder |
|
Q_snprintf( tempfile, sizeof( tempfile ), "maps/%s.txt", stripped ); |
|
if ( !pCampaignKeys->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) ) |
|
{ |
|
bNoOverview = true; |
|
} |
|
} |
|
|
|
CampaignDetails_t *pDetails = new CampaignDetails_t; |
|
pDetails->m_pCampaignKeys = pCampaignKeys; |
|
Q_snprintf( pDetails->szCampaignName, sizeof( pDetails->szCampaignName ), "%s", szCampaignName ); |
|
m_CampaignDetails.AddToTail( pDetails ); |
|
|
|
return pCampaignKeys; |
|
} |