//============================================================================= // KFGameplayEventsWriter //============================================================================= // Class Description //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // - @author Jonathan Howard 140807 // - Author 11/1/2013 //============================================================================= class KFGameplayEventsWriter extends GameplayEventsWriter config(GameStats) native(Analytics); `include(KFGame\KFGameAnalytics.uci); /* * Gameplay */ /* Zed events*/ const GAMEEVENT_ZED_DIED = 1010; const GAMEEVENT_ZED_SPAWNED = 1011; /* * Debugging */ /** Spawn volume: 1100 - 1110 */ const GAMEEVENT_SPAWNVOLUME_RATING = 2002; const GAMEEVENT_SPAWNVOLUME_PLAYERS = 2003; const GAMEEVENT_SPAWNVOLUME_BESTRATING = 2004; /** AI Events: 1200 - 1299 */ const GAMEEVENT_AI_MOVEFAILURE = 2100; const GAMEEVENT_AI_BUMP = 2101; const GAMEEVENT_AI_JUMPSPOT = 2102; const GAMEEVENT_AI_HITWALL = 2103; const GAMEEVENT_AI_JUMPOVERWALL = 2104; const GAMEEVENT_AI_WAITFORDOOR = 2105; const GAMEEVENT_AI_FINISHEDWAITFORDOOR = 2106; const GAMEEVENT_AI_FAILEDADJUSTFROMWALL = 2107; const GAMEEVENT_AI_CHARGEATTACK = 2108; const GAMEEVENT_AI_CHANGEDENEMY = 2109; const GAMEEVENT_AI_GETNEXTMOVEGOALFAILURE = 2110; const GAMEEVENT_AI_REDIRECTEDPATH = 2111; const GAMEEVENT_AI_BLOCKEDPATH = 2112; const GAMEEVENT_AI_PATHOBSTRUCTION_FAIL = 2113; const GAMEEVENT_AI_PATHOBSTRUCTION_REPATH = 2114; const GAMEEVENT_AI_MOVE_TIMEOUT = 2115; const GAMEEVENT_AI_INVALIDATE_ANCHOR = 2116; const GAMEEVENT_AI_ANCHOR_FALLBACK_FAILED = 2117; const GAMEEVENT_AI_FAILED_ANCHOR = 2118; const GAMEEVENT_AI_BASED_ON_PAWN = 2119; const GAMEEVENT_AI_HEADLESS_WANDER = 2120; const GAMEEVENT_AI_DESTROYED_DOOR = 2121; const GAMEEVENT_AI_PATHGOALEVAL_ABORT = 2122; const GAMEEVENT_AI_PATH_FAILURE = 2123; var globalconfig bool bRecordAIDebugInfo; /** Index of file in use by this writer */ var int StatsFileIndex; /** Emulate a singleton/static variable for the event */ var transient Controller LastDiedController; cpptext { /** Cleanup the native memory allocations */ virtual void BeginDestroy(); }; /* * @name Aliased resolution functions into script from native (defined from GameplayEventsWriter) */ /** Turns a weapon class into an index, possibly adding new information to the array **/ function native int ResolveWeaponClassIndex(class WeaponClass); /** Turns a damage class into an index, possibly adding new information to the array **/ function native int ResolveDamageClassIndex(class DamageClass); /** Turns a projectile class into an index, possibly adding new information to the array **/ function native int ResolveProjectileClassIndex(class ProjectileClass); /** Turns a pawn class into an index, possibly adding new information to the array **/ function native int ResolvePawnIndex(class PawnClass); /** Turns an actor class into an index, possibly adding new information to the array **/ function native int ResolveActorIndex(Actor Actor); /** * Creates the archive that we are going to write to * @param Filename - name of the file that will be open for serialization * @return TRUE if successful, else FALSE */ native function bool OpenStatsFile(string Filename); /** * Can the writer get access to a file to write to (XBOX specific) */ native function bool CanAcquireFile(); function LogSpawnVolumeRating( KFSpawnVolume SpawnVolume, float FinalRating, float UsageRating, float LocationRating ) { local GenericParamListStatEntry PLE; local vector InteriorBB; local string ToolTipText; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_SPAWNVOLUME_RATING); PLE.AddVector( 'BaseLocation', SpawnVolume.Location ); PLE.AddString('Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_TrafficLight'"); PLE.AddString('Name', SpawnVolume.Name ); PLE.AddVector( 'BoxLoc', SpawnVolume.Location ); PLE.AddVector('BoxColor',vect(0,255,0)); InteriorBB = SpawnVolume.VisibilityBounds.BoxExtent; PLE.AddVector( 'BoxExtent', InteriorBB ); PLE.AddVector('HeatmapPoint',SpawnVolume.Location); ToolTipText = "Final Rating: "$FinalRating$"\nUsage Rating: "$UsageRating$"\nLocation Rating: "$LocationRating; PLE.AddString('Text',ToolTipText); PLE.CommitToDisk(); } function LogPlayersAtSpawn( KFPlayerController KFPC, vector ViewLoc, rotator ViewRot ) { local GenericParamListStatEntry PLE; local string ToolTipText; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_SPAWNVOLUME_PLAYERS); PLE.AddVector( 'BaseLocation', ViewLoc ); PLE.AddString('Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Attractor'"); PLE.AddString('Name', KFPC.Name ); ToolTipText = "Player: "$KFPC.Name$"\nViewLoc: "$ViewLoc$"\nViewRot: "$ViewRot; PLE.AddString('Text',ToolTipText); PLE.AddVector( 'LineStart', ViewLoc ); PLE.AddVector('LineColor',vect(0,255,0)); PLE.AddVector( 'LineEnd', ViewLoc + vector(ViewRot) * 750.f ); PLE.AddInt('PlayerIndex',ResolvePlayerIndex(KFPC)); PLE.CommitToDisk(); } function LogBestSpawnVolume(KFSpawnVolume KFSV, int WaveNum, int SpawnCount) { local GenericParamListStatEntry PLE; local string ToolTipText; local vector InteriorBB; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_SPAWNVOLUME_BESTRATING); PLE.AddVector( 'BaseLocation', KFSV.Location ); PLE.AddString('Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Spawn'"); PLE.AddString('Name',KFSV.Name); ToolTipText = "Wave " $WaveNum $"\nSpawned "$ SpawnCount $" Zeds" $"\nTimes chosen for spawn: "$KFSV.VolumeChosenCount; PLE.AddString('Text',ToolTipText); PLE.AddVector( 'BoxLoc', KFSV.Location ); InteriorBB = KFSV.VisibilityBounds.BoxExtent; PLE.AddVector( 'BoxExtent', InteriorBB ); PLE.AddVector('HeatmapPoint',KFSV.Location); PLE.CommitToDisk(); } /********************************************************************************************* * AI specific gameplay events **********************************************************************************************/ function LogAIPathFailure( KFAIController KFAIC, NavigationPoint Anchor, actor Goal, string ToolTipText ) { local GenericParamListStatEntry PLE; local int i; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_PATH_FAILURE ); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); if( Anchor != None ) { PLE.AddVector( 'SphereLoc', Anchor.Location ); PLE.AddFloat( 'SphereRadius', 32.f ); PLE.AddVector( 'SphereColor', vect(0,255,0) ); } if( Goal != None ) { PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'LineEnd', KFAIC.MoveGoal.Location ); PLE.AddVector( 'LineColor', vect(0,0,255) ); } if( KFAIC.RouteCache.Length > 0 ) { ToolTipText = ToolTipText$" RCItems:"$KFAIC.RouteCache.Length; for( i = 0; i < KFAIC.RouteCache.Length; i++ ) { ToolTipText = ToolTipText$" RC0:"$KFAIC.RouteCache[i]; } } ToolTipText = ToolTipText; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'HeatmapPoint', KFAIC.Pawn.Location); PLE.CommitToDisk(); } function LogAIHeadlessWander( KFAIController KFAIC, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_HEADLESS_WANDER ); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'HeatmapPoint', KFAIC.Pawn.Location); PLE.CommitToDisk(); } function LogAIBasedOnPawn( KFAIController KFAIC, Pawn BasePawn, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_BASED_ON_PAWN ); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'LineEnd', BasePawn.Location ); PLE.AddVector( 'HeatmapPoint', KFAIC.Pawn.Location); PLE.CommitToDisk(); } /********************************************************************************************* * Anchor issues * Checking issues with NPC path starting navigation points. ********************************************************************************************* */ /** This is evil - when initial abort for Goal_At evaluator happens, when NPC's anchor is also the goal */ function LogAIPathGoalEvalAbort( KFAIController KFAIC, PathGoalEvaluator PathEvaluator, NavigationPoint StartNav, string ToolTipText ) { local GenericParamListStatEntry PLE; local KFPawn NPC; if ( !bRecordAIDebugInfo ) return; // Get the NPC Controller's KFPawn NPC = KFAIC.MyKFPawn; PLE = GetGenericParamListEntry(); // For the visualizer to work, add the event ID param for the define you added in KFGameAnalytics.uci PLE.AddInt( 'EventID', GAMEEVENT_AI_PATHGOALEVAL_ABORT ); // For the visualizer to work, you must also add the 'name' parameter - the name of the main actor involved should be fine PLE.AddString( 'Name', NPC.Name ); // For the visualizer to work, you must add a BaseLocation vector, which is where the sprite will be drawn PLE.AddVector( 'BaseLocation', NPC.Location ); // The sprite to render at BaseLocation PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); // This is the text to show as a tooltip when the mouse pointer is hovering over a gameplayevent icon PLE.AddString( 'Text', ToolTipText ); // Define a red line to render from the NPC to StartNav's location, using the 'LineStart' and 'LineEnd' parameters PLE.AddVector( 'LineStart', NPC.Location ); PLE.AddVector( 'LineEnd', StartNav.Location ); PLE.AddVector( 'LineColor', vect(255,0,0) ); // Define a red sphere to render at StartNav's location PLE.AddVector( 'SphereLoc', StartNav.Location ); PLE.AddFloat( 'SphereRadius', 32.f ); PLE.AddVector( 'SphereColor', vect(255,0,0) ); // Marks the pawn's location for the HeatMapVisualizer PLE.AddVector( 'HeatmapPoint', NPC.Location ); // Save the data PLE.CommitToDisk(); } function LogAIDestroyedDoor( KFAIController KFAIC, KFDoorActor Door, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_DESTROYED_DOOR ); PLE.AddVector( 'BaseLocation', Door.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', Door.Location ); PLE.AddVector( 'LineEnd', KFAIC.Pawn.Location ); PLE.AddVector( 'HeatmapPoint', Door.Location ); PLE.CommitToDisk(); } /** * This is very bad - it happens when an NPC cannot find a valid starting navigation point when * generating a path. FindPath will fail, and no partial path will be found either. This is a * time when something has gone very wrong (like the NPC spawned in an invalid location with * no pathnodes). Teleporting the NPC to the nearest NavigationPoint that's not visible to * players is the quick-fix if other attempts fail (moving a bit and trying to find a valid anchor * again, etc). */ function LogAIFailedAnchor( int EventID, KFAIController KFAIC, Actor MoveTarget, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_ANCHOR_FALLBACK_FAILED); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); if( MoveTarget != none ) { ToolTipText = ToolTipText$" MoveTarget:"$MoveTarget; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'LineEnd', MoveTarget.Location ); } PLE.AddVector( 'HeatmapPoint',KFAIC.Pawn.Location); PLE.CommitToDisk(); } /** * An NPC on a path has been unexpectedly blocked, and the HandlePathObstruction() event * successfully built a path around it. */ function LogAIPathObstruction( int EventID, KFAIController KFAIC, Actor Obstruction, string ToolTipText ) { local GenericParamListStatEntry PLE; local float Radius, Height; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', EventID ); //GAMEEVENT_AI_PATHOBSTRUCTION_REPATH); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); PLE.AddVector( 'SphereLoc', Obstruction.Location ); Obstruction.GetBoundingCylinder( Radius,Height) ; PLE.AddFloat( 'SphereRadius',Radius ); PLE.AddVector( 'SphereColor',vect(255,0,0) ); ToolTipText = ToolTipText$" Obstruction:"$Obstruction; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'LineEnd', Obstruction.Location ); PLE.AddVector( 'HeatmapPoint',KFAIC.Pawn.Location); PLE.CommitToDisk(); } /** * Sometimes an NPC is blocked by something unexpected when on a path. After a few re-tries, * the NPC will often temporarily block the next pathnode he's attempting to reach and generate * a new path, in an effort to path around the obstruction. */ function LogAIBlockedPath( KFAIController KFAIC, NavigationPoint Start, NavigationPoint End, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_BlockedPath); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" MoveTarget: "$KFAIC.MoveTarget$" MoveGoal: "$KFAIC.MoveTarget; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', Start.Location ); PLE.AddVector( 'LineEnd', End.Location ); PLE.AddVector( 'HeatmapPoint',KFAIC.Pawn.Location); PLE.CommitToDisk(); } /** * An NPC using the AIMoveToGoal command has decided to alter its path due to an obstruction, which * can include other Zeds, doors, welded doors, etc. */ function LogAIRedirectedPath( KFAIController KFAIC, actor MoveGoal, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; `AILog_Ext( KFAIC.Pawn$" GAMEEVENT_AI_REDIRECTEDPATH at "$KFAIC.Pawn.Location$" MoveGoal: "$MoveGoal$" ToolTipText: "$ToolTipText, 'Critical', KFAIC ); PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_REDIRECTEDPATH ); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite', "Texture2D'EditorResources.Crowd.T_Crowd_Repulsor'" ); PLE.AddString( 'Name', KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Base: "$KFAIC.Pawn.Base$" MG Dist:"$VSize(MoveGoal.Location - KFAIC.Pawn.Location)$" SS? "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); //ToolTipText = "Wave " $WaveNum $"\nSpawned "$ SpawnCount $" Zeds" $"\nTimes chosen for spawn: "$KFSV.VolumeChosenCount; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'LineEnd', KFAIC.Pawn.Location + vector(KFAIC.Pawn.Rotation) * 350.f ); if( KFAIC.MoveGoal != none ) { PLE.AddVector( 'SecondLineStart', KFAIC.Pawn.Location ); PLE.AddVector( 'SecondLineEnd', KFAIC.MoveGoal.Location ); PLE.AddVector( 'SecondLineColor', vect(1,1,0) ); } PLE.AddVector( 'HeatmapPoint',KFAIC.Pawn.Location); PLE.CommitToDisk(); } function LogAIMoveFailure( KFAIController KFAIC, vector NPCLocation, rotator NPCRotation, actor MoveGoal, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_MOVEFAILURE ); PLE.AddVector( 'BaseLocation', NPCLocation ); PLE.AddString( 'Sprite', "Texture2D'EditorResources.Crowd.T_Crowd_Crosswalk'" ); PLE.AddString( 'Name', KFAIC.Pawn.Name ); if( MoveGoal != none ) { ToolTipText = ToolTipText$" MG Dist:"$VSize(MoveGoal.Location - KFAIC.Pawn.Location)$" SS? "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); } else { ToolTipText = ToolTipText$" SS? "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); } if( KFAIC.MyKFPawn.Base != none ) { ToolTipText = ToolTipText$" Base:"$KFAIC.MyKFPawn.Base; } else { ToolTipText = ToolTipText$" NO BASE!"; } //ToolTipText = "Wave " $WaveNum $"\nSpawned "$ SpawnCount $" Zeds" $"\nTimes chosen for spawn: "$KFSV.VolumeChosenCount; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', NPCLocation ); PLE.AddVector( 'LineEnd', NPCLocation + vector(NPCRotation) * 350.f ); if( KFAIC.MoveGoal != none || KFAIC.MoveTarget != none ) { PLE.AddVector( 'SecondLineStart', NPCLocation ); if( KFAIC.MoveTarget != none ) { PLE.AddVector( 'SecondLineEnd', KFAIC.MoveTarget.Location ); } else { PLE.AddVector( 'SecondLineEnd', KFAIC.MoveGoal.Location ); } PLE.AddVector( 'SecondLineColor', vect(1,1,0) ); } PLE.AddVector( 'HeatmapPoint', NPCLocation ); PLE.CommitToDisk(); } function LogAIGetNextMoveGoalFailure( KFAIController KFAIC, vector NPCLocation, rotator NPCRotation, actor MoveGoal, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; `AIlog_Ext( KFAIC.Pawn$" LogAIGetNextMoveGoalFailure at "$NPCLocation$" MoveGoal: "$MoveGoal$" ToolTipText: "$ToolTipText, 'PathWarning', KFAIC ); // KFAIC.AIBugItStringCreator( KFAIC.Pawn.Location, KFAIC.Pawn.Rotation, GoString, LocString ); // `Log( GoString ); // `Log( LocString ); PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', GAMEEVENT_AI_GETNEXTMOVEGOALFAILURE ); PLE.AddVector( 'BaseLocation', NPCLocation ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Spawn'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Base: "$KFAIC.Pawn.Base$" MG Dist:"$VSize(MoveGoal.Location - KFAIC.Pawn.Location)$" SS: "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); //ToolTipText = "Wave " $WaveNum $"\nSpawned "$ SpawnCount $" Zeds" $"\nTimes chosen for spawn: "$KFSV.VolumeChosenCount; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', NPCLocation ); PLE.AddVector( 'LineEnd', NPCLocation + vector(NPCRotation) * 350.f ); if( KFAIC.MoveGoal != none ) { PLE.AddVector( 'SecondLineStart', NPCLocation ); PLE.AddVector( 'SecondLineEnd', KFAIC.MoveGoal.Location ); PLE.AddVector( 'SecondLineColor', vect(1,1,0) ); } PLE.AddVector( 'HeatmapPoint',NPCLocation); PLE.CommitToDisk(); } /** * LogAIChargeAttack() * @param KFAIC KFAIController * @param ChargeStartLocation NPC's location at start of the charge attack * @param ChargeEndLocation NPC's location when finished the charge attack * @param ToolTipText ToolTip text shown for this event's icon */ function LogAIChargeAttack( KFAIController KFAIC, vector ChargeStartLocation, vector ChargeEndLocation, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_AI_CHARGEATTACK ); PLE.AddVector( 'BaseLocation', ChargeStartLocation ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Attractor'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Enemy: "$KFAIC.Enemy; //ToolTipText = "Wave " $WaveNum $"\nSpawned "$ SpawnCount $" Zeds" $"\nTimes chosen for spawn: "$KFSV.VolumeChosenCount; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', ChargeStartLocation ); PLE.AddVector( 'LineEnd', ChargeEndLocation ); PLE.AddVector( 'HeatmapPoint', ChargeStartLocation ); PLE.CommitToDisk(); } /** * LogAIBump() * @param KFAIC KFAIController * @param NPCLocation NPC's Location at time of event * @param NPCRotation NPC's Rotation at time of event * @param Other Other actor that was bumped into * @param ToolTipText ToolTip text shown for this event's icon */ function LogAIBump( int EventID, KFAIController KFAIC, vector NPCLocation, rotator NPCRotation, actor Other, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_AI_BUMP ); PLE.AddVector( 'BaseLocation', NPCLocation ); PLE.AddString( 'Sprite', "Texture2D'EditorResources.Crowd.T_Crowd_TrafficLight'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Other: "$Other$" Base: "$KFAIC.Pawn.Base$" SS: "$KFAIC.MyKFPawn.IsUsingSuperSpeed()$" Command: "$KFAIC.CachedAICommandList; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', NPCLocation ); PLE.AddVector( 'LineEnd', NPCLocation + vector(NPCRotation) * 750.f ); PLE.AddVector( 'HeatmapPoint',NPCLocation ); PLE.CommitToDisk(); } /** * LogAIFailedAdjustFromWall() * @param KFAIC KFAIController * @param NPCLocation NPC's Location at time of event * @param NPCRotation NPC's Rotation at time of event * @param Wall Wall actor that NPC couldn't adjust around * @param ToolTipText ToolTip text shown for this event's icon */ function LogAIWall( int EventID, KFAIController KFAIC, vector NPCLocation, rotator NPCRotation, actor Wall, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', EventID); // GAMEEVENT_AI_FAILEDADJUSTFROMWALL ); PLE.AddVector( 'BaseLocation', NPCLocation ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Spawn'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Wall: "$Wall$" Phys: "$KFAIC.Pawn.GetPhysicsName()$" SS: "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', NPCLocation ); PLE.AddVector( 'LineEnd', NPCLocation + vector(NPCRotation) * 250.f ); PLE.AddVector( 'HeatmapPoint',NPCLocation ); PLE.CommitToDisk(); } /** * LogAIWaitForDoor() * @param KFAIC KFAIController * @param NPCLocation NPC's Location at time of event * @param Door Door that NPC must wait for * @param ToolTipText ToolTip text shown for this event's icon */ function LogAIDoor( int EventID, KFAIController KFAIC, vector NPCLocation, KFDoorActor Door, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID', EventID); // GAMEEVENT_AI_WAITFORDOOR ); PLE.AddVector( 'BaseLocation', NPCLocation ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.door'" ); PLE.AddString( 'Name',KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" Command: "$KFAIC.CachedAICommandList; PLE.AddString( 'Text', ToolTipText ); PLE.AddVector( 'LineStart', NPCLocation ); PLE.AddVector( 'LineEnd', Door.Location ); PLE.AddVector( 'HeatmapPoint', NPCLocation ); PLE.CommitToDisk(); } /** * LogAIJumpSpot() * @param KFAIC KFAIController * @param NPCLocation NPC's Location at time of event * @param NPCRotation NPC's Rotation at time of event * @param JumpSpot JumpSpot that was used * @param ToolTipText ToolTip text shown for this event's icon */ //function LogAIJumpSpot( KFAIController KFAIC, vector NPCLocation, rotator NPCRotation, KFJumpSpot JumpSpot, string ToolTipText ) //{ // local GenericParamListStatEntry PLE; // if ( !bRecordAIDebugInfo ) return; // PLE = GetGenericParamListEntry(); // PLE.AddInt( 'EventID', GAMEEVENT_AI_JUMPSPOT ); // PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); // PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Spawn'" ); // PLE.AddString( 'Name', KFAIC.Pawn.Name ); // ToolTipText = ToolTipText$" SSpeed: "$KFAIC.MyKFPawn.IsUsingSuperSpeed(); // PLE.AddString( 'Text', ToolTipText ); // // Line to new enemy // PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); // PLE.AddVector('LineColor',vect(0,255,0)); // PLE.AddVector( 'LineEnd', JumpSpot.Location ); // PLE.CommitToDisk(); //} /** * LogAIChangedEnemy() * @param KFAIC KFAIController * @param NewEnemy New enemy to begin targeting * @param OldEnemy Previous enemy * @param ToolTipText ToolTip text shown for this event's icon */ function LogAIChangedEnemy( KFAIController KFAIC, Pawn NewEnemy, Pawn OldEnemy, string ToolTipText ) { local GenericParamListStatEntry PLE; if ( !bRecordAIDebugInfo ) return; PLE = GetGenericParamListEntry(); PLE.AddInt( 'EventID',GAMEEVENT_AI_CHANGEDENEMY); PLE.AddVector( 'BaseLocation', KFAIC.Pawn.Location ); PLE.AddString( 'Sprite',"Texture2D'EditorResources.Crowd.T_Crowd_Behavior'" ); PLE.AddString( 'Name', KFAIC.Pawn.Name ); ToolTipText = ToolTipText$" from "$OldEnemy.PlayerReplicationInfo.PlayerName$" to "$NewEnemy.PlayerReplicationInfo.PlayerName$" "$KFAIC.CachedAICommandList; PLE.AddString( 'Text', ToolTipText ); // Line to new enemy PLE.AddVector( 'LineStart', KFAIC.Pawn.Location ); PLE.AddVector('LineColor',vect(0,255,0)); PLE.AddVector( 'LineEnd', NewEnemy.Location ); PLE.CommitToDisk(); } defaultproperties { SupportedEvents.Add((EventID=GAMEEVENT_ZED_DIED,EventName="Zed died",StatGroup=(Group=GSG_Game,Level=3),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_SPAWNVOLUME_RATING,EventName="SpawnVolume Rating",StatGroup=(Group=GSG_Game,Level=3),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_SPAWNVOLUME_PLAYERS,EventName="Player positions at AISpawn",StatGroup=(Group=GSG_Game,Level=3),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_SPAWNVOLUME_BESTRATING,EventName="The best rated spawn volume",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_MOVEFAILURE,EventName="AI move failures",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_BUMP,EventName="AI Bumped",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_JUMPSPOT,EventName="AI using JumpSpot",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_HITWALL,EventName="AI HitWall events",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_JUMPOVERWALL,EventName="AI high-jump locations",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_WAITFORDOOR,EventName="AI waiting for door",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_FINISHEDWAITFORDOOR,EventName="AI finished waiting for door",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_FAILEDADJUSTFROMWALL,EventName="AI Failed AdjustFromWall",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_CHARGEATTACK,EventName="AI charge attack start/end locations",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_CHANGEDENEMY,EventName="AI changed enemy",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_REDIRECTEDPATH,EventName="AI path redirected",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_BLOCKEDPATH,EventName="AI blocked its path",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_PATHOBSTRUCTION_FAIL,EventName="AI Obstructed",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_PATHOBSTRUCTION_REPATH,EventName="AI Obstructed Repath",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_INVALIDATE_ANCHOR,EventName="AI Invalidated Anchor",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_MOVE_TIMEOUT,EventName="AI Move Timed Out",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_ANCHOR_FALLBACK_FAILED,EventName="AI Failed Fallback Anchor",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_FAILED_ANCHOR,EventName="AI Failed Anchor Find",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_BASED_ON_PAWN,EventName="AI Landed On Pawn",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_HEADLESS_WANDER,EventName="AI Headless Wander",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_DESTROYED_DOOR,EventName="AI Destroyed Door",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_PATHGOALEVAL_ABORT,EventName="GoalEval Initial Abort",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) SupportedEvents.Add((EventID=GAMEEVENT_AI_PATH_FAILURE,EventName="PathFind Failure",StatGroup=(Group=GSG_Game,Level=10),EventDataType=`GET_GenericParamList)) }