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.
1197 lines
37 KiB
1197 lines
37 KiB
1 year ago
|
#include "cbase.h"
|
||
|
#include "asw_campaign_save.h"
|
||
|
#include "asw_campaign_info.h"
|
||
|
#include "asw_player.h"
|
||
|
#include "asw_gamerules.h"
|
||
|
#include "filesystem.h"
|
||
|
#include "missionchooser/iasw_mission_chooser.h"
|
||
|
#include "missionchooser/iasw_mission_chooser_source.h"
|
||
|
#include "asw_marine_resource.h"
|
||
|
#include "asw_game_resource.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( asw_campaign_save, CASW_Campaign_Save );
|
||
|
|
||
|
ConVar asw_custom_skill_points( "asw_custom_skill_points", "0", FCVAR_ARCHIVE, "If set, marines will start with no skill points and will spend them as they progress through the campaign." );
|
||
|
|
||
|
void ASWSendProxy_String_tToString( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
|
||
|
{
|
||
|
string_t *pString = (string_t*)pData;
|
||
|
pOut->m_pString = (char*)STRING( *pString );
|
||
|
}
|
||
|
|
||
|
IMPLEMENT_SERVERCLASS_ST(CASW_Campaign_Save, DT_ASW_Campaign_Save)
|
||
|
SendPropString(SENDINFO(m_CampaignName)),
|
||
|
SendPropInt(SENDINFO(m_iCurrentPosition)),
|
||
|
SendPropInt(SENDINFO(m_iNumMissionsComplete)),
|
||
|
SendPropArray3( SENDINFO_ARRAY3( m_MissionComplete ), SendPropInt( SENDINFO_ARRAY( m_MissionComplete ), 8, SPROP_UNSIGNED ) ),
|
||
|
SendPropArray3( SENDINFO_ARRAY3( m_NumRetries ), SendPropInt( SENDINFO_ARRAY( m_NumRetries ), 9, SPROP_UNSIGNED ) ),
|
||
|
|
||
|
SendPropArray3( SENDINFO_ARRAY3( m_bMarineWounded ), SendPropBool( SENDINFO_ARRAY( m_bMarineWounded ) ) ),
|
||
|
SendPropArray3( SENDINFO_ARRAY3( m_bMarineDead ), SendPropBool( SENDINFO_ARRAY( m_bMarineDead ) ) ),
|
||
|
|
||
|
SendPropArray( SendPropString( SENDINFO_ARRAY( m_MissionsCompleteNames ), 0, ASWSendProxy_String_tToString ), m_MissionsCompleteNames ),
|
||
|
SendPropArray( SendPropString( SENDINFO_ARRAY( m_Medals ), 0, ASWSendProxy_String_tToString ), m_Medals ),
|
||
|
SendPropBool(SENDINFO(m_bMultiplayerGame)),
|
||
|
SendPropString(SENDINFO(m_DateTime)),
|
||
|
|
||
|
SendPropArray3( SENDINFO_ARRAY3( m_NumVotes ), SendPropInt( SENDINFO_ARRAY( m_NumVotes ), 8, SPROP_UNSIGNED ) ),
|
||
|
SendPropFloat( SENDINFO(m_fVoteEndTime) ),
|
||
|
SendPropBool( SENDINFO( m_bFixedSkillPoints ) ),
|
||
|
END_SEND_TABLE()
|
||
|
|
||
|
BEGIN_DATADESC( CASW_Campaign_Save )
|
||
|
DEFINE_THINKFUNC( MoveThink ),
|
||
|
DEFINE_THINKFUNC( VoteEndThink ),
|
||
|
DEFINE_FIELD( m_bNextMissionVoteEnded, FIELD_BOOLEAN )
|
||
|
END_DATADESC()
|
||
|
|
||
|
CASW_Campaign_Save::CASW_Campaign_Save()
|
||
|
{
|
||
|
m_iVersion = ASW_CURRENT_SAVE_VERSION;
|
||
|
m_CampaignName.GetForModify()[0] = 0;
|
||
|
m_iCurrentPosition = 0;
|
||
|
m_iNumMissionsComplete = 0;
|
||
|
m_iInitialNumMissionsComplete = 0;
|
||
|
m_iNumPlayers = 0;
|
||
|
m_bMultiplayerGame = false;
|
||
|
m_CurrentSaveFileName = NULL_STRING;
|
||
|
m_DateTime.GetForModify()[0] = 0;
|
||
|
m_iLowestSkillLevelPlayed = 0;
|
||
|
m_PlayerIDs.Purge();
|
||
|
m_PlayerNames.Purge();
|
||
|
m_fVoteEndTime = 0;
|
||
|
m_bFixedSkillPoints = true;
|
||
|
m_iMoveDestination = -1;
|
||
|
for (int i=0;i<ASW_MAX_MISSIONS_PER_CAMPAIGN;i++)
|
||
|
{
|
||
|
m_MissionComplete.Set(i, 0);
|
||
|
m_NumRetries.Set(i, 0);
|
||
|
m_NumVotes.Set(i, 0);
|
||
|
}
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
m_MissionsCompleteNames.Set(i, NULL_STRING);
|
||
|
m_Medals.Set(i, NULL_STRING);
|
||
|
m_bMarineDead.Set(i, false);
|
||
|
m_bMarineWounded.Set(i, false);
|
||
|
m_LastCommanders[i] = NULL_STRING;
|
||
|
m_LastMarineResourceSlot[i] = 0;
|
||
|
|
||
|
for (int k=0;k<ASW_NUM_SKILL_SLOTS;k++)
|
||
|
{
|
||
|
m_iMarineSkill[i][k] = 0;
|
||
|
m_iPreviousMarineSkill[i][k] = 0;
|
||
|
}
|
||
|
// give 2 skill points to spend at the start
|
||
|
m_iMarineSkill[i][ASW_SKILL_SLOT_SPARE] = 2;
|
||
|
m_iPreviousMarineSkill[i][ASW_SKILL_SLOT_SPARE] = 2;
|
||
|
m_iParasitesKilled[i] = 0;
|
||
|
}
|
||
|
m_iNumDeaths = 0;
|
||
|
}
|
||
|
|
||
|
CASW_Campaign_Save::~CASW_Campaign_Save()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// reads in current save data from a keyvalues file
|
||
|
bool CASW_Campaign_Save::LoadGameFromFile(const char *szFileName)
|
||
|
{
|
||
|
// make sure the path and extension are correct
|
||
|
char szFullFileName[256];
|
||
|
char tempbuffer[256];
|
||
|
Q_snprintf(tempbuffer, sizeof(tempbuffer), "%s", szFileName);
|
||
|
Q_SetExtension( tempbuffer, ".campaignsave", sizeof(tempbuffer) );
|
||
|
const char *pszNoPathName = Q_UnqualifiedFileName(tempbuffer);
|
||
|
Q_snprintf(szFullFileName, sizeof(szFullFileName), "save/%s", pszNoPathName);
|
||
|
|
||
|
KeyValues *pSaveKeyValues = new KeyValues( szFileName );
|
||
|
if (pSaveKeyValues->LoadFromFile(filesystem, szFullFileName))
|
||
|
{
|
||
|
m_CurrentSaveFileName = AllocPooledString(szFullFileName);
|
||
|
|
||
|
m_iVersion = pSaveKeyValues->GetInt("Version");
|
||
|
m_iLowestSkillLevelPlayed = pSaveKeyValues->GetInt("SkillLevel");
|
||
|
Q_strncpy( m_CampaignName.GetForModify(), pSaveKeyValues->GetString("CampaignName"), 255 );
|
||
|
m_iCurrentPosition = pSaveKeyValues->GetInt("CurrentPosition");
|
||
|
m_iNumMissionsComplete = pSaveKeyValues->GetInt("NumMissionsComplete");
|
||
|
m_iInitialNumMissionsComplete = pSaveKeyValues->GetInt("InitialNumMissionsComplete");
|
||
|
m_bMultiplayerGame = pSaveKeyValues->GetInt("Multiplayer") != 0;
|
||
|
Q_strncpy( m_DateTime.GetForModify(), pSaveKeyValues->GetString("DateTime"), 255 );
|
||
|
m_iNumDeaths = pSaveKeyValues->GetInt("NumDeaths");
|
||
|
m_bFixedSkillPoints = !asw_custom_skill_points.GetBool(); //pSaveKeyValues->GetBool( "FixedSkillPoints", true );
|
||
|
|
||
|
m_iNumPlayers = pSaveKeyValues->GetInt("NumPlayers");
|
||
|
m_PlayerNames.Purge();
|
||
|
m_PlayerIDs.Purge();
|
||
|
|
||
|
// go through each sub section, adding the relevant details
|
||
|
KeyValues *pkvSubSection = pSaveKeyValues->GetFirstSubKey();
|
||
|
while ( pkvSubSection )
|
||
|
{
|
||
|
// mission details
|
||
|
if (Q_stricmp(pkvSubSection->GetName(), "MISSION")==0)
|
||
|
{
|
||
|
int MissionID = pkvSubSection->GetInt("MissionID");
|
||
|
if (MissionID >=0 && MissionID < ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
{
|
||
|
m_MissionComplete.Set(MissionID, pkvSubSection->GetInt("MissionComplete"));
|
||
|
m_NumRetries.Set(MissionID, pkvSubSection->GetInt("NumRetries"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// marine details
|
||
|
if (Q_stricmp(pkvSubSection->GetName(), "MARINE")==0)
|
||
|
{
|
||
|
int MarineID = pkvSubSection->GetInt("MarineID");
|
||
|
if (MarineID >=0 && MarineID < ASW_NUM_MARINE_PROFILES)
|
||
|
{
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_0] = pkvSubSection->GetInt("SkillSlot0");
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_1] = pkvSubSection->GetInt("SkillSlot1");
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_2] = pkvSubSection->GetInt("SkillSlot2");
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_3] = pkvSubSection->GetInt("SkillSlot3");
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_4] = pkvSubSection->GetInt("SkillSlot4");
|
||
|
m_iMarineSkill[MarineID][ASW_SKILL_SLOT_SPARE] = pkvSubSection->GetInt("SkillSlotSpare");
|
||
|
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_0] = pkvSubSection->GetInt("UndoSkillSlot0");
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_1] = pkvSubSection->GetInt("UndoSkillSlot1");
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_2] = pkvSubSection->GetInt("UndoSkillSlot2");
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_3] = pkvSubSection->GetInt("UndoSkillSlot3");
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_4] = pkvSubSection->GetInt("UndoSkillSlot4");
|
||
|
m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_SPARE] = pkvSubSection->GetInt("UndoSkillSlotSpare");
|
||
|
|
||
|
m_iParasitesKilled[MarineID] = pkvSubSection->GetInt("ParasitesKilled");
|
||
|
|
||
|
m_MissionsCompleteNames.Set(MarineID, AllocPooledString(pkvSubSection->GetString("MissionsCompleted")));
|
||
|
m_Medals.Set(MarineID, AllocPooledString(pkvSubSection->GetString("Medals")));
|
||
|
|
||
|
m_bMarineWounded.Set(MarineID, (pkvSubSection->GetInt("Wounded") == 1));
|
||
|
m_bMarineDead.Set(MarineID, (pkvSubSection->GetInt("Dead") == 1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// player name
|
||
|
if (Q_stricmp(pkvSubSection->GetName(), "PLAYER")==0)
|
||
|
{
|
||
|
string_t stringName = AllocPooledString(pkvSubSection->GetString("PlayerName"));
|
||
|
m_PlayerNames.AddToTail(stringName);
|
||
|
}
|
||
|
// player ID
|
||
|
if (Q_stricmp(pkvSubSection->GetName(), "DATA")==0)
|
||
|
{
|
||
|
string_t stringID = AllocPooledString(pkvSubSection->GetString("DataBlock"));
|
||
|
m_PlayerIDs.AddToTail(stringID);
|
||
|
}
|
||
|
// last commanders
|
||
|
if (Q_stricmp(pkvSubSection->GetName(), "COMM")==0)
|
||
|
{
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
char buffer[16];
|
||
|
Q_snprintf(buffer, sizeof(buffer), "Comm%d", i);
|
||
|
string_t stringID = AllocPooledString(pkvSubSection->GetString(buffer));
|
||
|
m_LastCommanders[i] = stringID;
|
||
|
Q_snprintf(buffer, sizeof(buffer), "Slot%d", i);
|
||
|
m_LastMarineResourceSlot[i] = pkvSubSection->GetInt(buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pkvSubSection = pkvSubSection->GetNextKey();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
Msg("Failed to load KeyValues from file %s\n", szFullFileName);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// saves current save data to a keyvalues file
|
||
|
bool CASW_Campaign_Save::SaveGameToFile(const char *szFileName)
|
||
|
{
|
||
|
// make sure the path and extension are correct
|
||
|
char szFullFileName[256];
|
||
|
char tempbuffer[256];
|
||
|
if (szFileName==NULL)
|
||
|
{
|
||
|
if (m_CurrentSaveFileName == NULL_STRING)
|
||
|
{
|
||
|
Msg("Error: Couldn't save game to file as we have no name already and you didn't supply one.\n");
|
||
|
return false;
|
||
|
}
|
||
|
Q_snprintf(szFullFileName, sizeof(szFullFileName), "%s", STRING(m_CurrentSaveFileName));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_snprintf(tempbuffer, sizeof(tempbuffer), "%s", szFileName);
|
||
|
Q_SetExtension( tempbuffer, ".campaignsave", sizeof(tempbuffer) );
|
||
|
const char *pszNoPathName = Q_UnqualifiedFileName(tempbuffer);
|
||
|
Q_snprintf(szFullFileName, sizeof(szFullFileName), "save/%s", pszNoPathName);
|
||
|
}
|
||
|
|
||
|
// before saving, check if this is actually a completed campaign
|
||
|
if (ASWGameRules())
|
||
|
{
|
||
|
int iLeft = ASWGameRules()->CampaignMissionsLeft();
|
||
|
if (iLeft <= 0)
|
||
|
{
|
||
|
Msg("Deleting completed campaign %s", szFullFileName);
|
||
|
filesystem->RemoveFile( szFullFileName, "GAME" );
|
||
|
|
||
|
// notify the local mission source that a save has been deleted
|
||
|
if (missionchooser && missionchooser->LocalMissionSource())
|
||
|
{
|
||
|
const char *pszNoPathName = Q_UnqualifiedFileName(szFullFileName);
|
||
|
missionchooser->LocalMissionSource()->NotifySaveDeleted(pszNoPathName);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
// we don't want completed campaigns lingering on disk
|
||
|
|
||
|
KeyValues *pSaveKeyValues = new KeyValues( "CAMPAIGNSAVE" );
|
||
|
|
||
|
pSaveKeyValues->SetInt("Version", m_iVersion);
|
||
|
pSaveKeyValues->SetInt("SkillLevel", m_iLowestSkillLevelPlayed);
|
||
|
pSaveKeyValues->SetString("CampaignName", m_CampaignName.Get());
|
||
|
pSaveKeyValues->SetInt("CurrentPosition", m_iCurrentPosition);
|
||
|
pSaveKeyValues->SetInt("NumMissionsComplete", m_iNumMissionsComplete);
|
||
|
pSaveKeyValues->SetInt("InitialNumMissionsComplete", m_iInitialNumMissionsComplete);
|
||
|
pSaveKeyValues->SetInt("Multiplayer", m_bMultiplayerGame ? 1 : 0);
|
||
|
pSaveKeyValues->SetInt( "NumDeaths", m_iNumDeaths );
|
||
|
pSaveKeyValues->SetBool( "FixedSkillPoints", m_bFixedSkillPoints );
|
||
|
|
||
|
// update date
|
||
|
int year, month, dayOfWeek, day, hour, minute, second;
|
||
|
missionchooser->GetCurrentTimeAndDate(&year, &month, &dayOfWeek, &day, &hour, &minute, &second);
|
||
|
//year = month = dayOfWeek = day = hour = minute = second = 0;
|
||
|
Q_snprintf(m_DateTime.GetForModify(), 255, "%02d/%02d/%d %02d:%02d", month, day, year, hour, minute);
|
||
|
pSaveKeyValues->SetString("DateTime", m_DateTime.Get());
|
||
|
|
||
|
// 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);
|
||
|
pSubSection->SetInt("MissionComplete", m_MissionComplete[i]);
|
||
|
pSubSection->SetInt("NumRetries", m_NumRetries[i]);
|
||
|
pSaveKeyValues->AddSubKey(pSubSection);
|
||
|
}
|
||
|
|
||
|
// write out each marine's stats
|
||
|
for (int MarineID=0; MarineID<ASW_NUM_MARINE_PROFILES; MarineID++)
|
||
|
{
|
||
|
pSubSection = new KeyValues("MARINE");
|
||
|
pSubSection->SetInt("MarineID", MarineID);
|
||
|
|
||
|
pSubSection->SetInt("SkillSlot0", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_0]);
|
||
|
pSubSection->SetInt("SkillSlot1", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_1]);
|
||
|
pSubSection->SetInt("SkillSlot2", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_2]);
|
||
|
pSubSection->SetInt("SkillSlot3", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_3]);
|
||
|
pSubSection->SetInt("SkillSlot4", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_4]);
|
||
|
pSubSection->SetInt("SkillSlotSpare", m_iMarineSkill[MarineID][ASW_SKILL_SLOT_SPARE]);
|
||
|
|
||
|
pSubSection->SetInt("UndoSkillSlot0", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_0]);
|
||
|
pSubSection->SetInt("UndoSkillSlot1", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_1]);
|
||
|
pSubSection->SetInt("UndoSkillSlot2", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_2]);
|
||
|
pSubSection->SetInt("UndoSkillSlot3", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_3]);
|
||
|
pSubSection->SetInt("UndoSkillSlot4", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_4]);
|
||
|
pSubSection->SetInt("UndoSkillSlotSpare", m_iPreviousMarineSkill[MarineID][ASW_SKILL_SLOT_SPARE]);
|
||
|
|
||
|
pSubSection->SetInt("ParasitesKilled", m_iParasitesKilled[MarineID]);
|
||
|
pSubSection->SetString("MissionsCompleted", STRING(m_MissionsCompleteNames[MarineID]));
|
||
|
pSubSection->SetString("Medals", STRING(m_Medals[MarineID]));
|
||
|
|
||
|
int iWound = IsMarineWounded(MarineID) ? 1 : 0;
|
||
|
pSubSection->SetInt("Wounded", iWound);
|
||
|
int iDead = IsMarineAlive(MarineID) ? 0 : 1;
|
||
|
pSubSection->SetInt("Dead", iDead);
|
||
|
|
||
|
pSaveKeyValues->AddSubKey(pSubSection);
|
||
|
}
|
||
|
|
||
|
// check for any new players to add to our list
|
||
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||
|
{
|
||
|
CASW_Player* pPlayer = dynamic_cast<CASW_Player*>(UTIL_PlayerByIndex(i));
|
||
|
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
// first check his network ID
|
||
|
const char *pszNetworkID = pPlayer->GetASWNetworkID();
|
||
|
// check if it's in our list
|
||
|
bool bFound = false;
|
||
|
for (int k=0;k<m_PlayerIDs.Count();k++)
|
||
|
{
|
||
|
const char *p = STRING(m_PlayerIDs[k]);
|
||
|
if (!Q_strcmp(p, pszNetworkID))
|
||
|
bFound = true;
|
||
|
}
|
||
|
if (!bFound)
|
||
|
{
|
||
|
// add it to the list
|
||
|
string_t stringID = AllocPooledString(pszNetworkID);
|
||
|
m_PlayerIDs.AddToTail(stringID);
|
||
|
}
|
||
|
|
||
|
// then check player name
|
||
|
const char *pszPlayerName = pPlayer->GetPlayerName();
|
||
|
bFound = false;
|
||
|
for (int k=0;k<m_PlayerNames.Count();k++)
|
||
|
{
|
||
|
const char *p = STRING(m_PlayerNames[k]);
|
||
|
if (!Q_strcmp(p, pszPlayerName))
|
||
|
bFound = true;
|
||
|
}
|
||
|
if (!bFound)
|
||
|
{
|
||
|
// add it to the list
|
||
|
string_t stringName = AllocPooledString(pszPlayerName);
|
||
|
m_PlayerNames.AddToTail(stringName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pSaveKeyValues->SetInt("NumPlayers", m_PlayerNames.Count());
|
||
|
|
||
|
// write out player names
|
||
|
for (int i=0; i<m_PlayerNames.Count(); i++)
|
||
|
{
|
||
|
pSubSection = new KeyValues("PLAYER");
|
||
|
pSubSection->SetString("PlayerName", STRING(m_PlayerNames[i]));
|
||
|
pSaveKeyValues->AddSubKey(pSubSection);
|
||
|
}
|
||
|
// write out player IDs
|
||
|
for (int i=0; i<m_PlayerIDs.Count(); i++)
|
||
|
{
|
||
|
pSubSection = new KeyValues("DATA");
|
||
|
pSubSection->SetString("DataBlock", STRING(m_PlayerIDs[i]));
|
||
|
pSaveKeyValues->AddSubKey(pSubSection);
|
||
|
}
|
||
|
// write out last commanders section
|
||
|
pSubSection = new KeyValues("COMM");
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
char buffer[16];
|
||
|
Q_snprintf(buffer, sizeof(buffer), "Comm%d", i);
|
||
|
pSubSection->SetString(buffer, STRING(m_LastCommanders[i]));
|
||
|
Q_snprintf(buffer, sizeof(buffer), "Slot%d", i);
|
||
|
pSubSection->SetInt( buffer, m_LastMarineResourceSlot[i] );
|
||
|
}
|
||
|
pSaveKeyValues->AddSubKey(pSubSection);
|
||
|
|
||
|
if (pSaveKeyValues->SaveToFile(filesystem, szFullFileName))
|
||
|
{
|
||
|
if (missionchooser && missionchooser->LocalMissionSource())
|
||
|
{
|
||
|
const char *pszNoPathName = Q_UnqualifiedFileName(szFullFileName);
|
||
|
missionchooser->LocalMissionSource()->OnSaveUpdated(pszNoPathName);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::UpdateLastCommanders()
|
||
|
{
|
||
|
// save which marines the players have selected
|
||
|
// add which marines he has selected
|
||
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
||
|
if ( !pGameResource )
|
||
|
return;
|
||
|
|
||
|
// first check there were some marines selected (i.e. we're not in the campaign lobby map)
|
||
|
int iNumMarineResources = 0;
|
||
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
||
|
{
|
||
|
if (pGameResource->GetMarineResource(i))
|
||
|
iNumMarineResources++;
|
||
|
}
|
||
|
if ( iNumMarineResources <= 0 )
|
||
|
return;
|
||
|
|
||
|
char buffer[256];
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
// look for a marine info for this marine
|
||
|
bool bFound = false;
|
||
|
for (int k=0;k<pGameResource->GetMaxMarineResources();k++)
|
||
|
{
|
||
|
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(k);
|
||
|
if (pMR && pMR->GetProfileIndex() == i && pMR->GetCommander())
|
||
|
{
|
||
|
CASW_Player *pPlayer = pMR->GetCommander();
|
||
|
if (pPlayer)
|
||
|
{
|
||
|
// store the commander who has this marine
|
||
|
Q_snprintf(buffer, sizeof(buffer), "%s%s",pPlayer->GetPlayerName(), pPlayer->GetASWNetworkID());
|
||
|
m_LastCommanders[i] = AllocPooledString(buffer);
|
||
|
m_LastMarineResourceSlot[i] = k;
|
||
|
bFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!bFound)
|
||
|
{
|
||
|
m_LastCommanders[i] = AllocPooledString("");
|
||
|
m_LastMarineResourceSlot[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::DebugInfo()
|
||
|
{
|
||
|
Msg("Savegame contents:\n");
|
||
|
Msg(" Version: %d\n", m_iVersion);
|
||
|
Msg(" Lowest Skill Level Played: %d\n", m_iLowestSkillLevelPlayed);
|
||
|
Msg(" Campaign Name: %s\n", m_CampaignName.Get());
|
||
|
Msg(" Current Position: %d\n", m_iCurrentPosition);
|
||
|
Msg(" Num Missions Complete: %d\n", m_iNumMissionsComplete);
|
||
|
Msg(" Initial Num Missions Complete: %d\n", m_iInitialNumMissionsComplete);
|
||
|
Msg( "Num deaths: %d\n", m_iNumDeaths);
|
||
|
Msg(" [");
|
||
|
for (int i=0;i<ASW_MAX_MISSIONS_PER_CAMPAIGN;i++)
|
||
|
{
|
||
|
if (m_MissionComplete[i])
|
||
|
Msg("X");
|
||
|
else
|
||
|
Msg("-");
|
||
|
}
|
||
|
Msg("]\n");
|
||
|
Msg("Marine Stats:\n");
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
Msg(" Marine %d:\n", i);
|
||
|
//Msg(" Health: %d Speed: %d Accuracy: %d Special: %d Spare: %d\n",
|
||
|
//m_iMarineHealth[i], m_iMarineSpeed[i], m_iMarineAccuracy[i], m_iMarineSpecial[i], m_iMarineSparePoints[i]);
|
||
|
Msg(" Missions complete: %s\n", STRING(m_MissionsCompleteNames[i]));
|
||
|
Msg(" Medals: %s\n", STRING(m_Medals[i]));
|
||
|
}
|
||
|
Msg("Multiplayer: %d\n", m_bMultiplayerGame ? 1 : 0);
|
||
|
Msg("Date/Time: %s\n", m_DateTime.Get());
|
||
|
Msg("Players: %d\n", m_iNumPlayers);
|
||
|
Msg("Player names: %d\n", m_PlayerNames.Count());
|
||
|
for (int i=0;i<m_PlayerNames.Count();i++)
|
||
|
{
|
||
|
Msg(" %s\n", STRING(m_PlayerNames[i]));
|
||
|
}
|
||
|
Msg("Data blocks: %d\n", m_PlayerIDs.Count());
|
||
|
for (int i=0;i<m_PlayerIDs.Count();i++)
|
||
|
{
|
||
|
Msg(" %s\n", STRING(m_PlayerIDs[i]));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::SetMissionComplete(int iMission)
|
||
|
{
|
||
|
if (iMission>=0 && iMission<ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
{
|
||
|
if (!m_MissionComplete[iMission])
|
||
|
{
|
||
|
m_iNumMissionsComplete++;
|
||
|
m_MissionComplete.Set(iMission, 1);
|
||
|
// update the lowest skill level played
|
||
|
int iSkill = ASWGameRules()->GetSkillLevel();
|
||
|
if (iSkill < m_iLowestSkillLevelPlayed || m_iLowestSkillLevelPlayed == 0)
|
||
|
m_iLowestSkillLevelPlayed = iSkill;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::MoveTo(int iMission)
|
||
|
{
|
||
|
if (iMission>=0 && iMission<ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
m_iCurrentPosition = iMission;
|
||
|
}
|
||
|
|
||
|
const char * CASW_Campaign_Save::GetCampaignName()
|
||
|
{
|
||
|
return m_CampaignName;
|
||
|
}
|
||
|
|
||
|
bool CASW_Campaign_Save::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;
|
||
|
|
||
|
if (!missionchooser || !missionchooser->LocalMissionSource())
|
||
|
return false;
|
||
|
|
||
|
return missionchooser->LocalMissionSource()->ASW_Campaign_CreateNewSaveGame(szFileName, iFileNameMaxLen, szCampaignName, bMultiplayerGame, szStartingMission);
|
||
|
}
|
||
|
|
||
|
int CASW_Campaign_Save::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
||
|
{
|
||
|
return FL_EDICT_ALWAYS;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::IncreaseRetries()
|
||
|
{
|
||
|
if (m_iCurrentPosition <0 || m_iCurrentPosition >= ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
return;
|
||
|
|
||
|
int current = m_NumRetries[m_iCurrentPosition];
|
||
|
m_NumRetries.Set(m_iCurrentPosition, current+1);
|
||
|
}
|
||
|
|
||
|
int CASW_Campaign_Save::GetRetries()
|
||
|
{
|
||
|
if (m_iCurrentPosition <0 || m_iCurrentPosition >= ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
return -1;
|
||
|
|
||
|
return m_NumRetries[m_iCurrentPosition];
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::IncreaseMarineSkill( int nProfileIndex, int nSkillSlot )
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
if (nSkillSlot < 0 || nSkillSlot >= ASW_NUM_SKILL_SLOTS)
|
||
|
return;
|
||
|
|
||
|
m_iMarineSkill[nProfileIndex][nSkillSlot]++;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::ReduceMarineSkill(int nProfileIndex, int nSkillSlot)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
if (nSkillSlot < 0 || nSkillSlot >= ASW_NUM_SKILL_SLOTS)
|
||
|
return;
|
||
|
|
||
|
m_iMarineSkill[nProfileIndex][nSkillSlot]--;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::ReviveMarine(int nProfileIndex)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
if (IsMarineAlive(nProfileIndex)) // don't revive ones already alive!
|
||
|
return;
|
||
|
|
||
|
// revive
|
||
|
SetMarineDead(nProfileIndex, false);
|
||
|
SetMarineWounded(nProfileIndex, false);
|
||
|
|
||
|
// reset skills
|
||
|
for (int i=0;i<ASW_NUM_SKILL_SLOTS;i++)
|
||
|
{
|
||
|
m_iMarineSkill[nProfileIndex][i] = 0;
|
||
|
m_iPreviousMarineSkill[nProfileIndex][i] = 0;
|
||
|
}
|
||
|
|
||
|
// give marine skill points equivalent to a marine that didn't part in any missions
|
||
|
m_iMarineSkill[nProfileIndex][ASW_SKILL_SLOT_SPARE] = m_iNumMissionsComplete.Get() * 2 + 2;
|
||
|
m_iPreviousMarineSkill[nProfileIndex][ASW_SKILL_SLOT_SPARE] = m_iNumMissionsComplete.Get() * 2 + 2;
|
||
|
|
||
|
// misc
|
||
|
m_iParasitesKilled[nProfileIndex] = 0;
|
||
|
m_MissionsCompleteNames.Set(nProfileIndex, NULL_STRING);
|
||
|
m_Medals.Set(nProfileIndex, NULL_STRING);
|
||
|
m_LastCommanders[nProfileIndex] = NULL_STRING;
|
||
|
m_LastMarineResourceSlot[nProfileIndex] = 0;
|
||
|
}
|
||
|
|
||
|
int CASW_Campaign_Save::GetParasitesKilled(int nProfileIndex)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return 0;
|
||
|
|
||
|
return m_iParasitesKilled[nProfileIndex];
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::AddParasitesKilled(int nProfileIndex, int iParasitesKilled)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
m_iParasitesKilled[nProfileIndex] += iParasitesKilled;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::SetMarineWounded(int nProfileIndex, bool bWounded)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
m_bMarineWounded.Set(nProfileIndex, bWounded);
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::SetMarineDead(int nProfileIndex, bool bDead)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
m_bMarineDead.Set(nProfileIndex, bDead);
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::RevertSkillsToUndoState(int nProfileIndex)
|
||
|
{
|
||
|
if (nProfileIndex < 0 || nProfileIndex >= ASW_NUM_MARINE_PROFILES)
|
||
|
return;
|
||
|
|
||
|
for (int k=0;k<ASW_NUM_SKILL_SLOTS;k++)
|
||
|
{
|
||
|
m_iMarineSkill[nProfileIndex][k] = m_iPreviousMarineSkill[nProfileIndex][k];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::UpdateSkillUndoState()
|
||
|
{
|
||
|
for (int i=0;i<ASW_NUM_MARINE_PROFILES;i++)
|
||
|
{
|
||
|
for (int k=0;k<ASW_NUM_SKILL_SLOTS;k++)
|
||
|
{
|
||
|
m_iPreviousMarineSkill[i][k] = m_iMarineSkill[i][k];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::SetMoveDestination(int iMission)
|
||
|
{
|
||
|
if (!ASWGameRules() || !ASWGameRules()->GetCampaignInfo())
|
||
|
return;
|
||
|
|
||
|
CASW_Campaign_Info *pCI = ASWGameRules()->GetCampaignInfo();
|
||
|
if ( !pCI )
|
||
|
return;
|
||
|
|
||
|
CASW_Campaign_Info::CASW_Campaign_Mission_t *pMission = pCI->GetMission( iMission );
|
||
|
if (!pMission)
|
||
|
return;
|
||
|
|
||
|
if (iMission != m_iMoveDestination)
|
||
|
{
|
||
|
m_iMoveDestination = iMission;
|
||
|
SetThink( &CASW_Campaign_Save::MoveThink );
|
||
|
SetNextThink(gpGlobals->curtime + 0.1f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::MoveThink()
|
||
|
{
|
||
|
if (m_iMoveDestination != -1)
|
||
|
{
|
||
|
if (m_iMoveDestination == m_iCurrentPosition)
|
||
|
{
|
||
|
Msg("arrived!\n");
|
||
|
// notify game rules that we've arrived, so it can do any mission launching it desires
|
||
|
if (ASWGameRules())
|
||
|
ASWGameRules()->RequestCampaignLaunchMission(m_iCurrentPosition);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// build a route to the dest from current position
|
||
|
bool bRoute = BuildCampaignRoute(m_iMoveDestination, m_iCurrentPosition);
|
||
|
if (!bRoute)
|
||
|
{
|
||
|
Msg("Couldn't build route to dest\n");
|
||
|
SetThink(NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we've got a route, move ourselves to the next step in the route
|
||
|
campaign_route_node_t* pNode = FindMissionInClosedList(m_iRouteDest);
|
||
|
if (pNode)
|
||
|
pNode = FindMissionInClosedList(pNode->iParentMission);
|
||
|
MoveTo(pNode->iMission);
|
||
|
SetNextThink(gpGlobals->curtime + 1.0f); // move again in 1 second
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
float CASW_Campaign_Save::EstimateCost(CASW_Campaign_Info *pCI, int iStart, int iEnd)
|
||
|
{
|
||
|
// heuristic for A*
|
||
|
CASW_Campaign_Info::CASW_Campaign_Mission_t* pMission = pCI->GetMission(iStart);
|
||
|
CASW_Campaign_Info::CASW_Campaign_Mission_t* pEndMission = pCI->GetMission(iEnd);
|
||
|
if (!pMission || !pEndMission)
|
||
|
return 9000;
|
||
|
|
||
|
float diff_x = pEndMission->m_iLocationX - pMission->m_iLocationX;
|
||
|
float diff_y = pEndMission->m_iLocationY - pMission->m_iLocationY;
|
||
|
return (sqrt((diff_x * diff_x) + (diff_y * diff_y)));
|
||
|
}
|
||
|
|
||
|
bool CASW_Campaign_Save::BuildCampaignRoute(int iStart, int iEnd)
|
||
|
{
|
||
|
if (!ASWGameRules() || !ASWGameRules()->GetCampaignInfo())
|
||
|
return false;
|
||
|
|
||
|
CASW_Campaign_Info *pCI = ASWGameRules()->GetCampaignInfo();
|
||
|
if (iStart < 0 || iStart >= pCI->GetNumMissions())
|
||
|
return false;
|
||
|
if (iEnd < 0 || iEnd >= pCI->GetNumMissions())
|
||
|
return false;
|
||
|
|
||
|
// do A*
|
||
|
m_ClosedList.Purge();
|
||
|
m_OpenList.Purge();
|
||
|
m_iRouteDest = iEnd;
|
||
|
m_iRouteStart = iStart;
|
||
|
|
||
|
// add starting mission to the open list
|
||
|
campaign_route_node_t start_node;
|
||
|
start_node.iMission = iStart;
|
||
|
start_node.iParentMission = iStart;
|
||
|
start_node.g = 0;
|
||
|
start_node.h = EstimateCost(pCI, iStart, iEnd);
|
||
|
start_node.f = start_node.g + start_node.h;
|
||
|
m_OpenList.AddToTail(start_node);
|
||
|
int iOverflow = 0;
|
||
|
while (iOverflow < 1000)
|
||
|
{
|
||
|
// find the lowest node on the open list
|
||
|
int iLowestF = -1;
|
||
|
float fLowestF_Value = 99999;
|
||
|
//Msg("%d nodes in open list\n", m_OpenList.Count());
|
||
|
for (int i=0;i<m_OpenList.Count();i++)
|
||
|
{
|
||
|
if (m_OpenList[i].f < fLowestF_Value)
|
||
|
{
|
||
|
//Msg(" and node %d is lower than lowest\n", i);
|
||
|
fLowestF_Value = m_OpenList[i].f;
|
||
|
iLowestF = i;
|
||
|
}
|
||
|
}
|
||
|
// if open list is empty, that means we've searched all routes and still didn't arrive at the dest
|
||
|
if (iLowestF == -1)
|
||
|
{
|
||
|
//Msg("Failed to find route\n");
|
||
|
return false;
|
||
|
}
|
||
|
// move it to the closed list
|
||
|
int iCurrentMission = m_OpenList[iLowestF].iMission;
|
||
|
float fCurrentCost = m_OpenList[iLowestF].f;
|
||
|
m_ClosedList.AddToTail(m_OpenList[iLowestF]);
|
||
|
m_OpenList.Remove(iLowestF);
|
||
|
|
||
|
if (iCurrentMission == iEnd) // found the target
|
||
|
{
|
||
|
//Msg("current mission is the destination!\n");
|
||
|
return true;
|
||
|
}
|
||
|
//Msg("Current mission is %d, open list size %d closed list size %d\n", iCurrentMission, m_OpenList.Count(), m_ClosedList.Count());
|
||
|
// check all linked missions
|
||
|
CASW_Campaign_Info::CASW_Campaign_Mission_t *pMission = pCI->GetMission(iCurrentMission);
|
||
|
if (!pMission)
|
||
|
{
|
||
|
//Msg("Failed to get current mission in BuildCampaignRoute\n");
|
||
|
return false;
|
||
|
}
|
||
|
//Msg("Current mission (%d) has %d links\n", iCurrentMission, pMission->m_Links.Count());
|
||
|
for (int i=0;i<pMission->m_Links.Count();i++)
|
||
|
{
|
||
|
int iOtherMission = pMission->m_Links[i];
|
||
|
//Msg("other mission[%d] is %d\n", i, iOtherMission);
|
||
|
CASW_Campaign_Info::CASW_Campaign_Mission_t *pOtherMission = pCI->GetMission(pMission->m_Links[i]);
|
||
|
if (!pOtherMission)
|
||
|
continue;
|
||
|
|
||
|
// check if it's on the open list
|
||
|
int iOnOpenList = -1;
|
||
|
for (int k=0;k<m_OpenList.Count();k++)
|
||
|
{
|
||
|
if (m_OpenList[k].iMission == iOtherMission)
|
||
|
{
|
||
|
//Msg("Other mission (%d) is on the open list\n", iOtherMission);
|
||
|
iOnOpenList = k;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// if not, check if it's already on the closed list
|
||
|
if (iOnOpenList == -1)
|
||
|
{
|
||
|
int iOnClosedList = -1;
|
||
|
for (int k=0;k<m_ClosedList.Count();k++)
|
||
|
{
|
||
|
if (m_ClosedList[k].iMission == iOtherMission)
|
||
|
{
|
||
|
//Msg("Other mission (%d) is on the closed list already, so ignoring\n", iOtherMission);
|
||
|
iOnClosedList = k;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (iOnClosedList != -1)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
// if not, add it and calculate costs
|
||
|
if (iOnOpenList == -1)
|
||
|
{
|
||
|
//Msg("other mission %d not on the open list, so adding it...", iOtherMission);
|
||
|
campaign_route_node_t new_node;
|
||
|
new_node.iMission = iOtherMission;
|
||
|
new_node.iParentMission = iCurrentMission;
|
||
|
new_node.g = fCurrentCost + EstimateCost(pCI, iCurrentMission, iOtherMission); // actual cost from start to here
|
||
|
new_node.h = EstimateCost(pCI, iOtherMission, iEnd);
|
||
|
new_node.f = new_node.g + new_node.h;
|
||
|
m_OpenList.AddToTail(new_node);
|
||
|
}
|
||
|
else // if it is, check if going there from us is cheaper, if so update parent and costs
|
||
|
{
|
||
|
//Msg("other mission %d already on open list, checking costs\n", iOtherMission);
|
||
|
float my_g_cost = fCurrentCost + EstimateCost(pCI, iCurrentMission, iOtherMission);
|
||
|
if (my_g_cost < m_OpenList[iOnOpenList].g)
|
||
|
{
|
||
|
m_OpenList[iOnOpenList].iParentMission = iCurrentMission;
|
||
|
m_OpenList[iOnOpenList].g = my_g_cost;
|
||
|
m_OpenList[iOnOpenList].f = m_OpenList[iOnOpenList].g + m_OpenList[iOnOpenList].h;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
iOverflow++;
|
||
|
}
|
||
|
//Msg("Error, BuildCampaignRoute overflow!\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
CASW_Campaign_Save::campaign_route_node_t* CASW_Campaign_Save::FindMissionInClosedList(int iMission)
|
||
|
{
|
||
|
for (int i=0;i<m_ClosedList.Count();i++)
|
||
|
{
|
||
|
if (m_ClosedList[i].iMission == iMission)
|
||
|
{
|
||
|
return &m_ClosedList[i];
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::DebugBuiltRoute()
|
||
|
{
|
||
|
campaign_route_node_t* pNode = FindMissionInClosedList(m_iRouteDest);
|
||
|
int iCount = 0;
|
||
|
while (pNode != NULL && pNode->iMission != m_iRouteStart)
|
||
|
{
|
||
|
Msg("Node %d: Mission %d (Cost %f)\n", iCount++, pNode->iMission, pNode->f);
|
||
|
pNode = FindMissionInClosedList(pNode->iParentMission);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ASW_TestRoute_cc(const CCommand &args)
|
||
|
{
|
||
|
if ( args.ArgC() < 3 )
|
||
|
{
|
||
|
Warning( "Usage: ASW_TestRoute [start mission index] [end mission index]\n" );
|
||
|
return;
|
||
|
}
|
||
|
if (!ASWGameRules() || !ASWGameRules()->GetCampaignSave())
|
||
|
{
|
||
|
Msg("Must be playing a campaign game!\n");
|
||
|
return;
|
||
|
}
|
||
|
int iStart = atoi(args[1]);
|
||
|
int iEnd = atoi(args[2]);
|
||
|
CASW_Campaign_Save* pSave = ASWGameRules()->GetCampaignSave();
|
||
|
bool bFound = pSave->BuildCampaignRoute(iStart, iEnd);
|
||
|
if (bFound)
|
||
|
{
|
||
|
Msg("Found route:\n");
|
||
|
pSave->DebugBuiltRoute();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Msg("No route found!\n");
|
||
|
}
|
||
|
}
|
||
|
ConCommand ASW_TestRoute( "ASW_TestRoute", ASW_TestRoute_cc, "Tests loading a savegame", FCVAR_CHEAT );
|
||
|
|
||
|
void ASW_TestLoad_cc(const CCommand &args)
|
||
|
{
|
||
|
if ( args.ArgC() < 2 )
|
||
|
{
|
||
|
Warning( "You must specify the name of the saved campaign game to resume playing.\n" );
|
||
|
return;
|
||
|
}
|
||
|
CASW_Campaign_Save *pSave = new CASW_Campaign_Save();
|
||
|
if (!pSave->LoadGameFromFile(args[1]))
|
||
|
{
|
||
|
Msg("LoadGameFromFile failed!\n");
|
||
|
}
|
||
|
else
|
||
|
pSave->DebugInfo();
|
||
|
}
|
||
|
ConCommand ASW_TestLoad( "ASW_TestLoad", ASW_TestLoad_cc, "Tests loading a savegame", FCVAR_CHEAT );
|
||
|
|
||
|
void CASW_Campaign_Save::StartingCampaignVote()
|
||
|
{
|
||
|
if (!ASWGameRules())
|
||
|
return;
|
||
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
||
|
if (!pGameResource)
|
||
|
return;
|
||
|
|
||
|
// clear votes and ready
|
||
|
m_fVoteEndTime = 0;
|
||
|
m_bNextMissionVoteEnded = false;
|
||
|
for (int i=0;i<ASW_MAX_MISSIONS_PER_CAMPAIGN;i++)
|
||
|
{
|
||
|
m_NumVotes.Set(i, 0);
|
||
|
}
|
||
|
for (int i=0;i<ASW_MAX_READY_PLAYERS;i++)
|
||
|
{
|
||
|
pGameResource->m_bPlayerReady.Set(i, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::ForceNextMissionLaunch()
|
||
|
{
|
||
|
if (!m_fVoteEndTime != 0)
|
||
|
{
|
||
|
m_fVoteEndTime = gpGlobals->curtime + 6.0f;
|
||
|
}
|
||
|
SetThink( &CASW_Campaign_Save::VoteEndThink );
|
||
|
SetNextThink( m_fVoteEndTime );
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::VoteEndThink()
|
||
|
{
|
||
|
SetThink(NULL);
|
||
|
VoteEnded();
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::VoteEnded()
|
||
|
{
|
||
|
if (!ASWGameRules())
|
||
|
return;
|
||
|
|
||
|
if (!ASWGameRules()->GetCampaignInfo())
|
||
|
return;
|
||
|
Msg("CampVote ended\n");
|
||
|
|
||
|
m_bNextMissionVoteEnded = true;
|
||
|
|
||
|
CASW_Campaign_Info *pCI = ASWGameRules()->GetCampaignInfo();
|
||
|
// decide which mission to launch
|
||
|
// find the highest voted launchable,
|
||
|
int iHighestMission = -1;
|
||
|
int iHighestVotes = -1;
|
||
|
CUtlVector<int> Draws;
|
||
|
for (int i=0;i<pCI->GetNumMissions();i++)
|
||
|
{
|
||
|
//CASW_Campaign_Info::CASW_Campaign_Mission_t* pMission = pCI->
|
||
|
if (m_MissionComplete[i] != 0) // already complete
|
||
|
continue;
|
||
|
if (!IsMissionLinkedToACompleteMission(i, pCI) || i <= 0) // launchable
|
||
|
continue;
|
||
|
if (m_NumVotes[i] > iHighestVotes)
|
||
|
{
|
||
|
iHighestVotes = m_NumVotes[i];
|
||
|
Draws.Purge();
|
||
|
Draws.AddToTail(i);
|
||
|
iHighestMission = i;
|
||
|
}
|
||
|
else if (m_NumVotes[i] == iHighestVotes)
|
||
|
{
|
||
|
Draws.AddToTail(i);
|
||
|
}
|
||
|
}
|
||
|
int iMission = iHighestMission;
|
||
|
Msg("highest mission is %d draws is %d\n", iMission, Draws.Count());
|
||
|
if (Draws.Count() > 1)
|
||
|
{
|
||
|
// some missions have equal votes, pick one at random
|
||
|
int iPick = random->RandomInt(0, Draws.Count() - 1);
|
||
|
iMission = Draws[iPick];
|
||
|
}
|
||
|
|
||
|
Msg("Moving to %d\n", iMission);
|
||
|
// move there
|
||
|
ASWGameRules()->RequestCampaignMove(iMission);
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::PlayerDisconnected(CASW_Player *pPlayer)
|
||
|
{
|
||
|
if (!ASWGameRules() || !pPlayer)
|
||
|
return;
|
||
|
if (ASWGameRules()->GetGameState() != ASW_GS_CAMPAIGNMAP)
|
||
|
return;
|
||
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
||
|
if (!pGameResource)
|
||
|
return;
|
||
|
|
||
|
if (m_bNextMissionVoteEnded) // don't allow votes if the mission has already been chosen
|
||
|
return;
|
||
|
|
||
|
// if player is ready, that means he's already voted or a spectator
|
||
|
int iPlayer = pPlayer->entindex() -1 ;
|
||
|
if (iPlayer<0 ||iPlayer>ASW_MAX_READY_PLAYERS-1)
|
||
|
return;
|
||
|
if (pGameResource->IsPlayerReady(pPlayer->entindex()))
|
||
|
{
|
||
|
// subtract his old vote
|
||
|
int iVotedFor = pGameResource->m_iCampaignVote[iPlayer];
|
||
|
if (iVotedFor >=0 && iVotedFor < ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
{
|
||
|
int iVotes = m_NumVotes[iVotedFor] - 1;
|
||
|
m_NumVotes.Set(iVotedFor, iVotes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check for ending the vote
|
||
|
if (pGameResource->AreAllOtherPlayersReady(pPlayer->entindex()))
|
||
|
{
|
||
|
if ( gpGlobals->maxClients > 1 )
|
||
|
{
|
||
|
if (!m_fVoteEndTime != 0)
|
||
|
{
|
||
|
m_fVoteEndTime = gpGlobals->curtime + 4.0f;
|
||
|
}
|
||
|
SetThink( &CASW_Campaign_Save::VoteEndThink );
|
||
|
SetNextThink( m_fVoteEndTime );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VoteEnded();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::PlayerVote(CASW_Player* pPlayer, int iMission)
|
||
|
{
|
||
|
if (!ASWGameRules() || !pPlayer || pPlayer->m_bRequestedSpectator)
|
||
|
return;
|
||
|
if (ASWGameRules()->GetGameState() != ASW_GS_CAMPAIGNMAP)
|
||
|
return;
|
||
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
||
|
if (!pGameResource)
|
||
|
return;
|
||
|
|
||
|
if (m_bNextMissionVoteEnded) // don't allow votes if the mission has already been chosen
|
||
|
return;
|
||
|
|
||
|
// if player is ready, that means he's already voted or a spectator
|
||
|
int iPlayer = pPlayer->entindex() -1 ;
|
||
|
if (iPlayer<0 ||iPlayer>ASW_MAX_READY_PLAYERS-1)
|
||
|
return;
|
||
|
if (pGameResource->IsPlayerReady(pPlayer->entindex()))
|
||
|
{
|
||
|
// subtract his old vote
|
||
|
int iVotedFor = pGameResource->m_iCampaignVote[iPlayer];
|
||
|
if (iVotedFor >=0 && iVotedFor < ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
{
|
||
|
int iVotes = m_NumVotes[iVotedFor] - 1;
|
||
|
m_NumVotes.Set(iVotedFor, iVotes);
|
||
|
}
|
||
|
}
|
||
|
if (iMission < 0 || iMission >= ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
return;
|
||
|
|
||
|
// don't allow vote for this mission if this mission was already completed, or if it's the dropzone
|
||
|
if (m_MissionComplete[iMission] != 0 || iMission <= 0)
|
||
|
return;
|
||
|
|
||
|
// make sure the mission they want to vote for is reachable
|
||
|
if (!IsMissionLinkedToACompleteMission(iMission, ASWGameRules()->GetCampaignInfo()))
|
||
|
return;
|
||
|
|
||
|
// add his vote to the chosen map
|
||
|
pGameResource->m_iCampaignVote[iPlayer] = iMission;
|
||
|
int iVotes = m_NumVotes[iMission] + 1;
|
||
|
m_NumVotes.Set(iMission, iVotes);
|
||
|
// flag him as ready
|
||
|
pGameResource->m_bPlayerReady.Set(iPlayer, true);
|
||
|
|
||
|
if (pGameResource->AreAllOtherPlayersReady(pPlayer->entindex()))
|
||
|
{
|
||
|
if ( gpGlobals->maxClients > 1 )
|
||
|
{
|
||
|
if (!m_fVoteEndTime != 0)
|
||
|
{
|
||
|
m_fVoteEndTime = gpGlobals->curtime + 4.0f;
|
||
|
}
|
||
|
SetThink( &CASW_Campaign_Save::VoteEndThink );
|
||
|
SetNextThink( m_fVoteEndTime );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VoteEnded();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::PlayerSpectating(CASW_Player* pPlayer)
|
||
|
{
|
||
|
if (!ASWGameRules() || !pPlayer)
|
||
|
return;
|
||
|
if (ASWGameRules()->GetGameState() != ASW_GS_CAMPAIGNMAP)
|
||
|
return;
|
||
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
||
|
if (!pGameResource)
|
||
|
return;
|
||
|
|
||
|
if (m_bNextMissionVoteEnded) // don't allow votes if the mission has already been chosen
|
||
|
return;
|
||
|
|
||
|
pPlayer->m_bRequestedSpectator = true;
|
||
|
// if player is ready, that means he's already voted or a spectator
|
||
|
int iPlayer = pPlayer->entindex() -1 ;
|
||
|
if (iPlayer<0 ||iPlayer>ASW_MAX_READY_PLAYERS-1)
|
||
|
return;
|
||
|
if (pGameResource->IsPlayerReady(pPlayer->entindex()))
|
||
|
{
|
||
|
// subtract his old vote
|
||
|
int iVotedFor = pGameResource->m_iCampaignVote[iPlayer];
|
||
|
if (iVotedFor >=0 && iVotedFor < ASW_MAX_MISSIONS_PER_CAMPAIGN)
|
||
|
{
|
||
|
int iVotes = m_NumVotes[iVotedFor] - 1;
|
||
|
m_NumVotes.Set(iVotedFor, iVotes);
|
||
|
}
|
||
|
}
|
||
|
// clear his chosen mission and flag him as ready
|
||
|
pGameResource->m_iCampaignVote[iPlayer] = -1;
|
||
|
pGameResource->m_bPlayerReady.Set(iPlayer, true);
|
||
|
|
||
|
if (pGameResource->AreAllOtherPlayersReady(pPlayer->entindex()))
|
||
|
{
|
||
|
if ( gpGlobals->maxClients > 1 )
|
||
|
{
|
||
|
if (!m_fVoteEndTime != 0)
|
||
|
{
|
||
|
m_fVoteEndTime = gpGlobals->curtime + 4.0f;
|
||
|
}
|
||
|
SetThink( &CASW_Campaign_Save::VoteEndThink );
|
||
|
SetNextThink( m_fVoteEndTime );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VoteEnded();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::SelectDefaultNextCampaignMission()
|
||
|
{
|
||
|
if ( !ASWGameRules() || !ASWGameResource() )
|
||
|
return;
|
||
|
|
||
|
CASW_Campaign_Info *pInfo = ASWGameRules()->GetCampaignInfo();
|
||
|
if ( !pInfo )
|
||
|
return;
|
||
|
|
||
|
// find the first valid mission selection in our list
|
||
|
for ( int i = 1; i < pInfo->GetNumMissions(); i++ ) // skip first dummy entry
|
||
|
{
|
||
|
if ( !m_MissionComplete[i] && IsMissionLinkedToACompleteMission( i, pInfo ) )
|
||
|
{
|
||
|
ASWGameResource()->m_iNextCampaignMission = i;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CASW_Campaign_Save::OnMarineKilled()
|
||
|
{
|
||
|
m_iNumDeaths++;
|
||
|
SaveGameToFile();
|
||
|
}
|