#include "MAICharacterBase.h" #include "MCharacter.h" #include "Components/CapsuleComponent.h" #include "Components/ShapeComponent.h" #include "Components/WidgetComponent.h" #include "MGameplayActor.h" #include "MHealthComponent.h" #include "Misc/OutputDeviceDebug.h" AMAICharacterBase::AMAICharacterBase() { PrimaryActorTick.bCanEverTick = true; HealthComponent = CreateDefaultSubobject(TEXT("HealthComponent")); HealthComponent->OnActorDeath.AddDynamic(this, &AMAICharacterBase::HandleCharacterDeath); static ConstructorHelpers::FClassFinder SafeZoneClassFinder(TEXT("/Game/MechDefence/Blueprints/Buildings/BP_Beacon")); SafeZoneClass = SafeZoneClassFinder.Class; SafeZoneTags.AddTag(FGameplayTag::RequestGameplayTag("PlayerTeam.Beacon")); StatusIndicatorWidget = CreateDefaultSubobject(TEXT("Status Indicator")); StatusIndicatorWidget->SetupAttachment(RootComponent); bIsInSafeZone = true; } void AMAICharacterBase::BeginPlay() { Super::BeginPlay(); UShapeComponent* OverlappingShape = GetSafeZoneOverlappingShape(); if(OverlappingShape) { OverlappingShape->OnComponentBeginOverlap.AddDynamic(this, &AMAICharacterBase::HandleSafeZoneBeginOverlap); OverlappingShape->OnComponentEndOverlap.AddDynamic(this, &AMAICharacterBase::HandleSafeZoneEndOverlap); } // Perform these in the next tick to make sure everything is spawned and assigned before we go further GetWorldTimerManager().SetTimerForNextTick(this, &AMAICharacterBase::CheckSafeZone); GetWorldTimerManager().SetTimerForNextTick(this, &AMAICharacterBase::SetWidgetHealthComponentProperty); } void AMAICharacterBase::SetWidgetHealthComponentProperty() { UUserWidget* IndicatorWidget = StatusIndicatorWidget->GetWidget(); if(IndicatorWidget) { FObjectProperty* ObjProp = FindFProperty(IndicatorWidget->GetClass(), TEXT("HealthComponent")); if(ObjProp) { ObjProp->SetObjectPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0); //ObjProp->SetPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0); } const FString Command = FString::Printf(TEXT("SetupHealthChangeAnims")); FOutputDeviceDebug DebugOutputDevice; if(!IndicatorWidget->CallFunctionByNameWithArguments(*Command, DebugOutputDevice, IndicatorWidget, true)) { UE_LOG(LogTemp, Log, TEXT("Failed to setup health change anims")); } } } UShapeComponent* AMAICharacterBase::GetSafeZoneOverlappingShape() { return GetCapsuleComponent(); } void AMAICharacterBase::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AMAICharacterBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); } void AMAICharacterBase::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const { TagContainer = ActorTags; } void AMAICharacterBase::HandleSafeZoneBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { IGameplayTagAssetInterface* GameplayTagAsset = Cast(OtherActor); if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags) && !bIsInSafeZone) { EnterSafeZone(OtherActor); } } void AMAICharacterBase::HandleSafeZoneEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) { IGameplayTagAssetInterface* GameplayTagAsset = Cast(OtherActor); if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags) && bIsInSafeZone) { // Check again just to make sure CheckSafeZone(); } } void AMAICharacterBase::HandleCharacterDeath(UMHealthComponent* ActorHealthComponent, AActor* DeadActor, AActor* KillerActor) { UShapeComponent* ShapeMesh = GetSafeZoneOverlappingShape(); ShapeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); DeadActor->SetActorEnableCollision(false); } void AMAICharacterBase::AddGameplayTags(const FGameplayTagContainer& TagContainer) { ActorTags.AppendTags(TagContainer); } void AMAICharacterBase::AddGameplayTag(const FGameplayTag& Tag) { ActorTags.AddTag(Tag); } void AMAICharacterBase::LeaveSafeZone() { bIsInSafeZone = false; OnAILeaveSafeZone.Broadcast(this); if(StatusIndicatorWidget) { StatusIndicatorWidget->SetVisibility(false, true); } } void AMAICharacterBase::EnterSafeZone(AActor* SafeZoneActor) { OnAIEnterSafeZone.Broadcast(this, SafeZoneActor); bIsInSafeZone = true; if(StatusIndicatorWidget) { StatusIndicatorWidget->SetVisibility(true, true); } } void AMAICharacterBase::CheckSafeZone() { TArray OverlappingActors; UShapeComponent* SafeZoneOverlappingShape = GetSafeZoneOverlappingShape(); if(SafeZoneOverlappingShape) { SafeZoneOverlappingShape->GetOverlappingActors(OverlappingActors, SafeZoneClass); if(OverlappingActors.Num() == 0) { if(bIsInSafeZone) LeaveSafeZone(); } else if(OverlappingActors.Num() > 0) { IGameplayTagAssetInterface* GameplayTagAsset = Cast(OverlappingActors[0]); if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags)) { if(!bIsInSafeZone) // If we're not in safe zone yet, enter it { EnterSafeZone(OverlappingActors[0]); } else { // If we're already in safe zone, check if we need healing } } } } }