void GenerateEncounterTable (CUniverse &Universe, CXMLElement *pCmdLine)
	{
	ALERROR error;
	int i, j;

	//	Get the criteria from the command line

	SEncounterCriteria Criteria;
	ParseEncounterCriteria(pCmdLine->GetAttribute(CRITERIA_ATTRIB), &Criteria);
	bool bAll = pCmdLine->GetAttributeBool(ALL_ATTRIB);

	//	Generate a table of all matching encounters

	CSymbolTable Table(FALSE, TRUE);

	//	Loop over all items for this level and add them to
	//	a sorted table.

	for (i = 0; i < Universe.GetStationTypeCount(); i++)
		{
		CStationType *pType = Universe.GetStationType(i);
		int iLevel = pType->GetLevel();
		if (iLevel == 0 && !bAll)
			continue;

		//	If we don't match the criteria, then continue

		if (!MatchesEncounterCriteria(Criteria, pType->GetAttributes()))
			continue;

		//	Get the category and name

		CString sCategory = pType->GetDataField(FIELD_CATEGORY);
		CString sName = pType->GetDataField(FIELD_NAME);
		if (*sName.GetASCIIZPointer() == '(')
			sName = strSubString(sName, 1, -1);

		//	Figure out the sort order

		char szBuffer[1024];
		wsprintf(szBuffer, "%02d%s%s", 
				pType->GetLevel(),
				sCategory.GetASCIIZPointer(), 
				sName.GetASCIIZPointer());
		Table.AddEntry(CString(szBuffer), (CObject *)pType);
		}

	//	Generate a list of columns to display

	CStringArray Cols;
	Cols.AppendString(FIELD_LEVEL);
	Cols.AppendString(FIELD_CATEGORY);
	Cols.AppendString(FIELD_NAME);
	if (pCmdLine->GetAttributeBool(FIELD_ARMOR_CLASS))
		Cols.AppendString(FIELD_ARMOR_CLASS);
	if (pCmdLine->GetAttributeBool(FIELD_HP))
		Cols.AppendString(FIELD_HP);
	if (pCmdLine->GetAttributeBool(FIELD_FIRE_RATE_ADJ))
		Cols.AppendString(FIELD_FIRE_RATE_ADJ);
	if (pCmdLine->GetAttributeBool(FIELD_TOTAL_COUNT))
		Cols.AppendString(FIELD_TOTAL_COUNT);
	if (pCmdLine->GetAttributeBool(FIELD_CAN_ATTACK))
		Cols.AppendString(FIELD_CAN_ATTACK);
	if (pCmdLine->GetAttributeBool(FIELD_EXPLOSION_TYPE))
		Cols.AppendString(FIELD_EXPLOSION_TYPE);

	//	If we need to output total count, then load the table

	CSymbolTable TotalCount(TRUE, TRUE);
	if (pCmdLine->GetAttributeBool(FIELD_TOTAL_COUNT))
		{
		if (error = LoadTotalCount(TOTAL_COUNT_FILENAME, TotalCount))
			return;
		}

	//	If we've got any entries in the table, output now

	if (Table.GetCount())
		{
		//	Output the header

		for (j = 0; j < Cols.GetCount(); j++)
			{
			if (j != 0)
				printf("\t");

			printf(Cols.GetStringValue(j).GetASCIIZPointer());
			}

		printf("\n");

		//	Output each row

		for (i = 0; i < Table.GetCount(); i++)
			{
			CStationType *pType = (CStationType *)Table.GetValue(i);

			for (j = 0; j < Cols.GetCount(); j++)
				{
				if (j != 0)
					printf("\t");

				CString sField = Cols.GetStringValue(j);
				CString sValue = pType->GetDataField(sField);

				if (strEquals(sField, FIELD_FIRE_RATE_ADJ))
					printf("%.2f", strToInt(sValue, 0, NULL) / 1000.0);
				else if (strEquals(sField, FIELD_TOTAL_COUNT))
					{
					double rCount = 0.0;

					CString sKey = strFromInt(pType->GetUNID(), FALSE);
					EntryInfo *pEntry;
					if (TotalCount.Lookup(sKey, (CObject **)&pEntry) == NOERROR)
						rCount = pEntry->rTotalCount;

					printf("%.2f", rCount);
					}
				else
					printf(sValue.GetASCIIZPointer());
				}

			printf("\n");
			}

		printf("\n");
		}
	else
		printf("No entries match criteria.\n");
	}
Exemple #2
0
void RunEncounterSim (CUniverse &Universe, CXMLElement *pCmdLine)
	{
	int iRuns = pCmdLine->GetAttributeIntegerBounded(COUNT_ATTRIB, 1, -1, DEFAULT_RUN_COUNT);
	int iTimeOut = DEFAULT_TIME_OUT;

	//	Get the station criteria

	CString sCriteria = strPatternSubst(CONSTLIT("%s t"), pCmdLine->GetAttribute(DEFENDERS_ATTRIB));

	CDesignTypeCriteria DefenderCriteria;
	if (CDesignTypeCriteria::ParseCriteria(sCriteria, &DefenderCriteria) != NOERROR)
		{
		printf("ERROR: Unable to parse defenders criteria.\n");
		return;
		}

	bool bAll = pCmdLine->GetAttributeBool(CONSTLIT("all"));

	//	Get the criteria from the command line. Always append 's' because we
	//	want ship classes.

	sCriteria = strPatternSubst(CONSTLIT("%s s"), pCmdLine->GetAttribute(ATTACKERS_ATTRIB));

	CDesignTypeCriteria AttackerCriteria;
	if (CDesignTypeCriteria::ParseCriteria(sCriteria, &AttackerCriteria) != NOERROR)
		{
		printf("ERROR: Unable to parse attackers criteria.\n");
		return;
		}

	//	Create a viewer, if desired

	CSimViewer Viewer;
	if (pCmdLine->GetAttributeBool(VIEWER_ATTRIB))
		Viewer.Create();

	//	Print headers

	printf("Encounter\tLevel");

	int iAttacker;
	for (iAttacker = 0; iAttacker < Universe.GetShipClassCount(); iAttacker++)
		{
		CShipClass *pAttackerClass = Universe.GetShipClass(iAttacker);
		if (!pAttackerClass->MatchesCriteria(AttackerCriteria))
			continue;

		printf("\t%s", pAttackerClass->GetName().GetASCIIZPointer());
		}

	printf("\n");

	//	Loop over all defenders

	int iDefender;
	for (iDefender = 0; iDefender < Universe.GetStationTypeCount(); iDefender++)
		{
		CStationType *pDefenderType = Universe.GetStationType(iDefender);
		if (!bAll && pDefenderType->GetLevel() == 0)
			continue;

		if (!pDefenderType->MatchesCriteria(DefenderCriteria))
			continue;

		//	Compute an enemy of the station

		CSovereign *pAttackerSovereign = GetAttackerSovereign(Universe, pDefenderType);
		if (pAttackerSovereign == NULL)
			{
			printf("ERROR: Unable to find enemy sovereign of encounter: %s [%x].\n", pDefenderType->GetNounPhrase().GetASCIIZPointer(), pDefenderType->GetUNID());
			return;
			}

		printf("%s [%x]\t%d", pDefenderType->GetNounPhrase().GetASCIIZPointer(), pDefenderType->GetUNID(), pDefenderType->GetLevel());

		//	Loop over all attackers

		for (iAttacker = 0; iAttacker < Universe.GetShipClassCount(); iAttacker++)
			{
			CShipClass *pAttackerClass = Universe.GetShipClass(iAttacker);
			if (!pAttackerClass->MatchesCriteria(AttackerCriteria))
				continue;

			//	Do several runs so we can get some statistical data

			int iAttackerSurvived = 0;
			int iRun;
			for (iRun = 0; iRun < iRuns; iRun++)
				{
				EResults iResult = RunEncounter(Universe, Viewer, pDefenderType, pAttackerClass, pAttackerSovereign);
				if (iResult == resultError)
					return;

				//	Did the attacker survive?

				if (iResult != resultAttackerDestroyed)
					iAttackerSurvived++;
				}

			//	Output results for this attacker

			int iSurvivability = 100 * iAttackerSurvived / iRuns;
			printf("\t%d%%", iSurvivability);
			}

		printf("\n");
		}

	Viewer.Destroy();
	}
void GenerateEncounterTable (CUniverse &Universe, CXMLElement *pCmdLine)
	{
	ALERROR error;
	int i, j;

	//	Get the criteria from the command line. Always append 't' because we
	//	want station types.

	CString sCriteria = strPatternSubst(CONSTLIT("%s t"), pCmdLine->GetAttribute(CRITERIA_ATTRIB));

	//	Parse it

	CDesignTypeCriteria Criteria;
	if (CDesignTypeCriteria::ParseCriteria(sCriteria, &Criteria) != NOERROR)
		{
		printf("ERROR: Unable to parse criteria.\n");
		return;
		}

	bool bAll = pCmdLine->GetAttributeBool(ALL_ATTRIB);

	//	Generate a table of all matching encounters

	CSymbolTable Table(FALSE, TRUE);

	//	Loop over all items for this level and add them to
	//	a sorted table.

	for (i = 0; i < Universe.GetStationTypeCount(); i++)
		{
		CStationType *pType = Universe.GetStationType(i);
		int iLevel = pType->GetLevel();
		if (iLevel == 0 && !bAll)
			continue;

		//	If we don't match the criteria, then continue

		if (!pType->MatchesCriteria(Criteria))
			continue;

		//	Get the category and name

		CString sCategory = pType->GetDataField(FIELD_CATEGORY);
		CString sName = pType->GetDataField(FIELD_NAME);
		if (*sName.GetASCIIZPointer() == '(')
			sName = strSubString(sName, 1, -1);

		//	Figure out the sort order

		char szBuffer[1024];
		wsprintf(szBuffer, "%02d%s%s", 
				pType->GetLevel(),
				sCategory.GetASCIIZPointer(), 
				sName.GetASCIIZPointer());
		Table.AddEntry(CString(szBuffer), (CObject *)pType);
		}

	//	Generate a list of columns to display

	TArray<CString> Cols;
	Cols.Insert(FIELD_LEVEL);
	Cols.Insert(FIELD_CATEGORY);
	Cols.Insert(FIELD_NAME);

	for (i = 0; i < pCmdLine->GetAttributeCount(); i++)
		{
		CString sAttrib = pCmdLine->GetAttributeName(i);

		if (!strEquals(sAttrib, CONSTLIT("all"))
				&& !strEquals(sAttrib, CONSTLIT("criteria"))
				&& !strEquals(sAttrib, CONSTLIT("encountertable"))
				&& !strEquals(sAttrib, CONSTLIT("nologo")))
			{
			CString sValue = pCmdLine->GetAttribute(i);
			
			if (!strEquals(sValue, CONSTLIT("true")))
				Cols.Insert(strPatternSubst(CONSTLIT("%s:%s"), sAttrib, sValue));
			else
				Cols.Insert(sAttrib);
			}
		}

#if 0
	if (pCmdLine->GetAttributeBool(FIELD_ARMOR_CLASS))
		Cols.Insert(FIELD_ARMOR_CLASS);
	if (pCmdLine->GetAttributeBool(FIELD_HP))
		Cols.Insert(FIELD_HP);
	if (pCmdLine->GetAttributeBool(FIELD_FIRE_RATE_ADJ))
		Cols.Insert(FIELD_FIRE_RATE_ADJ);
	if (pCmdLine->GetAttributeBool(FIELD_TOTAL_COUNT))
		Cols.Insert(FIELD_TOTAL_COUNT);
	if (pCmdLine->GetAttributeBool(FIELD_CAN_ATTACK))
		Cols.Insert(FIELD_CAN_ATTACK);
	if (pCmdLine->GetAttributeBool(FIELD_EXPLOSION_TYPE))
		Cols.Insert(FIELD_EXPLOSION_TYPE);
#endif

	//	If we need to output total count, then load the table

	CSymbolTable TotalCount(TRUE, TRUE);
	if (pCmdLine->GetAttributeBool(FIELD_TOTAL_COUNT))
		{
		if (error = LoadTotalCount(TOTAL_COUNT_FILENAME, TotalCount))
			return;
		}

	//	If we've got any entries in the table, output now

	if (Table.GetCount())
		{
		//	Output the header

		for (j = 0; j < Cols.GetCount(); j++)
			{
			if (j != 0)
				printf("\t");

			printf(Cols[j].GetASCIIZPointer());
			}

		printf("\n");

		//	Output each row

		for (i = 0; i < Table.GetCount(); i++)
			{
			CStationType *pType = (CStationType *)Table.GetValue(i);

			for (j = 0; j < Cols.GetCount(); j++)
				{
				if (j != 0)
					printf("\t");

				const CString &sField = Cols[j];
				CString sValue = pType->GetDataField(sField);

				if (strEquals(sField, FIELD_FIRE_RATE_ADJ))
					printf("%.2f", strToInt(sValue, 0, NULL) / 1000.0);
				else if (strEquals(sField, FIELD_TOTAL_COUNT))
					{
					double rCount = 0.0;

					CString sKey = strFromInt(pType->GetUNID(), false);
					EntryInfo *pEntry;
					if (TotalCount.Lookup(sKey, (CObject **)&pEntry) == NOERROR)
						rCount = pEntry->rTotalCount;

					printf("%.2f", rCount);
					}
				else
					printf(sValue.GetASCIIZPointer());
				}

			printf("\n");
			}

		printf("\n");
		}
	else
		printf("No entries match criteria.\n");
	}
Exemple #4
0
void GenerateEncounterTable (CUniverse &Universe, CXMLElement *pCmdLine, CIDTable &EntityTable)
	{
	ALERROR error;
	int i, j;

	//	Get the criteria from the command line. Always append 't' because we
	//	want station types.

	CString sCriteria = strPatternSubst(CONSTLIT("%s t"), pCmdLine->GetAttribute(CRITERIA_ATTRIB));

	//	Parse it

	CDesignTypeCriteria Criteria;
	if (CDesignTypeCriteria::ParseCriteria(sCriteria, &Criteria) != NOERROR)
		{
		printf("ERROR: Unable to parse criteria.\n");
		return;
		}

	bool bAll = pCmdLine->GetAttributeBool(ALL_ATTRIB);
	bool bOnlyNotRandom = pCmdLine->GetAttributeBool(ONLY_NOT_RANDOM_ATTRIB);

	//	Generate a table of all matching encounters

	CSymbolTable Table(FALSE, TRUE);

	//	Loop over all items for this level and add them to
	//	a sorted table.

	for (i = 0; i < Universe.GetStationTypeCount(); i++)
		{
		CStationType *pType = Universe.GetStationType(i);
		int iLevel = pType->GetLevel();

		//	If we're selecting all types, then do it

		if (bAll)
			;

		//	If we only want non-random types, then skip any random encounters.

		else if (bOnlyNotRandom)
			{
			if (pType->CanBeEncounteredRandomly())
				continue;
			}

		//	Otherwise we skip anything except random encounters.

		else
			{
			if (!pType->CanBeEncounteredRandomly())
				continue;
			}

		//	If we don't match the criteria, then continue

		if (!pType->MatchesCriteria(Criteria))
			continue;

		//	Get the category and name

		CString sCategory = pType->GetDataField(FIELD_CATEGORY);
		CString sName = pType->GetDataField(FIELD_NAME);
		if (*sName.GetASCIIZPointer() == '(')
			sName = strSubString(sName, 1, -1);

		//	Figure out the sort order

		char szBuffer[1024];
		wsprintf(szBuffer, "%02d%s%s%08x", 
				iLevel,
				sCategory.GetASCIIZPointer(), 
				sName.GetASCIIZPointer(),
				pType->GetUNID());
		Table.AddEntry(CString(szBuffer), (CObject *)pType);
		}

	//	Generate a list of columns to display

	TArray<CString> Cols;
	Cols.Insert(FIELD_LEVEL);
	Cols.Insert(FIELD_CATEGORY);
	Cols.Insert(FIELD_NAME);

	for (i = 0; i < pCmdLine->GetAttributeCount(); i++)
		{
		CString sAttrib = pCmdLine->GetAttributeName(i);

		if (!IsMainCommandParam(sAttrib)
				&& !strEquals(sAttrib, ONLY_NOT_RANDOM_ATTRIB)
				&& !strEquals(sAttrib, CONSTLIT("encountertable")))
			{
			CString sValue = pCmdLine->GetAttribute(i);
			
			if (!strEquals(sValue, CONSTLIT("true")))
				Cols.Insert(strPatternSubst(CONSTLIT("%s:%s"), sAttrib, sValue));
			else
				Cols.Insert(sAttrib);
			}
		}

	//	If we need to output total count, then load the table

	CDesignTypeStats TotalCount;
	if (pCmdLine->GetAttributeBool(FIELD_TOTAL_COUNT)
			|| pCmdLine->GetAttributeBool(FIELD_COUNT_DISTRIBUTION))
		{
		if (error = LoadDesignTypeStats(&TotalCount))
			return;
		}

	//	If we've got any entries in the table, output now

	if (Table.GetCount())
		{
		//	Output the header

		for (j = 0; j < Cols.GetCount(); j++)
			{
			if (j != 0)
				printf("\t");

			printf(Cols[j].GetASCIIZPointer());
			}

		printf("\n");

		//	Output each row

		for (i = 0; i < Table.GetCount(); i++)
			{
			CStationType *pType = (CStationType *)Table.GetValue(i);

			for (j = 0; j < Cols.GetCount(); j++)
				{
				if (j != 0)
					printf("\t");

				const CString &sField = Cols[j];

				//	Get the value

				CString sValue;
				if (strEquals(sField, FIELD_ENTITY))
					{
					CString *pValue;
					if (EntityTable.Lookup(pType->GetUNID(), (CObject **)&pValue) == NOERROR)
						sValue = *pValue;
					else
						sValue = CONSTLIT("?");
					}
				else
					sValue = pType->GetDataField(sField);

				//	Format and output

				if (strEquals(sField, FIELD_FIRE_RATE_ADJ))
					printf("%.2f", strToInt(sValue, 0, NULL) / 1000.0);
				else if (strEquals(sField, FIELD_TOTAL_COUNT))
					{
					SDesignTypeInfo *pInfo = TotalCount.GetAt(pType->GetUNID());
					double rCount = (pInfo ? pInfo->rPerGameMeanCount : 0.0);
					printf("%.2f", rCount);
					}
				else if (strEquals(sField, FIELD_COUNT_DISTRIBUTION))
					{
					SDesignTypeInfo *pInfo = TotalCount.GetAt(pType->GetUNID());
					printf("%s", (pInfo ? pInfo->sDistribution : NULL_STR).GetASCIIZPointer());
					}
				else
					printf(sValue.GetASCIIZPointer());
				}

			printf("\n");
			}

		printf("\n");
		}
	else
		printf("No entries match criteria.\n");
	}
void CPlayerGameStats::GenerateGameStats (CGameStats &Stats, CSpaceObject *pPlayerShip, bool bGameOver) const

//	GenerateGameStats
//
//	Generates a stats for everything we track

	{
	int j;

	CShip *pShip = (pPlayerShip ? pPlayerShip->AsShip() : NULL);
	if (pShip == NULL)
		return;

	CPlayerShipController *pPlayer = (CPlayerShipController *)pShip->GetController();
	if (pPlayer == NULL)
		return;

	CSovereign *pPlayerSovereign = g_pUniverse->FindSovereign(g_PlayerSovereignUNID);
	if (pPlayerSovereign == NULL)
		return;

	//	Base stats

	Stats.Insert(CONSTLIT("Genome"), strCapitalize(GetGenomeName(pPlayer->GetPlayerGenome())));
	Stats.Insert(CONSTLIT("Score"), strFormatInteger(CalcEndGameScore(), -1, FORMAT_THOUSAND_SEPARATOR | FORMAT_UNSIGNED));
	Stats.Insert(CONSTLIT("Ship class"), pShip->GetNounPhrase(0));

	CTimeSpan Time = GetPlayTime();
	if (!Time.IsBlank())
		Stats.Insert(CONSTLIT("Time played"), Time.Format(NULL_STR));

#ifdef REAL_TIME
	Time = GetGameTime();
	if (!Time.IsBlank())
		Stats.Insert(CONSTLIT("Time elapsed in game"), Time.Format(NULL_STR));
#endif

	//	Some combat stats

	CString sDestroyed = GetStat(ENEMY_SHIPS_DESTROYED_STAT);
	if (!sDestroyed.IsBlank())
		Stats.Insert(CONSTLIT("Enemy ships destroyed"), sDestroyed, CONSTLIT("combat"));

	sDestroyed = GetStat(FRIENDLY_SHIPS_DESTROYED_STAT);
	if (!sDestroyed.IsBlank())
		Stats.Insert(CONSTLIT("Friendly ships destroyed"), sDestroyed, CONSTLIT("combat"));

	sDestroyed = GetStat(ENEMY_STATIONS_DESTROYED_STAT);
	if (!sDestroyed.IsBlank())
		Stats.Insert(CONSTLIT("Enemy stations destroyed"), sDestroyed, CONSTLIT("combat"));

	sDestroyed = GetStat(FRIENDLY_STATIONS_DESTROYED_STAT);
	if (!sDestroyed.IsBlank())
		Stats.Insert(CONSTLIT("Friendly stations destroyed"), sDestroyed, CONSTLIT("combat"));

	//	Add stat for every station destroyed

	CStatCounterArray CounterArray;

	CMapIterator i;
	m_StationStats.Reset(i);
	while (m_StationStats.HasMore(i))
		{
		SStationTypeStats *pStats;
		DWORD dwUNID = m_StationStats.GetNext(i, &pStats);
		CStationType *pType = g_pUniverse->FindStationType(dwUNID);
		if (pType == NULL)
			continue;

		CString sName = pType->GetNounPhrase(0);
		CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - pType->GetLevel(), sName);

		if (pType->GetSovereign()->IsEnemy(pPlayerSovereign))
			CounterArray.Insert(sName, pStats->iDestroyed, CONSTLIT("Enemy stations destroyed"), sSort);
		else
			CounterArray.Insert(sName, pStats->iDestroyed, CONSTLIT("Friendly stations destroyed"), sSort);
		}

	CounterArray.GenerateGameStats(Stats);
		
	//	Add stat for every ship class destroyed

	CounterArray.DeleteAll();
	m_ShipStats.Reset(i);
	while (m_ShipStats.HasMore(i))
		{
		SShipClassStats *pStats;
		DWORD dwUNID = m_ShipStats.GetNext(i, &pStats);
		CShipClass *pClass = g_pUniverse->FindShipClass(dwUNID);
		if (pClass == NULL)
			continue;

		CString sName = pClass->GetNounPhrase(0);
		CString sSort = strPatternSubst(CONSTLIT("%09d%s"), 100000000 - pClass->GetScore(), sName);

		if (pStats->iEnemyDestroyed > 0)
			CounterArray.Insert(sName, pStats->iEnemyDestroyed, CONSTLIT("Enemy ships destroyed"), sSort);

		if (pStats->iFriendDestroyed > 0)
			CounterArray.Insert(sName, pStats->iFriendDestroyed, CONSTLIT("Friendly ships destroyed"), sSort);
		}

	CounterArray.GenerateGameStats(Stats);

	//	Add stat for every weapon fired

	m_ItemStats.Reset(i);
	while (m_ItemStats.HasMore(i))
		{
		SItemTypeStats *pStats;
		DWORD dwUNID = m_ItemStats.GetNext(i, &pStats);
		CItemType *pItemType = g_pUniverse->FindItemType(dwUNID);
		if (pItemType == NULL)
			continue;

		CString sName = pItemType->GetNounPhrase(nounShort);
		CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - pItemType->GetLevel(), sName);

		//	Installed items

		if (pStats->dwFirstInstalled != INVALID_TIME)
			Stats.Insert(sName, NULL_STR, CONSTLIT("Items installed"), sSort);

		if (pStats->iCountFired > 0)
			Stats.Insert(sName, 
					strFormatInteger(pStats->iCountFired, -1, FORMAT_THOUSAND_SEPARATOR | FORMAT_UNSIGNED), 
					CONSTLIT("Weapons fired"), 
					sSort);
		}

	//	Stats for player equipment (but only if the game is done)

	if (bGameOver)
		{
		TSortMap<CString, CItem> InstalledItems;

		//	First we generate a sorted list of installed items
		//	(We do this in case there are multiple of the same device/armor so that
		//	we can coalesce them together into a single line).

		CItemListManipulator ItemList(pShip->GetItemList());
		ItemList.ResetCursor();
		while (ItemList.MoveCursorForward())
			{
			const CItem &Item(ItemList.GetItemAtCursor());

			if (Item.IsInstalled())
				{
				CString sEnhancement = Item.GetEnhancedDesc(pShip);
				CString sItemName = Item.GetNounPhrase(nounActual | nounCountOnly | nounShort);
				CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement));

				bool bInserted;
				CItem *pEntry = InstalledItems.SetAt(sLine, &bInserted);
				if (bInserted)
					{
					*pEntry = Item;
					pEntry->SetCount(1);
					}
				else
					pEntry->SetCount(pEntry->GetCount() + 1);
				}
			}

		//	Now add all the installed items to the stats

		for (j = 0; j < InstalledItems.GetCount(); j++)
			{
			//	Redo the line now that we know the proper count

			CString sEnhancement = InstalledItems[j].GetEnhancedDesc(pShip);
			CString sItemName = InstalledItems[j].GetNounPhrase(nounActual | nounCountOnly);
			CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement));

			//	Compute the sort order

			int iOrder;
			switch (InstalledItems[j].GetType()->GetCategory())
				{
				case itemcatWeapon:
					iOrder = 0;
					break;

				case itemcatLauncher:
					iOrder = 1;
					break;

				case itemcatShields:
					iOrder = 2;
					break;

				case itemcatArmor:
					iOrder = 3;
					break;

				case itemcatReactor:
					iOrder = 4;
					break;

				case itemcatDrive:
					iOrder = 5;
					break;

				default:
					iOrder = 6;
					break;
				}

			CString sSort = strPatternSubst(CONSTLIT("%d%03d%s"), iOrder, 100 - InstalledItems[j].GetType()->GetLevel(), sLine);
			Stats.Insert(sLine, NULL_STR, CONSTLIT("Final equipment"), sSort);
			}

		//	Add the remaining items

		ItemList.ResetCursor();
		while (ItemList.MoveCursorForward())
			{
			const CItem &Item(ItemList.GetItemAtCursor());

			if (!Item.IsInstalled())
				{
				CString sEnhancement = Item.GetEnhancedDesc(pShip);
				CString sItemName = Item.GetNounPhrase(nounActual | nounCountOnly);
				CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement));
				CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - Item.GetType()->GetLevel(), sLine);

				Stats.Insert(sLine, NULL_STR, CONSTLIT("Final items"), sSort);
				}
			}
		}
	}
void GenerateEncounterFrequency (CUniverse &Universe, CXMLElement *pCmdLine)
	{
	enum ETypes
		{
		outputFreq,
		outputFillLocations,
		};

	int i, j;

	//	Options

	int iSystemSample = pCmdLine->GetAttributeIntegerBounded(CONSTLIT("count"), 1, -1, 1);
	bool bLogo = !pCmdLine->GetAttributeBool(CONSTLIT("noLogo"));
	bool bAll = pCmdLine->GetAttributeBool(CONSTLIT("all"));

	bool bRawData = pCmdLine->GetAttributeBool(CONSTLIT("rawData"));
	
	ETypes iType;
	if (pCmdLine->GetAttributeBool(CONSTLIT("fillLocations")))
		iType = outputFillLocations;
	else
		iType = outputFreq;

	//	Additional columns

	TArray<CString> Cols;
	for (i = 0; i < pCmdLine->GetAttributeCount(); i++)
		{
		CString sAttrib = pCmdLine->GetAttributeName(i);

		if (!IsMainCommandParam(sAttrib)
				&& !strEquals(sAttrib, CONSTLIT("count"))
				&& !strEquals(sAttrib, CONSTLIT("fillLocations"))
				&& !strEquals(sAttrib, CONSTLIT("rawData"))
				&& !strEquals(sAttrib, CONSTLIT("encounterfreq")))
			{
			CString sValue = pCmdLine->GetAttribute(i);
			
			if (!strEquals(sValue, CONSTLIT("true")))
				Cols.Insert(strPatternSubst(CONSTLIT("%s:%s"), sAttrib, sValue));
			else
				Cols.Insert(sAttrib);
			}
		}

	//	Generate systems for multiple games

	CSystemCreateStats Stats;
	for (i = 0; i < iSystemSample; i++)
		{
		if (bLogo)
			printf("pass %d...\n", i+1);

		//	Initialize the game

		CString sError;
		if (Universe.InitGame(0, &sError) != NOERROR)
			{
			printf("%s\n", sError.GetASCIIZPointer());
			return;
			}

		for (j = 0; j < Universe.GetTopologyNodeCount(); j++)
			{
			CTopologyNode *pNode = Universe.GetTopologyNode(j);
			if (pNode->IsEndGame())
				continue;

			//	Create the system

			CSystem *pSystem;
			if (Universe.CreateStarSystem(pNode, &pSystem, NULL, &Stats) != NOERROR)
				{
				printf("ERROR: Unable to create star system.\n");
				return;
				}

			//	Done with old system

			Universe.DestroySystem(pSystem);
			}

		Universe.Reinit();
		}

	//	Output based on type

	if (iType == outputFreq)
		{
		//	Generate a table for each encounter

		TSortMap<CStationType *, SEncounterFreqEntry> EncounterFreq;
		for (i = 0; i < Stats.GetEncounterTableCount(); i++)
			{
			const CSystemCreateStats::SEncounterTable &Table = Stats.GetEncounterTable(i);

			//	Skip if only planets and asteroids

			if (!Table.bHasStation && !bAll)
				continue;

			//	For each entry in the table, add to the frequency table

			for (j = 0; j < Table.Table.GetCount(); j++)
				{
				CStationType *pEncounterType = Table.Table.GetAt(j);
				int iChance = Table.Table.GetChance(j);

				SEncounterFreqEntry *pEntry = EncounterFreq.SetAt(pEncounterType);
				pEntry->iCount += Table.iCount;
				pEntry->iTotalChance += Table.iCount * iChance;

				if (pEntry->pMaxTable == NULL || iChance > pEntry->iMaxChance)
					{
					pEntry->pMaxTable = &Table;
					pEntry->iMaxChance = iChance;
					}

				if (pEntry->pMinTable == NULL || iChance < pEntry->iMinChance)
					{
					pEntry->pMinTable = &Table;
					pEntry->iMinChance = iChance;
					}
				}
			}

		//	Output encounter frequency distribution

		printf("Level\tCategory\tEncounter\tTotal Chances\tAverage Chance\tMin Chance\tMax Chance");
		for (i = 0; i < Cols.GetCount(); i++)
			printf("\t%s", Cols[i].GetASCIIZPointer());
		printf("\n");

		for (i = 0; i < EncounterFreq.GetCount(); i++)
			{
			CStationType *pEncounterType = EncounterFreq.GetKey(i);
			int iAveWhole = EncounterFreq[i].iTotalChance / EncounterFreq[i].iCount;
			int iAveFrac = 1000 * (EncounterFreq[i].iTotalChance % EncounterFreq[i].iCount) / EncounterFreq[i].iCount;

			printf("%d\t%s\t%s\t%d\t%d.%03d\t%d\t%d",
					pEncounterType->GetLevel(),
					pEncounterType->GetDataField(CONSTLIT("category")).GetASCIIZPointer(),
					pEncounterType->GetName().GetASCIIZPointer(),
					EncounterFreq[i].iCount,
					iAveWhole,
					iAveFrac,
					EncounterFreq[i].iMinChance,
					EncounterFreq[i].iMaxChance);

			for (j = 0; j < Cols.GetCount(); j++)
				{
				CString sValue = pEncounterType->GetDataField(Cols[j]);
				printf("\t%s", sValue.GetASCIIZPointer());
				}

			printf("\n");
			}

		//	Output raw data

		if (bRawData)
			{
			//	Loop over stats and output tables

			for (i = 0; i < Stats.GetEncounterTableCount(); i++)
				{
				const CSystemCreateStats::SEncounterTable &Table = Stats.GetEncounterTable(i);

				//	Skip if only planets and asteroids

				if (!Table.bHasStation && !bAll)
					continue;

				//	Header

				printf("LEVEL %d (%d location%s)\nSystem Type: %08x\nStations: %s\n", 
						Table.iLevel,
						Table.iCount,
						(Table.iCount == 1 ? "" : "s"),
						Table.pSystemType->GetUNID(),
						Table.sStationCriteria.GetASCIIZPointer());

				//	Location attributes

				printf("Location: ");
				for (j = 0; j < Table.LabelAttribs.GetCount(); j++)
					{
					if (j != 0)
						printf(", ");
					printf(Table.LabelAttribs[j].GetASCIIZPointer());
					}
				printf("\n");

				//	Entries

				for (j = 0; j < Table.Table.GetCount(); j++)
					printf("%s\t%d\n", Table.Table.GetAt(j)->GetName().GetASCIIZPointer(), Table.Table.GetChance(j));

				printf("\n");
				}
			}
		}
	else if (iType == outputFillLocations)
		{
		printf("Level\tSystem\tEncounter\tChance\n");

		for (i = 0; i < Stats.GetFillLocationsTableCount(); i++)
			{
			const CSystemCreateStats::SFillLocationsTable &Entry = Stats.GetFillLocationsTable(i);

			for (j = 0; j < Entry.Table.GetCount(); j++)
				{
				int iPercent = (int)(1000.0 * Entry.Table.GetChance(j) / (Metric)Entry.Table.GetTotalChance());

				printf("%d\t%s\t%s\t%d.%d\n",
						Entry.iLevel,
						Entry.sSystemName.GetASCIIZPointer(),
						Entry.Table[j]->GetName().GetASCIIZPointer(), 
						iPercent / 10,
						iPercent % 10);
				}
			}
		}
	}