/** * @file * @author Flugente (bears-pit.com) */ #include "CampaignStats.h" #include "message.h" #include "SaveLoadGame.h" #include "GameVersion.h" #include "Campaign Types.h" #include "Game Clock.h" #include "Soldier macros.h" #include "Text.h" #include "Laptop.h" #include "LaptopSave.h" #include "email.h" #include "DynamicDialogue.h" Campaign_Stats gCampaignStats; Incident_Stats gCurrentIncident; // we might save during an incident, thus we have to store the ongoing incident CAMPAIGNSTATSEVENT zCampaignStatsEvent[NUM_CAMPAIGNSTATSEVENTS]; void Incident_Stats::clear() { usID = 0; usTime = 0; usSector = 0; usLevel = 0; usInterestRating = 0; usNPCDied = 0; usCivFactionFought = 0; usIncidentFlags = 0; usOneTimeEventFlags = 0; for (UINT8 i = 0; i < CAMPAIGNHISTORY_SD_MAX; ++i) { usKills[i] = 0; usWounds[i] = 0; usPrisoners[i] = 0; usShots[i] = 0; usParticipants[i] = 0; usPromotions[i] = 0; } } // determine how 'interesting' an incident is - the more happens, the more interesting it is void Incident_Stats::CalcInterestRating() { usInterestRating = 0; for (UINT8 i = 0; i < CAMPAIGNHISTORY_SD_MAX; ++i) { usInterestRating += 100 * usKills[i]; usInterestRating += 50 * usWounds[i]; usInterestRating += 75 * usPrisoners[i]; usInterestRating += usShots[i] / 30; usInterestRating += 20 * usParticipants[i]; usInterestRating += 5 * usPromotions[i]; } if ( usNPCDied ) usInterestRating += 500; if ( usCivFactionFought ) usInterestRating += 1000; // evaluate flags if ( usIncidentFlags & INCIDENT_ARTILLERY_ENEMY ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_ARTILLERY_PLAYERSIDE ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_TANKS_ENEMY ) usInterestRating += 1000; if ( usIncidentFlags & INCIDENT_TANKS_PLAYERSIDE ) usInterestRating += 2000; if ( usIncidentFlags & INCIDENT_MUSTARDGAS_ENEMY ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_MUSTARDGAS_PLAYERSIDE ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_ATTACK_ENEMY ) usInterestRating += 200; if ( usIncidentFlags & INCIDENT_ATTACK_PLAYERSIDE ) usInterestRating += 100; if ( usIncidentFlags & INCIDENT_REINFORCEMENTS_ENEMY ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_REINFORCEMENTS_PLAYERSIDE ) usInterestRating += 500; if ( usIncidentFlags & INCIDENT_SNIPERS_ENEMY ) usInterestRating += 200; if ( usIncidentFlags & INCIDENT_SNIPERS_PLAYERSIDE ) usInterestRating += 100; if ( usIncidentFlags & INCIDENT_AMBUSH ) usInterestRating += 100; if ( usIncidentFlags & INCIDENT_AIRDROP ) usInterestRating += 100; if ( usIncidentFlags & INCIDENT_SURGERY ) usInterestRating += 50; if ( usIncidentFlags & INCIDENT_BUILDINGS_DAMAGED ) usInterestRating += 100; if ( usIncidentFlags & INCIDENT_WIN ) usInterestRating += 100; else usInterestRating += 200; if ( usIncidentFlags & INCIDENT_SAMSITE_SABOTAGED ) usInterestRating += 1500; if ( usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_OMERTA ) usInterestRating += 1000; if ( usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_DEATH_KINGPIN ) usInterestRating += 500; if ( usOneTimeEventFlags & (INCIDENT_ONETIMEEVENT_MASSACRE_HICKS|INCIDENT_ONETIMEEVENT_MASSACRE_BLOODCATS) ) usInterestRating += 800; if ( usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_CITY_RETAKEN ) usInterestRating += 2000; if ( usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_CITY_LIBERATED ) usInterestRating += 1000; } BOOLEAN Incident_Stats::Save( HWFILE hFile ) { UINT32 uiNumBytesWritten = 0; if ( !FileWrite( hFile, this, SIZEOF_INCIDENT_STATS_POD, &uiNumBytesWritten ) ) { return(FALSE); } return( TRUE ); } BOOLEAN Incident_Stats::Load( HWFILE hwFile ) { if(guiCurrentSaveGameVersion >= CAMPAIGNSTATS) { UINT32 numBytesRead = 0; numBytesRead = ReadFieldByField(hwFile, &usID, sizeof(usID), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usTime, sizeof(usTime), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usSector, sizeof(usSector), sizeof(UINT8), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usLevel, sizeof(usLevel), sizeof(UINT8), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usInterestRating, sizeof(usInterestRating), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usNPCDied, sizeof(usNPCDied), sizeof(UINT8), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usCivFactionFought, sizeof(usCivFactionFought), sizeof(UINT8), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usIncidentFlags, sizeof(usIncidentFlags), sizeof(UINT64), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usOneTimeEventFlags, sizeof(usOneTimeEventFlags),sizeof(UINT64), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usKills, sizeof(usKills), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usWounds, sizeof(usWounds), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usPrisoners, sizeof(usPrisoners), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usShots, sizeof(usShots), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usParticipants, sizeof(usParticipants), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usPromotions, sizeof(usPromotions), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usFiller, sizeof(usFiller), sizeof(UINT8), numBytesRead); if( numBytesRead != SIZEOF_INCIDENT_STATS_POD ) return(FALSE); } return( TRUE ); } void Incident_Stats::AddStat( SOLDIERTYPE* pSoldier, UINT8 aType ) { UINT8 group = CAMPAIGNHISTORY_SD_MERC; if ( pSoldier ) { switch ( pSoldier->bTeam ) { case OUR_TEAM: group = CAMPAIGNHISTORY_SD_MERC; break; case ENEMY_TEAM: { if ( ARMED_VEHICLE( pSoldier ) || ENEMYROBOT( pSoldier ) ) group = CAMPAIGNHISTORY_SD_ENEMY_TANK; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) group = CAMPAIGNHISTORY_SD_ENEMY_ADMIN; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE ) group = CAMPAIGNHISTORY_SD_ENEMY_ELITE; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY ) group = CAMPAIGNHISTORY_SD_ENEMY_ARMY; } break; case CREATURE_TEAM: if ( pSoldier->IsZombie() ) group = CAMPAIGNHISTORY_SD_ZOMBIE; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) group = CAMPAIGNHISTORY_SD_ENEMY_ADMIN; else group = CAMPAIGNHISTORY_SD_CREATURE; break; case MILITIA_TEAM: { if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) group = CAMPAIGNHISTORY_SD_MILITIA_GREEN; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) group = CAMPAIGNHISTORY_SD_MILITIA_REGULAR; else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) group = CAMPAIGNHISTORY_SD_MILITIA_ELITE; } break; case CIV_TEAM: default: group = CAMPAIGNHISTORY_SD_CIV; break; } if ( ARMED_VEHICLE( pSoldier ) ) { if ( pSoldier->bSide == 0 ) usIncidentFlags |= INCIDENT_TANKS_PLAYERSIDE; else usIncidentFlags |= INCIDENT_TANKS_ENEMY; } } switch ( aType ) { case CAMPAIGNHISTORY_TYPE_KILL: { usKills[group]++; if ( pSoldier ) { if ( pSoldier->ubProfile == KINGPIN ) usOneTimeEventFlags |= INCIDENT_ONETIMEEVENT_DEATH_KINGPIN; else if ( pSoldier->ubProfile == DARREL ) usOneTimeEventFlags |= INCIDENT_ONETIMEEVENT_MASSACRE_HICKS; } } break; case CAMPAIGNHISTORY_TYPE_WOUND: usWounds[group]++; break; case CAMPAIGNHISTORY_TYPE_PRISONER: usPrisoners[group]++; break; case CAMPAIGNHISTORY_TYPE_SHOT: usShots[group]++; break; case CAMPAIGNHISTORY_TYPE_PARTICIPANT: usParticipants[group]++; break; case CAMPAIGNHISTORY_TYPE_PROMOTION: default: usPromotions[group]++; break; } } UINT8 tmpnr = 0; static CHAR16 tmpdirstring[2][ 50 ]; // we need 2 arrays, in case we need 2 name pointers in one string STR16 Incident_Stats::GetAttackerDirString( BOOLEAN fAttacker ) { CHAR16 helperstr[ 50 ]; swprintf(helperstr, L"" ); ++tmpnr; if ( tmpnr > 1 ) tmpnr = 0; tmpdirstring[tmpnr][0] = '\0' ; UINT8 dirs = 0; CHAR16 dir1[ 10 ], dir2[ 10 ], dir3[ 10 ], dir4[ 10 ]; swprintf(dir1, L"" ); swprintf(dir2, L"" ); swprintf(dir3, L"" ); swprintf(dir4, L"" ); if ( (fAttacker && usIncidentFlags & INCIDENT_ATTACK_ENEMY) || (!fAttacker && usIncidentFlags & INCIDENT_ATTACK_PLAYERSIDE) ) { if ( usIncidentFlags & INCIDENT_ATTACKDIR_NORTH_ENEMY ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_NORTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_WEST_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_WEST] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH_ENEMY ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir4, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_WEST_ENEMY ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_WEST] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH_ENEMY ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST_ENEMY ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else { if ( usIncidentFlags & INCIDENT_ATTACKDIR_NORTH ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_NORTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_WEST ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_WEST] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir4, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_WEST ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_WEST] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_SOUTH ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SOUTH] ); ++dirs; if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } else if ( usIncidentFlags & INCIDENT_ATTACKDIR_EAST ) { swprintf(dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_EAST] ); ++dirs; } } switch ( dirs ) { case 4: swprintf(helperstr, L"%s, %s, %s %s %s", dir1, dir2, dir3, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AND], dir4 ); break; case 3: swprintf(helperstr, L"%s, %s %s %s", dir1, dir2, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AND], dir3 ); break; case 2: swprintf(helperstr, L"%s %s %s", dir1, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AND], dir2 ); break; case 1: swprintf(helperstr, L"%s", dir1 ); break; case 0: swprintf(helperstr, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_UNKNOWNLOCATION] ); break; default: break; } wcscat( tmpdirstring[tmpnr], helperstr ); return tmpdirstring[tmpnr]; } void Incident_Stats::GetTerrainandType(UINT8& arTerrain, UINT8& arType) { if ( usLevel > 0 ) arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_UNDERGROUND; else { SECTORINFO *pSector = &SectorInfo[ usSector ]; switch( pSector->ubTraversability[ THROUGH_STRATEGIC_MOVE ] ) { case COASTAL: case COASTAL_ROAD: case WATER: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_COASTAL; break; case SAND: case SAND_ROAD: case SAND_SAM_SITE: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_DESERT; break; case DENSE: case DENSE_ROAD: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_FOREST; break; case GROUNDBARRIER: case EDGEOFWORLD: case HILLS: case HILLS_ROAD: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_MOUNTAIN; break; case NS_RIVER: case EW_RIVER: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_RIVER; break; case SWAMP: case SWAMP_ROAD: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_SWAMP; break; case TROPICS: case TROPICS_ROAD: case TROPICS_SAM_SITE: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_TROPICAL; break; case TOWN: case MEDUNA_SAM_SITE: case CAMBRIA_HOSPITAL_SITE: case DRASSEN_AIRPORT_SITE: case MEDUNA_AIRPORT_SITE: case SAM_SITE: case REBEL_HIDEOUT: case TIXA_DUNGEON: case CREATURE_LAIR: case ORTA_BASEMENT: case TUNNEL: case SHELTER: case ABANDONED_MINE: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_URBAN; break; case ROAD: case PLAINS: case SPARSE: case FARMLAND: case PLAINS_ROAD: case SPARSE_ROAD: case FARMLAND_ROAD: case SPARSE_SAM_SITE: default: arTerrain = CAMPAINGHISTORY_PICLIBRARY_TERRAIN_SPARSE; break; } } if ( usIncidentFlags & INCIDENT_AIRDROP ) arType = CAMPAINGHISTORY_PICLIBRARY_TYPE_AIRDROP; else if ( usIncidentFlags & (INCIDENT_TANKS_ENEMY|INCIDENT_TANKS_PLAYERSIDE) ) arType = CAMPAINGHISTORY_PICLIBRARY_TYPE_TANKS; else if ( usIncidentFlags & INCIDENT_WIN ) arType = CAMPAINGHISTORY_PICLIBRARY_TYPE_WIN; else arType = CAMPAINGHISTORY_PICLIBRARY_TYPE_LOSS; } Campaign_Stats::Campaign_Stats() { //*mspSelf = *this; } Campaign_Stats::~Campaign_Stats() { //if ( mspSelf ) //delete mspSelf; } /*Campaign_Stats* Campaign_Stats::GetObject() { if ( !mspSelf ) mspSelf = new Campaign_Stats; return mspSelf; }*/ void Campaign_Stats::clear() { memset(this, 0, SIZEOF_CAMPAIGN_STATS_POD); mIncidentVector.clear(); } BOOLEAN Campaign_Stats::Save( HWFILE hFile ) { if(guiCurrentSaveGameVersion >= CAMPAIGNSTATS) { UINT32 uiNumBytesWritten = 0; if ( !FileWrite( hFile, this, SIZEOF_CAMPAIGN_STATS_POD, &uiNumBytesWritten ) ) return(FALSE); std::vector::iterator itend = mIncidentVector.end(); for (std::vector::iterator it = mIncidentVector.begin(); it != itend; ++it) { if ( !(*it).Save ( hFile ) ) return(FALSE); } } return( TRUE ); } BOOLEAN Campaign_Stats::Load( HWFILE hwFile ) { if(guiCurrentSaveGameVersion >= CAMPAIGNSTATS) { clear(); UINT32 numBytesRead = 0; numBytesRead = ReadFieldByField(hwFile, &usKills, sizeof(usKills), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usWounds, sizeof(usWounds), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usPrisoners, sizeof(usPrisoners), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usShots, sizeof(usShots), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &sMoneyEarned, sizeof(sMoneyEarned), sizeof(INT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usConsumed, sizeof(usConsumed), sizeof(FLOAT), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usNumIncidents, sizeof(usNumIncidents), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usHighestID, sizeof(usHighestID), sizeof(UINT32), numBytesRead); numBytesRead = ReadFieldByField(hwFile, &usFiller, sizeof(usFiller), sizeof(UINT8), numBytesRead); if( numBytesRead != SIZEOF_CAMPAIGN_STATS_POD ) return(FALSE); for (UINT32 i = 0; i < usNumIncidents; ++i) { Incident_Stats incident; if ( !incident.Load ( hwFile ) ) return(FALSE); // if option is active, only read the last reports and forget the rest if ( gGameExternalOptions.usReportsToLoad < 0 || i >= usNumIncidents - gGameExternalOptions.usReportsToLoad ) mIncidentVector.push_back(incident); } // re-evaluate the vector size - we may have been ordered to forget some usNumIncidents = mIncidentVector.size(); } return( TRUE ); } // Warning! As this also updates the overall kill counters, don't use this function when loading incidents void Campaign_Stats::AddNewIncident(Incident_Stats arIncident) { // make sure interest rating is up to date arIncident.CalcInterestRating(); mIncidentVector.push_back(arIncident); ++usNumIncidents; // update kills/wounds/prisoner numbers for (UINT16 i = 0; i < CAMPAIGNHISTORY_SD_MAX; ++i) { usKills[i] += arIncident.usKills[i]; usWounds[i] += arIncident.usWounds[i]; usPrisoners[i] += arIncident.usPrisoners[i]; usShots[i] += arIncident.usShots[i]; } } void Campaign_Stats::AddMoneyEarned(UINT8 aType, INT32 aVal) { if ( aType >= CAMPAIGN_MONEY_MAX ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Error: Unknown money type!" ); return; } sMoneyEarned[aType] += aVal; } void Campaign_Stats::AddConsumption(UINT8 aType, FLOAT aVal) { if ( aType >= CAMPAIGN_CONSUMED_MAX ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Error: Unknown consumption type!" ); return; } usConsumed[aType] += aVal; } // add this incident to the campaign stats and then clear it void FinishIncident(INT16 sX, INT16 sY, INT8 sZ) { // it is possible to enter a combat without any real fighting. // For now, lets just assume this happens with undetected spies. If nothing of interest happened, don't add this incident (it would be boring to read anyway) if ( gCurrentIncident.usIncidentFlags & (INCIDENT_SPYACTION_ENEMY|INCIDENT_SPYACTION_PLAYERSIDE) && !(gCurrentIncident.usIncidentFlags & INCIDENT_SPYACTION_UNCOVERED) ) { // if noting of interest happened... if ( !(gCurrentIncident.usIncidentFlags & INCIDENT_EVENT) && gCurrentIncident.usOneTimeEventFlags == 0 ) { UINT16 i = 0; // if nobody was harmed... for (i = 0; i < CAMPAIGNHISTORY_SD_MAX; ++i) { if ( gCurrentIncident.usKills[i] || gCurrentIncident.usWounds[i] || gCurrentIncident.usPrisoners[i] ) break; } if ( i == CAMPAIGNHISTORY_SD_MAX ) { // totally boring. Don't add this, just clean it and exit gCurrentIncident.clear(); return; } } } // due to odd coding, we do not know when an incident starts // (checking for entering combat isn't enough, as we do that multiple times per battle) // we thus set the relevant data when finishing an incident gCampaignStats.usHighestID++; gCurrentIncident.usID = gCampaignStats.usHighestID; gCurrentIncident.usTime = GetWorldTotalSeconds(); gCurrentIncident.usSector = SECTOR(sX, sY); gCurrentIncident.usLevel = sZ; // add flags depending on quests etc. // if this is incident 1, we just landed - special text then if ( gCampaignStats.usHighestID == 1 ) gCurrentIncident.usOneTimeEventFlags |= INCIDENT_ONETIMEEVENT_OMERTA; // dynamic opinions if (gGameExternalOptions.fDynamicOpinions) { HandleDynamicOpinionBattleFinished((gCurrentIncident.usIncidentFlags & INCIDENT_WIN) != 0); } gCampaignStats.AddNewIncident(gCurrentIncident); // reset incident after adding it gCurrentIncident.clear(); } // start a new incident void StartIncident(INT16 sX, INT16 sY, INT8 sZ) { gCampaignStats.usHighestID++; gCurrentIncident.usID = gCampaignStats.usHighestID; gCurrentIncident.usTime = GetWorldTotalSeconds(); gCurrentIncident.usSector = SECTOR(sX, sY); gCurrentIncident.usLevel = sZ; } INT32 GetPositionOfIncident( UINT32 aIncidentId ) { INT32 cnt = 0; std::vector::iterator itend = gCampaignStats.mIncidentVector.end( ); for ( std::vector::iterator it = gCampaignStats.mIncidentVector.begin( ); it != itend; ++it ) { if ( (*it).usID == aIncidentId ) return cnt; ++cnt; } return -1; } static CHAR16 gIncidentNameText[100]; STR16 GetIncidentName( UINT32 aIncidentId ) { swprintf( gIncidentNameText, L"NAME NOT FOUND" ); std::vector::iterator itend = gCampaignStats.mIncidentVector.end( ); for ( std::vector::iterator it = gCampaignStats.mIncidentVector.begin( ); it != itend; ++it ) { if ( (*it).usID == aIncidentId ) { Incident_Stats incident = (*it); UINT32 prefix = (incident.usTime + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_PREFIX; UINT32 suffix = (incident.usTime + incident.usShots[CAMPAIGNHISTORY_SD_MERC] + 7 * incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_SUFFIX; swprintf( gIncidentNameText, szCampaignStatsOperationPrefix[prefix], szCampaignStatsOperationSuffix[suffix] ); break; } } return gIncidentNameText; } UINT32 GetIdOfCurrentlyOngoingIncident() { return gCampaignStats.usHighestID + 1; }