diff --git a/EndlessVendetta/Content/FirstPerson/Blueprints/BP_FirstPersonCharacter.uasset b/EndlessVendetta/Content/FirstPerson/Blueprints/BP_FirstPersonCharacter.uasset index 701e0537..161871e8 100644 --- a/EndlessVendetta/Content/FirstPerson/Blueprints/BP_FirstPersonCharacter.uasset +++ b/EndlessVendetta/Content/FirstPerson/Blueprints/BP_FirstPersonCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bedfc53528df762af9be906a8707ddff7568baf324a5d7df56d6bdfe1575ee3d -size 23020 +oid sha256:97d5406f3cd7dcb656b8f29ebc71700d765f690ece26250ae04a0de9b2e19ce1 +size 22718 diff --git a/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.cpp b/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.cpp index c497cd67..ae8fbd8d 100644 --- a/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.cpp +++ b/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.cpp @@ -11,17 +11,120 @@ ABountyClass::ABountyClass() } -// Called when the game starts or when spawned void ABountyClass::BeginPlay() { Super::BeginPlay(); + + SpawnCheckpoints(); } -// Called every frame void ABountyClass::Tick(float DeltaTime) { Super::Tick(DeltaTime); } +void ABountyClass::SpawnCheckpoints() +{ + // Spawn all checkpoints associated with this bounty and store them in order + FActorSpawnParameters SpawnParameters; + SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + + for(TSubclassOf CheckpointClass : CheckpointsToSpawn) + { + if (CheckpointClass == nullptr) + { + // An unassigned checkpoint class can lead to undefined behaviour, hence the fatal log + UE_LOG(LogTemp, Fatal, TEXT("A checkpoint class wasn't set for %s"), *BountyTitle); + return; + } + + FVector Loc = CheckpointClass.GetDefaultObject()->GetCheckpointSpawnTransform().GetLocation(); + FRotator Rot = CheckpointClass.GetDefaultObject()->GetCheckpointSpawnTransform().GetRotation().Rotator(); + ACheckpointClass* SpawnedCheckpoint = Cast(GetWorld()->SpawnActor(CheckpointClass, Loc, Rot, SpawnParameters)); + BountyCheckpoints.Add(SpawnedCheckpoint); + } + + // Activate the first checkpoint and listen for its completion + BountyCheckpoints[0]->Active = true; + BountyCheckpoints[0]->CompletedCheckpoint.AddDynamic(this, &ABountyClass::IncrementBountyCheckpoint); +} + +void ABountyClass::IncrementBountyCheckpoint() +{ + // Broadcast that the first Checkpoint has been completed so that side bounties can be destroyed + if (!BountyStarted) + { + BountyStarted = true; + CompletedFirstCheckpoint.Broadcast(); + } + + // Bounty Completion Condition + if (BountyCheckpoints.Num() <= MinCPsRequiredForCompletion) + { + Completed = true; + BountyCheckpoints[0]->Active = false; + BountyCheckpoints[0]->Destroy(); + BountyCheckpoints.RemoveAt(0); + UE_LOG(LogTemp, Warning, TEXT(" You've Completed the Bounty!! Well done")); + return; + } + + if (BountyCheckpoints[0] == nullptr) + { + UE_LOG(LogTemp, Fatal, TEXT("Missing checkpoint in bounty checkpoints, could've failed to spawn or cast")); + return; + } + + // Destroy Actor and Shrink Array + BountyCheckpoints[0]->Active = false; + BountyCheckpoints[0]->Destroy(); + BountyCheckpoints.RemoveAt(0); + + // Set the new checkpoint in pos 0 to be active and listen for it's completion + BountyCheckpoints[0]->Active = true; + BountyCheckpoints[0]->CompletedCheckpoint.AddDynamic(this, &ABountyClass::IncrementBountyCheckpoint); + +} + +void ABountyClass::CollectRewards_Implementation() +{ + UE_LOG(LogTemp, Warning, TEXT("The player has gained $%d for completing the bounty!"), RewardMoney); +} + +void ABountyClass::UpdateBountyCheckpoints(TMap> ReplacementCheckpoints) +{ + if (ReplacementCheckpoints.IsEmpty()) + { + UE_LOG(LogTemp, Warning, TEXT("No Replacement Steps found")); + return; + } + + FActorSpawnParameters SpawnParameters; + SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + + for (auto ReplacementCheckpoint : ReplacementCheckpoints) + { + BountyCheckpoints[ReplacementCheckpoint.Key]->Destroy(); + ReplacementCheckpointClass = ReplacementCheckpoint.Value; + + FVector Loc = ReplacementCheckpointClass.GetDefaultObject()->GetCheckpointSpawnTransform().GetLocation(); + FRotator Rot = ReplacementCheckpointClass.GetDefaultObject()->GetCheckpointSpawnTransform().GetRotation().Rotator(); + ACheckpointClass* SpawnedCheckpoint = Cast(GetWorld()->SpawnActor(ReplacementCheckpoint.Value, Loc, Rot, SpawnParameters)); + + if (SpawnedCheckpoint == nullptr) + { + UE_LOG(LogTemp, Fatal, TEXT("The new checkpoint hasn't spawned in properly or can't be cast to")); + return; + } + + BountyCheckpoints[ReplacementCheckpoint.Key] = SpawnedCheckpoint; + if (ReplacementCheckpoint.Key == 0) + { + BountyCheckpoints[ReplacementCheckpoint.Key]->Active = true; + BountyCheckpoints[ReplacementCheckpoint.Key]->CompletedCheckpoint.AddDynamic(this, &ABountyClass::IncrementBountyCheckpoint); + } + } +} + diff --git a/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.h b/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.h index d56e3d21..9dfb87e1 100644 --- a/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.h +++ b/EndlessVendetta/Source/EndlessVendetta/BountySystem/BountyClass.h @@ -3,24 +3,140 @@ #pragma once #include "CoreMinimal.h" +#include "CheckpointClass.h" #include "GameFramework/Actor.h" #include "BountyClass.generated.h" +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FCompletedFirstCheckpoint) + UCLASS() class ENDLESSVENDETTA_API ABountyClass : public AActor { GENERATED_BODY() + + // Used to temp store value of incoming replacement checkpoint class + TSubclassOf ReplacementCheckpointClass; -public: - // Sets default values for this actor's properties - ABountyClass(); + bool BountyStarted = false; protected: + // ------- Properties Set in Editor -------------- + UPROPERTY(EditDefaultsOnly, Category = "Bounty") + TArray> CheckpointsToSpawn; + + UPROPERTY(EditDefaultsOnly, Category = "Bounty") + int RewardMoney = 0; + + UPROPERTY(EditDefaultsOnly, Category = "Bounty") + FString BountyTitle; + + UPROPERTY(EditDefaultsOnly, Category = "Bounty") + FString BountyDesc; + // ----------------------------------------------- + + bool Completed = false; + + /** + * Default is 2 as final step in most Bounties is to return to ship, + * which can't be completed. Change to 1 if that's not the case. + */ + int MinCPsRequiredForCompletion = 2; + + // Spawned in Checkpoints for this Bounty + TArray BountyCheckpoints; + + // Spawns and stores this Bounties Checkpoints in order + void SpawnCheckpoints(); + // Called when the game starts or when spawned virtual void BeginPlay() override; -public: - // Called every frame - virtual void Tick(float DeltaTime) override; + +public: + // Broadcast when first checkpoint from this bounty is completed + FCompletedFirstCheckpoint CompletedFirstCheckpoint; + // ------ Getters for Bounty and Checkpoint Properties ------ + bool IsCompleted() + { + return Completed; + } + + FString GetBountyTitle() + { + return BountyTitle; + } + + FString GetBountyDesc() + { + return BountyDesc; + } + + FVector GetCheckpointLocation() + { + if (BountyCheckpoints.IsEmpty() || BountyCheckpoints[0] == nullptr) + { + return FVector(0, 0, 0); + } + + return BountyCheckpoints[0]->GetCheckpointSpawnTransform().GetLocation(); + } + + FVector GetActiveWaypointLocation() + { + if (BountyCheckpoints.IsEmpty() || BountyCheckpoints[0] == nullptr) + { + return FVector(0, 0, 0); + } + + return BountyCheckpoints[0]->GetWaypointLoc(); + } + + UTexture2D* GetActiveWaypointIcon() + { + if (BountyCheckpoints.IsEmpty() || BountyCheckpoints[0] == nullptr) + { + return nullptr; + } + + return BountyCheckpoints[0]->GetWaypointIcon(); + } + + FString GetCheckpointDescription() + { + if (BountyCheckpoints.IsEmpty() || BountyCheckpoints[0] == nullptr) + { + return FString("No more Bounty checkpoints, check BountyClass.h GetCheckpointDescription()"); + } + + return BountyCheckpoints[0]->GetCheckpointDesc(); + } + // ---------------------------------------------------------- + + // Sets default values for this actor's properties + ABountyClass(); + + // Called every frame + virtual void Tick(float DeltaTime) override; + + // Called when a checkpoint is completed, handles moving onto next checkpoint and discarding the old one + UFUNCTION() + virtual void IncrementBountyCheckpoint(); + + // Collect Money in C++, any other special reward will be implemented in BP if neccessary + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void CollectRewards(); + + void DestroyReturnToShipStep() + { + if (BountyCheckpoints.Num() > 1) + { + UE_LOG(LogTemp, Warning, TEXT("Didn't Destroy final step as more than one steps still left in Bounty")); + } + + BountyCheckpoints[0]->Destroy(); + } + + // Called by Bounty Director, replaces specified checkpoints to alter Bounty when player completes a side bounty + void UpdateBountyCheckpoints(TMap> ReplacementCheckpoints); };