Skip to content

End of Mission / End of Game Statistics can be incorrect #1503

@BlackDog86

Description

@BlackDog86

XCGS_Analytics::AddTacticalGameEnd uses XGPlayer::GetOriginalUnits to populate the array of units which are valid for the "UNITS_LOST" analytics object, which counts the number of XCom units killed in a mission / throughout the game.

Battle.GetHumanPlayer( ).GetOriginalUnits( MissionUnits, true );

	bFlawless = true;

foreach MissionUnits( Unit )
	{
		UnitRef.ObjectID = Unit.ObjectID;

		if (Unit.kAppearance.bGhostPawn)
			continue;

		AnalyticsObject.AddValue( "ACC_UNIT_MISSIONS", 1, UnitRef );
		
		if (Unit.IsDead( ))
		{
			bFlawless = false;
			AnalyticsObject.AddValue( "UNITS_LOST", 1 );
		}
		else if (Unit.IsBleedingOut() && !BattleData.AllTacticalObjectivesCompleted())
		{
			bFlawless = false;
			AnalyticsObject.AddValue( "UNITS_LOST", 1 );
		}

		if (Unit.WasInjuredOnMission( ))
		{
			bFlawless = false;
		}
	}

This function is not particulaly robust in what it counts & allows through certain types of non-XCom units (Psi-zombies, additional advent / resistance soldiers added by the resistance cards and likely some other edge-cases).

//  Checks for mind control and hands back all units that originally belonged to this player.
//  Hands back the CURRENT state of the unit (e.g. mind controlled units will reflect that they are on another team)
simulated function GetOriginalUnits(out array<XComGameState_Unit> Units, bool bSkipTurrets=false, bool bSkipCosmetic=true, bool bSkipMindControlledUnits=false)
{
	local XComGameStateHistory History;
	local XComGameState_Unit CurrentUnitState, OldUnitState;
	local XComGameState_Effect MindControlEffect, OriginalEffect;
	local array<StateObjectReference> UnitRefs;

	History = `XCOMHISTORY;
	foreach History.IterateByClassType(class'XComGameState_Unit', CurrentUnitState, eReturnType_Reference)
	{
		if (CurrentUnitState.GetMyTemplate().bIsCosmetic && bSkipCosmetic)
			continue;

		if (CurrentUnitState.GetMyTemplateName() == 'MimicBeacon')
			continue;

		if (CurrentUnitState.IsTurret() && bSkipTurrets)
			continue;

		MindControlEffect = CurrentUnitState.GetUnitAffectedByEffectState(class'X2Effect_MindControl'.default.EffectName);

		if (MindControlEffect != none && bSkipMindControlledUnits)
			continue;

		if (MindControlEffect != none)
		{
			OriginalEffect = XComGameState_Effect(History.GetOriginalGameStateRevision(MindControlEffect.ObjectID));
			OldUnitState = XComGameState_Unit(History.GetGameStateForObjectID(CurrentUnitState.ObjectID,,OriginalEffect.GetParentGameState().HistoryIndex - 1));
			if(OldUnitState.ControllingPlayer.ObjectID == ObjectID && UnitRefs.Find('ObjectID', CurrentUnitState.ObjectID) == INDEX_NONE)
			{
				Units.AddItem(CurrentUnitState);
				UnitRefs.AddItem(CurrentUnitState.GetReference());
			}
		}
		else if(CurrentUnitState.ControllingPlayer.ObjectID == ObjectID && UnitRefs.Find('ObjectID', CurrentUnitState.ObjectID) == INDEX_NONE)
		{
			Units.AddItem(CurrentUnitState);
			UnitRefs.AddItem(CurrentUnitState.GetReference());
		}
	}
}

Additional guards should be added, either to the analytics object, or to GetOriginalUnits (probably preferred in the analytics object) so that the UNITS_LOST statistic accurately tracks XCom soldiers, friendly VIPs, soldiers which have been rescued by XCom but not psi-zombies and additional units awarded through resistance cards.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions