// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraPlayerBotController.h" #include "AbilitySystemComponent.h" #include "AbilitySystemGlobals.h" #include "Containers/UnrealString.h" #include "Delegates/Delegate.h" #include "Engine/EngineBaseTypes.h" #include "Engine/World.h" #include "GameFramework/Pawn.h" #include "GameFramework/PlayerState.h" #include "GameModes/LyraGameMode.h" #include "Logging/LogCategory.h" #include "Logging/LogMacros.h" #include "LyraLogChannels.h" #include "Misc/AssertionMacros.h" #include "Perception/AIPerceptionComponent.h" #include "Templates/Casts.h" #include "Trace/Detail/Channel.h" #include "UObject/ObjectPtr.h" #include "UObject/UObjectBaseUtility.h" #include "UObject/UnrealNames.h" class UObject; ALyraPlayerBotController::ALyraPlayerBotController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bWantsPlayerState = true; bStopAILogicOnUnposses = false; } void ALyraPlayerBotController::OnPlayerStateChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam) { ConditionalBroadcastTeamChanged(this, IntegerToGenericTeamId(OldTeam), IntegerToGenericTeamId(NewTeam)); } void ALyraPlayerBotController::OnPlayerStateChanged() { // Empty, place for derived classes to implement without having to hook all the other events } void ALyraPlayerBotController::BroadcastOnPlayerStateChanged() { OnPlayerStateChanged(); // Unbind from the old player state, if any FGenericTeamId OldTeamID = FGenericTeamId::NoTeam; if (LastSeenPlayerState != nullptr) { if (ILyraTeamAgentInterface* PlayerStateTeamInterface = Cast(LastSeenPlayerState)) { OldTeamID = PlayerStateTeamInterface->GetGenericTeamId(); PlayerStateTeamInterface->GetTeamChangedDelegateChecked().RemoveAll(this); } } // Bind to the new player state, if any FGenericTeamId NewTeamID = FGenericTeamId::NoTeam; if (PlayerState != nullptr) { if (ILyraTeamAgentInterface* PlayerStateTeamInterface = Cast(PlayerState)) { NewTeamID = PlayerStateTeamInterface->GetGenericTeamId(); PlayerStateTeamInterface->GetTeamChangedDelegateChecked().AddDynamic(this, &ThisClass::OnPlayerStateChangedTeam); } } // Broadcast the team change (if it really has) ConditionalBroadcastTeamChanged(this, OldTeamID, NewTeamID); LastSeenPlayerState = PlayerState; } void ALyraPlayerBotController::InitPlayerState() { Super::InitPlayerState(); BroadcastOnPlayerStateChanged(); } void ALyraPlayerBotController::CleanupPlayerState() { Super::CleanupPlayerState(); BroadcastOnPlayerStateChanged(); } void ALyraPlayerBotController::OnRep_PlayerState() { Super::OnRep_PlayerState(); BroadcastOnPlayerStateChanged(); } void ALyraPlayerBotController::SetGenericTeamId(const FGenericTeamId& NewTeamID) { UE_LOG(LogLyraTeams, Error, TEXT("You can't set the team ID on a player bot controller (%s); it's driven by the associated player state"), *GetPathNameSafe(this)); } FGenericTeamId ALyraPlayerBotController::GetGenericTeamId() const { if (ILyraTeamAgentInterface* PSWithTeamInterface = Cast(PlayerState)) { return PSWithTeamInterface->GetGenericTeamId(); } return FGenericTeamId::NoTeam; } FOnLyraTeamIndexChangedDelegate* ALyraPlayerBotController::GetOnTeamIndexChangedDelegate() { return &OnTeamChangedDelegate; } void ALyraPlayerBotController::ServerRestartController() { if (GetNetMode() == NM_Client) { return; } ensure((GetPawn() == nullptr) && IsInState(NAME_Inactive)); if (IsInState(NAME_Inactive) || (IsInState(NAME_Spectating))) { ALyraGameMode* const GameMode = GetWorld()->GetAuthGameMode(); if ((GameMode == nullptr) || !GameMode->ControllerCanRestart(this)) { return; } // If we're still attached to a Pawn, leave it if (GetPawn() != nullptr) { UnPossess(); } // Re-enable input, similar to code in ClientRestart ResetIgnoreInputFlags(); GameMode->RestartPlayer(this); } } ETeamAttitude::Type ALyraPlayerBotController::GetTeamAttitudeTowards(const AActor& Other) const { if (const APawn* OtherPawn = Cast(&Other)) { if (const ILyraTeamAgentInterface* TeamAgent = Cast(OtherPawn->GetController())) { FGenericTeamId OtherTeamID = TeamAgent->GetGenericTeamId(); //Checking Other pawn ID to define Attitude if (OtherTeamID.GetId() != GetGenericTeamId().GetId()) { return ETeamAttitude::Hostile; } else { return ETeamAttitude::Friendly; } } } return ETeamAttitude::Neutral; } void ALyraPlayerBotController::UpdateTeamAttitude(UAIPerceptionComponent* AIPerception) { if (AIPerception) { AIPerception->RequestStimuliListenerUpdate(); } } void ALyraPlayerBotController::OnUnPossess() { // Make sure the pawn that is being unpossessed doesn't remain our ASC's avatar actor if (APawn* PawnBeingUnpossessed = GetPawn()) { if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(PlayerState)) { if (ASC->GetAvatarActor() == PawnBeingUnpossessed) { ASC->SetAvatarActor(nullptr); } } } Super::OnUnPossess(); }