#include "MConstructableBuilding.h" #include "MHealthComponent.h" #include "Components/BoxComponent.h" #include "Components/ShapeComponent.h" #include "../MechDefence.h" #include "Blueprint/UserWidget.h" #include "Components/ArrowComponent.h" #include "Components/WidgetComponent.h" #include "Misc/OutputDeviceDebug.h" AMConstructableBuilding::AMConstructableBuilding() { PrimaryActorTick.bCanEverTick = true; BaseBuildingMesh = CreateDefaultSubobject(TEXT("Base Mesh")); RootComponent = BaseBuildingMesh; BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_ConstructedBuilding, ECR_Block); BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Ignore); BaseBuildingMesh->SetCollisionResponseToChannel(COLLISION_OBJECT_UIEnablingVolume, ECR_Overlap); BaseBuildingMesh->OnComponentBeginOverlap.AddDynamic(this, &AMConstructableBuilding::HandleMeshComponentBeginOverlap); BaseBuildingMesh->OnComponentEndOverlap.AddDynamic(this, &AMConstructableBuilding::HandleMeshComponentEndOverlap); BaseBuildingMesh->SetGenerateOverlapEvents(true); PlacementOverlapChecker = CreateDefaultSubobject(TEXT("PlacementOverlapChecker")); PlacementOverlapChecker->SetupAttachment(RootComponent); PlacementOverlapChecker->SetCollisionResponseToAllChannels(ECR_Overlap); PlacementOverlapChecker->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Ignore); ArrowMesh = CreateDefaultSubobject(TEXT("ArrowMesh")); ArrowMesh->SetupAttachment(RootComponent); ArrowMesh->SetHiddenInGame(false); ArrowMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); InvalidActorsFilter.AddUnique(GetClass()); HealthComponent = CreateDefaultSubobject(TEXT("HealthComp")); StatusIndicatorWidget = CreateDefaultSubobject(TEXT("Health Indicator")); StatusIndicatorWidget->SetupAttachment(RootComponent); StatusIndicatorWidget->SetHiddenInGame(true); BuildingState = EBuildingState::UnderConstruction; } void AMConstructableBuilding::HandleMeshComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) { if(OtherComp->GetCollisionObjectType() == COLLISION_OBJECT_UIEnablingVolume) { StatusIndicatorWidget->SetHiddenInGame(true); } } void AMConstructableBuilding::HandleMeshComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { if(OtherComp->GetCollisionObjectType() == COLLISION_OBJECT_UIEnablingVolume) { StatusIndicatorWidget->SetHiddenInGame(false); } } void AMConstructableBuilding::SetupHealthIndicator() { UUserWidget* IndicatorWidget = StatusIndicatorWidget->GetWidget(); if(IndicatorWidget) { FObjectProperty* ObjProp = FindFProperty(IndicatorWidget->GetClass(), TEXT("HealthComponent")); if(ObjProp) { ObjProp->SetObjectPropertyValue_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")); } } } void AMConstructableBuilding::BeginPlay() { Super::BeginPlay(); DefaultMaterials = BaseBuildingMesh->GetMaterials(); if(bNetStartup) SetBuildingState(EBuildingState::Constructed); GetWorldTimerManager().SetTimerForNextTick(this, &AMConstructableBuilding::SetupHealthIndicator); HealthComponent->Deactivate(); } void AMConstructableBuilding::ConstructionCompleted() { SetBuildingState(EBuildingState::Constructed); } void AMConstructableBuilding::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AMConstructableBuilding::SetBuildingState(TEnumAsByte NewState) { // if(NewState == BuildingState) // return; const EBuildingState PreviousState = BuildingState; BuildingState = NewState; if(NewState != EBuildingState::Constructed) { CurrentBuildingStateMaterial = UMaterialInstanceDynamic::Create(BuildingDynamicStateMaterial, this); //Set instance color param according to the building state FLinearColor ColorValue; switch(NewState) { case EBuildingState::Constructable: ColorValue = ConstructableColor; break; case EBuildingState::UnConstructable: ColorValue = UnConstructableColor; break; case EBuildingState::UnderConstruction: ColorValue = UnderConstructionColor; break; default: ColorValue = FLinearColor(0, 0, 0); break; } CurrentBuildingStateMaterial->SetVectorParameterValue("Color", ColorValue); for(int i = 0; i < DefaultMaterials.Num(); i++) BaseBuildingMesh->SetMaterial(i, CurrentBuildingStateMaterial); } if(BuildingState == EBuildingState::Constructed) { for(int i = 0; i < DefaultMaterials.Num(); i++) BaseBuildingMesh->SetMaterial(i, DefaultMaterials[i]); BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Block); HealthComponent->Activate(); ArrowMesh->SetHiddenInGame(true); } BuildingStateSet(NewState, PreviousState); OnBuildingStateChanged.Broadcast(this, NewState, PreviousState); } void AMConstructableBuilding::StartBuildingConstruction(float TimeToConstruct) { GetWorldTimerManager().SetTimer(TimerHandle_UnderConstructionTimer, this, &AMConstructableBuilding::ConstructionCompleted, TimeToConstruct); } void AMConstructableBuilding::SetHighlighted_Implementation(bool Highlighted) { BaseBuildingMesh->SetRenderCustomDepth(Highlighted); } bool AMConstructableBuilding::IsAtValidLocation_Implementation() { bool PositionIsValid = true; if(BaseBuildingMesh) { TArray OverlappingActors; for(const TSubclassOf FilteredActorClass : InvalidActorsFilter) { PlacementOverlapChecker->GetOverlappingActors(OverlappingActors, FilteredActorClass); if(FilteredActorClass == GetClass()) { // Check overlapping against ourselves for(const AActor* OverlappingActor : OverlappingActors) { if(OverlappingActor != this) { PositionIsValid = false; break; } } } else if(OverlappingActors.Num() != 0) { PositionIsValid = false; } if(!PositionIsValid) break; } // We are not overlapping with any unwanted actors, now to check if we are overlapping against // required actors if(PositionIsValid) { for(const TSubclassOf RequiredActorClass : RequiredOverlappingActorsFilter) { PlacementOverlapChecker->GetOverlappingActors(OverlappingActors, RequiredActorClass); if(OverlappingActors.Num() == 0) { PositionIsValid = false; break; } } } } return PositionIsValid; }